Skip to content

Commit 588bcdc

Browse files
authored
Add support for tab completion (#12411)
* Add support for tab completion * make flake8 happy * Add documentation
1 parent b33e679 commit 588bcdc

23 files changed

+169
-86
lines changed

docs/_summary.md

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
* [Overview](cli.md)
3030
* [Configuration](cli_configuration.md)
3131
* [Commands](cli_commands.md)
32+
* [Tab Completion](cli_tab_complete.md)
3233

3334
* Using QMK
3435
* Guides

docs/cli_tab_complete.md

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Tab Completion for QMK
2+
3+
If you are using Bash 4.2 or later, Zsh, or FiSH you can enable Tab Completion for the QMK CLI. This will let you tab complete the names of flags, keyboards, files, and other `qmk` options.
4+
5+
## Setup
6+
7+
There are several ways you can setup tab completion.
8+
9+
### For Your User Only
10+
11+
Add this to the end of your `.profile` or `.bashrc`:
12+
13+
source ~/qmk_firmware/util/qmk_tab_complete.sh
14+
15+
If you put `qmk_firmware` into another location you will need to adjust this path.
16+
17+
### System Wide Symlink
18+
19+
If you want the tab completion available to all users of the system you can add a symlink to the `qmk_tab_complete.sh` script:
20+
21+
`ln -s ~/qmk_firmware/util/qmk_tab_complete.sh /etc/profile.d/qmk_tab_complete.sh`
22+
23+
### System Wide Copy
24+
25+
In some cases a symlink may not work. Instead you can copy the file directly into place. Be aware that updates to the tab complete script may happen from time to time, you will want to recopy the file periodically.
26+
27+
cp util/qmk_tab_complete.sh /etc/profile.d

lib/python/qmk/cli/__init__.py

+12-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"""
55
import sys
66

7-
from milc import cli
7+
from milc import cli, __VERSION__
88

99
from . import c2json
1010
from . import cformat
@@ -47,5 +47,15 @@
4747
# void: 3.9
4848

4949
if sys.version_info[0] != 3 or sys.version_info[1] < 7:
50-
cli.log.error('Your Python is too old! Please upgrade to Python 3.7 or later.')
50+
print('Error: Your Python is too old! Please upgrade to Python 3.7 or later.')
51+
exit(127)
52+
53+
milc_version = __VERSION__.split('.')
54+
55+
if int(milc_version[0]) < 2 and int(milc_version[1]) < 3:
56+
from pathlib import Path
57+
58+
requirements = Path('requirements.txt').resolve()
59+
60+
print(f'Your MILC library is too old! Please upgrade: python3 -m pip install -U -r {str(requirements)}')
5161
exit(127)

lib/python/qmk/cli/c2json.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,21 @@
22
"""
33
import json
44

5+
from argcomplete.completers import FilesCompleter
56
from milc import cli
67

78
import qmk.keymap
89
import qmk.path
910
from qmk.json_encoders import InfoJSONEncoder
10-
from qmk.keyboard import keyboard_folder
11+
from qmk.keyboard import keyboard_completer, keyboard_folder
1112

1213

1314
@cli.argument('--no-cpp', arg_only=True, action='store_false', help='Do not use \'cpp\' on keymap.c')
1415
@cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to')
1516
@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
16-
@cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, required=True, help='The keyboard\'s name')
17+
@cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, completer=keyboard_completer, required=True, help='The keyboard\'s name')
1718
@cli.argument('-km', '--keymap', arg_only=True, required=True, help='The keymap\'s name')
18-
@cli.argument('filename', arg_only=True, help='keymap.c file')
19+
@cli.argument('filename', arg_only=True, completer=FilesCompleter('.c'), help='keymap.c file')
1920
@cli.subcommand('Creates a keymap.json from a keymap.c file.')
2021
def c2json(cli):
2122
"""Generate a keymap.json from a keymap.c file.

lib/python/qmk/cli/cformat.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import subprocess
44
from shutil import which
55

6+
from argcomplete.completers import FilesCompleter
67
from milc import cli
78

89
from qmk.path import normpath
@@ -33,7 +34,7 @@ def cformat_run(files, all_files):
3334

3435
@cli.argument('-a', '--all-files', arg_only=True, action='store_true', help='Format all core files.')
3536
@cli.argument('-b', '--base-branch', default='origin/master', help='Branch to compare to diffs to.')
36-
@cli.argument('files', nargs='*', arg_only=True, help='Filename(s) to format.')
37+
@cli.argument('files', nargs='*', arg_only=True, completer=FilesCompleter('.c'), help='Filename(s) to format.')
3738
@cli.subcommand("Format C code according to QMK's style.", hidden=False if cli.config.user.developer else True)
3839
def cformat(cli):
3940
"""Format C code according to QMK's style.

lib/python/qmk/cli/compile.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@
22
33
You can compile a keymap already in the repo or using a QMK Configurator export.
44
"""
5+
from argcomplete.completers import FilesCompleter
56
from milc import cli
67

78
import qmk.path
89
from qmk.decorators import automagic_keyboard, automagic_keymap
910
from qmk.commands import compile_configurator_json, create_make_command, parse_configurator_json
10-
from qmk.keyboard import keyboard_folder
11+
from qmk.keyboard import keyboard_completer, keyboard_folder
12+
from qmk.keymap import keymap_completer
1113

1214

13-
@cli.argument('filename', nargs='?', arg_only=True, type=qmk.path.FileType('r'), help='The configurator export to compile')
14-
@cli.argument('-kb', '--keyboard', type=keyboard_folder, help='The keyboard to build a firmware for. Ignored when a configurator export is supplied.')
15-
@cli.argument('-km', '--keymap', help='The keymap to build a firmware for. Ignored when a configurator export is supplied.')
15+
@cli.argument('filename', nargs='?', arg_only=True, type=qmk.path.FileType('r'), completer=FilesCompleter('.json'), help='The configurator export to compile')
16+
@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='The keyboard to build a firmware for. Ignored when a configurator export is supplied.')
17+
@cli.argument('-km', '--keymap', completer=keymap_completer, help='The keymap to build a firmware for. Ignored when a configurator export is supplied.')
1618
@cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help="Don't actually build, just show the make command to be run.")
1719
@cli.argument('-j', '--parallel', type=int, default=1, help="Set the number of parallel make jobs to run.")
1820
@cli.argument('-e', '--env', arg_only=True, action='append', default=[], help="Set a variable to be passed to make. May be passed multiple times.")

lib/python/qmk/cli/flash.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
A bootloader must be specified.
55
"""
66

7+
from argcomplete.completers import FilesCompleter
78
from milc import cli
89

910
import qmk.path
1011
from qmk.decorators import automagic_keyboard, automagic_keymap
1112
from qmk.commands import compile_configurator_json, create_make_command, parse_configurator_json
12-
from qmk.keyboard import keyboard_folder
13+
from qmk.keyboard import keyboard_completer, keyboard_folder
1314

1415

1516
def print_bootloader_help():
@@ -30,11 +31,11 @@ def print_bootloader_help():
3031
cli.echo('For more info, visit https://docs.qmk.fm/#/flashing')
3132

3233

33-
@cli.argument('filename', nargs='?', arg_only=True, type=qmk.path.FileType('r'), help='The configurator export JSON to compile.')
34+
@cli.argument('filename', nargs='?', arg_only=True, type=qmk.path.FileType('r'), completer=FilesCompleter('.json'), help='The configurator export JSON to compile.')
3435
@cli.argument('-b', '--bootloaders', action='store_true', help='List the available bootloaders.')
3536
@cli.argument('-bl', '--bootloader', default='flash', help='The flash command, corresponding to qmk\'s make options of bootloaders.')
3637
@cli.argument('-km', '--keymap', help='The keymap to build a firmware for. Use this if you dont have a configurator file. Ignored when a configurator file is supplied.')
37-
@cli.argument('-kb', '--keyboard', type=keyboard_folder, help='The keyboard to build a firmware for. Use this if you dont have a configurator file. Ignored when a configurator file is supplied.')
38+
@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='The keyboard to build a firmware for. Use this if you dont have a configurator file. Ignored when a configurator file is supplied.')
3839
@cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help="Don't actually build, just show the make command to be run.")
3940
@cli.argument('-j', '--parallel', type=int, default=1, help="Set the number of parallel make jobs to run.")
4041
@cli.argument('-e', '--env', arg_only=True, action='append', default=[], help="Set a variable to be passed to make. May be passed multiple times.")

lib/python/qmk/cli/generate/config_h.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from qmk.decorators import automagic_keyboard, automagic_keymap
99
from qmk.info import info_json
1010
from qmk.json_schema import json_load
11-
from qmk.keyboard import keyboard_folder
11+
from qmk.keyboard import keyboard_completer, keyboard_folder
1212
from qmk.path import is_keyboard, normpath
1313

1414

@@ -75,7 +75,7 @@ def matrix_pins(matrix_pins):
7575

7676
@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')
7777
@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
78-
@cli.argument('-kb', '--keyboard', type=keyboard_folder, help='Keyboard to generate config.h for.')
78+
@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Keyboard to generate config.h for.')
7979
@cli.subcommand('Used by the make system to generate info_config.h from info.json', hidden=True)
8080
@automagic_keyboard
8181
@automagic_keymap

lib/python/qmk/cli/generate/dfu_header.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66
from qmk.decorators import automagic_keyboard
77
from qmk.info import info_json
88
from qmk.path import is_keyboard, normpath
9+
from qmk.keyboard import keyboard_completer
910

1011

1112
@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')
1213
@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
13-
@cli.argument('-kb', '--keyboard', help='Keyboard to generate LUFA Keyboard.h for.')
14+
@cli.argument('-kb', '--keyboard', completer=keyboard_completer, help='Keyboard to generate LUFA Keyboard.h for.')
1415
@cli.subcommand('Used by the make system to generate LUFA Keyboard.h from info.json', hidden=True)
1516
@automagic_keyboard
1617
def generate_dfu_header(cli):

lib/python/qmk/cli/generate/info_json.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from qmk.info import info_json
1212
from qmk.json_encoders import InfoJSONEncoder
1313
from qmk.json_schema import load_jsonschema
14-
from qmk.keyboard import keyboard_folder
14+
from qmk.keyboard import keyboard_completer, keyboard_folder
1515
from qmk.path import is_keyboard
1616

1717

@@ -41,7 +41,7 @@ def strip_info_json(kb_info_json):
4141
return validator(kb_info_json)
4242

4343

44-
@cli.argument('-kb', '--keyboard', type=keyboard_folder, help='Keyboard to show info for.')
44+
@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Keyboard to show info for.')
4545
@cli.argument('-km', '--keymap', help='Show the layers for a JSON keymap too.')
4646
@cli.subcommand('Generate an info.json file for a keyboard.', hidden=False if cli.config.user.developer else True)
4747
@automagic_keyboard

lib/python/qmk/cli/generate/layouts.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from qmk.constants import COL_LETTERS, ROW_LETTERS
66
from qmk.decorators import automagic_keyboard, automagic_keymap
77
from qmk.info import info_json
8-
from qmk.keyboard import keyboard_folder
8+
from qmk.keyboard import keyboard_completer, keyboard_folder
99
from qmk.path import is_keyboard, normpath
1010

1111
usb_properties = {
@@ -17,7 +17,7 @@
1717

1818
@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')
1919
@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
20-
@cli.argument('-kb', '--keyboard', type=keyboard_folder, help='Keyboard to generate config.h for.')
20+
@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Keyboard to generate config.h for.')
2121
@cli.subcommand('Used by the make system to generate layouts.h from info.json', hidden=True)
2222
@automagic_keyboard
2323
@automagic_keymap

lib/python/qmk/cli/generate/rules_mk.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from qmk.decorators import automagic_keyboard, automagic_keymap
99
from qmk.info import info_json
1010
from qmk.json_schema import json_load
11-
from qmk.keyboard import keyboard_folder
11+
from qmk.keyboard import keyboard_completer, keyboard_folder
1212
from qmk.path import is_keyboard, normpath
1313

1414

@@ -39,7 +39,7 @@ def process_mapping_rule(kb_info_json, rules_key, info_dict):
3939
@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')
4040
@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
4141
@cli.argument('-e', '--escape', arg_only=True, action='store_true', help="Escape spaces in quiet mode")
42-
@cli.argument('-kb', '--keyboard', type=keyboard_folder, help='Keyboard to generate config.h for.')
42+
@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Keyboard to generate config.h for.')
4343
@cli.subcommand('Used by the make system to generate info_config.h from info.json', hidden=True)
4444
@automagic_keyboard
4545
@automagic_keymap

lib/python/qmk/cli/info.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from qmk.json_encoders import InfoJSONEncoder
1111
from qmk.constants import COL_LETTERS, ROW_LETTERS
1212
from qmk.decorators import automagic_keyboard, automagic_keymap
13-
from qmk.keyboard import keyboard_folder, render_layouts, render_layout
13+
from qmk.keyboard import keyboard_completer, keyboard_folder, render_layouts, render_layout
1414
from qmk.keymap import locate_keymap
1515
from qmk.info import info_json
1616
from qmk.path import is_keyboard
@@ -124,7 +124,7 @@ def print_text_output(kb_info_json):
124124
show_keymap(kb_info_json, False)
125125

126126

127-
@cli.argument('-kb', '--keyboard', type=keyboard_folder, help='Keyboard to show info for.')
127+
@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Keyboard to show info for.')
128128
@cli.argument('-km', '--keymap', help='Show the layers for a JSON keymap too.')
129129
@cli.argument('-l', '--layouts', action='store_true', help='Render the layouts.')
130130
@cli.argument('-m', '--matrix', action='store_true', help='Render the layouts with matrix information.')

lib/python/qmk/cli/json2c.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"""
33
import json
44

5+
from argcomplete.completers import FilesCompleter
56
from milc import cli
67

78
import qmk.keymap
@@ -10,7 +11,7 @@
1011

1112
@cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to')
1213
@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
13-
@cli.argument('filename', type=qmk.path.FileType('r'), arg_only=True, help='Configurator JSON file')
14+
@cli.argument('filename', type=qmk.path.FileType('r'), arg_only=True, completer=FilesCompleter('.json'), help='Configurator JSON file')
1415
@cli.subcommand('Creates a keymap.c from a QMK Configurator export.')
1516
def json2c(cli):
1617
"""Generate a keymap.c from a configurator export.

lib/python/qmk/cli/kle2json.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44
import os
55
from pathlib import Path
66

7+
from argcomplete.completers import FilesCompleter
78
from milc import cli
89
from kle2xy import KLE2xy
910

1011
from qmk.converter import kle2qmk
1112
from qmk.json_encoders import InfoJSONEncoder
1213

1314

14-
@cli.argument('filename', help='The KLE raw txt to convert')
15+
@cli.argument('filename', completer=FilesCompleter('.json'), help='The KLE raw txt to convert')
1516
@cli.argument('-f', '--force', action='store_true', help='Flag to overwrite current info.json')
1617
@cli.subcommand('Convert a KLE layout to a Configurator JSON', hidden=False if cli.config.user.developer else True)
1718
def kle2json(cli):

lib/python/qmk/cli/lint.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44

55
from qmk.decorators import automagic_keyboard, automagic_keymap
66
from qmk.info import info_json
7+
from qmk.keyboard import keyboard_completer
78
from qmk.keymap import locate_keymap
89
from qmk.path import is_keyboard, keyboard
910

1011

1112
@cli.argument('--strict', action='store_true', help='Treat warnings as errors.')
12-
@cli.argument('-kb', '--keyboard', help='The keyboard to check.')
13+
@cli.argument('-kb', '--keyboard', completer=keyboard_completer, help='The keyboard to check.')
1314
@cli.argument('-km', '--keymap', help='The keymap to check.')
1415
@cli.subcommand('Check keyboard and keymap for common mistakes.')
1516
@automagic_keyboard

lib/python/qmk/cli/list/keymaps.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44

55
import qmk.keymap
66
from qmk.decorators import automagic_keyboard
7-
from qmk.keyboard import keyboard_folder
7+
from qmk.keyboard import keyboard_completer, keyboard_folder
88

99

10-
@cli.argument("-kb", "--keyboard", type=keyboard_folder, help="Specify keyboard name. Example: 1upkeyboards/1up60hse")
10+
@cli.argument("-kb", "--keyboard", type=keyboard_folder, completer=keyboard_completer, help="Specify keyboard name. Example: 1upkeyboards/1up60hse")
1111
@cli.subcommand("List the keymaps for a specific keyboard")
1212
@automagic_keyboard
1313
def list_keymaps(cli):

lib/python/qmk/cli/new/keymap.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55

66
import qmk.path
77
from qmk.decorators import automagic_keyboard, automagic_keymap
8-
from qmk.keyboard import keyboard_folder
8+
from qmk.keyboard import keyboard_completer, keyboard_folder
99
from milc import cli
1010

1111

12-
@cli.argument('-kb', '--keyboard', type=keyboard_folder, help='Specify keyboard name. Example: 1upkeyboards/1up60hse')
12+
@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Specify keyboard name. Example: 1upkeyboards/1up60hse')
1313
@cli.argument('-km', '--keymap', help='Specify the name for the new keymap directory')
1414
@cli.subcommand('Creates a new keymap for the keyboard of your choosing')
1515
@automagic_keyboard

0 commit comments

Comments
 (0)