Skip to content

Commit c5de14f

Browse files
Patrick Carrollclaude
andcommitted
Add --print-layout / -p flag to both commands
Parses the keymap and prints each layer as a formatted table, then exits without performing any conversion. Respects --cols and --keyboard for column width; defaults to 10 columns. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 3980988 commit c5de14f

4 files changed

Lines changed: 113 additions & 0 deletions

File tree

src/bin/qmk2zmk.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ struct Cli {
6464
/// Suppress warnings for unmapped keycodes
6565
#[arg(long)]
6666
no_warn: bool,
67+
68+
/// Parse the keymap and print a layout table, then exit without converting
69+
#[arg(short = 'p', long)]
70+
print_layout: bool,
6771
}
6872

6973
/// Process exit boundary for the binary.
@@ -108,6 +112,12 @@ fn run() -> Result<(), Error> {
108112
let cols = cli
109113
.cols
110114
.or_else(|| cli.keyboard.as_deref().and_then(codes::keyboard_cols));
115+
116+
if cli.print_layout {
117+
qmk2zmk::print_layout(&keyboard, cols);
118+
return Ok(());
119+
}
120+
111121
let output = zmk::render(&keyboard, cols);
112122
io::write_output(&output, cli.output.as_deref())
113123
}

src/bin/zmk2qmk.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ struct Cli {
5252
/// Suppress warnings for unmapped keycodes
5353
#[arg(long)]
5454
no_warn: bool,
55+
56+
/// Parse the keymap and print a layout table, then exit without converting
57+
#[arg(short = 'p', long)]
58+
print_layout: bool,
5559
}
5660

5761
/// Process exit boundary for the binary.
@@ -89,6 +93,12 @@ fn run() -> Result<(), Error> {
8993
let cols = cli
9094
.cols
9195
.or_else(|| cli.keyboard.as_deref().and_then(codes::keyboard_cols));
96+
97+
if cli.print_layout {
98+
qmk2zmk::print_layout(&keyboard, cols);
99+
return Ok(());
100+
}
101+
92102
let output = match cli.format {
93103
OutputFormat::Json => qmk::render_json(&keyboard),
94104
OutputFormat::C => qmk::render_c(&keyboard, cols),

src/lib.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,60 @@ pub mod ir;
1111
pub mod qmk;
1212
pub mod zmk;
1313

14+
/// Write all layers of `keyboard` as a formatted table, `cols` keys per row.
15+
///
16+
/// If `cols` is `None`, defaults to 10 (a common 40% keyboard row width).
17+
/// Output is written to `out`; I/O errors are silently ignored (same as
18+
/// `println!`).
19+
pub fn print_layout_to(keyboard: &ir::Keyboard, cols: Option<usize>, out: &mut impl std::io::Write) {
20+
for layer in &keyboard.layers {
21+
let _ = writeln!(out, "Layer {}: {}", layer.index, layer.name);
22+
let labels: Vec<String> = layer.keys.iter().map(key_label).collect();
23+
if labels.is_empty() {
24+
let _ = writeln!(out);
25+
continue;
26+
}
27+
let col_count = cols.unwrap_or(10).min(labels.len());
28+
let width = labels.iter().map(String::len).max().unwrap_or(1);
29+
for row in labels.chunks(col_count) {
30+
let parts: Vec<String> = row.iter().map(|l| format!("{l:<width$}")).collect();
31+
let _ = writeln!(out, " {}", parts.join(" "));
32+
}
33+
let _ = writeln!(out);
34+
}
35+
}
36+
37+
/// Print all layers of `keyboard` as a formatted table to stdout.
38+
pub fn print_layout(keyboard: &ir::Keyboard, cols: Option<usize>) {
39+
print_layout_to(keyboard, cols, &mut std::io::stdout());
40+
}
41+
42+
fn key_label(key: &ir::Key) -> String {
43+
match key {
44+
ir::Key::Kp(e) => e.to_string(),
45+
ir::Key::Mo(n) => format!("MO({n})"),
46+
ir::Key::Lt(n, e) => format!("LT({n},{e})"),
47+
ir::Key::Mt(m, e) => format!("MT({m},{e})"),
48+
ir::Key::Tog(n) => format!("TG({n})"),
49+
ir::Key::Sk(m) => format!("SK({m})"),
50+
ir::Key::Sl(n) => format!("SL({n})"),
51+
ir::Key::To(n) => format!("TO({n})"),
52+
ir::Key::Df(n) => format!("DF({n})"),
53+
ir::Key::Mmv(m) => m.to_string(),
54+
ir::Key::Mkp(b) => b.to_string(),
55+
ir::Key::Msc(s) => s.to_string(),
56+
ir::Key::Trans => "_____".to_string(),
57+
ir::Key::None => "XXXXX".to_string(),
58+
ir::Key::CapsWord => "CAPS_WORD".to_string(),
59+
ir::Key::Bootloader => "BOOT".to_string(),
60+
ir::Key::SysReset => "RESET".to_string(),
61+
ir::Key::RgbUg(a) => a.to_string(),
62+
ir::Key::Macro(name) => format!("M({name})"),
63+
ir::Key::TapDance(n) => format!("TD({n})"),
64+
ir::Key::Unknown(s) => format!("?({s})"),
65+
}
66+
}
67+
1468
/// Print a stderr warning for every `Key::Unknown` in the parsed keyboard.
1569
pub fn warn_unknowns(keyboard: &ir::Keyboard) {
1670
for layer in &keyboard.layers {

tests/integration.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,45 @@ fn zmk_rgb_survives_to_qmk() {
306306
assert!(out.contains("RGB_TOG"));
307307
}
308308

309+
// ── print_layout ─────────────────────────────────────────────────────────────
310+
311+
#[test]
312+
fn print_layout_contains_layer_headers_and_keys() {
313+
let km = qmk_c::parse(KEYMAP_C).unwrap();
314+
let mut buf = Vec::new();
315+
qmk2zmk::print_layout_to(&km, Some(12), &mut buf);
316+
let out = String::from_utf8(buf).unwrap();
317+
318+
assert!(out.contains("Layer 0: _BASE"));
319+
assert!(out.contains("Layer 1: _LOWER"));
320+
assert!(out.contains("Layer 2: _RAISE"));
321+
assert!(out.contains("Layer 3: _ADJUST"));
322+
323+
assert!(out.contains("TAB"));
324+
assert!(out.contains("MO(1)"));
325+
assert!(out.contains("MO(2)"));
326+
assert!(out.contains("_____"));
327+
assert!(out.contains("XXXXX"));
328+
}
329+
330+
#[test]
331+
fn print_layout_respects_col_count() {
332+
let km = qmk_c::parse(KEYMAP_C).unwrap();
333+
let mut buf = Vec::new();
334+
qmk2zmk::print_layout_to(&km, Some(6), &mut buf);
335+
let out = String::from_utf8(buf).unwrap();
336+
337+
// 48 keys at 6 cols = 8 rows per layer; each row ends with a newline before
338+
// the next row starts, so we can verify there are more rows than at 12-wide.
339+
let mut buf12 = Vec::new();
340+
qmk2zmk::print_layout_to(&km, Some(12), &mut buf12);
341+
let out12 = String::from_utf8(buf12).unwrap();
342+
assert!(
343+
out.lines().count() > out12.lines().count(),
344+
"6-col layout should have more lines than 12-col"
345+
);
346+
}
347+
309348
// ── Round-trip tests ──────────────────────────────────────────────────────────
310349

311350
/// ZMK → render ZMK → parse → render ZMK again: the two renders must be equal.

0 commit comments

Comments
 (0)