CLI: add support for list_keymaps

List all the available keymaps for a given keyboard

Add bs4 to requirements.txt

UnicodeDammit is needed from bs4 for reading files.

Major update to work better with revisions

Find the community keymaps supported by each revision.

Get all buildable keymaps for each revision

The command now return all keymaps that's buildable for a
keyboard/revision. If the base directory of a keyboard does not contain
a 'rules.mk' file, nothing is returned. If the base directory contains a
'keymaps' directory, those keycaps will be returned for every revision.

Fix help message.

Try to figure out revision, drop -rv/--revision argument

Fix output format

Another major refactoring, add documentation

Move all useful functions to the qmk module and use the cli subcommand
as a wrapper around it.
Add both inline comments and documentation.

Add test for list_keymaps

Fix regex for parsing rules.mk files

I don't know why it couldn't put it together before... ¯\_(ツ)_/¯

Drop bs4 dependency, update docs, minor improvements

Return only the unique keymaps

Fix merging community and base keymaps

Major rework, no regex/globbing, more walking

Instead of using regexes and globbing to find the rules.mk and keymap.c
files, walk the directory tree to find them.
Also, do away with the concept of revision.

Fix commandline parsing and flake8 findings, rebase

Fixed commandline and config parsing. Thx @xplusplus.
Rebased on master and fixed merge conflicts.

Code cleanup, use pathlib, use pytest keyboard

Clean up checks and logics that are unnecessary due to MILC updates.
Use pathlib instead of os.path for readability.
Use the 'pytest' keyboard for the tests.
Add community layout for 'handwired/onekey/pytest' so we can test
community layouts.

Pathlib-ify qmk.keymap.list_keymaps()

fix list_keymaps for python 3.5
This commit is contained in:
Erovia
2019-10-08 21:50:21 +02:00
committed by Drashna Jael're
parent 2b0c1a7661
commit 4c3335b00b
7 changed files with 169 additions and 0 deletions

View File

@@ -0,0 +1 @@
[""]

View File

@@ -0,0 +1,12 @@
#include QMK_KEYBOARD_H
/* This keyboard/layout is used to test community layout discovery/compilation. */
#define _DEFAULT 0
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[_DEFAULT] = LAYOUT (
KC_B
),
};

View File

@@ -1 +1,2 @@
from . import keyboards
from . import keymaps

View File

@@ -0,0 +1,22 @@
"""List the keymaps for a specific keyboard
"""
from milc import cli
import qmk.keymap
from qmk.errors import NoSuchKeyboardError
@cli.argument("-kb", "--keyboard", help="Specify keyboard name. Example: 1upkeyboards/1up60hse")
@cli.subcommand("List the keymaps for a specific keyboard")
def list_keymaps(cli):
"""List the keymaps for a specific keyboard
"""
try:
for name in qmk.keymap.list_keymaps(cli.config.list_keymaps.keyboard):
# We echo instead of cli.log.info to allow easier piping of this output
cli.echo('%s', name)
except NoSuchKeyboardError as e:
cli.echo("{fg_red}%s: %s", cli.config.list_keymaps.keyboard, e.message)
except (FileNotFoundError, PermissionError) as e:
cli.echo("{fg_red}%s: %s", cli.config.list_keymaps.keyboard, e)
except TypeError:
cli.echo("{fg_red}Something went wrong. Did you specify a keyboard?")

View File

@@ -1,8 +1,10 @@
"""Functions that help you work with QMK keymaps.
"""
import os
from pathlib import Path
import qmk.path
import qmk.makefile
# The `keymap.c` template to use when a keyboard doesn't have its own
DEFAULT_KEYMAP_C = """#include QMK_KEYBOARD_H
@@ -94,3 +96,39 @@ def write(keyboard, keymap, layout, layers):
keymap_fd.write(keymap_c)
return keymap_file
def list_keymaps(keyboard_name):
""" List the available keymaps for a keyboard.
Args:
keyboard_name: the keyboards full name with vendor and revision if necessary, example: clueboard/66/rev3
Returns:
a set with the names of the available keymaps
"""
# parse all the rules.mk files for the keyboard
rules_mk = qmk.makefile.get_rules_mk(keyboard_name)
names = set()
if rules_mk:
# qmk_firmware/keyboards
keyboards_dir = Path.cwd() / "keyboards"
# path to the keyboard's directory
kb_path = keyboards_dir / keyboard_name
# 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 for keymap in os.listdir(str(keymaps_dir)) if (keymaps_dir / keymap / "keymap.c").is_file()])
kb_path = kb_path.parent
# if community layouts are supported, get them
if "LAYOUTS" in rules_mk:
for layout in rules_mk["LAYOUTS"].split():
cl_path = Path.cwd() / "layouts" / "community" / layout
if cl_path.exists():
names = names.union([keymap for keymap in os.listdir(str(cl_path)) if (cl_path / keymap / "keymap.c").is_file()])
return sorted(names)

View File

@@ -0,0 +1,83 @@
""" Functions for working with Makefiles
"""
from pathlib import Path
from qmk.errors import NoSuchKeyboardError
def parse_rules_mk_file(file, rules_mk=None):
"""Turn a rules.mk file into a dictionary.
Args:
file: path to the rules.mk file
rules_mk: already parsed rules.mk the new file should be merged with
Returns:
a dictionary with the file's content
"""
if not rules_mk:
rules_mk = {}
file = Path(file)
if file.exists():
rules_mk_lines = file.read_text().split("\n")
for line in rules_mk_lines:
# Filter out comments
if line.strip().startswith("#"):
continue
# Strip in-line comments
if '#' in line:
line = line[:line.index('#')].strip()
if '=' in line:
# Append
if '+=' in line:
key, value = line.split('+=', 1)
if key.strip() not in rules_mk:
rules_mk[key.strip()] = value.strip()
else:
rules_mk[key.strip()] += ' ' + value.strip()
# Set if absent
elif "?=" in line:
key, value = line.split('?=', 1)
if key.strip() not in rules_mk:
rules_mk[key.strip()] = value.strip()
else:
if ":=" in line:
line.replace(":", "")
key, value = line.split('=', 1)
rules_mk[key.strip()] = value.strip()
return rules_mk
def get_rules_mk(keyboard):
""" Get a rules.mk for a keyboard
Args:
keyboard: name of the keyboard
Raises:
NoSuchKeyboardError: when the keyboard does not exists
Returns:
a dictionary with the content of the rules.mk file
"""
# Start with qmk_firmware/keyboards
kb_path = Path.cwd() / "keyboards"
# walk down the directory tree
# and collect all rules.mk files
kb_dir = kb_path / keyboard
if kb_dir.exists():
rules_mk = dict()
for directory in Path(keyboard).parts:
kb_path = kb_path / directory
rules_mk_path = kb_path / "rules.mk"
if rules_mk_path.exists():
rules_mk = parse_rules_mk_file(rules_mk_path, rules_mk)
else:
raise NoSuchKeyboardError("The requested keyboard and/or revision does not exist.")
return rules_mk

View File

@@ -54,3 +54,15 @@ def test_list_keyboards():
# check to see if a known keyboard is returned
# this will fail if handwired/onekey/pytest is removed
assert 'handwired/onekey/pytest' in result.stdout
def test_list_keymaps():
result = check_subcommand("list-keymaps", "-kb", "handwired/onekey/pytest")
assert result.returncode == 0
assert "default" and "test" in result.stdout
def test_list_keymaps_no_keyboard_found():
result = check_subcommand("list-keymaps", "-kb", "asdfghjkl")
assert result.returncode == 0
assert "does not exist" in result.stdout