diff options
Diffstat (limited to 'src/pardes.zig')
| -rw-r--r-- | src/pardes.zig | 61 |
1 files changed, 56 insertions, 5 deletions
diff --git a/src/pardes.zig b/src/pardes.zig index f08cf01b..fbc19b8a 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -4574,7 +4574,7 @@ pub const Pardes = struct { const path = opts.file.?; const opened = initial_doc: { if (comptime pdf_enabled) if (look.isPdfPath(path)) - break :initial_doc panes.Pdf.openPane(p, 0, path, opts.file_line); + break :initial_doc panes.Pdf.openPane(p, 0, path, opts.file_line, .last); if (look.isImagePath(path)) break :initial_doc panes.Image.create(p, 0, path, &.{}); break :initial_doc panes.File.open(p, 0, path, opts.file_line); @@ -4709,13 +4709,25 @@ pub const Pardes = struct { return exec.isFile(local); } - /// A file pane closing: its place noted in Recent, when Recent keeps it. + /// A file, PDF or image pane closing: its place noted in Recent, when + /// Recent keeps it. fn noteClosed(p: *Pardes, pane: *const Pane) void { - const d = recentDot(pane) orelse return; + const d = recentPlace(pane) orelse return; if (p.recent.get(d.path) == null and !recentKeeps(d.path)) return; p.recent.closedSel(p.recentGpa(), d.path, d.line, d.col, d.aline, d.acol); } + pub const RecentPlace = struct { path: []const u8, line: u32, col: u32, aline: u32 = 0, acol: u32 = 0 }; + + /// Where a pane is, as Recent keeps it: a file's dot, a PDF's page + /// (`:page:1`), an image's path alone (line 0). Null for any other. + pub fn recentPlace(pane: *const Pane) ?RecentPlace { + if (recentDot(pane)) |d| return .{ .path = d.path, .line = d.line, .col = d.col, .aline = d.aline, .acol = d.acol }; + if (pane.pdfPath()) |path| return .{ .path = path, .line = @intCast(pane.pdfPage().? + 1), .col = 1 }; + if (pane.image) |iv| return .{ .path = iv.path, .line = 0, .col = 0 }; + return null; + } + fn recentDot(pane: *const Pane) ?struct { path: []const u8, line: u32, col: u32, aline: u32 = 0, acol: u32 = 0 } { const f = pane.file orelse return null; if (f.output != null) return null; @@ -6656,7 +6668,7 @@ pub const Pardes = struct { // fall through: a multi-file rename previews as rows } else if (w.kind == .format and rows.len > 0) { // fall through: "cannot format" and friends are prose - } else return; + } else if (w.kind == .format) return; // a rename of nothing says so, below } if (rows.len == 0) { if (w.kind == .completion and pane.body.mode == .insert and pane.body.nsel == 0 and @@ -6664,7 +6676,8 @@ pub const Pardes = struct { // Nothing found fails, but for a file with no diagnostics, // which is no failure, and a completion's Tab. const none: ?[]const u8 = switch (w.kind) { - .completion, .format, .rename => null, + .completion, .format => null, + .rename => "nothing here to rename", .diagnostics, .workspace_diagnostics => return p.setMessage(w.pane, std.fmt.bufPrint(&said, "{s}: none", .{lspWord(w.kind)}) catch "none"), .incoming_calls => "callers not found", .outgoing_calls => "callees not found", @@ -6889,6 +6902,12 @@ pub const Pardes = struct { break; }; } + // Two in a row the same (a closed one reopened beside its own, + // or the entry between them gone): one entry, one step. + if (w > 0 and std.meta.eql(p.jumps[w - 1], kept)) { + if (i == p.jcur) cur = w - 1; + continue; + } p.jumps[w] = kept; w += 1; } @@ -8034,6 +8053,38 @@ test "raw tty Ctrl-B switches to editor mode without sending child input" { try std.testing.expectEqualStrings("", drainWrites(p, &buf)); } +test "the jump list never holds two entries in a row the same, when the one between them goes" { + const p = try Pardes.init(std.testing.allocator, .{ .cols = 60, .rows = 40 }); + defer p.deinit(); + const pane = try p.setTestFile("a\n" ** 200); + const at: Loc = .{ .pane = 0, .serial = pane.serial, .line = 1, .col = 1 }; + // A, a pane long closed with no file to keep it, A again, then far on. + p.jumps[0] = at; + p.jumps[1] = .{ .pane = 5, .serial = 999, .line = 1, .col = 1 }; + p.jumps[2] = at; + p.jumps[3] = .{ .pane = 0, .serial = pane.serial, .line = 150, .col = 1 }; + p.njumps = 4; + p.jcur = 3; + pane.body.cur_row = 80; // where you are now: far from both + p.trackJump(); + for (1..p.njumps) |i| try std.testing.expect(!std.meta.eql(p.jumps[i - 1], p.jumps[i])); + try std.testing.expectEqual(@as(usize, 3), p.njumps); + try std.testing.expectEqual(@as(u32, 1), p.jumps[0].line); + try std.testing.expectEqual(@as(u32, 150), p.jumps[1].line); +} + +test "a Rename the language server finds nothing for says so, and changes nothing" { + if (!lsp.supports.contains(.rename)) return; + const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true }); + defer p.deinit(); + const pane = try p.setTestFile("const value=1;\n"); + p.lspRequest(p.active, .rename, "renamed"); + const wait = p.lsp_wait.?; + p.update(.{ .lsp_resp = .{ .id = wait.id, .rows = "" } }); + try std.testing.expectEqualStrings("Rename: nothing here to rename", pane.msg[0..pane.msg_len]); + try std.testing.expectEqualStrings("const value=1;\n", pane.file.?.content); +} + test "hopping between two panes does not grow the jump stack" { if (platform == .web) return; const gpa = std.testing.allocator; |
