aboutsummaryrefslogtreecommitdiff
path: root/lib/python/qmk/keymap.py
diff options
context:
space:
mode:
authorZach White <skullydazed@gmail.com>2020-10-25 14:48:44 -0700
committerGitHub <noreply@github.com>2020-10-25 14:48:44 -0700
commit0c42f91f4ccf98a37f055afb777ed491da56335e (patch)
tree547344d80fe7bf75ff3f348eefbc19dbdd346a8a /lib/python/qmk/keymap.py
parent8ef82c466e73e555fd74107d4c57e678d7152ecc (diff)
downloadqmk_firmware-0c42f91f4ccf98a37f055afb777ed491da56335e.tar.gz
qmk_firmware-0c42f91f4ccf98a37f055afb777ed491da56335e.zip
Generate api data on each push (#10609)
* add new qmk generate-api command, to generate a complete set of API data. * Generate api data and push it to the keyboard repo * fix typo * Apply suggestions from code review Co-authored-by: Joel Challis <git@zvecr.com> * fixup api workflow * remove file-changes-action * use a more mainstream github action * fix yaml error * Apply suggestions from code review Co-authored-by: Erovia <Erovia@users.noreply.github.com> * more uniform date handling * make flake8 happy * Update lib/python/qmk/decorators.py Co-authored-by: Erovia <Erovia@users.noreply.github.com> Co-authored-by: Joel Challis <git@zvecr.com> Co-authored-by: Erovia <Erovia@users.noreply.github.com>
Diffstat (limited to 'lib/python/qmk/keymap.py')
-rw-r--r--lib/python/qmk/keymap.py219
1 files changed, 153 insertions, 66 deletions
diff --git a/lib/python/qmk/keymap.py b/lib/python/qmk/keymap.py
index 166697ee6..31c61ae6a 100644
--- a/lib/python/qmk/keymap.py
+++ b/lib/python/qmk/keymap.py
@@ -29,33 +29,37 @@ __KEYMAP_GOES_HERE__
29""" 29"""
30 30
31 31
32def template(keyboard, type='c'): 32def template_json(keyboard):
33 """Returns the `keymap.c` or `keymap.json` template for a keyboard. 33 """Returns a `keymap.json` template for a keyboard.
34 34
35 If a template exists in `keyboards/<keyboard>/templates/keymap.c` that 35 If a template exists in `keyboards/<keyboard>/templates/keymap.json` that text will be used instead of an empty dictionary.
36 text will be used instead of `DEFAULT_KEYMAP_C`.
37
38 If a template exists in `keyboards/<keyboard>/templates/keymap.json` that
39 text will be used instead of an empty dictionary.
40 36
41 Args: 37 Args:
42 keyboard 38 keyboard
43 The keyboard to return a template for. 39 The keyboard to return a template for.
40 """
41 template_file = Path('keyboards/%s/templates/keymap.json' % keyboard)
42 template = {'keyboard': keyboard}
43 if template_file.exists():
44 template.update(json.loads(template_file.read_text()))
45
46 return template
47
44 48
45 type 49def template_c(keyboard):
46 'json' for `keymap.json` and 'c' (or anything else) for `keymap.c` 50 """Returns a `keymap.c` template for a keyboard.
51
52 If a template exists in `keyboards/<keyboard>/templates/keymap.c` that text will be used instead of an empty dictionary.
53
54 Args:
55 keyboard
56 The keyboard to return a template for.
47 """ 57 """
48 if type == 'json': 58 template_file = Path('keyboards/%s/templates/keymap.c' % keyboard)
49 template_file = Path('keyboards/%s/templates/keymap.json' % keyboard) 59 if template_file.exists():
50 template = {'keyboard': keyboard} 60 template = template_file.read_text()
51 if template_file.exists():
52 template.update(json.loads(template_file.read_text()))
53 else: 61 else:
54 template_file = Path('keyboards/%s/templates/keymap.c' % keyboard) 62 template = DEFAULT_KEYMAP_C
55 if template_file.exists():
56 template = template_file.read_text()
57 else:
58 template = DEFAULT_KEYMAP_C
59 63
60 return template 64 return template
61 65
@@ -69,15 +73,65 @@ def _strip_any(keycode):
69 return keycode 73 return keycode
70 74
71 75
72def is_keymap_dir(keymap): 76def is_keymap_dir(keymap, c=True, json=True, additional_files=None):
73 """Return True if Path object `keymap` has a keymap file inside. 77 """Return True if Path object `keymap` has a keymap file inside.
78
79 Args:
80 keymap
81 A Path() object for the keymap directory you want to check.
82
83 c
84 When true include `keymap.c` keymaps.
85
86 json
87 When true include `keymap.json` keymaps.
88
89 additional_files
90 A sequence of additional filenames to check against to determine if a directory is a keymap. All files must exist for a match to happen. For example, if you want to match a C keymap with both a `config.h` and `rules.mk` file: `is_keymap_dir(keymap_dir, json=False, additional_files=['config.h', 'rules.mk'])`
74 """ 91 """
75 for file in ('keymap.c', 'keymap.json'): 92 files = []
93
94 if c:
95 files.append('keymap.c')
96
97 if json:
98 files.append('keymap.json')
99
100 for file in files:
76 if (keymap / file).is_file(): 101 if (keymap / file).is_file():
102 if additional_files:
103 for file in additional_files:
104 if not (keymap / file).is_file():
105 return False
106
77 return True 107 return True
78 108
79 109
80def generate(keyboard, layout, layers, type='c', keymap=None): 110def generate_json(keymap, keyboard, layout, layers):
111 """Returns a `keymap.json` for the specified keyboard, layout, and layers.
112
113 Args:
114 keymap
115 A name for this keymap.
116
117 keyboard
118 The name of the keyboard.
119
120 layout
121 The LAYOUT macro this keymap uses.
122
123 layers
124 An array of arrays describing the keymap. Each item in the inner array should be a string that is a valid QMK keycode.
125 """
126 new_keymap = template_json(keyboard)
127 new_keymap['keymap'] = keymap
128 new_keymap['layout'] = layout
129 new_keymap['layers'] = layers
130
131 return new_keymap
132
133
134def generate_c(keyboard, layout, layers):
81 """Returns a `keymap.c` or `keymap.json` for the specified keyboard, layout, and layers. 135 """Returns a `keymap.c` or `keymap.json` for the specified keyboard, layout, and layers.
82 136
83 Args: 137 Args:
@@ -89,33 +143,33 @@ def generate(keyboard, layout, layers, type='c', keymap=None):
89 143
90 layers 144 layers
91 An array of arrays describing the keymap. Each item in the inner array should be a string that is a valid QMK keycode. 145 An array of arrays describing the keymap. Each item in the inner array should be a string that is a valid QMK keycode.
92
93 type
94 'json' for `keymap.json` and 'c' (or anything else) for `keymap.c`
95 """ 146 """
96 new_keymap = template(keyboard, type) 147 new_keymap = template_c(keyboard)
97 if type == 'json': 148 layer_txt = []
98 new_keymap['keymap'] = keymap 149 for layer_num, layer in enumerate(layers):
99 new_keymap['layout'] = layout 150 if layer_num != 0:
100 new_keymap['layers'] = layers 151 layer_txt[-1] = layer_txt[-1] + ','
101 else: 152 layer = map(_strip_any, layer)
102 layer_txt = [] 153 layer_keys = ', '.join(layer)
103 for layer_num, layer in enumerate(layers): 154 layer_txt.append('\t[%s] = %s(%s)' % (layer_num, layout, layer_keys))
104 if layer_num != 0: 155
105 layer_txt[-1] = layer_txt[-1] + ',' 156 keymap = '\n'.join(layer_txt)
157 new_keymap = new_keymap.replace('__KEYMAP_GOES_HERE__', keymap)
106 158
107 layer = map(_strip_any, layer) 159 return new_keymap
108 layer_keys = ', '.join(layer)
109 layer_txt.append('\t[%s] = %s(%s)' % (layer_num, layout, layer_keys))
110 160
111 keymap = '\n'.join(layer_txt)
112 new_keymap = new_keymap.replace('__KEYMAP_GOES_HERE__', keymap)
113 161
114 return new_keymap 162def write_file(keymap_filename, keymap_content):
163 keymap_filename.parent.mkdir(parents=True, exist_ok=True)
164 keymap_filename.write_text(keymap_content)
115 165
166 cli.log.info('Wrote keymap to {fg_cyan}%s', keymap_filename)
167
168 return keymap_filename
116 169
117def write(keyboard, keymap, layout, layers, type='c'): 170
118 """Generate the `keymap.c` and write it to disk. 171def write_json(keyboard, keymap, layout, layers):
172 """Generate the `keymap.json` and write it to disk.
119 173
120 Returns the filename written to. 174 Returns the filename written to.
121 175
@@ -131,23 +185,36 @@ def write(keyboard, keymap, layout, layers, type='c'):
131 185
132 layers 186 layers
133 An array of arrays describing the keymap. Each item in the inner array should be a string that is a valid QMK keycode. 187 An array of arrays describing the keymap. Each item in the inner array should be a string that is a valid QMK keycode.
134
135 type
136 'json' for `keymap.json` and 'c' (or anything else) for `keymap.c`
137 """ 188 """
138 keymap_content = generate(keyboard, layout, layers, type) 189 keymap_json = generate_json(keyboard, keymap, layout, layers)
139 if type == 'json': 190 keymap_content = json.dumps(keymap_json)
140 keymap_file = qmk.path.keymap(keyboard) / keymap / 'keymap.json' 191 keymap_file = qmk.path.keymap(keyboard) / keymap / 'keymap.json'
141 keymap_content = json.dumps(keymap_content) 192
142 else: 193 return write_file(keymap_file, keymap_content)
143 keymap_file = qmk.path.keymap(keyboard) / keymap / 'keymap.c' 194
195
196def write(keyboard, keymap, layout, layers):
197 """Generate the `keymap.c` and write it to disk.
198
199 Returns the filename written to.
200
201 Args:
202 keyboard
203 The name of the keyboard
144 204
145 keymap_file.parent.mkdir(parents=True, exist_ok=True) 205 keymap
146 keymap_file.write_text(keymap_content) 206 The name of the keymap
147 207
148 cli.log.info('Wrote keymap to {fg_cyan}%s', keymap_file) 208 layout
209 The LAYOUT macro this keymap uses.
210
211 layers
212 An array of arrays describing the keymap. Each item in the inner array should be a string that is a valid QMK keycode.
213 """
214 keymap_content = generate_c(keyboard, layout, layers)
215 keymap_file = qmk.path.keymap(keyboard) / keymap / 'keymap.c'
149 216
150 return keymap_file 217 return write_file(keymap_file, keymap_content)
151 218
152 219
153def locate_keymap(keyboard, keymap): 220def locate_keymap(keyboard, keymap):
@@ -189,38 +256,58 @@ def locate_keymap(keyboard, keymap):
189 return community_layout / 'keymap.c' 256 return community_layout / 'keymap.c'
190 257
191 258
192def list_keymaps(keyboard): 259def list_keymaps(keyboard, c=True, json=True, additional_files=None, fullpath=False):
193 """ List the available keymaps for a keyboard. 260 """List the available keymaps for a keyboard.
194 261
195 Args: 262 Args:
196 keyboard: the keyboards full name with vendor and revision if necessary, example: clueboard/66/rev3 263 keyboard
264 The keyboards full name with vendor and revision if necessary, example: clueboard/66/rev3
265
266 c
267 When true include `keymap.c` keymaps.
268
269 json
270 When true include `keymap.json` keymaps.
271
272 additional_files
273 A sequence of additional filenames to check against to determine if a directory is a keymap. All files must exist for a match to happen. For example, if you want to match a C keymap with both a `config.h` and `rules.mk` file: `is_keymap_dir(keymap_dir, json=False, additional_files=['config.h', 'rules.mk'])`
274
275 fullpath
276 When set to True the full path of the keymap relative to the `qmk_firmware` root will be provided.
197 277
198 Returns: 278 Returns:
199 a set with the names of the available keymaps 279 a sorted list of valid keymap names.
200 """ 280 """
201 # parse all the rules.mk files for the keyboard 281 # parse all the rules.mk files for the keyboard
202 rules = rules_mk(keyboard) 282 rules = rules_mk(keyboard)
203 names = set() 283 names = set()
204 284
205 if rules: 285 if rules:
206 # qmk_firmware/keyboards
207 keyboards_dir = Path('keyboards') 286 keyboards_dir = Path('keyboards')
208 # path to the keyboard's directory
209 kb_path = keyboards_dir / keyboard 287 kb_path = keyboards_dir / keyboard
288
210 # walk up the directory tree until keyboards_dir 289 # walk up the directory tree until keyboards_dir
211 # and collect all directories' name with keymap.c file in it 290 # and collect all directories' name with keymap.c file in it
212 while kb_path != keyboards_dir: 291 while kb_path != keyboards_dir:
213 keymaps_dir = kb_path / "keymaps" 292 keymaps_dir = kb_path / "keymaps"
214 if keymaps_dir.exists(): 293
215 names = names.union([keymap.name for keymap in keymaps_dir.iterdir() if is_keymap_dir(keymap)]) 294 if keymaps_dir.is_dir():
295 for keymap in keymaps_dir.iterdir():
296 if is_keymap_dir(keymap, c, json, additional_files):
297 keymap = keymap if fullpath else keymap.name
298 names.add(keymap)
299
216 kb_path = kb_path.parent 300 kb_path = kb_path.parent
217 301
218 # if community layouts are supported, get them 302 # if community layouts are supported, get them
219 if "LAYOUTS" in rules: 303 if "LAYOUTS" in rules:
220 for layout in rules["LAYOUTS"].split(): 304 for layout in rules["LAYOUTS"].split():
221 cl_path = Path('layouts/community') / layout 305 cl_path = Path('layouts/community') / layout
222 if cl_path.exists(): 306 if cl_path.is_dir():
223 names = names.union([keymap.name for keymap in cl_path.iterdir() if is_keymap_dir(keymap)]) 307 for keymap in cl_path.iterdir():
308 if is_keymap_dir(keymap, c, json, additional_files):
309 keymap = keymap if fullpath else keymap.name
310 names.add(keymap)
224 311
225 return sorted(names) 312 return sorted(names)
226 313