JSON encoder: improve sorting of layout dict keys (#19974)
parent
cf5e9d172d
commit
91e0457b0e
|
@ -11,7 +11,7 @@ from milc import cli
|
||||||
|
|
||||||
from qmk.comment_remover import comment_remover
|
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+/[/*].*$')
|
single_comment_regex = re.compile(r'\s+/[/*].*$')
|
||||||
multi_comment_regex = re.compile(r'/\*(.|\n)*?\*/', re.MULTILINE)
|
multi_comment_regex = re.compile(r'/\*(.|\n)*?\*/', re.MULTILINE)
|
||||||
layout_macro_define_regex = re.compile(r'^#\s*define')
|
layout_macro_define_regex = re.compile(r'^#\s*define')
|
||||||
|
|
|
@ -27,7 +27,10 @@ class QMKJSONEncoder(json.JSONEncoder):
|
||||||
|
|
||||||
return float(obj)
|
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.
|
"""Encode a list-like object.
|
||||||
"""
|
"""
|
||||||
if self.primitives_only(obj):
|
if self.primitives_only(obj):
|
||||||
|
@ -35,22 +38,28 @@ class QMKJSONEncoder(json.JSONEncoder):
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.indentation_level += 1
|
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
|
self.indentation_level -= 1
|
||||||
|
|
||||||
return "[\n" + ",\n".join(output) + "\n" + self.indent_str + "]"
|
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.
|
"""Encode keymap.json objects for QMK.
|
||||||
"""
|
"""
|
||||||
if isinstance(obj, Decimal):
|
if isinstance(obj, Decimal):
|
||||||
return self.encode_decimal(obj)
|
return self.encode_decimal(obj)
|
||||||
|
|
||||||
elif isinstance(obj, (list, tuple)):
|
elif isinstance(obj, (list, tuple)):
|
||||||
return self.encode_list(obj)
|
return self.encode_list(obj, key)
|
||||||
|
|
||||||
elif isinstance(obj, dict):
|
elif isinstance(obj, dict):
|
||||||
return self.encode_dict(obj)
|
return self.encode_dict(obj, key)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return super().encode(obj)
|
return super().encode(obj)
|
||||||
|
@ -71,22 +80,43 @@ class QMKJSONEncoder(json.JSONEncoder):
|
||||||
class InfoJSONEncoder(QMKJSONEncoder):
|
class InfoJSONEncoder(QMKJSONEncoder):
|
||||||
"""Custom encoder to make info.json's a little nicer to work with.
|
"""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.
|
"""Encode info.json dictionaries.
|
||||||
"""
|
"""
|
||||||
if obj:
|
if obj:
|
||||||
if set(("x", "y")).issubset(obj.keys()):
|
self.indentation_level += 1
|
||||||
# These are part of a layout/led_config, put them on a single line.
|
output = [self.indent_str + f"{json.dumps(k)}: {self.encode(v, k)}" for k, v in sorted(obj.items(), key=self.sort_dict)]
|
||||||
return "{ " + ", ".join(f"{self.encode(key)}: {self.encode(element)}" for key, element in sorted(obj.items())) + " }"
|
self.indentation_level -= 1
|
||||||
|
return "{\n" + ",\n".join(output) + "\n" + self.indent_str + "}"
|
||||||
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 + "}"
|
|
||||||
else:
|
else:
|
||||||
return "{}"
|
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):
|
def sort_dict(self, key):
|
||||||
"""Forces layout to the back of the sort order.
|
"""Forces layout to the back of the sort order.
|
||||||
"""
|
"""
|
||||||
|
@ -120,21 +150,19 @@ class InfoJSONEncoder(QMKJSONEncoder):
|
||||||
class KeymapJSONEncoder(QMKJSONEncoder):
|
class KeymapJSONEncoder(QMKJSONEncoder):
|
||||||
"""Custom encoder to make keymap.json's a little nicer to work with.
|
"""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.
|
"""Encode dictionary objects for keymap.json.
|
||||||
"""
|
"""
|
||||||
if obj:
|
if obj:
|
||||||
self.indentation_level += 1
|
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 = [self.indent_str + f"{json.dumps(k)}: {self.encode(v, k)}" for k, v in sorted(obj.items(), key=self.sort_dict)]
|
||||||
output = ',\n'.join(output_lines)
|
|
||||||
self.indentation_level -= 1
|
self.indentation_level -= 1
|
||||||
|
return "{\n" + ",\n".join(output) + "\n" + self.indent_str + "}"
|
||||||
return f"{{\n{output}\n{self.indent_str}}}"
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return "{}"
|
return "{}"
|
||||||
|
|
||||||
def encode_list(self, obj):
|
def encode_list(self, obj, k=None):
|
||||||
"""Encode a list-like object.
|
"""Encode a list-like object.
|
||||||
"""
|
"""
|
||||||
if self.indentation_level == 2:
|
if self.indentation_level == 2:
|
||||||
|
|
|
@ -291,7 +291,7 @@ def test_generate_version_h():
|
||||||
def test_format_json_keyboard():
|
def test_format_json_keyboard():
|
||||||
result = check_subcommand('format-json', '--format', 'keyboard', 'lib/python/qmk/tests/minimal_info.json')
|
result = check_subcommand('format-json', '--format', 'keyboard', 'lib/python/qmk/tests/minimal_info.json')
|
||||||
check_returncode(result)
|
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():
|
def test_format_json_keymap():
|
||||||
|
@ -303,7 +303,7 @@ def test_format_json_keymap():
|
||||||
def test_format_json_keyboard_auto():
|
def test_format_json_keyboard_auto():
|
||||||
result = check_subcommand('format-json', '--format', 'auto', 'lib/python/qmk/tests/minimal_info.json')
|
result = check_subcommand('format-json', '--format', 'auto', 'lib/python/qmk/tests/minimal_info.json')
|
||||||
check_returncode(result)
|
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():
|
def test_format_json_keymap_auto():
|
||||||
|
|
Loading…
Reference in New Issue