Skip to content

Commit

Permalink
Add support for tab completion (qmk#12411)
Browse files Browse the repository at this point in the history
* Add support for tab completion

* make flake8 happy

* Add documentation
  • Loading branch information
skullydazed authored Apr 15, 2021
1 parent b33e679 commit 588bcdc
Show file tree
Hide file tree
Showing 23 changed files with 169 additions and 86 deletions.
1 change: 1 addition & 0 deletions docs/_summary.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
* [Overview](cli.md)
* [Configuration](cli_configuration.md)
* [Commands](cli_commands.md)
* [Tab Completion](cli_tab_complete.md)

* Using QMK
* Guides
Expand Down
27 changes: 27 additions & 0 deletions docs/cli_tab_complete.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Tab Completion for QMK

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.

## Setup

There are several ways you can setup tab completion.

### For Your User Only

Add this to the end of your `.profile` or `.bashrc`:

source ~/qmk_firmware/util/qmk_tab_complete.sh

If you put `qmk_firmware` into another location you will need to adjust this path.

### System Wide Symlink

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:

`ln -s ~/qmk_firmware/util/qmk_tab_complete.sh /etc/profile.d/qmk_tab_complete.sh`

### System Wide Copy

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.

cp util/qmk_tab_complete.sh /etc/profile.d
14 changes: 12 additions & 2 deletions lib/python/qmk/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"""
import sys

from milc import cli
from milc import cli, __VERSION__

from . import c2json
from . import cformat
Expand Down Expand Up @@ -47,5 +47,15 @@
# void: 3.9

if sys.version_info[0] != 3 or sys.version_info[1] < 7:
cli.log.error('Your Python is too old! Please upgrade to Python 3.7 or later.')
print('Error: Your Python is too old! Please upgrade to Python 3.7 or later.')
exit(127)

milc_version = __VERSION__.split('.')

if int(milc_version[0]) < 2 and int(milc_version[1]) < 3:
from pathlib import Path

requirements = Path('requirements.txt').resolve()

print(f'Your MILC library is too old! Please upgrade: python3 -m pip install -U -r {str(requirements)}')
exit(127)
7 changes: 4 additions & 3 deletions lib/python/qmk/cli/c2json.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,21 @@
"""
import json

from argcomplete.completers import FilesCompleter
from milc import cli

import qmk.keymap
import qmk.path
from qmk.json_encoders import InfoJSONEncoder
from qmk.keyboard import keyboard_folder
from qmk.keyboard import keyboard_completer, keyboard_folder


@cli.argument('--no-cpp', arg_only=True, action='store_false', help='Do not use \'cpp\' on keymap.c')
@cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to')
@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
@cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, required=True, help='The keyboard\'s name')
@cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, completer=keyboard_completer, required=True, help='The keyboard\'s name')
@cli.argument('-km', '--keymap', arg_only=True, required=True, help='The keymap\'s name')
@cli.argument('filename', arg_only=True, help='keymap.c file')
@cli.argument('filename', arg_only=True, completer=FilesCompleter('.c'), help='keymap.c file')
@cli.subcommand('Creates a keymap.json from a keymap.c file.')
def c2json(cli):
"""Generate a keymap.json from a keymap.c file.
Expand Down
3 changes: 2 additions & 1 deletion lib/python/qmk/cli/cformat.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import subprocess
from shutil import which

from argcomplete.completers import FilesCompleter
from milc import cli

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

@cli.argument('-a', '--all-files', arg_only=True, action='store_true', help='Format all core files.')
@cli.argument('-b', '--base-branch', default='origin/master', help='Branch to compare to diffs to.')
@cli.argument('files', nargs='*', arg_only=True, help='Filename(s) to format.')
@cli.argument('files', nargs='*', arg_only=True, completer=FilesCompleter('.c'), help='Filename(s) to format.')
@cli.subcommand("Format C code according to QMK's style.", hidden=False if cli.config.user.developer else True)
def cformat(cli):
"""Format C code according to QMK's style.
Expand Down
10 changes: 6 additions & 4 deletions lib/python/qmk/cli/compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@
You can compile a keymap already in the repo or using a QMK Configurator export.
"""
from argcomplete.completers import FilesCompleter
from milc import cli

import qmk.path
from qmk.decorators import automagic_keyboard, automagic_keymap
from qmk.commands import compile_configurator_json, create_make_command, parse_configurator_json
from qmk.keyboard import keyboard_folder
from qmk.keyboard import keyboard_completer, keyboard_folder
from qmk.keymap import keymap_completer


@cli.argument('filename', nargs='?', arg_only=True, type=qmk.path.FileType('r'), help='The configurator export to compile')
@cli.argument('-kb', '--keyboard', type=keyboard_folder, help='The keyboard to build a firmware for. Ignored when a configurator export is supplied.')
@cli.argument('-km', '--keymap', help='The keymap to build a firmware for. Ignored when a configurator export is supplied.')
@cli.argument('filename', nargs='?', arg_only=True, type=qmk.path.FileType('r'), completer=FilesCompleter('.json'), help='The configurator export to compile')
@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.')
@cli.argument('-km', '--keymap', completer=keymap_completer, help='The keymap to build a firmware for. Ignored when a configurator export is supplied.')
@cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help="Don't actually build, just show the make command to be run.")
@cli.argument('-j', '--parallel', type=int, default=1, help="Set the number of parallel make jobs to run.")
@cli.argument('-e', '--env', arg_only=True, action='append', default=[], help="Set a variable to be passed to make. May be passed multiple times.")
Expand Down
7 changes: 4 additions & 3 deletions lib/python/qmk/cli/flash.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
A bootloader must be specified.
"""

from argcomplete.completers import FilesCompleter
from milc import cli

import qmk.path
from qmk.decorators import automagic_keyboard, automagic_keymap
from qmk.commands import compile_configurator_json, create_make_command, parse_configurator_json
from qmk.keyboard import keyboard_folder
from qmk.keyboard import keyboard_completer, keyboard_folder


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


@cli.argument('filename', nargs='?', arg_only=True, type=qmk.path.FileType('r'), help='The configurator export JSON to compile.')
@cli.argument('filename', nargs='?', arg_only=True, type=qmk.path.FileType('r'), completer=FilesCompleter('.json'), help='The configurator export JSON to compile.')
@cli.argument('-b', '--bootloaders', action='store_true', help='List the available bootloaders.')
@cli.argument('-bl', '--bootloader', default='flash', help='The flash command, corresponding to qmk\'s make options of bootloaders.')
@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.')
@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.')
@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.')
@cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help="Don't actually build, just show the make command to be run.")
@cli.argument('-j', '--parallel', type=int, default=1, help="Set the number of parallel make jobs to run.")
@cli.argument('-e', '--env', arg_only=True, action='append', default=[], help="Set a variable to be passed to make. May be passed multiple times.")
Expand Down
4 changes: 2 additions & 2 deletions lib/python/qmk/cli/generate/config_h.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from qmk.decorators import automagic_keyboard, automagic_keymap
from qmk.info import info_json
from qmk.json_schema import json_load
from qmk.keyboard import keyboard_folder
from qmk.keyboard import keyboard_completer, keyboard_folder
from qmk.path import is_keyboard, normpath


Expand Down Expand Up @@ -75,7 +75,7 @@ def matrix_pins(matrix_pins):

@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')
@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
@cli.argument('-kb', '--keyboard', type=keyboard_folder, help='Keyboard to generate config.h for.')
@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Keyboard to generate config.h for.')
@cli.subcommand('Used by the make system to generate info_config.h from info.json', hidden=True)
@automagic_keyboard
@automagic_keymap
Expand Down
3 changes: 2 additions & 1 deletion lib/python/qmk/cli/generate/dfu_header.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
from qmk.decorators import automagic_keyboard
from qmk.info import info_json
from qmk.path import is_keyboard, normpath
from qmk.keyboard import keyboard_completer


@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')
@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
@cli.argument('-kb', '--keyboard', help='Keyboard to generate LUFA Keyboard.h for.')
@cli.argument('-kb', '--keyboard', completer=keyboard_completer, help='Keyboard to generate LUFA Keyboard.h for.')
@cli.subcommand('Used by the make system to generate LUFA Keyboard.h from info.json', hidden=True)
@automagic_keyboard
def generate_dfu_header(cli):
Expand Down
4 changes: 2 additions & 2 deletions lib/python/qmk/cli/generate/info_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from qmk.info import info_json
from qmk.json_encoders import InfoJSONEncoder
from qmk.json_schema import load_jsonschema
from qmk.keyboard import keyboard_folder
from qmk.keyboard import keyboard_completer, keyboard_folder
from qmk.path import is_keyboard


Expand Down Expand Up @@ -41,7 +41,7 @@ def strip_info_json(kb_info_json):
return validator(kb_info_json)


@cli.argument('-kb', '--keyboard', type=keyboard_folder, help='Keyboard to show info for.')
@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Keyboard to show info for.')
@cli.argument('-km', '--keymap', help='Show the layers for a JSON keymap too.')
@cli.subcommand('Generate an info.json file for a keyboard.', hidden=False if cli.config.user.developer else True)
@automagic_keyboard
Expand Down
4 changes: 2 additions & 2 deletions lib/python/qmk/cli/generate/layouts.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from qmk.constants import COL_LETTERS, ROW_LETTERS
from qmk.decorators import automagic_keyboard, automagic_keymap
from qmk.info import info_json
from qmk.keyboard import keyboard_folder
from qmk.keyboard import keyboard_completer, keyboard_folder
from qmk.path import is_keyboard, normpath

usb_properties = {
Expand All @@ -17,7 +17,7 @@

@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')
@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
@cli.argument('-kb', '--keyboard', type=keyboard_folder, help='Keyboard to generate config.h for.')
@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Keyboard to generate config.h for.')
@cli.subcommand('Used by the make system to generate layouts.h from info.json', hidden=True)
@automagic_keyboard
@automagic_keymap
Expand Down
4 changes: 2 additions & 2 deletions lib/python/qmk/cli/generate/rules_mk.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from qmk.decorators import automagic_keyboard, automagic_keymap
from qmk.info import info_json
from qmk.json_schema import json_load
from qmk.keyboard import keyboard_folder
from qmk.keyboard import keyboard_completer, keyboard_folder
from qmk.path import is_keyboard, normpath


Expand Down Expand Up @@ -39,7 +39,7 @@ def process_mapping_rule(kb_info_json, rules_key, info_dict):
@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')
@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
@cli.argument('-e', '--escape', arg_only=True, action='store_true', help="Escape spaces in quiet mode")
@cli.argument('-kb', '--keyboard', type=keyboard_folder, help='Keyboard to generate config.h for.')
@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Keyboard to generate config.h for.')
@cli.subcommand('Used by the make system to generate info_config.h from info.json', hidden=True)
@automagic_keyboard
@automagic_keymap
Expand Down
4 changes: 2 additions & 2 deletions lib/python/qmk/cli/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from qmk.json_encoders import InfoJSONEncoder
from qmk.constants import COL_LETTERS, ROW_LETTERS
from qmk.decorators import automagic_keyboard, automagic_keymap
from qmk.keyboard import keyboard_folder, render_layouts, render_layout
from qmk.keyboard import keyboard_completer, keyboard_folder, render_layouts, render_layout
from qmk.keymap import locate_keymap
from qmk.info import info_json
from qmk.path import is_keyboard
Expand Down Expand Up @@ -124,7 +124,7 @@ def print_text_output(kb_info_json):
show_keymap(kb_info_json, False)


@cli.argument('-kb', '--keyboard', type=keyboard_folder, help='Keyboard to show info for.')
@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Keyboard to show info for.')
@cli.argument('-km', '--keymap', help='Show the layers for a JSON keymap too.')
@cli.argument('-l', '--layouts', action='store_true', help='Render the layouts.')
@cli.argument('-m', '--matrix', action='store_true', help='Render the layouts with matrix information.')
Expand Down
3 changes: 2 additions & 1 deletion lib/python/qmk/cli/json2c.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"""
import json

from argcomplete.completers import FilesCompleter
from milc import cli

import qmk.keymap
Expand All @@ -10,7 +11,7 @@

@cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to')
@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help="Quiet mode, only output error messages")
@cli.argument('filename', type=qmk.path.FileType('r'), arg_only=True, help='Configurator JSON file')
@cli.argument('filename', type=qmk.path.FileType('r'), arg_only=True, completer=FilesCompleter('.json'), help='Configurator JSON file')
@cli.subcommand('Creates a keymap.c from a QMK Configurator export.')
def json2c(cli):
"""Generate a keymap.c from a configurator export.
Expand Down
3 changes: 2 additions & 1 deletion lib/python/qmk/cli/kle2json.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
import os
from pathlib import Path

from argcomplete.completers import FilesCompleter
from milc import cli
from kle2xy import KLE2xy

from qmk.converter import kle2qmk
from qmk.json_encoders import InfoJSONEncoder


@cli.argument('filename', help='The KLE raw txt to convert')
@cli.argument('filename', completer=FilesCompleter('.json'), help='The KLE raw txt to convert')
@cli.argument('-f', '--force', action='store_true', help='Flag to overwrite current info.json')
@cli.subcommand('Convert a KLE layout to a Configurator JSON', hidden=False if cli.config.user.developer else True)
def kle2json(cli):
Expand Down
3 changes: 2 additions & 1 deletion lib/python/qmk/cli/lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@

from qmk.decorators import automagic_keyboard, automagic_keymap
from qmk.info import info_json
from qmk.keyboard import keyboard_completer
from qmk.keymap import locate_keymap
from qmk.path import is_keyboard, keyboard


@cli.argument('--strict', action='store_true', help='Treat warnings as errors.')
@cli.argument('-kb', '--keyboard', help='The keyboard to check.')
@cli.argument('-kb', '--keyboard', completer=keyboard_completer, help='The keyboard to check.')
@cli.argument('-km', '--keymap', help='The keymap to check.')
@cli.subcommand('Check keyboard and keymap for common mistakes.')
@automagic_keyboard
Expand Down
4 changes: 2 additions & 2 deletions lib/python/qmk/cli/list/keymaps.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

import qmk.keymap
from qmk.decorators import automagic_keyboard
from qmk.keyboard import keyboard_folder
from qmk.keyboard import keyboard_completer, keyboard_folder


@cli.argument("-kb", "--keyboard", type=keyboard_folder, help="Specify keyboard name. Example: 1upkeyboards/1up60hse")
@cli.argument("-kb", "--keyboard", type=keyboard_folder, completer=keyboard_completer, help="Specify keyboard name. Example: 1upkeyboards/1up60hse")
@cli.subcommand("List the keymaps for a specific keyboard")
@automagic_keyboard
def list_keymaps(cli):
Expand Down
4 changes: 2 additions & 2 deletions lib/python/qmk/cli/new/keymap.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@

import qmk.path
from qmk.decorators import automagic_keyboard, automagic_keymap
from qmk.keyboard import keyboard_folder
from qmk.keyboard import keyboard_completer, keyboard_folder
from milc import cli


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

0 comments on commit 588bcdc

Please sign in to comment.