Document Esc and Ctrl bracket mode exits

This commit is contained in:
slhx agent
2026-06-21 22:29:20 +02:00
parent 2e60a0961c
commit d5c31a92c5
4 changed files with 23 additions and 5 deletions
+9 -5
View File
@@ -121,14 +121,18 @@ selection entry, leader rails, tool commands, save/quit, and recoverability.
Text-entry mode for ordinary typing. `mim` starts in Normal mode; Insert mode is Text-entry mode for ordinary typing. `mim` starts in Normal mode; Insert mode is
entered explicitly with `i`, `a`, `o`, or another visible command. In Insert mode, entered explicitly with `i`, `a`, `o`, or another visible command. In Insert mode,
`Space` commits a literal space immediately and `Tab` inserts spaces by default `Space` commits a literal space immediately and `Tab` inserts spaces by default
(expandtab, matching the dotfiles Neovim baseline). Physical keyboards may use (expandtab, matching the dotfiles Neovim baseline). Escape returns to Normal;
conventional direct keys such as Escape as aliases back to Normal, but those Ctrl-[ is the same terminal byte and is documented as the Ctrl-capable SSH alias.
aliases are optional accelerators, not required controls. This preserves ordinary spaces and avoids reviving a Space-prefix escape chord.
If a client exposes neither Esc nor Ctrl-[, it is missing the mobile SSH
accelerator tier required for safe Insert-mode recovery; use the client's Esc
toolbar or an attached-keyboard path rather than overloading literal text input.
### Select mode ### Select mode
Selection-building mode. Movement extends the active selection by default. Selection-building mode. Escape or Ctrl-[ returns to Normal. Movement extends the
Vertical movement preserves a preferred cursor column across ragged lines, active selection by default. Vertical movement preserves a preferred cursor
column across ragged lines,
clamping only to the current line length until horizontal/editing motion resets clamping only to the current line length until horizontal/editing motion resets
that preference. Semantic object selections live behind the match/object rail that preference. Semantic object selections live behind the match/object rail
(`m w`, `m l`, `m i`, `m p`, etc.) so bare `h/j/k/l`, word motions, arrows, and (`m w`, `m l`, `m i`, `m p`, etc.) so bare `h/j/k/l`, word motions, arrows, and
+1
View File
@@ -27,6 +27,7 @@ Rows are redgate TSV requirements: `ring<TAB>id<TAB>summary [tag]`.
1 014 Normal-mode dot repeat SHALL replay the last deterministic local edit for the supported insert/change/delete/put subset without recording panel, search, or external side effects. [mobile] 1 014 Normal-mode dot repeat SHALL replay the last deterministic local edit for the supported insert/change/delete/put subset without recording panel, search, or external side effects. [mobile]
1 015 The keymap SHALL classify bindings into core mobile, mobile SSH accelerator, and attached-keyboard tiers so Esc/Ctrl/arrow-capable clients can be used without making those keys mandatory for core commands. [mobile] 1 015 The keymap SHALL classify bindings into core mobile, mobile SSH accelerator, and attached-keyboard tiers so Esc/Ctrl/arrow-capable clients can be used without making those keys mandatory for core commands. [mobile]
1 016 Every attached-keyboard or Ctrl/Esc accelerator that performs a core editing, navigation, recovery, or panel action SHALL have a documented visible mobile fallback or rail path. [mobile] 1 016 Every attached-keyboard or Ctrl/Esc accelerator that performs a core editing, navigation, recovery, or panel action SHALL have a documented visible mobile fallback or rail path. [mobile]
1 017 Insert and Select mode SHALL return to Normal via Escape and Ctrl-[ as equivalent terminal events while preserving literal Insert-mode Space. [mobile]
## ui ## ui
+12
View File
@@ -290,3 +290,15 @@ test "adversarial: replay names first event mismatch" {
.ok => return error.ExpectedTraceDivergence, .ok => return error.ExpectedTraceDivergence,
} }
} }
test "regular: Ctrl-[ normalizes as escape" {
try std.testing.expectEqual(Event{ .key = .escape }, normalize("\x1b"));
}
test "regular: Ctrl-[ trace labels as escape" {
var recorder = TraceRecorder.init(std.testing.allocator);
defer recorder.deinit();
try recorder.capture("\x1b");
try std.testing.expect(std.mem.indexOf(u8, recorder.text(), "escape") != null);
}
+1
View File
@@ -542,6 +542,7 @@ SCENARIOS = [
Scenario("attached-existing-medium", 72, 18, "existing", "attached-keyboard", "insert-normal", "truecolor", "file", setup_existing, ATTACHED_SAVE_QUIT, "alpha!\nbeta", ("1│", "2│", ""), max_key_events=8), Scenario("attached-existing-medium", 72, 18, "existing", "attached-keyboard", "insert-normal", "truecolor", "file", setup_existing, ATTACHED_SAVE_QUIT, "alpha!\nbeta", ("1│", "2│", ""), max_key_events=8),
Scenario("new-file-mono-narrow", 40, 12, "new", "ios-default-qwertz-space-path", "normal-symbol-save", "mono", "file", setup_new, MOBILE_SYMBOL_SAVE_QUIT, "/", ("1│", "", "mode:normal")), Scenario("new-file-mono-narrow", 40, 12, "new", "ios-default-qwertz-space-path", "normal-symbol-save", "mono", "file", setup_new, MOBILE_SYMBOL_SAVE_QUIT, "/", ("1│", "", "mode:normal")),
Scenario("esc-attached-key-mode-switch", 56, 14, "empty", "attached-keyboard", "insert-normal", "truecolor", "file", setup_empty, (b"i", b"e", b"s", b"c", b"\x1b", b" ", b"w", b" ", b"q"), "esc", ("1│", "", "mode:normal")), Scenario("esc-attached-key-mode-switch", 56, 14, "empty", "attached-keyboard", "insert-normal", "truecolor", "file", setup_empty, (b"i", b"e", b"s", b"c", b"\x1b", b" ", b"w", b" ", b"q"), "esc", ("1│", "", "mode:normal")),
Scenario("ctrl-bracket-mode-switch", 56, 14, "empty", "attached-keyboard", "insert-normal", "truecolor", "file", setup_empty, (b"i", b"c", b"t", b"r", b"l", b"\x1b", b" ", b"w", b" ", b"q"), "ctrl", ("1│", "", "mode:normal"), forbidden_raw=("^[",)),
Scenario("attached-cursor-left-insert", 56, 14, "empty", "attached-keyboard", "insert-cursor", "truecolor", "file", setup_empty, (b"i", b"a", b"b", b"\x1b[D", b"X", b"\x1b", b" ", b"w", b" ", b"q"), "aXb", ("1│", "", "aX", "mode:normal")), Scenario("attached-cursor-left-insert", 56, 14, "empty", "attached-keyboard", "insert-cursor", "truecolor", "file", setup_empty, (b"i", b"a", b"b", b"\x1b[D", b"X", b"\x1b", b" ", b"w", b" ", b"q"), "aXb", ("1│", "", "aX", "mode:normal")),
Scenario("normal-A-append", 56, 14, "existing", "attached-keyboard", "append-eol", "truecolor", "file", setup_existing, (b"A", b"!", b"\x1b", b" ", b"w", b" ", b"q"), "alpha!\nbeta", ("1│", "", "alpha")), Scenario("normal-A-append", 56, 14, "existing", "attached-keyboard", "append-eol", "truecolor", "file", setup_existing, (b"A", b"!", b"\x1b", b" ", b"w", b" ", b"q"), "alpha!\nbeta", ("1│", "", "alpha")),
Scenario("insert-enter-indent", 56, 14, "indented", "attached-keyboard", "newline-indent", "truecolor", "file", setup_indented, (b"A", b"\r", b"x", b"\x1b", b" ", b"w", b" ", b"q"), " item\n x", ("1│", "2│", "")), Scenario("insert-enter-indent", 56, 14, "indented", "attached-keyboard", "newline-indent", "truecolor", "file", setup_indented, (b"A", b"\r", b"x", b"\x1b", b" ", b"w", b" ", b"q"), " item\n x", ("1│", "2│", "")),