Skip to content

Commit 8fb1ef6

Browse files
committed
Run clang-format if available.
1 parent 3b76bb7 commit 8fb1ef6

File tree

7 files changed

+201
-197
lines changed

7 files changed

+201
-197
lines changed

.clang-format

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Copied from qmk_firmware
2+
---
3+
BasedOnStyle: Google
4+
AlignAfterOpenBracket: Align
5+
AlignConsecutiveAssignments: 'true'
6+
AlignConsecutiveDeclarations: 'true'
7+
AlignOperands: 'true'
8+
AllowAllParametersOfDeclarationOnNextLine: 'false'
9+
AllowShortCaseLabelsOnASingleLine: 'false'
10+
AllowShortFunctionsOnASingleLine: Empty
11+
AllowShortLoopsOnASingleLine: 'false'
12+
AlwaysBreakAfterDefinitionReturnType: None
13+
AlwaysBreakAfterReturnType: None
14+
AlwaysBreakBeforeMultilineStrings: 'false'
15+
BinPackArguments: 'true'
16+
BinPackParameters: 'true'
17+
ColumnLimit: '1000'
18+
IndentCaseLabels: 'true'
19+
IndentPPDirectives: AfterHash
20+
IndentWidth: '4'
21+
MaxEmptyLinesToKeep: '1'
22+
PointerAlignment: Right
23+
SortIncludes: 'false'
24+
SpaceBeforeAssignmentOperators: 'true'
25+
SpaceBeforeParens: ControlStatements
26+
SpaceInEmptyParentheses: 'false'
27+
SpacesBeforeTrailingComments: 1
28+
TabWidth: '4'
29+
UseTab: Never
30+
31+
...

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# QMK Format
2+
3+
`qmkfmt` is a tool to format the `keymaps` section of a `keymap.c` file in [qmk](https://qmk.fm/).
4+
5+
# Clang Format
6+
7+
If `clang-format` is available on `$PATH`, `qmkfmt` will also invoke `clang-format` to format the rest of the file.
8+
9+
# Editor Setup
10+
11+
## Helix
12+
13+
Put the following in `.helix/languages.toml` at the root of the `qmk_firmware` repository:
14+
15+
```toml
16+
[[language]]
17+
name = "c"
18+
auto-format = true
19+
formatter = { command = "qmkfmt" }
20+
```

src/main.rs

Lines changed: 75 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,57 @@
11
use core::str;
22
use prettytable::Table;
3-
use std::io::Read;
3+
use std::{
4+
io::{Read, Write},
5+
process::Stdio,
6+
};
47
use streaming_iterator::StreamingIterator;
58

9+
fn clang_format(text: &str) -> String {
10+
let mut cmd = std::process::Command::new("clang-format");
11+
cmd.stdin(Stdio::piped()).stdout(Stdio::piped());
12+
let mut cmd = match cmd.spawn() {
13+
Ok(cmd) => cmd,
14+
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
15+
// clang-format not installed
16+
return text.to_string();
17+
}
18+
Err(err) => panic!("Failed to exec clang-format: {err:?}"),
19+
};
20+
cmd.stdin
21+
.take()
22+
.unwrap()
23+
.write_all(text.as_bytes())
24+
.expect("Failed to write to clang-format");
25+
let output = cmd
26+
.wait_with_output()
27+
.expect("Failed to wait for clang-format");
28+
if !output.status.success() {
29+
panic!("clang-format exited with code: {:?}", output.status.code());
30+
}
31+
String::from_utf8(output.stdout).expect("clang-format output is not utf-8")
32+
}
33+
34+
fn find_keymaps<'a>(
35+
language: &'a tree_sitter::Language,
36+
tree: &'a tree_sitter::Tree,
37+
text: &str,
38+
) -> Option<tree_sitter::Node<'a>> {
39+
let query = tree_sitter::Query::new(
40+
&language,
41+
"(declaration (init_declarator (array_declarator (array_declarator (array_declarator (identifier) @id))))) @decl")
42+
.unwrap();
43+
let mut qc = tree_sitter::QueryCursor::new();
44+
let mut it = qc.matches(&query, tree.root_node(), text.as_bytes());
45+
46+
while let Some(x) = it.next() {
47+
let node = x.captures[1].node;
48+
if node_to_text(&text, &node) == "keymaps" {
49+
return Some(x.captures[0].node);
50+
}
51+
}
52+
None
53+
}
54+
655
fn main() {
756
let mut text = String::new();
857
std::io::stdin().read_to_string(&mut text).unwrap();
@@ -14,6 +63,14 @@ fn main() {
1463
.expect("Error loading C parser");
1564

1665
let tree = parser.parse(&text, None).unwrap();
66+
67+
// Print everything before keymaps, possibly formatting with clang-format
68+
let keymaps = find_keymaps(&language, &tree, &text).expect("No keymaps found");
69+
let prefix = &text.as_bytes()[0..keymaps.start_byte()];
70+
let prefix = str::from_utf8(prefix).expect("Text is not utf-8");
71+
print!("{prefix}");
72+
let mut last_byte = keymaps.start_byte();
73+
1774
let query = tree_sitter::Query::new(
1875
&language,
1976
"(call_expression (identifier) @id (argument_list) @args) @call",
@@ -24,40 +81,30 @@ fn main() {
2481
let call_idx = query.capture_index_for_name("call").unwrap();
2582

2683
let lines: Vec<_> = text.lines().collect();
27-
let mut last_byte = 0;
2884
let mut qc = tree_sitter::QueryCursor::new();
2985
let mut it = qc.matches(&query, tree.root_node(), text.as_bytes());
3086
while let Some(m) = it.next() {
3187
let name = m.nodes_for_capture_index(id_idx).next().unwrap();
3288
let (indent, _) = lines[name.start_position().row]
3389
.split_once(|c: char| !c.is_whitespace())
3490
.unwrap();
35-
let name = name
36-
.utf8_text(text.as_bytes())
37-
.expect("Failed to get text from node");
38-
if !name.starts_with("LAYOUT") {
91+
let name = node_to_text(&text, &name);
92+
if !name.starts_with("LAYOUT_") {
3993
continue;
4094
}
4195

4296
// Print everything before the call expression
43-
let call_node = m.nodes_for_capture_index(call_idx).next().unwrap();
44-
let prefix = &text.as_bytes()[last_byte..call_node.start_byte()];
97+
let args_node = m.nodes_for_capture_index(args_idx).next().unwrap();
98+
let prefix = &text.as_bytes()[last_byte..args_node.start_byte()];
4599
let prefix = str::from_utf8(prefix).expect("Text is not utf-8");
46-
last_byte = call_node.end_byte();
47100
print!("{prefix}");
48101

49102
// Print the formatted key list inside parens
50103
let mut table = Table::new();
51104
table.set_format(*prettytable::format::consts::FORMAT_CLEAN);
52-
let node = m.captures[1].node;
53-
let mut qc = node.walk();
105+
let mut qc = args_node.walk();
54106

55-
let keys: Vec<_> = m
56-
.nodes_for_capture_index(args_idx)
57-
.next()
58-
.unwrap()
59-
.named_children(&mut qc)
60-
.collect();
107+
let keys: Vec<_> = args_node.named_children(&mut qc).collect();
61108

62109
// Group keys by row
63110
let min_row = keys
@@ -102,29 +149,25 @@ fn main() {
102149
.map(|line| format!("{indent}{indent}{line}"))
103150
.collect::<Vec<_>>()
104151
.join("\n");
105-
print!("{name}(\n{table}\n{indent})");
152+
print!("(\n{table}\n{indent})");
153+
154+
let call_node = m.nodes_for_capture_index(call_idx).next().unwrap();
155+
last_byte = call_node.end_byte();
106156
}
107157

108-
let rest = &text.as_bytes()[last_byte..];
158+
let keymaps_end = keymaps.end_byte();
159+
let rest = &text.as_bytes()[last_byte..keymaps_end];
109160
let rest = str::from_utf8(rest).expect("Text is not utf-8");
110161
print!("{rest}");
162+
163+
let rest = &text.as_bytes()[keymaps_end..];
164+
let rest = str::from_utf8(rest).expect("Text is not utf-8");
165+
let rest = clang_format(rest);
166+
print!("{rest}");
111167
}
112168

113169
fn node_to_text(text: &str, node: &tree_sitter::Node) -> String {
114170
node.utf8_text(text.as_bytes())
115171
.expect("Failed to get text from node")
116172
.to_string()
117173
}
118-
119-
#[cfg(test)]
120-
mod tests {
121-
use insta_cmd::{assert_cmd_snapshot, get_cargo_bin};
122-
use std::process::Command;
123-
124-
#[test]
125-
fn test_fmt() {
126-
let mut cmd = Command::new(get_cargo_bin(env!("CARGO_PKG_NAME")));
127-
let keymap = std::fs::read_to_string("testdata/keymap.c").unwrap();
128-
assert_cmd_snapshot!(cmd.pass_stdin(keymap));
129-
}
130-
}

src/snapshots/qmkfmt__tests__fmt.snap

Lines changed: 0 additions & 91 deletions
This file was deleted.

testdata/keymap.c

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,11 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
4949
};
5050

5151
void keyboard_post_init_user(void) {
52-
// Customise these values to desired behaviour
53-
debug_enable=true;
54-
debug_matrix=true;
55-
debug_keyboard=true;
56-
debug_mouse=true;
52+
// Customise these values to desired behaviour
53+
debug_enable = true;
54+
debug_matrix = true;
55+
debug_keyboard = true;
56+
debug_mouse = true;
5757
}
5858

5959
// Override caps word to not shift - to _
@@ -62,7 +62,7 @@ bool caps_word_press_user(uint16_t keycode) {
6262
switch (keycode) {
6363
// Keycodes that continue Caps Word, with shift applied.
6464
case KC_A ... KC_Z:
65-
add_weak_mods(MOD_BIT(KC_LSFT)); // Apply shift to next key.
65+
add_weak_mods(MOD_BIT(KC_LSFT)); // Apply shift to next key.
6666
return true;
6767

6868
// Keycodes that continue Caps Word, without shifting.
@@ -74,6 +74,6 @@ bool caps_word_press_user(uint16_t keycode) {
7474
return true;
7575

7676
default:
77-
return false; // Deactivate Caps Word.
77+
return false; // Deactivate Caps Word.
7878
}
7979
}

0 commit comments

Comments
 (0)