ADR-0024 Phase F (full) step 1: walker-driven highlighting

Replaces the lex()-driven `base_runs` span builder in
`input_render.rs` with `walker::highlight_runs`. The new
walker-side `dsl::walker::highlight` module returns per-byte
`HighlightClass` assignments for every token shape in the source:

- For commands the walker engages on, `WalkResult::per_byte_class`
  is the authoritative source (keyword / identifier / number /
  string / punct / flag).
- Trailing junk past a partial match — and inputs the walker
  doesn't engage on at all (no registered entry word) — fall
  through to a byte-shape scanner over `lex_helpers` so unknown
  command words, stray punctuation, and unterminated strings
  still highlight sensibly.

`Theme::highlight_class_color` is the walker-side analogue of
`token_color(&TokenKind)`; the renderer reads `walker::highlight_runs`
output and looks up colours through it. `token_color` and the
`lex()` pre-pass remain in place for now — the lexer module is
still consumed by usage rendering and completion until the
remaining Phase F steps land.

`HighlightClass`'s and `WalkResult::per_byte_class`'s
`#[allow(dead_code)]` annotations come off — they're now part of
the production highlight path.

Tests:
- 16 new tests under `dsl::walker::highlight` cover end-to-end
  walks, byte-shape fallbacks (unknown commands, bare flags,
  numbers, punctuation), UTF-8 codepoint advance, and trailing-
  token handling after partial walks.
- Existing `input_render` tests pass unchanged.
- 860 total tests passing (727 lib + 133 integration), 1 ignored.

Clippy clean with `nursery` lints + `-D warnings`.
This commit is contained in:
claude@clouddev1
2026-05-15 08:19:52 +00:00
parent b3d3bdfe5b
commit 7bdd3987e1
6 changed files with 354 additions and 12 deletions
+10 -6
View File
@@ -25,6 +25,7 @@
use ratatui::style::{Modifier, Style};
use crate::dsl::lexer::lex;
use crate::dsl::walker;
use crate::dsl::{ParseError, parse_command};
use crate::theme::Theme;
@@ -300,13 +301,16 @@ pub fn lex_to_runs(input: &str, theme: &Theme) -> Vec<StyledRun> {
}
fn base_runs(input: &str, theme: &Theme) -> Vec<StyledRun> {
let tokens = lex(input);
let mut runs = Vec::with_capacity(tokens.len() * 2);
// Walker-driven highlighting (ADR-0024 §architecture, Phase F).
// `walker::highlight_runs` returns per-byte classes for every
// token shape in the source; whitespace gaps are not represented
// and we fill them with the default foreground colour below.
let classes = walker::highlight_runs(input);
let mut runs = Vec::with_capacity(classes.len() * 2);
let mut pos = 0;
for tok in tokens {
let (start, end) = tok.span;
for class in classes {
let (start, end) = (class.start, class.end);
if pos < start {
// Whitespace gap before this token.
runs.push(StyledRun {
byte_range: (pos, start),
style: Style::default().fg(theme.fg),
@@ -314,7 +318,7 @@ fn base_runs(input: &str, theme: &Theme) -> Vec<StyledRun> {
}
runs.push(StyledRun {
byte_range: (start, end),
style: Style::default().fg(theme.token_color(&tok.kind)),
style: Style::default().fg(theme.highlight_class_color(class.class)),
});
pos = end;
}