diff --git a/bench.sh b/bench.sh index a4e8542..e57f7c7 100755 --- a/bench.sh +++ b/bench.sh @@ -38,16 +38,17 @@ echo # --- file counts --- echo "=== File counts ===" -printf " findr --ignored : %8d\n" "$("$FINDR" --ignored "$TARGET" 2>/dev/null | wc -l)" +printf " fd -a . : %8d\n" "$(fd -a . "$TARGET" 2>/dev/null | wc -l)" +printf " findr : %8d\n" "$("$FINDR" "$TARGET" 2>/dev/null | wc -l)" echo -printf " fd . -t f --exclude .git : %8d\n" "$(fd . -t f --exclude .git "$TARGET" 2>/dev/null | wc -l)" -printf " findr --no-hidden : %8d\n" "$("$FINDR" --no-hidden "$TARGET" 2>/dev/null | wc -l)" +printf " fd -a -H . : %8d\n" "$(fd -a -H . "$TARGET" 2>/dev/null | wc -l)" +printf " findr -H : %8d\n" "$("$FINDR" -H "$TARGET" 2>/dev/null | wc -l)" echo -printf " fd . -H -t f --exclude .git : %8d\n" "$(fd . -H -t f --exclude .git "$TARGET" 2>/dev/null | wc -l)" -printf " findr (respect) : %8d\n" "$("$FINDR" "$TARGET" 2>/dev/null | wc -l)" +printf " fd -a -HI . : %8d\n" "$(fd -a -HI . "$TARGET" 2>/dev/null | wc -l)" +printf " findr -HI : %8d\n" "$("$FINDR" -HI "$TARGET" 2>/dev/null | wc -l)" echo -printf " fd . -HI -t f --exclude .git: %8d\n" "$(fd . -HI -t f --exclude .git "$TARGET" 2>/dev/null | wc -l)" -printf " findr -I (all) : %8d\n" "$("$FINDR" -I "$TARGET" 2>/dev/null | wc -l)" +printf " fd -a -E .git . : %8d\n" "$(fd -a -E .git . "$TARGET" 2>/dev/null | wc -l)" +printf " findr -E .git : %8d\n" "$("$FINDR" -E .git "$TARGET" 2>/dev/null | wc -l)" echo # --- benchmarks --- @@ -57,13 +58,14 @@ hyperfine \ --warmup 2 \ --runs 5 \ --export-markdown "$RESULTS_FILE" \ - "$FINDR --ignored \"$TARGET\" > /dev/null" \ - "fd . -t f --exclude .git \"$TARGET\" > /dev/null" \ - "$FINDR --no-hidden \"$TARGET\" > /dev/null" \ - "fd . -H -t f --exclude .git \"$TARGET\" > /dev/null" \ + "fd -a . \"$TARGET\" > /dev/null" \ "$FINDR \"$TARGET\" > /dev/null" \ - "fd . -HI -t f --exclude .git \"$TARGET\" > /dev/null" \ - "$FINDR -I \"$TARGET\" > /dev/null" + "fd -a -H . \"$TARGET\" > /dev/null" \ + "$FINDR -H \"$TARGET\" > /dev/null" \ + "fd -a -HI . \"$TARGET\" > /dev/null" \ + "$FINDR -HI \"$TARGET\" > /dev/null" \ + "fd -a -E .git . \"$TARGET\" > /dev/null" \ + "$FINDR -E .git \"$TARGET\" > /dev/null" echo echo "=== Results written to $RESULTS_FILE ===" diff --git a/findr.odin b/findr.odin index 66ee957..597ca25 100644 --- a/findr.odin +++ b/findr.odin @@ -8,7 +8,7 @@ main :: proc() { args := os.args opts: WalkOptions - opts.include_hidden = true + opts.include_hidden = false opts.ignore_mode = .Respected excludes := make([dynamic]string) @@ -22,12 +22,8 @@ main :: proc() { for i < len(args) { arg := args[i] switch { - case arg == "-I": - opts.ignore_mode = .All case arg == "--ignored": opts.ignore_mode = .Ignored - case arg == "--no-hidden": - opts.include_hidden = false case arg == "-E": i += 1 if i < len(args) { @@ -35,8 +31,17 @@ main :: proc() { } case strings.has_prefix(arg, "-E"): append(&excludes, arg[2:]) - case len(arg) > 0 && arg[0] == '-': - // unknown flag, skip + case len(arg) > 1 && arg[0] == '-': + for c, j in arg[1:] { + switch c { + case 'H': + opts.include_hidden = true + case 'I': + opts.ignore_mode = .All + case 'a': + // no-op: accepted for fd compatibility + } + } case: if pattern == "" { pattern = arg diff --git a/findr_test.odin b/findr_test.odin index 5a88247..74fc11a 100644 --- a/findr_test.odin +++ b/findr_test.odin @@ -63,7 +63,9 @@ test_dir_only_pattern :: proc(t: ^testing.T) { create_dir(env, "repo/ignored_dir") create_file(env, "repo/.gitignore", "ignored_dir/\n") - assert_output(t, env, nil, {include_hidden = true, ignore_mode = .Ignored}, {}) + assert_output(t, env, nil, {include_hidden = true, ignore_mode = .Ignored}, { + "repo/ignored_dir/", + }) } @(test) @@ -158,7 +160,7 @@ test_nested_gitignore_respected_mode :: proc(t: ^testing.T) { // important.log: un-ignored by nested negation → emitted // debug.log: ignored by root → skipped assert_output(t, env, nil, {include_hidden = true, ignore_mode = .Respected}, { - "repo/.gitignore", "repo/sub/.gitignore", "repo/sub/important.log", + "repo/", "repo/.gitignore", "repo/sub/", "repo/sub/.gitignore", "repo/sub/important.log", }) } @@ -253,7 +255,7 @@ test_all_mode_emits_all_files :: proc(t: ^testing.T) { create_file(env, "repo/normal.txt") assert_output(t, env, nil, {include_hidden = true, ignore_mode = .All}, { - "repo/.env", "repo/.gitignore", "repo/secrets.env", "repo/normal.txt", + "repo/", "repo/.env", "repo/.gitignore", "repo/secrets.env", "repo/normal.txt", }) } @@ -268,7 +270,7 @@ test_all_mode_descends_everywhere :: proc(t: ^testing.T) { create_file(env, "repo/build/output.txt") assert_output(t, env, nil, {include_hidden = true, ignore_mode = .All}, { - "repo/.gitignore", "repo/build/output.txt", + "repo/", "repo/.gitignore", "repo/build/", "repo/build/output.txt", }) } @@ -288,7 +290,7 @@ test_respected_mode_skips_gitignored :: proc(t: ^testing.T) { create_file(env, "repo/normal.txt") assert_output(t, env, nil, {include_hidden = true, ignore_mode = .Respected}, { - "repo/.gitignore", "repo/normal.txt", + "repo/", "repo/.gitignore", "repo/normal.txt", }) } @@ -304,7 +306,7 @@ test_respected_mode_prunes_ignored_dirs :: proc(t: ^testing.T) { create_file(env, "repo/main.txt") assert_output(t, env, nil, {include_hidden = true, ignore_mode = .Respected}, { - "repo/.gitignore", "repo/main.txt", + "repo/", "repo/.gitignore", "repo/main.txt", }) } diff --git a/walker.odin b/walker.odin index 678c2a0..ca36aee 100644 --- a/walker.odin +++ b/walker.odin @@ -230,6 +230,12 @@ process_dir :: proc(pool: ^WalkerPool, item: WorkItem) { } if is_dir { + if should_emit && matches_pattern(pool, entry.name) { + dir_path_out := join_path_dir(dir_path, entry.name) + sync.mutex_lock(&pool.results_mutex) + append(pool.results, dir_path_out) + sync.mutex_unlock(&pool.results_mutex) + } if !ignored { child_rel, _ := strings.clone(entry_rel) child_path := join_path(dir_path, entry.name) @@ -367,3 +373,19 @@ join_path :: proc(parent, child: string) -> string { result, _ := strings.clone(s) return result } + +join_path_dir :: proc(parent, child: string) -> string { + b: strings.Builder + strings.builder_init(&b) + defer strings.builder_destroy(&b) + + fmt.sbprintf(&b, "%s", parent) + if len(parent) == 0 || parent[len(parent) - 1] != '/' { + fmt.sbprintf(&b, "/") + } + fmt.sbprintf(&b, "%s/", child) + + s := strings.to_string(b) + result, _ := strings.clone(s) + return result +}