diff options
| -rw-r--r-- | docs/typ/cheatsheet.typ | 2 | ||||
| -rw-r--r-- | docs/typ/guide.typ | 6 | ||||
| -rw-r--r-- | src/config.zig | 6 | ||||
| -rw-r--r-- | src/gui/gui.zig | 5 | ||||
| -rw-r--r-- | src/normal.zig | 5 | ||||
| -rw-r--r-- | src/pardes.zig | 53 | ||||
| -rw-r--r-- | src/tutor.txt | 8 | ||||
| -rw-r--r-- | test/snapshots/builtins.golden | 4 | ||||
| -rw-r--r-- | test/snapshots/tty.golden | 17 | ||||
| -rw-r--r-- | test/snapshots/tty.snap | 25 |
10 files changed, 78 insertions, 53 deletions
diff --git a/docs/typ/cheatsheet.typ b/docs/typ/cheatsheet.typ index cec31428..0487611a 100644 --- a/docs/typ/cheatsheet.typ +++ b/docs/typ/cheatsheet.typ @@ -78,7 +78,7 @@ Helix-style: motions *select*, then an edit acts on the selection. Mode box: bla #pairs( [#key("Ctrl-w") #keys("h", "j", "k", "l")], [then a direction: focus a neighbour (not in insert or raw `$`)], [#key("Esc")], [back to the previous pane (raw: only at a prompt with nothing typed; insert: to normal)], - [#key("Shift-Esc")], [As Esc, but always back to the previous pane from raw `$` or a PDF, and into raw from a terminal's normal or insert mode.], + [#key("Shift-Esc")], [back to the previous pane from any mode or pane, as Esc from normal; a raw program never sees it], [#keys("Ctrl-o", "Ctrl-i")], [jump history back, forward], [#key("Alt-n")], [new terminal below], [#key("Alt-c")], [move the pane to a new column], diff --git a/docs/typ/guide.typ b/docs/typ/guide.typ index 8bab9e56..6d140da7 100644 --- a/docs/typ/guide.typ +++ b/docs/typ/guide.typ @@ -27,13 +27,13 @@ copy from. #word("Mode") in the tag steps raw, normal, insert. Esc and Shift-Esc depend on the mode: #pairs( - [normal], [#key("Esc"): back to the previous pane (#word("Last")). #key("Shift-Esc"): the same, except in a terminal, where it switches to raw.], - [insert], [#key("Esc"): back to normal. #key("Shift-Esc"): the same, except in a terminal, where it switches to raw.], + [normal], [#key("Esc"): back to the previous pane (#word("Last")). #key("Shift-Esc"): the same.], + [insert], [#key("Esc"): back to normal. #key("Shift-Esc"): out of insert and back to the previous pane.], [raw `$`], [#key("Esc"): to the program, except at a shell prompt with nothing typed on it, where it goes back to the previous pane. #key("Shift-Esc"): always back to the previous pane. Either way the terminal stays `$`.], [a PDF], [#key("Esc"): clears the selection and the search highlights. #key("Shift-Esc"): back to the previous pane.], ) -In short, #key("Shift-Esc") is as Esc, but always back to the previous pane from raw `$` or a PDF, and into raw from a terminal's normal or insert mode. "A shell prompt with nothing +In short, #key("Shift-Esc") is the Esc nothing swallows: in every mode and pane it goes back to the previous pane, as Esc does from normal, and a raw program never sees it. #key("Ctrl-b") alone switches a terminal between raw and normal. "A shell prompt with nothing typed on it" is one pardes knows from the prompt marks it injects into bash and fish (and, for other shells, from no program holding the terminal). "The previous pane" is the last other pane on the jump list; with none, the diff --git a/src/config.zig b/src/config.zig index cc46adfc..7559d481 100644 --- a/src/config.zig +++ b/src/config.zig @@ -249,10 +249,12 @@ pub const jump_keys = [_]struct { chord: Chord, cmd: Builtin }{ .{ .chord = .{ .cp = 'i', .ctrl = true }, .cmd = .Forward }, }; -pub const leave_pane: []const Chord = &.{.{ .cp = Key.escape, .shift = true }}; +/// Shift-Esc: the Esc nothing swallows. In every mode and pane it does what +/// Esc does from normal mode, back to the previous pane (Last); a raw +/// terminal's program never sees it. Ctrl-b alone toggles raw. +pub const back_out: []const Chord = &.{.{ .cp = Key.escape, .shift = true }}; pub const tty_toggle_default: u21 = 'b'; -pub const tty_toggle_alt: []const Chord = &.{.{ .cp = Key.escape, .shift = true }}; // OSC 133 prompt cells are hidden in normal mode; input columns stay intact. pub const tty_blank: enum { prompt, prompt_and_input } = .prompt; diff --git a/src/gui/gui.zig b/src/gui/gui.zig index 739e5a69..e8dc99b9 100644 --- a/src/gui/gui.zig +++ b/src/gui/gui.zig @@ -3790,10 +3790,7 @@ fn dispatch(g: *Gui, in: *Input, sev: *const c.SDL_Event) void { .cap_n => .{ .cp = 'N', .text = "N" }, .enter => .{ .cp = pardes.Key.enter }, .tab => .{ .cp = pardes.Key.tab }, - .tty_toggle => if (in.core) |core| - .{ .cp = core.opts.tty_toggle, .ctrl = true } - else - .{ .cp = config.tty_toggle_alt[0].cp, .shift = true }, + .tty_toggle => .{ .cp = if (in.core) |core| core.opts.tty_toggle else config.tty_toggle_default, .ctrl = true }, }, }), .rumble => if (g.gamepad) |pad| { diff --git a/src/normal.zig b/src/normal.zig index 17ebaac6..93487e88 100644 --- a/src/normal.zig +++ b/src/normal.zig @@ -815,11 +815,6 @@ fn executeNormalAction(p: *Pardes, t: *Text, semantic: modal.Normal.Action) void pub fn handlePdfNormal(p: *Pardes, pane: *Pane, key: Key) void { if (comptime !pdf_enabled) return; if (pane.pdf == null) return; - if (hit(key, config.leave_pane)) { - pane.body.normal.clear(); - pane.body.select = false; - return exec.runBuiltin(p, .Last, p.active, "", null); - } if (pane.body.normal.prefix == .none and isPrefix(key, 'f')) { pane.body.normal.clear(); return exec.runBuiltin(p, .PdfSections, p.active, "", null); diff --git a/src/pardes.zig b/src/pardes.zig index dbb37084..0690b7eb 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -6122,6 +6122,24 @@ pub const Pardes = struct { } const pane = p.panes[p.active] orelse return; p.noteTyped(); + // Shift-Esc backs out of whatever the pane is in -- a prompt, insert + // mode, a pending count, a raw program -- to the previous pane, as + // Esc does from normal mode. Nothing under it sees the key. + if (hit(key, config.back_out)) { + p.leader_on = false; + p.ctrl_w_pending = false; + if (pane.prompt != .none) { + pane.closePrompt(); + pane.focus = .body; + } + for ([_]*Text{ &pane.body, &pane.tag }) |t| { + if (t.mode == .insert) edit.exitInsert(p, t); + t.normal.clear(); + t.select = false; + } + tagline.exitHeader(p); + return exec.runBuiltin(p, .Last, p.active, "", null); + } // Alt-Down and Alt-Up in a pane's tag expand and collapse it, in // either mode: acme's Tagdown and Tagup (plan9port text.c:776). if (pane.focus == .tag and key.alt and !key.ctrl and (key.cp == Key.down or key.cp == Key.up) and tagline.focusedHeader(p) == null) { @@ -6135,10 +6153,8 @@ pub const Pardes = struct { p.ctrl_w_pending = false; if (key.ctrl and !key.alt and key.cp == p.opts.tty_toggle) return p.toggleTty(p.active); - // Shift-Escape is the unconditional way out, even mid-application; - // bare Escape only leaves at a shell prompt. - if (hit(key, config.tty_toggle_alt)) - return exec.runBuiltin(p, .Last, p.active, "", null); + // Bare Escape leaves only at a shell prompt (Shift-Esc, above, + // always). if (key.cp == Key.escape and !key.ctrl and !key.alt and !key.shift and exec.takesCommandLine(p, p.active)) return exec.runBuiltin(p, .Last, p.active, "", null); panes.terminal.followOutput(pane); // typing snaps back to live output @@ -6196,8 +6212,7 @@ pub const Pardes = struct { } return; } - const tty_alt = hit(key, config.tty_toggle_alt); - const tty_toggle = (key.ctrl and key.cp == p.opts.tty_toggle) or tty_alt; + const tty_toggle = key.ctrl and key.cp == p.opts.tty_toggle; if (pane.isTerminal() and tty_toggle) { return p.toggleTty(p.active); } @@ -7601,7 +7616,7 @@ test "Esc back into a tty leaves its view at the prompt" { p.update(.{ .mouse = .{ .button = .left, .kind = .release, .col = r.x + config.GUTTER + 1, .row = r.y + BOX_H } }); try std.testing.expectEqual(shell, p.active); - p.update(.{ .key = .{ .cp = Key.escape, .shift = true } }); + p.update(.{ .key = .{ .cp = 'b', .ctrl = true } }); try std.testing.expectEqual(Text.Mode.tty, sp.body.mode); // tty mode follows output to the bottom, so a screenful and a half of it @@ -8012,7 +8027,7 @@ test "tty output follows only from the bottom" { p.update(.{ .key = .{ .cp = 'n', .alt = true } }); // a shell under the doc const shell = p.active; const sp = p.panes[shell].?; - p.update(.{ .key = .{ .cp = Key.escape, .shift = true } }); + p.update(.{ .key = .{ .cp = 'b', .ctrl = true } }); try std.testing.expectEqual(Text.Mode.tty, sp.body.mode); for (0..60) |i| { @@ -8122,6 +8137,28 @@ test "raw tty Escape only leaves at a shell prompt" { try std.testing.expectEqual(Text.Mode.tty, pane.body.mode); } +test "Shift-Esc backs out to the previous pane in every mode, never into raw" { + if (platform == .web) return; + const p = try Pardes.init(std.testing.allocator, .{ .cols = 80, .rows = 24, .file = "mise.toml" }); + defer p.deinit(); + p.update(.{ .key = .{ .cp = 'n', .alt = true } }); // a shell under the doc + const shell = p.active; + const sp = p.panes[shell].?; + // A terminal in normal mode: out, still normal, never raw. + if (sp.body.mode == .tty) p.update(.{ .key = .{ .cp = 'b', .ctrl = true } }); + try std.testing.expectEqual(Text.Mode.normal, sp.body.mode); + p.update(.{ .key = .{ .cp = Key.escape, .shift = true } }); + try std.testing.expectEqual(@as(usize, 0), p.active); + try std.testing.expectEqual(Text.Mode.normal, sp.body.mode); + // A text pane in insert mode: out of insert, and out of the pane. + const doc = p.panes[0].?; + p.update(.{ .key = .{ .cp = 'i', .text = "i" } }); + try std.testing.expectEqual(Text.Mode.insert, doc.body.mode); + p.update(.{ .key = .{ .cp = Key.escape, .shift = true } }); + try std.testing.expectEqual(Text.Mode.normal, doc.body.mode); + try std.testing.expect(p.active != 0); +} + test "raw tty Ctrl-B switches to editor mode without sending child input" { if (platform == .web) return; const p = try Pardes.init(std.testing.allocator, .{ .file = "mise.toml" }); diff --git a/src/tutor.txt b/src/tutor.txt index 37938574..bd79d5a4 100644 --- a/src/tutor.txt +++ b/src/tutor.txt @@ -43,9 +43,9 @@ Esc and Shift-Esc, by mode: NORMAL Esc back to the previous pane (`Last`, SPC j j) - Shift-Esc the same; in a terminal it switches to RAW + Shift-Esc the same INSERT Esc back to NORMAL - Shift-Esc the same; in a terminal it switches to RAW + Shift-Esc out of INSERT and back to the previous pane RAW Esc goes to the program, except at an idle, EMPTY shell prompt: back to the previous pane Shift-Esc always back to the previous pane @@ -447,8 +447,8 @@ typed MODES blank NORMAL, ^ INSERT, $ RAW; each pane keeps its own Esc: back to the previous pane (RAW: only at an empty prompt; INSERT: back to NORMAL) - Shift-Esc: out of RAW always; in a terminal's NORMAL, - into RAW; Ctrl-b toggles RAW and NORMAL + Shift-Esc: back to the previous pane from any mode, + RAW included; Ctrl-b toggles RAW and NORMAL MOUSE L select/focus M execute R look 1-2 cut 1-3 paste 1-2 then 1-3 copy diff --git a/test/snapshots/builtins.golden b/test/snapshots/builtins.golden index 27b43a3a..68fab516 100644 --- a/test/snapshots/builtins.golden +++ b/test/snapshots/builtins.golden @@ -73,9 +73,9 @@ |58: 43 Esc and Shift-Esc, by mode: |59: 44 |60: 45 NORMAL Esc back to the previous pane (`Last`, SPC j j) -|61: 46 Shift-Esc the same; in a terminal it switches to RAW +|61: 46 Shift-Esc the same |62: 47 INSERT Esc back to NORMAL -|63: 48 Shift-Esc the same; in a terminal it switches to RAW +|63: 48 Shift-Esc out of INSERT and back to the previous pane |64: 49 RAW Esc goes to the program, except at an idle, |65: 50 EMPTY shell prompt: back to the previous pane |66: 51 Shift-Esc always back to the previous pane diff --git a/test/snapshots/tty.golden b/test/snapshots/tty.golden index 981a5c7f..b1ee7afd 100644 --- a/test/snapshots/tty.golden +++ b/test/snapshots/tty.golden @@ -29,15 +29,14 @@ |4: ls ls |6: echo SNAP''OK |8: -== snap shift-esc-on grid=100x31 cursor=4,8 -|2: $ /tmp/pardes-snap/tty/cwd Tty+bash Save Mode Fil /tmp/pardes-snap/tty/cwd Tty+bash Save Mode Fil -|4: $ ls ls -|6: $ echo SNAP''OK -|8: $ -== snap shift-esc-nodoc grid=100x31 cursor=4,21 -|4: $ ls Last ls -== snap ctrl-b-off grid=100x31 cursor=4,21 -|4: $ ls ls +== snap shift-esc-normal grid=100x31 cursor=4,21 +|4: ls Last ls +== snap ctrl-b-on grid=100x31 cursor=4,21 +|4: ls ls |17: $ /tmp/pardes-snap/tty/cwd Tty+bash Save Mode Fil |19: $ ls |21: $ +== snap ctrl-b-off grid=100x31 cursor=4,21 +|17: /tmp/pardes-snap/tty/cwd Tty+bash Save Mode Fil +|19: ls +|21: diff --git a/test/snapshots/tty.snap b/test/snapshots/tty.snap index 6ce0d5ba..48fffbd1 100644 --- a/test/snapshots/tty.snap +++ b/test/snapshots/tty.snap @@ -1,11 +1,8 @@ -# tty mode round-trip: Ctrl-b in, run a command, Ctrl-b out. Shift-Esc (CSI -# 27;2u — needs a host that reports modifiers on Escape) also gets you IN; a -# PLAIN Esc does not, it still goes to the shell. -# -# Getting OUT is Ctrl-b alone. Shift-Esc in tty is what Escape is in normal -# mode instead — Last, the pane you were in before this one — leaving this pane -# in tty so coming back lands in the program you left. The hop itself is -# covered by a unit test in pardes.zig. +# tty mode round-trip: Ctrl-b in, run a command, Ctrl-b out. Ctrl-b is the one +# switch between raw and normal. Shift-Esc (CSI 27;2u — needs a host that +# reports modifiers on Escape) never switches: in every mode it is what Escape +# is in normal mode — Last, the pane you were in before this one — and a raw +# terminal stays raw. The hop itself is covered by a unit test in pardes.zig. file m.txt marker start 31 100 -n 3 wait 8000 m.txt @@ -21,16 +18,14 @@ snap tty-echo key c-b stable 600 8000 snap tty-off -# Shift-Esc: in, the same as Ctrl-b... +# Shift-Esc from normal: never into raw; it backs out to the previous pane. send \e[27;2u stable 600 8000 -snap shift-esc-on -# ...but not out. With no doc pane to hop to it changes nothing: still tty, -# prompts still shown, same pane focused. -send \e[27;2u +snap shift-esc-normal +# Ctrl-b is the switch: into raw, and out again. +key c-b stable 600 8000 -snap shift-esc-nodoc -# Ctrl-b still leaves tty in place, which is now the only thing that does. +snap ctrl-b-on key c-b stable 600 8000 snap ctrl-b-off |
