From 0c42f91f4ccf98a37f055afb777ed491da56335e Mon Sep 17 00:00:00 2001 From: Zach White Date: Sun, 25 Oct 2020 14:48:44 -0700 Subject: 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 * 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 * more uniform date handling * make flake8 happy * Update lib/python/qmk/decorators.py Co-authored-by: Erovia Co-authored-by: Joel Challis Co-authored-by: Erovia --- lib/python/qmk/keymap.py | 219 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 153 insertions(+), 66 deletions(-) (limited to 'lib/python/qmk/keymap.py') diff --git a/lib/python/qmk/keymap.py b/lib/python/qmk/keymap.py index 166697ee6a..31c61ae6a8 100644 --- a/lib/python/qmk/keymap.py +++ b/lib/python/qmk/keymap.py @@ -29,33 +29,37 @@ __KEYMAP_GOES_HERE__ """ -def template(keyboard, type='c'): - """Returns the `keymap.c` or `keymap.json` template for a keyboard. +def template_json(keyboard): + """Returns a `keymap.json` template for a keyboard. - If a template exists in `keyboards//templates/keymap.c` that - text will be used instead of `DEFAULT_KEYMAP_C`. - - If a template exists in `keyboards//templates/keymap.json` that - text will be used instead of an empty dictionary. + If a template exists in `keyboards//templates/keymap.json` that text will be used instead of an empty dictionary. Args: keyboard The keyboard to return a template for. + """ + template_file = Path('keyboards/%s/templates/keymap.json' % keyboard) + template = {'keyboard': keyboard} + if template_file.exists(): + template.update(json.loads(template_file.read_text())) + + return template + - type - 'json' for `keymap.json` and 'c' (or anything else) for `keymap.c` +def template_c(keyboard): + """Returns a `keymap.c` template for a keyboard. + + If a template exists in `keyboards//templates/keymap.c` that text will be used instead of an empty dictionary. + + Args: + keyboard + The keyboard to return a template for. """ - if type == 'json': - template_file = Path('keyboards/%s/templates/keymap.json' % keyboard) - template = {'keyboard': keyboard} - if template_file.exists(): - template.update(json.loads(template_file.read_text())) + template_file = Path('keyboards/%s/templates/keymap.c' % keyboard) + if template_file.exists(): + template = template_file.read_text() else: - template_file = Path('keyboards/%s/templates/keymap.c' % keyboard) - if template_file.exists(): - template = template_file.read_text() - else: - template = DEFAULT_KEYMAP_C + template = DEFAULT_KEYMAP_C return template @@ -69,15 +73,65 @@ def _strip_any(keycode): return keycode -def is_keymap_dir(keymap): +def is_keymap_dir(keymap, c=True, json=True, additional_files=None): """Return True if Path object `keymap` has a keymap file inside. + + Args: + keymap + A Path() object for the keymap directory you want to check. + + c + When true include `keymap.c` keymaps. + + json + When true include `keymap.json` keymaps. + + additional_files + 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'])` """ - for file in ('keymap.c', 'keymap.json'): + files = [] + + if c: + files.append('keymap.c') + + if json: + files.append('keymap.json') + + for file in files: if (keymap / file).is_file(): + if additional_files: + for file in additional_files: + if not (keymap / file).is_file(): + return False + return True -def generate(keyboard, layout, layers, type='c', keymap=None): +def generate_json(keymap, keyboard, layout, layers): + """Returns a `keymap.json` for the specified keyboard, layout, and layers. + + Args: + keymap + A name for this keymap. + + keyboard + The name of the keyboard. + + layout + The LAYOUT macro this keymap uses. + + layers + An array of arrays describing the keymap. Each item in the inner array should be a string that is a valid QMK keycode. + """ + new_keymap = template_json(keyboard) + new_keymap['keymap'] = keymap + new_keymap['layout'] = layout + new_keymap['layers'] = layers + + return new_keymap + + +def generate_c(keyboard, layout, layers): """Returns a `keymap.c` or `keymap.json` for the specified keyboard, layout, and layers. Args: @@ -89,33 +143,33 @@ def generate(keyboard, layout, layers, type='c', keymap=None): layers An array of arrays describing the keymap. Each item in the inner array should be a string that is a valid QMK keycode. - - type - 'json' for `keymap.json` and 'c' (or anything else) for `keymap.c` """ - new_keymap = template(keyboard, type) - if type == 'json': - new_keymap['keymap'] = keymap - new_keymap['layout'] = layout - new_keymap['layers'] = layers - else: - layer_txt = [] - for layer_num, layer in enumerate(layers): - if layer_num != 0: - layer_txt[-1] = layer_txt[-1] + ',' + new_keymap = template_c(keyboard) + layer_txt = [] + for layer_num, layer in enumerate(layers): + if layer_num != 0: + layer_txt[-1] = layer_txt[-1] + ',' + layer = map(_strip_any, layer) + layer_keys = ', '.join(layer) + layer_txt.append('\t[%s] = %s(%s)' % (layer_num, layout, layer_keys)) + + keymap = '\n'.join(layer_txt) + new_keymap = new_keymap.replace('__KEYMAP_GOES_HERE__', keymap) - layer = map(_strip_any, layer) - layer_keys = ', '.join(layer) - layer_txt.append('\t[%s] = %s(%s)' % (layer_num, layout, layer_keys)) + return new_keymap - keymap = '\n'.join(layer_txt) - new_keymap = new_keymap.replace('__KEYMAP_GOES_HERE__', keymap) - return new_keymap +def write_file(keymap_filename, keymap_content): + keymap_filename.parent.mkdir(parents=True, exist_ok=True) + keymap_filename.write_text(keymap_content) + cli.log.info('Wrote keymap to {fg_cyan}%s', keymap_filename) + + return keymap_filename -def write(keyboard, keymap, layout, layers, type='c'): - """Generate the `keymap.c` and write it to disk. + +def write_json(keyboard, keymap, layout, layers): + """Generate the `keymap.json` and write it to disk. Returns the filename written to. @@ -131,23 +185,36 @@ def write(keyboard, keymap, layout, layers, type='c'): layers An array of arrays describing the keymap. Each item in the inner array should be a string that is a valid QMK keycode. - - type - 'json' for `keymap.json` and 'c' (or anything else) for `keymap.c` """ - keymap_content = generate(keyboard, layout, layers, type) - if type == 'json': - keymap_file = qmk.path.keymap(keyboard) / keymap / 'keymap.json' - keymap_content = json.dumps(keymap_content) - else: - keymap_file = qmk.path.keymap(keyboard) / keymap / 'keymap.c' + keymap_json = generate_json(keyboard, keymap, layout, layers) + keymap_content = json.dumps(keymap_json) + keymap_file = qmk.path.keymap(keyboard) / keymap / 'keymap.json' + + return write_file(keymap_file, keymap_content) + + +def write(keyboard, keymap, layout, layers): + """Generate the `keymap.c` and write it to disk. + + Returns the filename written to. + + Args: + keyboard + The name of the keyboard - keymap_file.parent.mkdir(parents=True, exist_ok=True) - keymap_file.write_text(keymap_content) + keymap + The name of the keymap - cli.log.info('Wrote keymap to {fg_cyan}%s', keymap_file) + layout + The LAYOUT macro this keymap uses. + + layers + An array of arrays describing the keymap. Each item in the inner array should be a string that is a valid QMK keycode. + """ + keymap_content = generate_c(keyboard, layout, layers) + keymap_file = qmk.path.keymap(keyboard) / keymap / 'keymap.c' - return keymap_file + return write_file(keymap_file, keymap_content) def locate_keymap(keyboard, keymap): @@ -189,38 +256,58 @@ def locate_keymap(keyboard, keymap): return community_layout / 'keymap.c' -def list_keymaps(keyboard): - """ List the available keymaps for a keyboard. +def list_keymaps(keyboard, c=True, json=True, additional_files=None, fullpath=False): + """List the available keymaps for a keyboard. Args: - keyboard: the keyboards full name with vendor and revision if necessary, example: clueboard/66/rev3 + keyboard + The keyboards full name with vendor and revision if necessary, example: clueboard/66/rev3 + + c + When true include `keymap.c` keymaps. + + json + When true include `keymap.json` keymaps. + + additional_files + 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'])` + + fullpath + When set to True the full path of the keymap relative to the `qmk_firmware` root will be provided. Returns: - a set with the names of the available keymaps + a sorted list of valid keymap names. """ # parse all the rules.mk files for the keyboard rules = rules_mk(keyboard) names = set() if rules: - # qmk_firmware/keyboards keyboards_dir = Path('keyboards') - # path to the keyboard's directory kb_path = keyboards_dir / keyboard + # walk up the directory tree until keyboards_dir # and collect all directories' name with keymap.c file in it while kb_path != keyboards_dir: keymaps_dir = kb_path / "keymaps" - if keymaps_dir.exists(): - names = names.union([keymap.name for keymap in keymaps_dir.iterdir() if is_keymap_dir(keymap)]) + + if keymaps_dir.is_dir(): + for keymap in keymaps_dir.iterdir(): + if is_keymap_dir(keymap, c, json, additional_files): + keymap = keymap if fullpath else keymap.name + names.add(keymap) + kb_path = kb_path.parent # if community layouts are supported, get them if "LAYOUTS" in rules: for layout in rules["LAYOUTS"].split(): cl_path = Path('layouts/community') / layout - if cl_path.exists(): - names = names.union([keymap.name for keymap in cl_path.iterdir() if is_keymap_dir(keymap)]) + if cl_path.is_dir(): + for keymap in cl_path.iterdir(): + if is_keymap_dir(keymap, c, json, additional_files): + keymap = keymap if fullpath else keymap.name + names.add(keymap) return sorted(names) -- cgit v1.2.3