Fail CI when a key is bound but not in the gesture book, or listed but not bound
The gesture book is generated from GESTURE tags, so it could not describe a gesture nobody tagged, but nothing made anyone tag one. The arrow keys, Enter, P, X, U, Delete, F1 and F2 all worked in the grid with no line in the help sheet, and a tag could name a key whose handler had gone. Key handlers now compare one canonical string, Keys.chord(event) == "Ctrl+Z", instead of reading event.text and the modifiers themselves. keys.slint folds the key and its modifiers into that spelling, so the literal in the handler is the whole binding and the checker reads exactly what the handler dispatches on. Each handler carries a KEYMAP comment naming the gesture-book section its keys belong to, and a tag's keys field names its keys between backticks. gestures-check now fails when a handler binds a key no tag in that section names, when a tag names a key no handler there binds, when any .slint file other than keys.slint reads event.text, when a compared literal is not canonical, and when keys.slint's named keys drift from the Rust list. Spellings are normalised in one place, chord.rs: Ctrl+z, Control+Z and LeftArrow all mean what the handler's "Ctrl+Z" and "Left" mean. Shift and Alt count only for letters and named keys, because on the French layout every digit needs shift and a 6 has to be a 6 however it was typed. A Rust keymap that both dispatched and was read by the generator was the alternative. It would have moved the handlers' decisions away from the Slint state they depend on, and a window that forgot to install it would have had no working keys at all. The keys that were already bound and undocumented are now tagged.
This commit is contained in:
@@ -11,7 +11,9 @@
|
||||
//! ```
|
||||
//!
|
||||
//! The gesture half scans a different tag out of the same files — see
|
||||
//! [`traceability::gestures`] for what it is and why it lives here.
|
||||
//! [`traceability::gestures`] for what it is and why it lives here — and holds
|
||||
//! the keys the Slint handlers bind to the keys those tags name, in both
|
||||
//! directions ([`traceability::keymap`]).
|
||||
//!
|
||||
//! The gate fails hard on a *misconfigured run* — zero requirements parsed, or
|
||||
//! zero source files scanned — rather than reporting a plausible-looking 0%.
|
||||
@@ -142,6 +144,8 @@ fn run_gestures(base: &Path, check: bool) -> Result<()> {
|
||||
|
||||
let mut found = Vec::new();
|
||||
let mut problems = Vec::new();
|
||||
let mut bindings = Vec::new();
|
||||
let mut keys_file_seen = false;
|
||||
for file in &files {
|
||||
let text = std::fs::read_to_string(file).unwrap_or_default();
|
||||
let rel = file
|
||||
@@ -159,6 +163,18 @@ fn run_gestures(base: &Path, check: bool) -> Result<()> {
|
||||
let (g, p) = gestures::extract_from_text(&text, &rel);
|
||||
found.extend(g);
|
||||
problems.extend(p);
|
||||
if rel.ends_with(".slint") {
|
||||
let (b, p) = keymap::extract_bindings(&text, &rel);
|
||||
bindings.extend(b);
|
||||
problems.extend(p);
|
||||
if rel.ends_with(&format!("/{}", keymap::KEYS_FILE)) {
|
||||
keys_file_seen = true;
|
||||
problems.extend(keymap::check_vocabulary(
|
||||
&keymap::slint_named_keys(&text),
|
||||
&rel,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A `manual:` must name a section the manual has, checked against the
|
||||
@@ -173,6 +189,7 @@ fn run_gestures(base: &Path, check: bool) -> Result<()> {
|
||||
println!("files scanned {}", files.len());
|
||||
println!("gestures found {}", found.len());
|
||||
println!("places {}", gestures::by_section(&found).len());
|
||||
println!("keys bound {}", bindings.len());
|
||||
|
||||
if !problems.is_empty() {
|
||||
for p in &problems {
|
||||
@@ -181,6 +198,26 @@ fn run_gestures(base: &Path, check: bool) -> Result<()> {
|
||||
bail!("{} malformed gesture tag(s)", problems.len());
|
||||
}
|
||||
|
||||
// The key half, after the tags parse: a key cannot be held to a tag that
|
||||
// did not. A run that found no bindings, or no `keys.slint`, read the
|
||||
// wrong tree — the application has keys, so zero is a misconfiguration.
|
||||
if !keys_file_seen || bindings.is_empty() {
|
||||
bail!(
|
||||
"no {} or no key bindings found — misconfigured, not an application without keys",
|
||||
keymap::KEYS_FILE
|
||||
);
|
||||
}
|
||||
let key_problems = keymap::cross_check(&found, &bindings);
|
||||
if !key_problems.is_empty() {
|
||||
for p in &key_problems {
|
||||
println!(" {p}");
|
||||
}
|
||||
bail!(
|
||||
"{} key(s) bound and undocumented, or documented and unbound",
|
||||
key_problems.len()
|
||||
);
|
||||
}
|
||||
|
||||
let doc = gestures::render_markdown(&found);
|
||||
let table = gestures::render_rust(&found);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user