diff options
Diffstat (limited to 'src/pardes.zig')
| -rw-r--r-- | src/pardes.zig | 62 |
1 files changed, 58 insertions, 4 deletions
diff --git a/src/pardes.zig b/src/pardes.zig index c78d4702..8bccfd89 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -9446,6 +9446,9 @@ pub const Pardes = struct { p.runBuiltin(config.exec_cmd, rid, "", std.mem.trim(u8, ln, " \t\r")); } else if (span) |selected| { p.lookAt(rid, ln[selected.start..selected.end]); + // This is the newly visited result, not a selection carried into + // the destination. Look cleared the previous source selection. + rp.msel = .{ .active = true, .r0 = r, .r1 = r }; } p.armLookWalk(rid); return true; @@ -12217,10 +12220,32 @@ pub const Pardes = struct { fn focusPaneByPath(p: *Pardes, path: []const u8, at: look.Spot) bool { const target = p.openPaneTarget(path, at) orelse return false; + p.clearLookSelection(p.panes[target.pane.id].?); p.focusPaneLine(target.pane.id, target.pane.at, .center); return true; } + /// Look starts a new selection; history navigation keeps the old one. + pub fn clearLookSelection(p: *Pardes, pane: *Pane) void { + pane.vsel = .{}; + pane.msel = .{}; + pane.nsel = 0; + pane.select = false; + pane.normal.clear(); + pane.append_at = null; + pane.sticky_col = -1; + for (0..pane.sel.len) |slot| pane.clearPointerSelection(slot); + pane.sel = @splat(.{}); + if (comptime pdf_enabled) if (pane.pdf) |*state| { + state.clearDrag(); + state.clearSelection(p.pdf_gpa); + }; + if (p.drag == .select and p.panes[p.drag.select.id] == pane) p.drag = .none; + if (p.jump_selection_cursor) |saved| if (saved.serial == pane.serial) { + p.jump_selection_cursor = null; + }; + } + /// Resolve without changing focus or falling back to search. PDF links use /// the same location rules as an ordinary Look before offering a choice. fn resolveLookTarget(p: *Pardes, id: usize, txt: []const u8, realbuf: *[4096]u8) look.Target { @@ -12308,6 +12333,7 @@ pub const Pardes = struct { const pane = p.panes[id] orelse return; const link = probe.link orelse return; const target = p.pdfLinkLocation(pane, link) orelse return; + p.clearLookSelection(pane); if (p.canonicalLookLocation(id, probe.text)) |visible| { if (!std.mem.eql(u8, visible, target)) { const content = std.fmt.allocPrint(p.gpa, "{s}\n{s}\n", .{ visible, target }) catch |err| @@ -12329,15 +12355,19 @@ pub const Pardes = struct { } } - pub fn lookAt(p: *Pardes, id: usize, txt: []const u8) void { + pub fn lookAt(p: *Pardes, id: usize, operand: []const u8) void { const pane = p.panes[id] orelse return; p.noteHaptic(.look); p.noteLookSource(id); - const trimmed = std.mem.trim(u8, txt, " \t\r\n"); - if (config.commandWord(trimmed)) |cmd| { + const command = std.mem.trim(u8, operand, " \t\r\n"); + if (config.commandWord(command)) |cmd| { if (p.execute(id, cmd)) |dst| p.active = dst; return; } + // The operand may borrow selected terminal/PDF text released below. + const txt = p.scratch.allocator().dupe(u8, operand) catch return; + const trimmed = std.mem.trim(u8, txt, " \t\r\n"); + p.clearLookSelection(pane); const pl = look.parsePathLine(trimmed); if (comptime pdf_enabled) if (panes.Pdf.lookSection(p, id, pl.path, pl.at)) return; var realbuf: [4096]u8 = undefined; @@ -12357,11 +12387,15 @@ pub const Pardes = struct { _ = p.searchStep(id, 1); } }, - .pane => |t| p.focusPaneLine(t.id, t.at, .center), + .pane => |t| { + p.clearLookSelection(p.panes[t.id].?); + p.focusPaneLine(t.id, t.at, .center); + }, .url => |u| if (u.len <= 256) p.emit(.{ .open_link = .from(u) }), .dir => |dir| { for (p.panes, 0..) |slot, i| { if (slot) |tt| if (std.mem.eql(u8, tt.cwdSlice(), dir) and p.takesCommandLine(i)) { + p.clearLookSelection(tt); p.active = i; p.emitWrite(i, "ls\r"); return; @@ -14931,6 +14965,26 @@ test "board heap: every allocation failure during boot is a clean OutOfMemory" { ); } +test "Look copies its operand before releasing pointer selection storage" { + const p = try Pardes.init(std.testing.allocator, .{ .cols = 60, .rows = 12 }); + defer p.deinit(); + const pane = try p.setTestFile("alpha beta\ngamma delta\nepsilon zeta\n"); + const operand = try p.gpa.dupe(u8, "/test.txt:3:2"); + pane.sel[0] = .{ .state = .done, .source_id = 1 }; + pane.pointer_selections[0] = .{ + .gesture = pane.sel[0], + .rows = try p.gpa.alloc(Pane.PointerRow, 0), + .raw_text = operand, + }; + p.drag = .{ .select = .{ .id = 0, .button = config.select_button } }; + p.lookAt(0, operand); + try std.testing.expectEqual(@as(i32, 2), pane.cur_row); + try std.testing.expectEqual(@as(i32, 1), pane.cur_col); + try std.testing.expect(pane.pointer_selections[0] == null); + try std.testing.expectEqual(.none, pane.sel[0].state); + try std.testing.expectEqual(.none, p.drag); +} + test "jump history preserves modal and mouse selections" { const p = try Pardes.init(std.testing.allocator, .{ .cols = 60, .rows = 12 }); defer p.deinit(); |
