diff options
-rw-r--r-- | docs/cli.md | 26 | ||||
-rw-r--r-- | lib/python/qmk/cli/cformat.py | 65 | ||||
-rw-r--r-- | lib/python/qmk/path.py | 14 | ||||
-rw-r--r-- | lib/python/qmk/tests/test_cli_commands.py | 3 |
4 files changed, 82 insertions, 26 deletions
diff --git a/docs/cli.md b/docs/cli.md index 8ee8ab0e8..7c3effcc0 100644 --- a/docs/cli.md +++ b/docs/cli.md | |||
@@ -71,14 +71,36 @@ There are some limitations to the local CLI compared to the global CLI: | |||
71 | 71 | ||
72 | ## `qmk cformat` | 72 | ## `qmk cformat` |
73 | 73 | ||
74 | This command formats C code using clang-format. Run it with no arguments to format all core code, or pass filenames on the command line to run it on specific files. | 74 | This command formats C code using clang-format. |
75 | 75 | ||
76 | **Usage**: | 76 | Run it with no arguments to format all core code that has been changed. Default checks `origin/master` with `git diff`, branch can be changed using `-b <branch_name>` |
77 | |||
78 | Run it with `-a` to format all core code, or pass filenames on the command line to run it on specific files. | ||
79 | |||
80 | **Usage for specified files**: | ||
77 | 81 | ||
78 | ``` | 82 | ``` |
79 | qmk cformat [file1] [file2] [...] [fileN] | 83 | qmk cformat [file1] [file2] [...] [fileN] |
80 | ``` | 84 | ``` |
81 | 85 | ||
86 | **Usage for all core files**: | ||
87 | |||
88 | ``` | ||
89 | qmk cformat -a | ||
90 | ``` | ||
91 | |||
92 | **Usage for only changed files against origin/master**: | ||
93 | |||
94 | ``` | ||
95 | qmk cformat | ||
96 | ``` | ||
97 | |||
98 | **Usage for only changed files against branch_name**: | ||
99 | |||
100 | ``` | ||
101 | qmk cformat -b branch_name | ||
102 | ``` | ||
103 | |||
82 | ## `qmk compile` | 104 | ## `qmk compile` |
83 | 105 | ||
84 | This command allows you to compile firmware from any directory. You can compile JSON exports from <https://config.qmk.fm>, compile keymaps in the repo, or compile the keyboard in the current working directory. | 106 | This command allows you to compile firmware from any directory. You can compile JSON exports from <https://config.qmk.fm>, compile keymaps in the repo, or compile the keyboard in the current working directory. |
diff --git a/lib/python/qmk/cli/cformat.py b/lib/python/qmk/cli/cformat.py index de55218ae..7e3a91dcf 100644 --- a/lib/python/qmk/cli/cformat.py +++ b/lib/python/qmk/cli/cformat.py | |||
@@ -1,16 +1,14 @@ | |||
1 | """Format C code according to QMK's style. | 1 | """Format C code according to QMK's style. |
2 | """ | 2 | """ |
3 | import os | ||
4 | import subprocess | 3 | import subprocess |
5 | from shutil import which | 4 | from shutil import which |
6 | 5 | ||
7 | from milc import cli | 6 | from milc import cli |
7 | import qmk.path | ||
8 | 8 | ||
9 | 9 | ||
10 | @cli.argument('files', nargs='*', arg_only=True, help='Filename(s) to format.') | 10 | def cformat_run(files, all_files): |
11 | @cli.subcommand("Format C code according to QMK's style.") | 11 | """Spawn clang-format subprocess with proper arguments |
12 | def cformat(cli): | ||
13 | """Format C code according to QMK's style. | ||
14 | """ | 12 | """ |
15 | # Determine which version of clang-format to use | 13 | # Determine which version of clang-format to use |
16 | clang_format = ['clang-format', '-i'] | 14 | clang_format = ['clang-format', '-i'] |
@@ -19,27 +17,48 @@ def cformat(cli): | |||
19 | if which(binary): | 17 | if which(binary): |
20 | clang_format[0] = binary | 18 | clang_format[0] = binary |
21 | break | 19 | break |
22 | |||
23 | # Find the list of files to format | ||
24 | if cli.args.files: | ||
25 | cli.args.files = [os.path.join(os.environ['ORIG_CWD'], file) for file in cli.args.files] | ||
26 | else: | ||
27 | ignores = ['tmk_core/protocol/usb_hid', 'quantum/template'] | ||
28 | for dir in ['drivers', 'quantum', 'tests', 'tmk_core']: | ||
29 | for dirpath, dirnames, filenames in os.walk(dir): | ||
30 | if any(i in dirpath for i in ignores): | ||
31 | dirnames.clear() | ||
32 | continue | ||
33 | |||
34 | for name in filenames: | ||
35 | if name.endswith(('.c', '.h', '.cpp')): | ||
36 | cli.args.files.append(os.path.join(dirpath, name)) | ||
37 | |||
38 | # Run clang-format on the files we've found | ||
39 | try: | 20 | try: |
40 | subprocess.run(clang_format + cli.args.files, check=True) | 21 | if not files: |
22 | cli.log.warn('No changes detected. Use "qmk cformat -a" to format all files') | ||
23 | return False | ||
24 | if files and all_files: | ||
25 | cli.log.warning('Filenames passed with -a, only formatting: %s', ','.join(cli.args.files)) | ||
26 | # 3.6+: Can remove the str casting, python will cast implicitly | ||
27 | subprocess.run(clang_format + [str(file) for file in files], check=True) | ||
41 | cli.log.info('Successfully formatted the C code.') | 28 | cli.log.info('Successfully formatted the C code.') |
42 | 29 | ||
43 | except subprocess.CalledProcessError: | 30 | except subprocess.CalledProcessError: |
44 | cli.log.error('Error formatting C code!') | 31 | cli.log.error('Error formatting C code!') |
45 | return False | 32 | return False |
33 | |||
34 | |||
35 | @cli.argument('-a', '--all-files', arg_only=True, action='store_true', help='Format all core files.') | ||
36 | @cli.argument('-b', '--base-branch', default='origin/master', help='Branch to compare to diffs to.') | ||
37 | @cli.argument('files', nargs='*', arg_only=True, help='Filename(s) to format.') | ||
38 | @cli.subcommand("Format C code according to QMK's style.") | ||
39 | def cformat(cli): | ||
40 | """Format C code according to QMK's style. | ||
41 | """ | ||
42 | # Empty array for files | ||
43 | files = [] | ||
44 | # Core directories for formatting | ||
45 | core_dirs = ['drivers', 'quantum', 'tests', 'tmk_core'] | ||
46 | ignores = ['tmk_core/protocol/usb_hid', 'quantum/template'] | ||
47 | # Find the list of files to format | ||
48 | if cli.args.files: | ||
49 | files.extend(qmk.path.normpath(file) for file in cli.args.files) | ||
50 | # If -a is specified | ||
51 | elif cli.args.all_files: | ||
52 | all_files = qmk.path.c_source_files(core_dirs) | ||
53 | # The following statement checks each file to see if the file path is in the ignored directories. | ||
54 | files.extend(file for file in all_files if not any(i in str(file) for i in ignores)) | ||
55 | # No files specified & no -a flag | ||
56 | else: | ||
57 | base_args = ['git', 'diff', '--name-only', cli.args.base_branch] | ||
58 | out = subprocess.run(base_args + core_dirs, check=True, stdout=subprocess.PIPE) | ||
59 | changed_files = filter(None, out.stdout.decode('UTF-8').split('\n')) | ||
60 | filtered_files = [qmk.path.normpath(file) for file in changed_files if not any(i in file for i in ignores)] | ||
61 | files.extend(file for file in filtered_files if file.exists() and file.suffix in ['.c', '.h', '.cpp']) | ||
62 | |||
63 | # Run clang-format on the files we've found | ||
64 | cformat_run(files, cli.args.all_files) | ||
diff --git a/lib/python/qmk/path.py b/lib/python/qmk/path.py index d16928afb..bfaa43924 100644 --- a/lib/python/qmk/path.py +++ b/lib/python/qmk/path.py | |||
@@ -68,3 +68,17 @@ def normpath(path): | |||
68 | return Path(path) | 68 | return Path(path) |
69 | 69 | ||
70 | return Path(os.environ['ORIG_CWD']) / path | 70 | return Path(os.environ['ORIG_CWD']) / path |
71 | |||
72 | |||
73 | def c_source_files(dir_names): | ||
74 | """Returns a list of all *.c, *.h, and *.cpp files for a given list of directories | ||
75 | |||
76 | Args: | ||
77 | |||
78 | dir_names | ||
79 | List of directories, relative pathing starts at qmk's cwd | ||
80 | """ | ||
81 | files = [] | ||
82 | for dir in dir_names: | ||
83 | files.extend(file for file in Path(dir).glob('**/*') if file.suffix in ['.c', '.h', '.cpp']) | ||
84 | return files | ||
diff --git a/lib/python/qmk/tests/test_cli_commands.py b/lib/python/qmk/tests/test_cli_commands.py index bb77952fa..a2595eb78 100644 --- a/lib/python/qmk/tests/test_cli_commands.py +++ b/lib/python/qmk/tests/test_cli_commands.py | |||
@@ -7,7 +7,8 @@ def check_subcommand(command, *args): | |||
7 | 7 | ||
8 | 8 | ||
9 | def test_cformat(): | 9 | def test_cformat(): |
10 | assert check_subcommand('cformat', 'tmk_core/common/keyboard.c').returncode == 0 | 10 | result = check_subcommand('cformat', 'quantum/matrix.c') |
11 | assert result.returncode == 0 | ||
11 | 12 | ||
12 | 13 | ||
13 | def test_compile(): | 14 | def test_compile(): |