Skip to content
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

Closed
wants to merge 3 commits into from
Closed

Conversation

lf-
Copy link
Contributor

@lf- lf- commented Oct 31, 2019

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

  • Core
  • Bugfix
  • New feature
  • Enhancement/optimization
  • Keyboard (addition or update)
  • Keymap/layout/userspace (addition or update)
  • Documentation

Issues Fixed or Closed by This PR

Checklist

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes. (yes, doctest where feasible but I don't know how we integrate this into the build process)
  • I have tested the changes and verified that they work and don't break anything (as well as I can manage). (yes, it has been tested on keyboards/vitamins_included/keymaps/default/keymap.c)

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
@lf-
Copy link
Contributor Author

lf- commented Oct 31, 2019

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?

@noroadsleft
Copy link
Member

IIRC @zvecr set up the current CI configuration.

@drashna drashna requested review from zvecr and a team October 31, 2019 19:30
@universam1
Copy link

eagerly waiting on this one

@drashna
Copy link
Member

drashna commented Dec 16, 2019

There are merge conflicts here.

@joshdick
Copy link

The only merge conflicts are present in requirements.txt and look simple to resolve, is that the only thing that's preventing this PR from being merged?

@skullydazed skullydazed added cli qmk cli command core python labels Jan 20, 2020
@skullydazed
Copy link
Member

skullydazed commented Jan 20, 2020

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.

@jimmybrawn
Copy link

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

Copy link
Member

@skullydazed skullydazed left a 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.

Comment on lines +59 to +71
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
Copy link
Member

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.

Comment on lines +97 to +98
# I'm guessing make probably doesn't just put the entire source tree as
# an include path??
Copy link
Member

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):
Copy link
Member

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.)

Comment on lines +134 to +136
class Layer(NamedTuple):
name: str
keys: List[List[Key]]
Copy link
Member

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]]
Copy link
Member

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.

Suggested change
keys: List[List[Key]]
keys: List[List[str]]

keys: List[List[Key]]


MODIFIER_MAP = {
Copy link
Member

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):
Copy link
Member

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
Copy link
Member

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.

Comment on lines +302 to +312
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)
Copy link
Member

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.

@Erovia
Copy link
Member

Erovia commented Feb 10, 2020

Hi @lf-, are you still interested in this PR?

Could you take a look at the suggestions?

Thanks!

@lf-
Copy link
Contributor Author

lf- commented Feb 10, 2020

I am interested but am currently doing midterms. I'll be able to look at it next week.

@skullydazed
Copy link
Member

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.

@skullydazed
Copy link
Member

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.

@lf-
Copy link
Contributor Author

lf- commented Mar 17, 2020

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.

@skullydazed
Copy link
Member

Hope things improve soon @lf-. We're talking internally and someone may take this on soon.

@Erovia Erovia mentioned this pull request Apr 15, 2020
13 tasks
@stale
Copy link

stale bot commented May 6, 2020

Thank you for your contribution!
This pull request has been automatically marked as stale because it has not had activity in the last 45 days. It will be closed in 30 days if no further activity occurs. Please feel free to give a status update now, or re-open when it's ready.
For maintainers: Please label with awaiting review, breaking_change, in progress, or on hold to prevent the issue from being re-flagged.

@lf-
Copy link
Contributor Author

lf- commented May 6, 2020

Given this is superceded, I'm going to close it.

@lf- lf- closed this May 6, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Command to convert a keymap.c to keymap.json
8 participants