aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTsan-Kuang Lee <1425438+tsankuanglee@users.noreply.github.com>2019-11-05 01:14:15 -0600
committerDrashna Jaelre <drashna@live.com>2019-11-04 23:14:15 -0800
commit8b832c494cd6d50e4f9c8384ce54733308eda95f (patch)
treeaa86f942fcabf6df270a4baa9160ba87bb9803e0
parentdcb2d63302fefe1b37b8ca606ce7241ae8655389 (diff)
downloadqmk_firmware-8b832c494cd6d50e4f9c8384ce54733308eda95f.tar.gz
qmk_firmware-8b832c494cd6d50e4f9c8384ce54733308eda95f.zip
[Keyboard] add keymap beautifier for Ergodox EZ (#4393)
* add beautifier * add example * Update keyboards/ergodox_ez/util/keymap_beautifier.py Co-Authored-By: tsankuanglee <1425438+tsankuanglee@users.noreply.github.com> * Update keyboards/ergodox_ez/util/keymap_beautifier.py Co-Authored-By: tsankuanglee <1425438+tsankuanglee@users.noreply.github.com> * works for regular layout * all planned features implemented * add justification switch * docker support * doc and starting script * clean up the container after done
-rwxr-xr-x[-rw-r--r--]keyboards/ergodox_ez/util/compile_keymap.py0
-rw-r--r--keyboards/ergodox_ez/util/keymap_beautifier/Dockerfile8
-rwxr-xr-xkeyboards/ergodox_ez/util/keymap_beautifier/KeymapBeautifier.py399
-rw-r--r--keyboards/ergodox_ez/util/keymap_beautifier/README.md139
-rwxr-xr-xkeyboards/ergodox_ez/util/keymap_beautifier/docker_run.sh3
-rw-r--r--keyboards/ergodox_ez/util/keymap_beautifier/requirements.txt1
-rw-r--r--keyboards/ergodox_ez/util/readme.md8
7 files changed, 558 insertions, 0 deletions
diff --git a/keyboards/ergodox_ez/util/compile_keymap.py b/keyboards/ergodox_ez/util/compile_keymap.py
index f427d6fd8..f427d6fd8 100644..100755
--- a/keyboards/ergodox_ez/util/compile_keymap.py
+++ b/keyboards/ergodox_ez/util/compile_keymap.py
diff --git a/keyboards/ergodox_ez/util/keymap_beautifier/Dockerfile b/keyboards/ergodox_ez/util/keymap_beautifier/Dockerfile
new file mode 100644
index 000000000..fbee1d0df
--- /dev/null
+++ b/keyboards/ergodox_ez/util/keymap_beautifier/Dockerfile
@@ -0,0 +1,8 @@
1FROM python:3.7.4-alpine3.10
2
3WORKDIR /usr/src/app
4COPY requirements.txt ./
5RUN pip install --no-cache-dir -r requirements.txt
6COPY ./KeymapBeautifier.py ./KeymapBeautifier.py
7
8CMD [ "python", "./KeymapBeautifier.py", "-h" ]
diff --git a/keyboards/ergodox_ez/util/keymap_beautifier/KeymapBeautifier.py b/keyboards/ergodox_ez/util/keymap_beautifier/KeymapBeautifier.py
new file mode 100755
index 000000000..b96e4c96c
--- /dev/null
+++ b/keyboards/ergodox_ez/util/keymap_beautifier/KeymapBeautifier.py
@@ -0,0 +1,399 @@
1#!/usr/bin/env python
2
3import argparse
4import pycparser
5import re
6
7class KeymapBeautifier:
8 justify_toward_center = False
9 filename_in = None
10 filename_out = None
11 output_layout = None
12 output = None
13
14 column_max_widths = {}
15
16 KEY_ALIASES = {
17 "KC_TRANSPARENT": "_______",
18 "KC_TRNS": "_______",
19 "KC_NO": "XXXXXXX",
20 }
21 KEYMAP_START = 'const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n'
22 KEYMAP_END = '};\n'
23 KEYMAP_START_REPLACEMENT = "const int keymaps[]={\n"
24 KEY_CHART = """
25 /*
26 * ,--------------------------------------------------. ,--------------------------------------------------.
27 * | 0 | 1 | 2 | 3 | 4 | 5 | 6 | | 38 | 39 | 40 | 41 | 42 | 43 | 44 |
28 * |--------+------+------+------+------+------+------| |------+------+------+------+------+------+--------|
29 * | 7 | 8 | 9 | 10 | 11 | 12 | 13 | | 45 | 46 | 47 | 48 | 49 | 50 | 51 |
30 * |--------+------+------+------+------+------| | | |------+------+------+------+------+--------|
31 * | 14 | 15 | 16 | 17 | 18 | 19 |------| |------| 52 | 53 | 54 | 55 | 56 | 57 |
32 * |--------+------+------+------+------+------| 26 | | 58 |------+------+------+------+------+--------|
33 * | 20 | 21 | 22 | 23 | 24 | 25 | | | | 59 | 60 | 61 | 62 | 63 | 64 |
34 * `--------+------+------+------+------+-------------' `-------------+------+------+------+------+--------'
35 * | 27 | 28 | 29 | 30 | 31 | | 65 | 66 | 67 | 68 | 69 |
36 * `----------------------------------' `----------------------------------'
37 * ,-------------. ,-------------.
38 * | 32 | 33 | | 70 | 71 |
39 * ,------+------+------| |------+------+------.
40 * | | | 34 | | 72 | | |
41 * | 35 | 36 |------| |------| 74 | 75 |
42 * | | | 37 | | 73 | | |
43 * `--------------------' `--------------------'
44 */
45"""
46 KEY_COORDINATES = {
47 'LAYOUT_ergodox': [
48 # left hand
49 (0,0), (0,1), (0,2), (0,3), (0,4), (0,5), (0,6),
50 (1,0), (1,1), (1,2), (1,3), (1,4), (1,5), (1,6),
51 (2,0), (2,1), (2,2), (2,3), (2,4), (2,5),
52 (3,0), (3,1), (3,2), (3,3), (3,4), (3,5), (3,6),
53 (4,0), (4,1), (4,2), (4,3), (4,4),
54 # left thumb
55 (5,5), (5,6),
56 (6,6),
57 (7,4), (7,5), (7,6),
58 # right hand
59 (8,0), (8,1), (8,2), (8,3), (8,4), (8,5), (8,6),
60 (9,0), (9,1), (9,2), (9,3), (9,4), (9,5), (9,6),
61 (10,1), (10,2), (10,3), (10,4), (10,5), (10,6),
62 (11,0), (11,1), (11,2), (11,3), (11,4), (11,5), (11,6),
63 (12,2), (12,3), (12,4), (12,5), (12,6),
64 # right thumb
65 (13,0), (13,1),
66 (14,0),
67 (15,0), (15,1), (15,2)
68 ],
69 'LAYOUT_ergodox_pretty': [
70 # left hand and right hand
71 (0,0), (0,1), (0,2), (0,3), (0,4), (0,5), (0,6), (0,7), (0,8), (0,9), (0,10), (0,11), (0,12), (0,13),
72 (1,0), (1,1), (1,2), (1,3), (1,4), (1,5), (1,6), (1,7), (1,8), (1,9), (1,10), (1,11), (1,12), (1,13),
73 (2,0), (2,1), (2,2), (2,3), (2,4), (2,5), (2,8), (2,9), (2,10), (2,11), (2,12), (2,13),
74 (3,0), (3,1), (3,2), (3,3), (3,4), (3,5), (3,6), (3,7), (3,8), (3,9), (3,10), (3,11), (3,12), (3,13),
75 (4,0), (4,1), (4,2), (4,3), (4,4), (4,9), (4,10), (4,11), (4,12), (4,13),
76
77 # left thumb and right thumb
78 (5,5), (5,6), (5,7), (5,8),
79 (6,6), (6,7),
80 (7,4), (7,5), (7,6), (7,7), (7,8), (7,9)
81 ],
82 }
83 current_converted_KEY_COORDINATES = []
84
85 # each column is aligned within each group (tuples of row indexes are inclusive)
86 KEY_ROW_GROUPS = {
87 'LAYOUT_ergodox': [(0,4),(5,7),(8,12),(13,15)],
88 'LAYOUT_ergodox_pretty': [(0,7)],
89 #'LAYOUT_ergodox_pretty': [(0,5),(6,7)],
90 #'LAYOUT_ergodox_pretty': [(0,3),(4,4),(5,7)],
91 #'LAYOUT_ergodox_pretty': [(0,4),(5,7)],
92 }
93
94
95 INDEX_CONVERSTION_LAYOUT_ergodox_pretty_to_LAYOUT_ergodox = [
96 0, 1, 2, 3, 4, 5, 6, 38,39,40,41,42,43,44,
97 7, 8, 9,10,11,12,13, 45,46,47,48,49,50,51,
98 14,15,16,17,18,19, 52,53,54,55,56,57,
99 20,21,22,23,24,25,26, 58,59,60,61,62,63,64,
100 27,28,29,30,31, 65,66,67,68,69,
101 32,33, 70,71,
102 34, 72,
103 35,36,37, 73,74,75,
104 ]
105
106
107 def index_conversion_map_reversed(self, conversion_map):
108 return [conversion_map.index(i) for i in range(len(conversion_map))]
109
110
111 def __init__(self, source_code = "", output_layout="LAYOUT_ergodox", justify_toward_center = False):
112 self.output_layout = output_layout
113 self.justify_toward_center = justify_toward_center
114 # determine the conversion map
115 #if input_layout == self.output_layout:
116 # conversion_map = [i for i in range(len(self.INDEX_CONVERSTION_LAYOUT_ergodox_pretty_to_LAYOUT_ergodox))]
117 #conversion_map = self.INDEX_CONVERSTION_LAYOUT_ergodox_pretty_to_LAYOUT_ergodox
118 if self.output_layout == "LAYOUT_ergodox_pretty":
119 index_conversion_map = self.index_conversion_map_reversed(self.INDEX_CONVERSTION_LAYOUT_ergodox_pretty_to_LAYOUT_ergodox)
120 else:
121 index_conversion_map = list(range(len(self.INDEX_CONVERSTION_LAYOUT_ergodox_pretty_to_LAYOUT_ergodox)))
122 self.current_converted_KEY_COORDINATES = [
123 self.KEY_COORDINATES[self.output_layout][index_conversion_map[i]]
124 for i in range(len(self.KEY_COORDINATES[self.output_layout]))
125 ]
126
127 self.output = self.beautify_source_code(source_code)
128
129 def beautify_source_code(self, source_code):
130 # to keep it simple for the parser, we only use the parser to parse the key definition part
131 src = {
132 "before": [],
133 "keys": [],
134 "after": [],
135 }
136
137 current_section = "before"
138 for line in source_code.splitlines(True):
139 if current_section == 'before' and line == self.KEYMAP_START:
140 src[current_section].append("\n")
141 current_section = 'keys'
142 src[current_section].append(self.KEYMAP_START_REPLACEMENT)
143 continue
144 elif current_section == 'keys' and line == self.KEYMAP_END:
145 src[current_section].append(self.KEYMAP_END)
146 current_section = 'after'
147 continue
148 src[current_section].append(line)
149 output_lines = src['before'] + self.beautify_keys_section("".join(src['keys'])) + src['after']
150 return "".join(output_lines)
151
152 def beautify_keys_section(self, src):
153 parsed = self.parser(src)
154 layer_output = []
155
156 keymap = parsed.children()[0]
157 layers = keymap[1]
158 for layer in layers.init.exprs:
159 input_layout = layer.expr.name.name
160
161 key_symbols = self.layer_expr(layer)
162 # re-order keys from input_layout to regular layout
163 if input_layout == "LAYOUT_ergodox_pretty":
164 key_symbols = [key_symbols[i] for i in self.index_conversion_map_reversed(self.INDEX_CONVERSTION_LAYOUT_ergodox_pretty_to_LAYOUT_ergodox)]
165
166 padded_key_symbols = self.pad_key_symbols(key_symbols, input_layout)
167 current_pretty_output_layer = self.pretty_output_layer(layer.name[0].value, padded_key_symbols)
168 # strip trailing spaces from padding
169 layer_output.append(re.sub(r" +\n", "\n", current_pretty_output_layer))
170
171 return [self.KEYMAP_START + "\n",
172 self.KEY_CHART + "\n",
173 ",\n\n".join(layer_output) + "\n",
174 self.KEYMAP_END + "\n"]
175
176 def get_row_group(self, row):
177 for low, high in self.KEY_ROW_GROUPS[self.output_layout]:
178 if low <= row <= high:
179 return (low, high)
180 raise Exception("Cannot find row groups in KEY_ROW_GROUPS")
181
182
183 def calculate_column_max_widths(self, key_symbols):
184 # calculate the max width for each column
185 self.column_max_widths = {}
186 for i in range(len(key_symbols)):
187 row_index, column_index = self.current_converted_KEY_COORDINATES[i]
188 row_group = self.get_row_group(row_index)
189 if (row_group, column_index) in self.column_max_widths:
190 self.column_max_widths[(row_group, column_index)] = max(self.column_max_widths[(row_group, column_index)], len(key_symbols[i]))
191 else:
192 self.column_max_widths[(row_group, column_index)] = len(key_symbols[i])
193
194
195 def pad_key_symbols(self, key_symbols, input_layout, just='left'):
196 self.calculate_column_max_widths(key_symbols)
197
198 padded_key_symbols = []
199 # pad each key symbol
200 for i in range(len(key_symbols)):
201 key = key_symbols[i]
202 # look up column coordinate to determine number of spaces to pad
203 row_index, column_index = self.current_converted_KEY_COORDINATES[i]
204 row_group = self.get_row_group(row_index)
205 if just == 'left':
206 padded_key_symbols.append(key.ljust(self.column_max_widths[(row_group, column_index)]))
207 else:
208 padded_key_symbols.append(key.rjust(self.column_max_widths[(row_group, column_index)]))
209 return padded_key_symbols
210
211
212 layer_keys_pointer = 0
213 layer_keys = None
214 def grab_next_n_columns(self, n_columns, input_layout, layer_keys = None, from_beginning = False):
215 if layer_keys:
216 self.layer_keys = layer_keys
217 if from_beginning:
218 self.layer_keys_pointer = 0
219
220 begin = self.layer_keys_pointer
221 end = begin + n_columns
222 return self.layer_keys[self.layer_keys_pointer-n_keys:self.layer_keys_pointer]
223
224 key_coordinates_counter = 0
225 def get_padded_line(self, source_keys, key_from, key_to, just="left"):
226 if just == "right":
227 keys = [k.strip().rjust(len(k)) for k in source_keys[key_from:key_to]]
228 else:
229 keys = [k for k in source_keys[key_from:key_to]]
230
231 from_row, from_column = self.KEY_COORDINATES[self.output_layout][self.key_coordinates_counter]
232 row_group = self.get_row_group(from_row)
233 self.key_coordinates_counter += key_to - key_from
234 columns_before_key_from = sorted([col for row, col in self.KEY_COORDINATES[self.output_layout] if row == from_row and col < from_column])
235 # figure out which columns in this row needs padding; only pad empty columns to the right of an existing column
236 columns_to_pad = { c: True for c in range(from_column) }
237 if columns_before_key_from:
238 for c in range(max(columns_before_key_from)+1):
239 columns_to_pad[c] = False
240
241 # for rows with fewer columns that don't start with column 0, we need to insert leading spaces
242 spaces = 0
243 for c, v in columns_to_pad.items():
244 if not v:
245 continue
246 if (row_group,c) in self.column_max_widths:
247 spaces += self.column_max_widths[(row_group,c)] + len(", ")
248 else:
249 spaces += 0
250 return " " * spaces + ", ".join(keys) + ","
251
252 def pretty_output_layer(self, layer, keys):
253 self.key_coordinates_counter = 0
254 if self.output_layout == "LAYOUT_ergodox":
255 formatted_key_symbols = """
256// left hand
257
258{}
259{}
260{}
261{}
262{}
263
264// left thumb
265
266{}
267{}
268{}
269
270// right hand
271
272{}
273{}
274{}
275{}
276{}
277
278// right thumb
279
280{}
281{}
282{}
283""".format(
284 # left hand
285 self.get_padded_line(keys, 0, 7, just="left"),
286 self.get_padded_line(keys, 7, 14, just="left"),
287 self.get_padded_line(keys, 14, 20, just="left"),
288 self.get_padded_line(keys, 20, 27, just="left"),
289 self.get_padded_line(keys, 27, 32, just="left"),
290 # left thumb
291 self.get_padded_line(keys, 32, 34, just="left"),
292 self.get_padded_line(keys, 34, 35, just="left"),
293 self.get_padded_line(keys, 35, 38, just="left"),
294 # right hand
295 self.get_padded_line(keys, 38, 45, just="left"),
296 self.get_padded_line(keys, 45, 52, just="left"),
297 self.get_padded_line(keys, 52, 58, just="left"),
298 self.get_padded_line(keys, 58, 65, just="left"),
299 self.get_padded_line(keys, 65, 70, just="left"),
300 # right thumb
301 self.get_padded_line(keys, 70, 72, just="left"),
302 self.get_padded_line(keys, 72, 73, just="left"),
303 self.get_padded_line(keys, 73, 76, just="left"),
304 )
305 elif self.output_layout == "LAYOUT_ergodox_pretty":
306 left_half_justification = "right" if self.justify_toward_center else "left"
307 formatted_key_symbols = """
308{} {}
309{} {}
310{} {}
311{} {}
312{} {}
313
314{} {}
315{} {}
316{} {}
317""".format(
318 self.get_padded_line(keys, 0, 7, just=left_half_justification), self.get_padded_line(keys, 38, 45, just="left"),
319 self.get_padded_line(keys, 7, 14, just=left_half_justification), self.get_padded_line(keys, 45, 52, just="left"),
320 self.get_padded_line(keys, 14, 20, just=left_half_justification), self.get_padded_line(keys, 52, 58, just="left"),
321 self.get_padded_line(keys, 20, 27, just=left_half_justification), self.get_padded_line(keys, 58, 65, just="left"),
322 self.get_padded_line(keys, 27, 32, just=left_half_justification), self.get_padded_line(keys, 65, 70, just="left"),
323
324 self.get_padded_line(keys, 32, 34, just=left_half_justification), self.get_padded_line(keys, 70, 72, just="left"),
325 self.get_padded_line(keys, 34, 35, just=left_half_justification), self.get_padded_line(keys, 72, 73, just="left"),
326 self.get_padded_line(keys, 35, 38, just=left_half_justification), self.get_padded_line(keys, 73, 76, just="left"),
327
328 )
329 else:
330 formatted_key_symbols = ""
331
332 # rid of the trailing comma
333 formatted_key_symbols = formatted_key_symbols[0:len(formatted_key_symbols)-2] + "\n"
334 s = "[{}] = {}({})".format(layer, self.output_layout, formatted_key_symbols)
335 return s
336
337 # helper functions for pycparser
338 def parser(self, src):
339 src = self.comment_remover(src)
340 return pycparser.CParser().parse(src)
341 def comment_remover(self, text):
342 # remove comments since pycparser cannot deal with them
343 # credit: https://stackoverflow.com/a/241506
344 def replacer(match):
345 s = match.group(0)
346 if s.startswith('/'):
347 return " " # note: a space and not an empty string
348 else:
349 return s
350 pattern = re.compile(
351 r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
352 re.DOTALL | re.MULTILINE
353 )
354 return re.sub(pattern, replacer, text)
355
356 def function_expr(self, f):
357 name = f.name.name
358 args = []
359 for arg in f.args.exprs:
360 if type(arg) is pycparser.c_ast.Constant:
361 args.append(arg.value)
362 elif type(arg) is pycparser.c_ast.ID:
363 args.append(arg.name)
364 return "{}({})".format(name, ",".join(args))
365
366 def key_expr(self, raw):
367 if type(raw) is pycparser.c_ast.ID:
368 if raw.name in self.KEY_ALIASES:
369 return self.KEY_ALIASES[raw.name]
370 return raw.name
371 elif type(raw) is pycparser.c_ast.FuncCall:
372 return self.function_expr(raw)
373
374 def layer_expr(self, layer):
375 transformed = [self.key_expr(k) for k in layer.expr.args.exprs]
376 return transformed
377
378
379if __name__ == "__main__":
380
381 parser = argparse.ArgumentParser(description="Beautify keymap.c downloaded from ErgoDox-Ez Configurator for easier customization.")
382 parser.add_argument("input_filename", help="input file: c source code file that has the layer keymaps")
383 parser.add_argument("-o", "--output-filename", help="output file: beautified c filename. If not given, output to STDOUT.")
384 parser.add_argument("-p", "--pretty-output-layout", action="store_true", help="use LAYOUT_ergodox_pretty for output instead of LAYOUT_ergodox")
385 parser.add_argument("-c", "--justify-toward-center", action="store_true", help="for LAYOUT_ergodox_pretty, align right for the left half, and align left for the right half. Default is align left for both halves.")
386 args = parser.parse_args()
387 if args.pretty_output_layout:
388 output_layout="LAYOUT_ergodox_pretty"
389 else:
390 output_layout="LAYOUT_ergodox"
391 with open(args.input_filename) as f:
392 source_code = f.read()
393 result = KeymapBeautifier(source_code, output_layout=output_layout, justify_toward_center=args.justify_toward_center).output
394 if args.output_filename:
395 with open(args.output_filename, "w") as f:
396 f.write(result)
397 else:
398 print(result)
399
diff --git a/keyboards/ergodox_ez/util/keymap_beautifier/README.md b/keyboards/ergodox_ez/util/keymap_beautifier/README.md
new file mode 100644
index 000000000..bd3d125a6
--- /dev/null
+++ b/keyboards/ergodox_ez/util/keymap_beautifier/README.md
@@ -0,0 +1,139 @@
1# keymap_beautifier.py
2
3## About
4This Python 3 script, by [Tsan-Kuang Lee](https://github.com/tsankuanglee) takes the keymap.c downloaded from [ErgoDox EZ Configurator](https://configure.ergodox-ez.com/) and beautifies it for easier customization, allowing one to quickly draft a layout to build upon.
5
6## Features
7For example, the original `keymap.c` looks like
8
9```
10[0] = LAYOUT_ergodox(KC_EQUAL,KC_1,KC_2,KC_3,KC_4,KC_5,LCTL(KC_MINUS),KC_DELETE,KC_Q,KC_W,KC_E,KC_R,KC_T,KC_LBRACKET,KC_BSPACE,KC_A,KC_S,KC_D,KC_F,KC_G,KC_LSPO,CTL_T(KC_Z),KC_X,KC_C,KC_V,KC_B,ALL_T(KC_NO),LT(1,KC_GRAVE),KC_QUOTE,LALT(KC_LSHIFT),KC_LEFT,KC_RIGHT,ALT_T(KC_APPLICATION),KC_LGUI,KC_HOME,KC_SPACE,KC_UNDS,KC_END,LCTL(KC_EQUAL),KC_6,KC_7,KC_8,KC_9,KC_0,KC_MINUS,KC_RBRACKET,KC_Y,KC_U,KC_I,KC_O,KC_P,KC_BSLASH,KC_H,ALT_T(KC_J),KC_K,KC_L,LT(2,KC_SCOLON),GUI_T(KC_QUOTE),MEH_T(KC_NO),KC_N,KC_M,KC_COMMA,KC_DOT,CTL_T(KC_SLASH),KC_RSPC,KC_UP,KC_DOWN,KC_LBRACKET,KC_RBRACKET,TT(1),KC_LALT,CTL_T(KC_ESCAPE),KC_PGUP,KC_PGDOWN,LT(1,KC_TAB),KC_ENTER),
11```
12
13The beautifier parses it and outputs:
14
15```
16[0] = LAYOUT_ergodox(
17// left hand
18
19KC_EQUAL , KC_1 , KC_2 , KC_3 , KC_4 , KC_5, LCTL(KC_MINUS),
20KC_DELETE , KC_Q , KC_W , KC_E , KC_R , KC_T, KC_LBRACKET ,
21KC_BSPACE , KC_A , KC_S , KC_D , KC_F , KC_G,
22KC_LSPO , CTL_T(KC_Z), KC_X , KC_C , KC_V , KC_B, ALL_T(KC_NO) ,
23LT(1,KC_GRAVE), KC_QUOTE , LALT(KC_LSHIFT), KC_LEFT, KC_RIGHT,
24
25// left thumb
26
27 ALT_T(KC_APPLICATION), KC_LGUI,
28 KC_HOME,
29KC_SPACE, KC_UNDS , KC_END ,
30
31// right hand
32
33LCTL(KC_EQUAL), KC_6, KC_7 , KC_8 , KC_9 , KC_0 , KC_MINUS ,
34KC_RBRACKET , KC_Y, KC_U , KC_I , KC_O , KC_P , KC_BSLASH ,
35 KC_H, ALT_T(KC_J), KC_K , KC_L , LT(2,KC_SCOLON), GUI_T(KC_QUOTE),
36MEH_T(KC_NO) , KC_N, KC_M , KC_COMMA, KC_DOT , CTL_T(KC_SLASH), KC_RSPC ,
37 KC_UP , KC_DOWN , KC_LBRACKET, KC_RBRACKET , TT(1) ,
38
39// right thumb
40
41KC_LALT , CTL_T(KC_ESCAPE),
42KC_PGUP ,
43KC_PGDOWN, LT(1,KC_TAB) , KC_ENTER
44)
45```
46
47Optionally, it can also render [LAYOUT_ergodox_pretty](https://github.com/qmk/qmk_firmware/blob/ee700b2e831067bdb7584425569b61bc6329247b/keyboards/ergodox_ez/keymaps/bpruitt-goddard/keymap.c#L49-L57):
48```
49[0] = LAYOUT_ergodox_pretty(
50 KC_ESCAPE, KC_1, KC_2, KC_3, KC_4, KC_5, KC_LEAD, KC_LEAD, KC_6 , KC_7 , KC_8 , KC_9 , KC_0 , KC_BSPACE ,
51 KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_HYPR, KC_HYPR, KC_Y , KC_U , KC_I , KC_O , KC_P , KC_BSLASH ,
52 KC_LCTRL, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H , KC_J , KC_K , KC_L , KC_SCOLON , KC_QUOTE ,
53 KC_LSHIFT, KC_Z, KC_X, KC_C, KC_V, KC_B, SH_MON, SH_MON , KC_N , KC_M , KC_COMMA , KC_DOT , KC_SLASH , KC_RSHIFT ,
54LT(6,KC_NO), LT(7,KC_NO), KC_LCTRL, KC_LGUI, KC_LALT, ALGR_T(KC_MINUS), RGUI_T(KC_EQUAL), RCTL_T(KC_LBRACKET), LT(10,KC_RBRACKET), LT(6,KC_APPLICATION),
55
56 LT(6,KC_GRAVE), MEH_T(KC_NO), KC_LEFT, KC_RIGHT ,
57 LT(10,KC_DELETE), KC_UP ,
58 KC_SPACE, LT(8,KC_ENTER), LT(7,KC_BSPACE), KC_DOWN, LT(7,KC_SPACE), LT(8,KC_ENTER)
59)
60```
61
62We can also align everythng t othe left (easier editing in my opinon):
63```
64[0] = LAYOUT_ergodox_pretty(
65KC_ESCAPE , KC_1 , KC_2 , KC_3 , KC_4 , KC_5 , KC_LEAD , KC_LEAD, KC_6 , KC_7 , KC_8 , KC_9 , KC_0 , KC_BSPACE ,
66KC_TAB , KC_Q , KC_W , KC_E , KC_R , KC_T , KC_HYPR , KC_HYPR, KC_Y , KC_U , KC_I , KC_O , KC_P , KC_BSLASH ,
67KC_LCTRL , KC_A , KC_S , KC_D , KC_F , KC_G , KC_H , KC_J , KC_K , KC_L , KC_SCOLON , KC_QUOTE ,
68KC_LSHIFT , KC_Z , KC_X , KC_C , KC_V , KC_B , SH_MON , SH_MON , KC_N , KC_M , KC_COMMA , KC_DOT , KC_SLASH , KC_RSHIFT ,
69LT(6,KC_NO), LT(7,KC_NO), KC_LCTRL, KC_LGUI, KC_LALT , ALGR_T(KC_MINUS), RGUI_T(KC_EQUAL), RCTL_T(KC_LBRACKET), LT(10,KC_RBRACKET), LT(6,KC_APPLICATION),
70
71 LT(6,KC_GRAVE), MEH_T(KC_NO) , KC_LEFT, KC_RIGHT ,
72 LT(10,KC_DELETE), KC_UP ,
73 KC_SPACE, LT(8,KC_ENTER), LT(7,KC_BSPACE) , KC_DOWN, LT(7,KC_SPACE), LT(8,KC_ENTER)
74)
75```
76
77## Usage
78
79### With docker
80This is the cleaner way. `Docker` is the only requirement. The program executes within a container that has all dependencies installed.
81
82First build the images. (Run once)
83```
84cd QMK_GIT_REPO_dir/keyboards/ergodox_ez/util/keymap_beautifier
85docker build -t keymapbeautifier:1.0 .
86```
87Run it
88```
89cd QMK_GIT_REPO_dir/keyboards/ergodox_ez/util/keymap_beautifier
90cp PATH_TO_YOUR_C_SOURCE_FILE.c input.c
91./docker_run.sh input.c -p -c -o output.c
92```
93The prettified file is written to `output.c`. See the section Tweaks for non-default settings.
94
95### Without docker
96Requirements:
97* python3 (tested on 3.7.4)
98* python module `pycparser` installed (with `pip install pycparser`)
99
100To run:
101```
102cd QMK_GIT_REPO_dir/keyboards/ergodox_ez/util/keymap_beautifier
103cp PATH_TO_YOUR_C_SOURCE_FILE.c input.c
104./KeymapBeautifier.py input.c -p -c -o output.c
105```
106The prettified file is written to `output.c`. See the section Tweaks for non-default settings.
107
108## Tweaks
109```
110usage: KeymapBeautifier.py [-h] [-o OUTPUT_FILENAME] [-p] [-c] input_filename
111
112Beautify keymap.c downloaded from ErgoDox-Ez Configurator for easier
113customization.
114
115positional arguments:
116 input_filename input file: c source code file that has the layer
117 keymaps
118
119optional arguments:
120 -h, --help show this help message and exit
121 -o OUTPUT_FILENAME, --output-filename OUTPUT_FILENAME
122 output file: beautified c filename. If not given,
123 output to STDOUT.
124 -p, --pretty-output-layout
125 use LAYOUT_ergodox_pretty for output instead of
126 LAYOUT_ergodox
127 -c, --justify-toward-center
128 for LAYOUT_ergodox_pretty, align right for the left
129 half, and align left for the right half. Default is
130 align left for both halves.
131```
132For example,
133```
134./docker_run.sh input.c -p -c -o output.c
135# or if you don't want to use docker:
136#./KeymapBeautifier.py input.c -p -c -o output.c
137```
138will read `input.c`, and produce `output.c` with LAYOUT_ergodox_pretty, and have the key symbols gravitating toward the center.
139
diff --git a/keyboards/ergodox_ez/util/keymap_beautifier/docker_run.sh b/keyboards/ergodox_ez/util/keymap_beautifier/docker_run.sh
new file mode 100755
index 000000000..1ce43a6dd
--- /dev/null
+++ b/keyboards/ergodox_ez/util/keymap_beautifier/docker_run.sh
@@ -0,0 +1,3 @@
1#!/bin/sh
2
3docker run --mount type=bind,source="${PWD}",target=/usr/src/app --name keymapbeautifier --rm keymapbeautifier:1.0 ./KeymapBeautifier.py $*
diff --git a/keyboards/ergodox_ez/util/keymap_beautifier/requirements.txt b/keyboards/ergodox_ez/util/keymap_beautifier/requirements.txt
new file mode 100644
index 000000000..dc1c9e101
--- /dev/null
+++ b/keyboards/ergodox_ez/util/keymap_beautifier/requirements.txt
@@ -0,0 +1 @@
pycparser
diff --git a/keyboards/ergodox_ez/util/readme.md b/keyboards/ergodox_ez/util/readme.md
index 26c5e5d99..deb0cad5d 100644
--- a/keyboards/ergodox_ez/util/readme.md
+++ b/keyboards/ergodox_ez/util/readme.md
@@ -1,3 +1,11 @@
1# ErgoDox EZ Utilities 1# ErgoDox EZ Utilities
2 2
3## compile_keymap.py
4
3The Python script in this directory, by [mbarkhau](https://github.com/mbarkhau) allows you to write out a basic ErgoDox EZ keymap using Markdown notation, and then transpile it to C, which you can then compile. It's experimental, but if you're not comfortable using C, it's a nice option. 5The Python script in this directory, by [mbarkhau](https://github.com/mbarkhau) allows you to write out a basic ErgoDox EZ keymap using Markdown notation, and then transpile it to C, which you can then compile. It's experimental, but if you're not comfortable using C, it's a nice option.
6
7## keymap_beautifier.py
8
9This Python 3 script, by [Tsan-Kuang Lee](https://github.com/tsankuanglee) takes the keymap.c downloaded from [ErgoDox EZ Configurator](https://configure.ergodox-ez.com/) and beautifies it for easier customization, allowing one to quickly draft a layout to build upon.
10
11See [README.md](./keymap_beautifier/README.md) for this utility for more details.