summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/pardes.zig53
-rw-r--r--src/tutor.txt5
2 files changed, 56 insertions, 2 deletions
diff --git a/src/pardes.zig b/src/pardes.zig
index fc216b24..994b0785 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -3805,6 +3805,10 @@ pub const Pardes = struct {
if (!pane.isTerminal()) pane.mode = .normal;
pane.msel.active = false;
pane.pending = 0;
+ // a chord/motion selection is residue: a fresh click
+ // dismisses it rather than dragging the anchored span
+ // to the click (explicit v/x keeps vim's click-extend)
+ if (!pane.vsel.explicit) pane.vsel.active = false;
}
} else {
// acme execute (middle) / look (right): a no-drag click
@@ -3829,14 +3833,60 @@ pub const Pardes = struct {
/// selection; Paste replaces it with the yank register, or splices the
/// register in literally at a bare cursor — so 1-2 then 1-3 in one hold
/// nets out to Snarf (copy: the text returns, the register keeps it).
+ /// In a tty-mode shell 1-3 instead pastes into the pty (forwarded click
+ /// + bracketed paste) and 1-2 is inert.
fn chordCutPaste(p: *Pardes, cut: bool) void {
const s = &p.drag.select;
const pane = p.panes[s.id] orelse return;
+ if (pane.mode == .tty) {
+ // tty: nothing can be cut — 1-2 stays inert; the pty owns the
+ // screen. 1-3 pastes like a terminal emulator: the click is
+ // forwarded first (only when the app listens for mouse) so
+ // mouse-aware programs put their cursor under it, then the
+ // register is typed — bracketed when the app set mode 2004
+ // (readline/vim/helix strip the markers), else with \n turned
+ // to \r like any unbracketed paste.
+ if (!s.chorded) {
+ s.chorded = true;
+ pane.sel[0].state = .none; // drop the sweep highlight
+ }
+ if (cut) return;
+ const y = p.yank orelse return;
+ if (y.len == 0) return;
+ const m = &pane.vt.modes;
+ if (m.get(.mouse_event_normal) or m.get(.mouse_event_button) or m.get(.mouse_event_any)) {
+ // dragUpdate keeps sel[0] tracking the held left button, so
+ // the click lands where the mouse is at this tap; 1-based,
+ // body-relative (the tag row is ours, not the app's)
+ const col: u16 = @intCast(std.math.clamp(pane.sel[0].c1 + 1, 1, 9999));
+ const row: u16 = @intCast(std.math.clamp(pane.sel[0].r1 - @as(i32, BOX_H) + 1, 1, 9999));
+ var mb: [32]u8 = undefined;
+ if (m.get(.mouse_format_sgr)) {
+ p.emitWrite(s.id, std.fmt.bufPrint(&mb, "\x1b[<0;{d};{d}M\x1b[<0;{d};{d}m", .{ col, row, col, row }) catch return);
+ } else {
+ // ponytail: legacy X10 bytes; add utf8/urxvt formats if an app ever wants them
+ const cb: u8 = @intCast(32 + @min(col, 222));
+ const rb: u8 = @intCast(32 + @min(row, 222));
+ p.emitWrite(s.id, &[_]u8{ 0x1b, '[', 'M', 32, cb, rb, 0x1b, '[', 'M', 35, cb, rb });
+ }
+ }
+ if (m.get(.bracketed_paste)) {
+ p.emitWrite(s.id, "\x1b[200~");
+ p.emitWrite(s.id, y);
+ p.emitWrite(s.id, "\x1b[201~");
+ } else {
+ const cp = p.scratch.allocator().dupe(u8, y) catch return;
+ for (cp) |*c| {
+ if (c.* == '\n') c.* = '\r';
+ }
+ p.emitWrite(s.id, cp);
+ }
+ return;
+ }
if (!s.chorded) {
s.chorded = true;
const sl = pane.sel[0];
pane.sel[0].state = .none;
- if (pane.mode == .tty) return; // the pty owns the cursor there
const pfx: i32 = if (pane.file != null) PREFIX_W else 0;
const hs: i32 = if (pane.file != null) pane.hscroll else 0;
const row0 = @max(0, sl.r0 - @as(i32, BOX_H)) + pane.scroll();
@@ -3852,7 +3902,6 @@ pub const Pardes = struct {
if (!pane.isTerminal()) pane.mode = .normal;
pane.vsel = .{ .active = row0 != row1 or col0 != col1, .row = row0, .col = col0, .explicit = false };
}
- if (pane.mode == .tty) return;
if (cut) {
if (pane.vsel.active) p.normalDelete(pane, true);
} else if (pane.vsel.active) {
diff --git a/src/tutor.txt b/src/tutor.txt
index 50b92558..7a8c7026 100644
--- a/src/tutor.txt
+++ b/src/tutor.txt
@@ -85,6 +85,11 @@
— a kept left selection or a v/x one, wherever it lives.
2-3 / 3-1 / 3-2 tapping the other button during a MIDDLE (execute)
or RIGHT (look) drag CANCELS the gesture.
+ In a TTY-mode shell 1-3 pastes into the program like a terminal:
+ the click is forwarded first (if it listens for mouse) so the
+ paste lands under it, then the register arrives bracketed.
+ The selection a chord leaves behind is dismissed by the next left
+ click (it doesn't stretch to the click).
THE TAG: each pane has a one-line tag: its MODE (NOR/INS/TTY) + directory
or file path + builtins. File panes show "Save Del" by default; Save