diff options
Diffstat (limited to 'src')
| -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 |
5 files changed, 54 insertions, 23 deletions
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 |
