summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-10-01 08:15:56 -0300
committerGabriel Schneider <[email protected]>2026-10-01 08:53:11 -0300
commit12cf44acc4edede7f20440be5ac4fd81a8c7a8c0 (patch)
treefa5e7434c254b446ab72fe0a4d9676420bc4fb54 /src
parenta274bd2c706fdfd3effa0ad88c5b4083c0dc0f33 (diff)
downloadpardes-12cf44acc4edede7f20440be5ac4fd81a8c7a8c0.tar.gz
pardes-12cf44acc4edede7f20440be5ac4fd81a8c7a8c0.zip
Find's notes stay at the end, under the names it found, as Grep's do: `1 directory skipped` no longer sorts first
Find's rows are sorted by path as they are filled in, and the note line was sorted with them, so it led the list. The rows above the first note are sorted, the notes left where they are. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src')
-rw-r--r--src/Output.zig9
-rw-r--r--src/ninep/ctl.zig32
2 files changed, 41 insertions, 0 deletions
diff --git a/src/Output.zig b/src/Output.zig
index fc300e8b..5422411f 100644
--- a/src/Output.zig
+++ b/src/Output.zig
@@ -363,6 +363,15 @@ fn sortResults(arena: std.mem.Allocator, from: Origin, content: []u8, anchor: ?u
}
if (content.len == 0) return anchor;
const path_only = std.meta.eql(from, Origin{ .cmd = .Find });
+ // A walk's notes (fs.WalkNotes) stay under the hits: only the rows
+ // above the first are sorted.
+ if (path_only) {
+ var off: usize = 0;
+ var it = std.mem.splitScalar(u8, content, '\n');
+ while (it.next()) |line| : (off += line.len + 1) if (@import("fs.zig").WalkNotes.isNote(line)) {
+ return if (off == 0) anchor else sortResults(arena, from, content[0..off], anchor);
+ };
+ }
const trailing_newline = content[content.len - 1] == '\n';
const body = content[0 .. content.len - @intFromBool(trailing_newline)];
var lines = std.mem.splitScalar(u8, body, '\n');
diff --git a/src/ninep/ctl.zig b/src/ninep/ctl.zig
index d4f36bea..3b3873e3 100644
--- a/src/ninep/ctl.zig
+++ b/src/ninep/ctl.zig
@@ -4262,3 +4262,35 @@ test "a Rename the server answered for this file alone, with other open files sa
p.lspResponse(p.lsp_wait.?.id, "util.zig:1:8 sum\nmain.zig: also says add, which the server did not rename\n");
try testing.expect(th.logHas(p, "Rename: previewed, not applied: the server renamed in this file only, and 1 other open file(s) say the name"));
}
+
+test "Find ends with what it skipped, and a Find or Grep that finds nothing leaves the +Search as it was" {
+ if (comptime !pardes.hosted) return;
+ const gpa = testing.allocator;
+ var tmp = testing.tmpDir(.{});
+ defer tmp.cleanup();
+ try tmp.dir.writeFile(testing.io, .{ .sub_path = "zz-needle.txt", .data = "needle\n" });
+ try tmp.dir.createDirPath(testing.io, "locked");
+ var dir_buf: [4096]u8 = undefined;
+ const dir = dir_buf[0..try tmp.dir.realPath(testing.io, &dir_buf)];
+ var locked_buf: [4200]u8 = undefined;
+ const locked = try std.fmt.bufPrintSentinel(&locked_buf, "{s}/locked", .{dir}, 0);
+ if (std.c.chmod(locked, 0) != 0) return;
+ defer _ = std.c.chmod(locked, 0o755);
+ var path_buf: [4200]u8 = undefined;
+ const path = try std.fmt.bufPrint(&path_buf, "{s}/zz-needle.txt", .{dir});
+ const p = try Pardes.init(gpa, .{ .file = path, .tty_only = true, .cols = 80, .rows = 30 });
+ defer p.deinit();
+ try testing.expect(p.executeBuiltinLine(0, "Find needle"));
+ const search = p.panes[p.panes[0].?.search_pane.?].?;
+ const listed = search.file.?.content;
+ try testing.expect(std.mem.endsWith(u8, listed, "1 directory skipped: permission denied\n"));
+ try testing.expect(std.mem.startsWith(u8, listed, "zz-needle.txt\n"));
+ // Nothing found where it looked, and nothing passed over: the miss is
+ // said, and the +Search keeps the last search's rows.
+ _ = std.c.chmod(locked, 0o755);
+ const before = try gpa.dupe(u8, listed);
+ defer gpa.free(before);
+ try testing.expect(p.executeBuiltinLine(0, "Find nosuchname"));
+ try testing.expectEqualStrings(before, search.file.?.content);
+ try testing.expect(th.logHas(p, "Find: name not found"));
+}