-
-
Notifications
You must be signed in to change notification settings - Fork 39.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for converting keymap.c -> keymap.json #7218
Conversation
This uses a full C parser and reads the AST to extract the keymap. Thus it can theoretically work on any keymap. Bugs: it doesn't support TO(layer) yet
I can confirm that the CI has been broken by these changes. I did add the packages to requirements.txt. What else do I need to do to fix the CI? |
IIRC @zvecr set up the current CI configuration. |
eagerly waiting on this one |
There are merge conflicts here. |
The only merge conflicts are present in |
Looks like it was missing the labels that would help me find it. Now that they're in place I'll be able to get to it during one of my upcoming scrubbing sessions so it can be reviewed. |
This is epic. Would be nice to get a link to the toolbox to see your keymap in the UI, but thats a feature for in the future |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for writing this! This is fantastic, exactly what I wanted to do with my original keymap parsing code but didn't have the time/patience to figure out. :)
From a structural standpoint the parse_keymap_c(path)
function is a good entrypoint. I'd like to see this abstracted out into the qmk.keymap
module so that it will be available to other commands.
You're duplicating a lot of functionality that already exists in our python library. Using that should simplify what you're doing a lot. You can look at the qmk json-keymap
command to get a sense of how I'd want qmk c2json
to operate. (Side note: after this is merged it'll definitely be time to rename json-keymap
to json2c
.)
I'll try to annotate the major changes I want, but it may take a couple iterations until we find the right place for everything. Feel free to hit up #cli on discord if you'd like to have a more realtime back and forth on any of this.
def get_qmk_root(from_path): | ||
""" | ||
Finds the path of the QMK repository root relative to a keymaps directory | ||
|
||
Parameters: | ||
from_path -- Path to find the QMK root relative to | ||
|
||
Signature: | ||
(from_path: Path) -> Path | ||
""" | ||
for part in iter_upwards(from_path.absolute()): | ||
if (part / 'keyboards').exists(): | ||
return part |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can always assume that os.getcwd() is the qmk_firmware root, you shouldn't ever need to look for it.
# I'm guessing make probably doesn't just put the entire source tree as | ||
# an include path?? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't, but discovering the current paths outside of make is... problematic. You can use make VERBOSE=1 <keyboard>:<keymap>
to see the final list, but it may take some make trickery to get something that's futureproof.
yield from (Path(dirpath) / d for d in dirs) | ||
|
||
|
||
def parse_keymap_c(path): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function should be moved to qmk.keymap
. The helper functions it relies on should probably live in their own module (maybe qmk.c_parse
? I'm open to better names.)
class Layer(NamedTuple): | ||
name: str | ||
keys: List[List[Key]] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should live above the function definitions.
Can you add a docstring explaining what this is used for?
|
||
class Layer(NamedTuple): | ||
name: str | ||
keys: List[List[Key]] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using Key here seems to obfuscate rather than improve readability.
keys: List[List[Key]] | |
keys: List[List[str]] |
keys: List[List[Key]] | ||
|
||
|
||
MODIFIER_MAP = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should live at the top of the module, just below the imports.
return layers | ||
|
||
|
||
def find_keyboard_name_from_keymap_c(keymap_c): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is a keyboard name always required? What happens if someone is trying to convert a keymap in the qmk_firmware/layouts
directory? We will also soon support keymaps in userspace.
obj = { | ||
'keyboard': find_keyboard_name_from_keymap_c(keymap_c), | ||
'keymap': keymap_c.parent.name, | ||
'layout': 'KEYMAP', # I don't know what this means |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the LAYOUT
macro for the keyboard. It defines the relationship between the physical layouts of keys and their position on the scanning matrix. Many keyboards support multiple layouts macros. In my example below the layout macro is LAYOUT_all
:
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[0] = LAYOUT_all(KC_A),
[1] = LAYOUT_all(KC_B)
}
Note: KEYMAP
is a deprecated name we try not to use anymore.
def keymap_c_to_json_string(keymap_c, log): | ||
""" | ||
Performs the full process of parsing a keymap.c and turning it into a JSON | ||
keymap file | ||
|
||
Parameters: | ||
keymap_c -- Path to the keymap.c | ||
""" | ||
keymaps = find_keymaps_in_ast(parse_keymap_c(keymap_c)) | ||
layers = extract_layers(keymaps, log=log) | ||
return make_keymap_json(keymap_c, layers) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This functionality should be folded into the j2json(cli)
function.
Hi @lf-, are you still interested in this PR? Could you take a look at the suggestions? Thanks! |
I am interested but am currently doing midterms. I'll be able to look at it next week. |
Good luck with your midterms! We're not in a hurry to merge this as long as you're planning to come back to it. |
Hi @lf-, I was doing a pass of PR's and noticed it had been a while. Do you still plan to revisit this? Given the state of the world it's understandable if you don't have time right now, we just want to know if we should keep this open for you. |
I got sick over break and acquired more midterms when I was feeling better. I really want this work to be merged but I'm uncertain as to when I'll find time to work on it before end of term, the way things are going (and indeed external events are making my life messier as well in this regard). I'm sorry it has been taking so long to get to this contribution. If anyone wants to adopt it, I would appreciate if they did so. |
Hope things improve soon @lf-. We're talking internally and someone may take this on soon. |
Thank you for your contribution! |
Given this is superceded, I'm going to close it. |
This uses a full C parser and reads the AST to extract the keymap. Thus
it can theoretically work on any keymap. Spooky Hacktoberfest PR :)
Bugs: it doesn't support TO(layer) yet. This is mostly because we only see the output of the macro so it's more difficult to parse this kind of structure out.
Description
This implements at least a basic case of #6877, while allowing for handling keymaps of theoretically unlimited complexity by using a C parser to parse the AST of the keymap.c file.
I realized halfway through the project that we're supposed to target Python 3.5 so
unfortunately I think there are still f-strings in here, sorry. I developed this on 3.7 and have a habit of trying to use the newest language features, of which there may still be remnants. If anyone has a copy of 3.5 can you please check that at least this doesn't break the rest of the CLI?Types of Changes
Issues Fixed or Closed by This PR
Checklist
keyboards/vitamins_included/keymaps/default/keymap.c
)