summaryrefslogtreecommitdiff
path: root/src/look.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/look.zig')
-rw-r--r--src/look.zig35
1 files changed, 23 insertions, 12 deletions
diff --git a/src/look.zig b/src/look.zig
index 61970de1..2b788bde 100644
--- a/src/look.zig
+++ b/src/look.zig
@@ -179,13 +179,24 @@ const find_max_hits = 512;
const find_max_depth = 16;
const find_max_steps = 100_000;
+/// Directories a source tree has no answers in, skipped whole. fd reads
+/// .gitignore for this; pardes has no ignore parser, and every one of these
+/// costs a real search: agave's `target/` alone is 456_000 of its 460_000
+/// entries and holds 1_200 of the 1_242 paths matching "bank", so a Find for
+/// `bank` burned the whole 512-hit budget on build artifacts and never
+/// reached `runtime/src/bank.rs`. That looked like a broken matcher.
+const find_skip = [_][]const u8{
+ ".git", ".jj", "target", "node_modules",
+ ".venv", "__pycache__", ".zig-cache", "zig-out",
+};
+
/// `fd`, in-core: every path under `dir` whose NAME contains `pat` (plain
/// case-insensitive substring — fd's default is a regex and pardes has no
-/// regex engine to spend on one), one ABSOLUTE path per line into `out`.
-/// Absolute because each line is an ordinary look target that outlives the
-/// walk: a terminal's cwd can move out from under its own results buffer.
-/// Only real directories are entered, so a symlink can never close a cycle,
-/// and `.git` is skipped whole (fd's default too).
+/// regex engine to spend on one), one path per line into `out`, RELATIVE to
+/// `dir` — the results buffer is itself named `dir/+Search`, so every row
+/// resolves against the same directory the walk started in and reads as the
+/// short name the searcher was looking for. Only real directories are
+/// entered, so a symlink can never close a cycle.
pub fn find(arena: std.mem.Allocator, dir: []const u8, pat: []const u8, out: *std.ArrayList(u8)) void {
var hits: std.ArrayList([]const u8) = .empty;
if (platform_has_fs) {
@@ -196,19 +207,19 @@ pub fn find(arena: std.mem.Allocator, dir: []const u8, pat: []const u8, out: *st
var root = std.Io.Dir.cwd().openDir(io, dir, .{ .iterate = true }) catch return;
defer root.close(io);
// walkSelectively, not walk: descending is opt-in, which is the only
- // way to express the depth cap and the .git skip at all.
+ // way to express the depth cap and find_skip at all.
var w = root.walkSelectively(arena) catch return;
defer w.deinit();
var steps: usize = 0;
- while (steps < find_max_steps and hits.items.len < find_max_hits) {
+ walk: while (steps < find_max_steps and hits.items.len < find_max_hits) {
steps += 1; // an unreadable dir burns a step too, so it cannot spin
const e = (w.next(io) catch continue) orelse break;
if (std.ascii.indexOfIgnoreCase(e.basename, pat) != null)
- hits.append(arena, std.fmt.allocPrint(arena, "{s}/{s}", .{
- std.mem.trimEnd(u8, dir, "/"), e.path,
- }) catch break) catch break;
- if (e.kind == .directory and e.depth() < find_max_depth and !std.mem.eql(u8, e.basename, ".git"))
- w.enter(io, e) catch {};
+ // e.path points into the walker's own buffer, dead at next()
+ hits.append(arena, arena.dupe(u8, e.path) catch break) catch break;
+ if (e.kind != .directory or e.depth() >= find_max_depth) continue;
+ for (find_skip) |s| if (std.mem.eql(u8, e.basename, s)) continue :walk;
+ w.enter(io, e) catch {};
}
} else {
// web: the build-generated source archive IS the filesystem, and it is