Raise the declared minimums in pubspec.yaml to what our deps already
require: Flutter >=3.35.0 / Dart >=3.9.0 (was 3.19.0 / 3.5.0). alchemist
0.12 needs Flutter 3.32; Dart 3.9 first ships in Flutter 3.35, so 3.35 is
the binding floor. Pin the exact build toolchain in .fvmrc (Flutter
3.44.1).
Moving to the Dart 3.9 language level switches `dart format` to the new
"tall" style and enables two new lints. This commit is the resulting
mechanical churn, isolated from any behaviour change:
- whole-tree `dart format` reformat (tall style)
- `dart fix` for unnecessary_underscores + use_null_aware_elements
No runtime behaviour change; `make test` green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds test/terminal/input/input_test.dart — 58 unit tests covering
the keytab tokenizer, parser, unescape helper, KeytabRecord
toString shapes, Keytab.find modifier-matching rules, and the four
TerminalInputHandler implementations (CascadeInputHandler,
KeytabInputHandler, CtrlInputHandler, AltInputHandler).
Highlights:
- keytabUnescape: every documented backslash escape + \xHH hex.
- LineReader: peek/take/done, whitespace skip, readString
(alphanumeric/underscore), readUntil (both exclusive and
inclusive variants).
- tokenize: keyboard-name and key-define lines, comment + blank
stripping, shortcut vs string actions, error paths on malformed
input.
- KeytabParser: full mode-flag matrix, error paths on every
defensive throw reachable through the public addTokens API
(stray non-keyboard token, missing colon, modeStatus value other
than '+'/'-', non-mode token after modeStatus, action token of
wrong type, second token of wrong kind for both _parseName and
_parseKeyDefine).
- KeytabRecord.toString covers every supported flag (Alt, Control,
Shift, AnyMod, Ansi, AppScreen, KeyPad, AppCuKeys, AppKeyPad,
NewLine, Mac).
- Keytab.find: -Shift / +AnyMod / -AnyMod gating, mode-flag
filters (newLine, appKeyPad, appScreen, macos, appCursorKeys,
keyPad), -Ansi (VT52) skip, fallthrough to fallback record,
null when no key matches.
- KeytabInputHandler: every modifier combination's `*` placeholder
expansion (1..8 inclusive), default-keytab fallback, no-match
null, no-* passthrough.
- CtrlInputHandler: A..Z → 0x01..0x1A; null without ctrl, with
shift / alt, or on non-letter keys.
- AltInputHandler: A..Z → ESC + uppercase; null without alt, with
shift / ctrl, on macOS, or on non-letter keys.
- defaultInputHandler integration: keytab routing, fallthrough to
CtrlInputHandler.
Coverage delta:
- core/input/handler.dart: 4/54 → 54/54.
- keytab.dart: 0/29 → 29/29.
- keytab_record.dart: 0/44 → 44/44.
- keytab_token.dart: 0/82 → 80/82 (the two remaining lines are
defensive throws inside `_parseKeyboardNameDefine` /
`_parseKeyDefine` that are unreachable from tokenize() — the
callers only enter those functions after the `_isKeyboardNameDefine`
/ `_isKeyDefine` guards in the same file, so the inner readString
always matches).
- keytab_parse.dart: 0/65 → 63/65 (the two remaining lines mirror
the same shape — _parseName and _parseKeyDefine both check the
first token's type, but addTokens only delegates to them after
matching that type, so the throws are dead defensive code).
- keytab_default.dart: 0/4 unchanged — that's the file's own
`void main()` debug entrypoint that prints the parsed default
keytab; not part of the runtime contract.
- keytab_escape.dart: 0/14 → 14/14.
- Total project: 49.31% → 52.53%; coverage_floor bumped 49 → 52.
The 4 dead defensive throws are flagged but not removed in this
commit — they're a code-style call (defensive paranoia vs. dead-
code cleanup) that belongs in a separate review, not folded into a
test sweep.
Co-Authored-By: Claude <noreply@anthropic.com>