diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/python/qmk/c_parse.py | 23 | ||||
| -rw-r--r-- | lib/python/qmk/cli/generate/keycodes.py | 2 | ||||
| -rwxr-xr-x | lib/python/qmk/json_encoders.py | 70 | ||||
| -rw-r--r-- | lib/python/qmk/tests/test_cli_commands.py | 4 | 
4 files changed, 68 insertions, 31 deletions
| diff --git a/lib/python/qmk/c_parse.py b/lib/python/qmk/c_parse.py index 83ab1d1e6d..663688e240 100644 --- a/lib/python/qmk/c_parse.py +++ b/lib/python/qmk/c_parse.py @@ -11,7 +11,7 @@ from milc import cli  from qmk.comment_remover import comment_remover -default_key_entry = {'x': -1, 'y': 0, 'w': 1} +default_key_entry = {'x': -1, 'y': 0}  single_comment_regex = re.compile(r'\s+/[/*].*$')  multi_comment_regex = re.compile(r'/\*(.|\n)*?\*/', re.MULTILINE)  layout_macro_define_regex = re.compile(r'^#\s*define') @@ -213,10 +213,13 @@ def _coerce_led_token(_type, value):          return value_map[value] -def _validate_led_config(matrix, matrix_rows, matrix_indexes, position, position_raw, flags): +def _validate_led_config(matrix, matrix_rows, matrix_cols, matrix_indexes, position, position_raw, flags):      # TODO: Improve crude parsing/validation      if len(matrix) != matrix_rows and len(matrix) != (matrix_rows / 2):          raise ValueError("Unable to parse g_led_config matrix data") +    for index, row in enumerate(matrix): +        if len(row) != matrix_cols: +            raise ValueError(f"Number of columns in row {index} ({len(row)}) does not match matrix ({matrix_cols})")      if len(position) != len(flags):          raise ValueError(f"Number of g_led_config physical positions ({len(position)}) does not match number of flags ({len(flags)})")      if len(matrix_indexes) and (max(matrix_indexes) >= len(flags)): @@ -230,13 +233,16 @@ def _validate_led_config(matrix, matrix_rows, matrix_indexes, position, position  def _parse_led_config(file, matrix_cols, matrix_rows):      """Return any 'raw' led/rgb matrix config      """ -    matrix_raw = [] +    matrix = []      position_raw = []      flags = []      found_led_config = False      bracket_count = 0      section = 0 +    current_row_index = 0 +    current_row = [] +      for _type, value in lex(_preprocess_c_file(file), CLexer()):          # Assume g_led_config..stuff..;          if value == 'g_led_config': @@ -250,12 +256,16 @@ def _parse_led_config(file, matrix_cols, matrix_rows):                  if bracket_count == 2:                      section += 1              elif value == '}': +                if section == 1 and bracket_count == 3: +                    matrix.append(current_row) +                    current_row = [] +                    current_row_index += 1                  bracket_count -= 1              else:                  # Assume any non whitespace value here is important enough to stash                  if _type in [Token.Literal.Number.Integer, Token.Literal.Number.Float, Token.Literal.Number.Hex, Token.Name]:                      if section == 1 and bracket_count == 3: -                        matrix_raw.append(_coerce_led_token(_type, value)) +                        current_row.append(_coerce_led_token(_type, value))                      if section == 2 and bracket_count == 3:                          position_raw.append(_coerce_led_token(_type, value))                      if section == 3 and bracket_count == 2: @@ -265,16 +275,15 @@ def _parse_led_config(file, matrix_cols, matrix_rows):                      return None      # Slightly better intrim format -    matrix = list(_get_chunks(matrix_raw, matrix_cols))      position = list(_get_chunks(position_raw, 2)) -    matrix_indexes = list(filter(lambda x: x is not None, matrix_raw)) +    matrix_indexes = list(filter(lambda x: x is not None, sum(matrix, [])))      # If we have not found anything - bail with no error      if not section:          return None      # Throw any validation errors -    _validate_led_config(matrix, matrix_rows, matrix_indexes, position, position_raw, flags) +    _validate_led_config(matrix, matrix_rows, matrix_cols, matrix_indexes, position, position_raw, flags)      return (matrix, position, flags) diff --git a/lib/python/qmk/cli/generate/keycodes.py b/lib/python/qmk/cli/generate/keycodes.py index 17503bac63..3b69b17ed1 100644 --- a/lib/python/qmk/cli/generate/keycodes.py +++ b/lib/python/qmk/cli/generate/keycodes.py @@ -143,7 +143,7 @@ def generate_keycode_extras(cli):      """      # Build the header file. -    keycodes_h_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once', '#include "keymap.h"', '// clang-format off'] +    keycodes_h_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once', '#include "keycodes.h"', '// clang-format off']      keycodes = load_spec(cli.args.version, cli.args.lang) diff --git a/lib/python/qmk/json_encoders.py b/lib/python/qmk/json_encoders.py index f968b3dbb2..e61c63aff3 100755 --- a/lib/python/qmk/json_encoders.py +++ b/lib/python/qmk/json_encoders.py @@ -27,7 +27,10 @@ class QMKJSONEncoder(json.JSONEncoder):          return float(obj) -    def encode_list(self, obj): +    def encode_dict_single_line(self, obj): +        return "{" + ", ".join(f"{self.encode(key)}: {self.encode(element)}" for key, element in sorted(obj.items(), key=self.sort_layout)) + "}" + +    def encode_list(self, obj, key=None):          """Encode a list-like object.          """          if self.primitives_only(obj): @@ -35,22 +38,28 @@ class QMKJSONEncoder(json.JSONEncoder):          else:              self.indentation_level += 1 -            output = [self.indent_str + self.encode(element) for element in obj] + +            if key in ('layout', 'rotary'): +                # These are part of a layout or led/encoder config, put them on a single line. +                output = [self.indent_str + self.encode_dict_single_line(element) for element in obj] +            else: +                output = [self.indent_str + self.encode(element) for element in obj] +              self.indentation_level -= 1              return "[\n" + ",\n".join(output) + "\n" + self.indent_str + "]" -    def encode(self, obj): +    def encode(self, obj, key=None):          """Encode keymap.json objects for QMK.          """          if isinstance(obj, Decimal):              return self.encode_decimal(obj)          elif isinstance(obj, (list, tuple)): -            return self.encode_list(obj) +            return self.encode_list(obj, key)          elif isinstance(obj, dict): -            return self.encode_dict(obj) +            return self.encode_dict(obj, key)          else:              return super().encode(obj) @@ -71,22 +80,43 @@ class QMKJSONEncoder(json.JSONEncoder):  class InfoJSONEncoder(QMKJSONEncoder):      """Custom encoder to make info.json's a little nicer to work with.      """ -    def encode_dict(self, obj): +    def encode_dict(self, obj, key):          """Encode info.json dictionaries.          """          if obj: -            if set(("x", "y")).issubset(obj.keys()): -                # These are part of a layout/led_config, put them on a single line. -                return "{ " + ", ".join(f"{self.encode(key)}: {self.encode(element)}" for key, element in sorted(obj.items())) + " }" - -            else: -                self.indentation_level += 1 -                output = [self.indent_str + f"{json.dumps(key)}: {self.encode(value)}" for key, value in sorted(obj.items(), key=self.sort_dict)] -                self.indentation_level -= 1 -                return "{\n" + ",\n".join(output) + "\n" + self.indent_str + "}" +            self.indentation_level += 1 +            output = [self.indent_str + f"{json.dumps(k)}: {self.encode(v, k)}" for k, v in sorted(obj.items(), key=self.sort_dict)] +            self.indentation_level -= 1 +            return "{\n" + ",\n".join(output) + "\n" + self.indent_str + "}"          else:              return "{}" +    def sort_layout(self, key): +        key = key[0] + +        if key == 'label': +            return '00label' + +        elif key == 'matrix': +            return '01matrix' + +        elif key == 'x': +            return '02x' + +        elif key == 'y': +            return '03y' + +        elif key == 'w': +            return '04w' + +        elif key == 'h': +            return '05h' + +        elif key == 'flags': +            return '06flags' + +        return key +      def sort_dict(self, key):          """Forces layout to the back of the sort order.          """ @@ -120,21 +150,19 @@ class InfoJSONEncoder(QMKJSONEncoder):  class KeymapJSONEncoder(QMKJSONEncoder):      """Custom encoder to make keymap.json's a little nicer to work with.      """ -    def encode_dict(self, obj): +    def encode_dict(self, obj, key):          """Encode dictionary objects for keymap.json.          """          if obj:              self.indentation_level += 1 -            output_lines = [f"{self.indent_str}{json.dumps(key)}: {self.encode(value)}" for key, value in sorted(obj.items(), key=self.sort_dict)] -            output = ',\n'.join(output_lines) +            output = [self.indent_str + f"{json.dumps(k)}: {self.encode(v, k)}" for k, v in sorted(obj.items(), key=self.sort_dict)]              self.indentation_level -= 1 - -            return f"{{\n{output}\n{self.indent_str}}}" +            return "{\n" + ",\n".join(output) + "\n" + self.indent_str + "}"          else:              return "{}" -    def encode_list(self, obj): +    def encode_list(self, obj, k=None):          """Encode a list-like object.          """          if self.indentation_level == 2: diff --git a/lib/python/qmk/tests/test_cli_commands.py b/lib/python/qmk/tests/test_cli_commands.py index e598b281a6..28838ec368 100644 --- a/lib/python/qmk/tests/test_cli_commands.py +++ b/lib/python/qmk/tests/test_cli_commands.py @@ -291,7 +291,7 @@ def test_generate_version_h():  def test_format_json_keyboard():      result = check_subcommand('format-json', '--format', 'keyboard', 'lib/python/qmk/tests/minimal_info.json')      check_returncode(result) -    assert result.stdout == '{\n    "keyboard_name": "tester",\n    "maintainer": "qmk",\n    "layouts": {\n        "LAYOUT": {\n            "layout": [\n                { "label": "KC_A", "matrix": [0, 0], "x": 0, "y": 0 }\n            ]\n        }\n    }\n}\n' +    assert result.stdout == '{\n    "keyboard_name": "tester",\n    "maintainer": "qmk",\n    "layouts": {\n        "LAYOUT": {\n            "layout": [\n                {"label": "KC_A", "matrix": [0, 0], "x": 0, "y": 0}\n            ]\n        }\n    }\n}\n'  def test_format_json_keymap(): @@ -303,7 +303,7 @@ def test_format_json_keymap():  def test_format_json_keyboard_auto():      result = check_subcommand('format-json', '--format', 'auto', 'lib/python/qmk/tests/minimal_info.json')      check_returncode(result) -    assert result.stdout == '{\n    "keyboard_name": "tester",\n    "maintainer": "qmk",\n    "layouts": {\n        "LAYOUT": {\n            "layout": [\n                { "label": "KC_A", "matrix": [0, 0], "x": 0, "y": 0 }\n            ]\n        }\n    }\n}\n' +    assert result.stdout == '{\n    "keyboard_name": "tester",\n    "maintainer": "qmk",\n    "layouts": {\n        "LAYOUT": {\n            "layout": [\n                {"label": "KC_A", "matrix": [0, 0], "x": 0, "y": 0}\n            ]\n        }\n    }\n}\n'  def test_format_json_keymap_auto(): | 
