summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/config.zig6
-rw-r--r--src/gui/gui.zig5
-rw-r--r--src/normal.zig5
-rw-r--r--src/pardes.zig53
-rw-r--r--src/tutor.txt8
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