diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/pardes.zig | 53 | ||||
| -rw-r--r-- | src/tutor.txt | 5 |
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 |
