diff options
| author | Tsan-Kuang Lee <1425438+tsankuanglee@users.noreply.github.com> | 2019-11-05 01:14:15 -0600 |
|---|---|---|
| committer | Drashna Jaelre <drashna@live.com> | 2019-11-04 23:14:15 -0800 |
| commit | 8b832c494cd6d50e4f9c8384ce54733308eda95f (patch) | |
| tree | aa86f942fcabf6df270a4baa9160ba87bb9803e0 | |
| parent | dcb2d63302fefe1b37b8ca606ce7241ae8655389 (diff) | |
| download | qmk_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.py | 0 | ||||
| -rw-r--r-- | keyboards/ergodox_ez/util/keymap_beautifier/Dockerfile | 8 | ||||
| -rwxr-xr-x | keyboards/ergodox_ez/util/keymap_beautifier/KeymapBeautifier.py | 399 | ||||
| -rw-r--r-- | keyboards/ergodox_ez/util/keymap_beautifier/README.md | 139 | ||||
| -rwxr-xr-x | keyboards/ergodox_ez/util/keymap_beautifier/docker_run.sh | 3 | ||||
| -rw-r--r-- | keyboards/ergodox_ez/util/keymap_beautifier/requirements.txt | 1 | ||||
| -rw-r--r-- | keyboards/ergodox_ez/util/readme.md | 8 |
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 @@ | |||
| 1 | FROM python:3.7.4-alpine3.10 | ||
| 2 | |||
| 3 | WORKDIR /usr/src/app | ||
| 4 | COPY requirements.txt ./ | ||
| 5 | RUN pip install --no-cache-dir -r requirements.txt | ||
| 6 | COPY ./KeymapBeautifier.py ./KeymapBeautifier.py | ||
| 7 | |||
| 8 | CMD [ "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 | |||
| 3 | import argparse | ||
| 4 | import pycparser | ||
| 5 | import re | ||
| 6 | |||
| 7 | class 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 | |||
| 379 | if __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 | ||
| 4 | This 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 | ||
| 7 | For 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 | |||
| 13 | The beautifier parses it and outputs: | ||
| 14 | |||
| 15 | ``` | ||
| 16 | [0] = LAYOUT_ergodox( | ||
| 17 | // left hand | ||
| 18 | |||
| 19 | KC_EQUAL , KC_1 , KC_2 , KC_3 , KC_4 , KC_5, LCTL(KC_MINUS), | ||
| 20 | KC_DELETE , KC_Q , KC_W , KC_E , KC_R , KC_T, KC_LBRACKET , | ||
| 21 | KC_BSPACE , KC_A , KC_S , KC_D , KC_F , KC_G, | ||
| 22 | KC_LSPO , CTL_T(KC_Z), KC_X , KC_C , KC_V , KC_B, ALL_T(KC_NO) , | ||
| 23 | LT(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, | ||
| 29 | KC_SPACE, KC_UNDS , KC_END , | ||
| 30 | |||
| 31 | // right hand | ||
| 32 | |||
| 33 | LCTL(KC_EQUAL), KC_6, KC_7 , KC_8 , KC_9 , KC_0 , KC_MINUS , | ||
| 34 | KC_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), | ||
| 36 | MEH_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 | |||
| 41 | KC_LALT , CTL_T(KC_ESCAPE), | ||
| 42 | KC_PGUP , | ||
| 43 | KC_PGDOWN, LT(1,KC_TAB) , KC_ENTER | ||
| 44 | ) | ||
| 45 | ``` | ||
| 46 | |||
| 47 | Optionally, 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 , | ||
| 54 | LT(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 | |||
| 62 | We can also align everythng t othe left (easier editing in my opinon): | ||
| 63 | ``` | ||
| 64 | [0] = LAYOUT_ergodox_pretty( | ||
| 65 | 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 , | ||
| 66 | 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 , | ||
| 67 | KC_LCTRL , KC_A , KC_S , KC_D , KC_F , KC_G , KC_H , KC_J , KC_K , KC_L , KC_SCOLON , KC_QUOTE , | ||
| 68 | 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 , | ||
| 69 | LT(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 | ||
| 80 | This is the cleaner way. `Docker` is the only requirement. The program executes within a container that has all dependencies installed. | ||
| 81 | |||
| 82 | First build the images. (Run once) | ||
| 83 | ``` | ||
| 84 | cd QMK_GIT_REPO_dir/keyboards/ergodox_ez/util/keymap_beautifier | ||
| 85 | docker build -t keymapbeautifier:1.0 . | ||
| 86 | ``` | ||
| 87 | Run it | ||
| 88 | ``` | ||
| 89 | cd QMK_GIT_REPO_dir/keyboards/ergodox_ez/util/keymap_beautifier | ||
| 90 | cp PATH_TO_YOUR_C_SOURCE_FILE.c input.c | ||
| 91 | ./docker_run.sh input.c -p -c -o output.c | ||
| 92 | ``` | ||
| 93 | The prettified file is written to `output.c`. See the section Tweaks for non-default settings. | ||
| 94 | |||
| 95 | ### Without docker | ||
| 96 | Requirements: | ||
| 97 | * python3 (tested on 3.7.4) | ||
| 98 | * python module `pycparser` installed (with `pip install pycparser`) | ||
| 99 | |||
| 100 | To run: | ||
| 101 | ``` | ||
| 102 | cd QMK_GIT_REPO_dir/keyboards/ergodox_ez/util/keymap_beautifier | ||
| 103 | cp PATH_TO_YOUR_C_SOURCE_FILE.c input.c | ||
| 104 | ./KeymapBeautifier.py input.c -p -c -o output.c | ||
| 105 | ``` | ||
| 106 | The prettified file is written to `output.c`. See the section Tweaks for non-default settings. | ||
| 107 | |||
| 108 | ## Tweaks | ||
| 109 | ``` | ||
| 110 | usage: KeymapBeautifier.py [-h] [-o OUTPUT_FILENAME] [-p] [-c] input_filename | ||
| 111 | |||
| 112 | Beautify keymap.c downloaded from ErgoDox-Ez Configurator for easier | ||
| 113 | customization. | ||
| 114 | |||
| 115 | positional arguments: | ||
| 116 | input_filename input file: c source code file that has the layer | ||
| 117 | keymaps | ||
| 118 | |||
| 119 | optional 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 | ``` | ||
| 132 | For 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 | ``` | ||
| 138 | will 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 | |||
| 3 | docker 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 | |||
| 3 | The 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. | 5 | The 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 | |||
| 9 | This 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 | |||
| 11 | See [README.md](./keymap_beautifier/README.md) for this utility for more details. | ||
