Gate the gesture vocabulary the way the matrix is gated
🐳 Android image / Build and push (push) Successful in 3s
Build and test / android-image (push) Successful in 3s
Build and test / Desktop (Linux) (push) Failing after 1h16m50s
Build and test / Layer separation (push) Successful in 39s
Traceability / Requirement traces (push) Successful in 50s
Build and test / Android (aarch64) (push) Successful in 22m27s
🐳 Android image / Build and push (push) Successful in 3s
Build and test / android-image (push) Successful in 3s
Build and test / Desktop (Linux) (push) Failing after 1h16m50s
Build and test / Layer separation (push) Successful in 39s
Traceability / Requirement traces (push) Successful in 50s
Build and test / Android (aarch64) (push) Successful in 22m27s
Three holes, all found by the gate catching itself out. **Prose that mentions the tag was read as a tag.** `dr-ui`'s module list carries a comment saying where the generated table comes from, and it names `GESTURE:` in passing; the scan extracted that sentence fragment as a gesture with no place and no way to perform it. A tag must now *open* its comment. A line that merely mentions it is describing the mechanism, not declaring a member of it, and position is the only thing that tells the two apart — which also makes the string-literal guard fall out for free rather than being a special case. **Neither artefact was regenerated on commit.** They cite line numbers, so they go stale on anything that moves a line — the sheet commit made the document wrong about every gesture in `library.slint` without touching a single one. The pre-commit hook that already keeps the matrix in step now keeps these too, and unlike the matrix it *fails* rather than shrugging when the scan does: a matrix that will not build leaves a stale one in place, where a malformed gesture block means a user about to be told the wrong thing. **CI did not check them at all.** It does now, blocking. The matrix is read; the gesture table is *shown to somebody using the application*, and a stale one tells them to perform a gesture that no longer exists — from which they will conclude the application is broken rather than the page. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -45,8 +45,14 @@
|
||||
//! Works in `.rs` and `.slint` alike, because both comment with `//` and the
|
||||
//! gestures live in both — the arbitration in Rust, the affordance in Slint.
|
||||
//!
|
||||
//! A block runs from the `GESTURE:` line to the first line that is not a
|
||||
//! comment, or to a comment line that is empty after its marker. A line reading
|
||||
//! **The tag opens its comment or it is not a tag.** A line that merely
|
||||
//! mentions it — a sentence about the vocabulary, a worked example in a doc
|
||||
//! comment like the one above — is describing the mechanism rather than
|
||||
//! declaring a gesture, and position is the only thing that tells the two
|
||||
//! apart.
|
||||
//!
|
||||
//! A block runs from that line to the first line that is not a comment, or to a
|
||||
//! comment line that is empty after its marker. A line reading
|
||||
//! `key: value` sets a field; any other line inside the block continues the
|
||||
//! field before it, which is what lets `why` run to a sentence. An unrecognised
|
||||
//! key is an error rather than a silently dropped line: `pointr:` should not
|
||||
@@ -127,20 +133,26 @@ pub fn extract_from_text(text: &str, path: &str) -> (Vec<Gesture>, Vec<GesturePr
|
||||
|
||||
let mut i = 0;
|
||||
while i < lines.len() {
|
||||
let Some(pos) = lines[i].find("GESTURE:") else {
|
||||
// **The tag must open its comment**, not merely appear somewhere in
|
||||
// one. Prose that mentions it — "generated from the `GESTURE:` comments
|
||||
// beside the code" — is talking *about* the vocabulary, not declaring a
|
||||
// member of it, and the two are only distinguishable by position. The
|
||||
// gate caught exactly that sentence in `dr-ui`'s own module list, which
|
||||
// is the right outcome for a typo and the wrong one for a description.
|
||||
//
|
||||
// This also excludes a tag inside a string literal, which has code in
|
||||
// front of it and so does not open a comment at all.
|
||||
let Some(body) = comment_body(lines[i]) else {
|
||||
i += 1;
|
||||
continue;
|
||||
};
|
||||
// Only in a comment. A `GESTURE:` inside a string literal — this very
|
||||
// file's doc comment shows the tag, and so does the scanner's own test
|
||||
// fixture — is not a tag.
|
||||
if !is_comment(lines[i]) {
|
||||
let Some(title) = body.trim_start().strip_prefix("GESTURE:") else {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let start = i;
|
||||
let title = lines[i][pos + "GESTURE:".len()..].trim().to_string();
|
||||
let title = title.trim().to_string();
|
||||
|
||||
let mut fields: BTreeMap<String, String> = BTreeMap::new();
|
||||
let mut last: Option<String> = None;
|
||||
@@ -236,17 +248,6 @@ pub fn extract_from_text(text: &str, path: &str) -> (Vec<Gesture>, Vec<GesturePr
|
||||
(out, problems)
|
||||
}
|
||||
|
||||
/// Whether a line is a `//` comment and nothing else of substance before it.
|
||||
///
|
||||
/// Deliberately strict about what precedes the marker: `let s = "// GESTURE:"`
|
||||
/// has code in front of it and is not a comment line. Trailing comments after
|
||||
/// code are not a place gesture blocks are written, so refusing them costs
|
||||
/// nothing and keeps string literals out of the document.
|
||||
fn is_comment(line: &str) -> bool {
|
||||
let t = line.trim_start();
|
||||
t.starts_with("//")
|
||||
}
|
||||
|
||||
/// The text of a comment line, marker stripped. `None` for anything else.
|
||||
///
|
||||
/// `///` and `//!` are stripped to the same thing: a gesture block written as a
|
||||
@@ -525,6 +526,21 @@ property <bool> ranging: false;
|
||||
assert!(p.is_empty());
|
||||
}
|
||||
|
||||
/// Prose *about* the vocabulary is not a member of it. The gate caught this
|
||||
/// exact sentence in `dr-ui`'s module list, where a comment explaining
|
||||
/// where the generated table comes from named the tag in passing.
|
||||
#[test]
|
||||
fn a_sentence_that_mentions_the_tag_is_not_a_gesture() {
|
||||
let text = "\
|
||||
// Generated from the `GESTURE:` comments beside the code that implements
|
||||
// each one — see `tools/traceability`.
|
||||
mod gesture_book;
|
||||
";
|
||||
let (g, p) = extract_from_text(text, "lib.rs");
|
||||
assert!(g.is_empty(), "{g:?}");
|
||||
assert!(p.is_empty(), "{p:?}");
|
||||
}
|
||||
|
||||
/// A blank comment line ends the block, so ordinary prose beneath a gesture
|
||||
/// is not swallowed into its `why`.
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user