@@ -40,37 +40,50 @@ def unexpected_keys(keys, symbols, msg):
4040 if len (unexpected ) > 0 :
4141 warn ("%s, unexpected: %s" % (msg , key_list_str (unexpected )))
4242
43- # Write to [keys] and [dup].
44- def parse_row_from_et (row , keys , dup ):
45- for key in row :
46- for attr in key .keys ():
43+ def duplicates (keys ):
44+ dup = [ k for k , key_elts in keys .items () if len (key_elts ) >= 2 ]
45+ if len (dup ) > 0 :
46+ warn ("Duplicate keys: " + key_list_str (dup ))
47+
48+ def should_have_role (keys_map , role , keys ):
49+ def is_center_key (key ):
50+ def center (key_elt ): return key_elt .get ("key0" , key_elt .get ("c" ))
51+ return any (( center (key_elt ) == key for key_elt in keys_map .get (key , []) ))
52+ def key_roles (key ):
53+ return ( key_elt .get ("role" , "normal" ) for key_elt in keys_map [key ] )
54+ for key in keys :
55+ if is_center_key (key ) and role not in key_roles (key ):
56+ warn ("Key '%s' is not on a key with role=\" %s\" " % (key , role ))
57+
58+ # Write to [keys], dict of keyvalue to the key elements they appear in
59+ def parse_row_from_et (row , keys ):
60+ for key_elt in row :
61+ for attr in key_elt .keys ():
4762 if attr in KEY_ATTRIBUTES :
48- k = key .get (attr ).removeprefix ("\\ " )
49- if k in keys : dup .add (k )
50- keys .add (k )
63+ k = key_elt .get (attr ).removeprefix ("\\ " )
64+ keys .setdefault (k , []).append (key_elt )
5165
5266def parse_layout (fname ):
53- keys = set ()
54- dup = set ()
67+ keys = {}
5568 root = ET .parse (fname ).getroot ()
5669 if root .tag != "keyboard" :
5770 return None
5871 for row in root :
59- parse_row_from_et (row , keys , dup )
60- return root , keys , dup
72+ parse_row_from_et (row , keys )
73+ return root , keys
6174
6275def parse_row (fname ):
63- keys = set ()
64- dup = set ()
76+ keys = {}
6577 root = ET .parse (fname ).getroot ()
6678 if root .tag != "row" :
6779 return None
68- parse_row_from_et (root , keys , dup )
69- return root , keys , dup
80+ parse_row_from_et (root , keys )
81+ return root , keys
7082
7183def check_layout (layout ):
72- root , keys , dup = layout
73- if len (dup ) > 0 : warn ("Duplicate keys: " + key_list_str (dup ))
84+ root , keys_map = layout
85+ keys = set (keys_map .keys ())
86+ duplicates (keys_map )
7487 missing_some_of (keys , "~!@#$%^&*(){}`[]=\\ -_;:/.,?<>'\" +|" , "ASCII punctuation" )
7588 missing_some_of (keys , "0123456789" , "digits" )
7689 missing_required (keys , ["backspace" , "delete" ], "Layout doesn't define some important keys" )
@@ -91,8 +104,8 @@ def check_layout(layout):
91104 missing_required (keys , ["shift" , "loc capslock" ], "Missing important key" )
92105 missing_required (keys , ["loc esc" , "loc tab" ], "Missing programming keys" )
93106
94- _ , bottom_row_keys , _ = parse_row ("res/xml/bottom_row.xml" )
95-
107+ _ , bottom_row_keys_map = parse_row ("res/xml/bottom_row.xml" )
108+ bottom_row_keys = set ( bottom_row_keys_map . keys ())
96109 if root .get ("bottom_row" ) == "false" :
97110 missing_required (keys , bottom_row_keys ,
98111 "Layout redefines the bottom row but some important keys are missing" )
@@ -103,6 +116,10 @@ def check_layout(layout):
103116 if root .get ("script" ) == None :
104117 warn ("Layout doesn't specify a script." )
105118
119+ should_have_role (keys_map , "action" ,
120+ [ "shift" , "ctrl" , "fn" , "backspace" , "enter" ])
121+ should_have_role (keys_map , "space_bar" , [ "space" ])
122+
106123for fname in sorted (glob .glob ("srcs/layouts/*.xml" )):
107124 layout_id , _ = os .path .splitext (os .path .basename (fname ))
108125 if layout_id in KNOWN_NOT_LAYOUT :
0 commit comments