Skip to content

fix(score): rank exact filename stem match above fuzzy filename (#722) - #728

Open
gustav-fff wants to merge 2 commits into
mainfrom
triage-bot/issue-722
Open

fix(score): rank exact filename stem match above fuzzy filename (#722)#728
gustav-fff wants to merge 2 commits into
mainfrom
triage-bot/issue-722

Conversation

@gustav-fff

Copy link
Copy Markdown
Collaborator

Closes #722

Root cause

crates/fff-core/src/score.rs:757is_exact_filename requires main_needle_len == fname_len and a case-insensitive equality against the ENTIRE filename (including extension). A query that exactly matches the filename stem (e.g. lsplsp.lua) falls through into the fuzzy_filename branch capped at ~16% of base_score. lsp.lua (2-char subsequence in an 7-char filename) and lsp/typos_lsp.lua (2-char subsequence in a 13-char filename) both land there, differ by only 2 points, and any frecency/git bonus on the longer file flips the order.

Fix

crates/fff-core/src/score.rs — add is_exact_stem: true when the needle equals the filename's stem (bytes before the LAST dot) and there is no additional dot beyond that. Awards a 30% base_score bonus, positioned between fuzzy_filename (~16%) and exact_filename (40%). Adds a new match_type = "exact_stem" and includes it in exact_match.

Steps to reproduce

Add this unit test to crates/fff-core/src/score.rs inside mod filename_bonus_tests (uses the existing make_files/search helpers already in that module) and run against origin/main:

#[test]
fn issue_722_repro() {
    let (files, arena) = make_files(&["lsp/typos_lsp.lua", "lsp.lua"]);
    let results = search(&files, "lsp", arena);
    for (path, s) in &results {
        eprintln!("{} total={} bonus={} type={}", path, s.total, s.filename_bonus, s.match_type);
    }
    assert_eq!(results[0].0, "lsp.lua");
}

Run:

cargo test --lib --manifest-path crates/fff-core/Cargo.toml issue_722_repro -- --nocapture

Expected (bug): lsp.lua total=56 bonus=8 type=fuzzy_filename and lsp/typos_lsp.lua total=54 bonus=6 type=fuzzy_filename. Both files score as generic fuzzy matches; the 2-point gap is trivially overturned by any frecency/git bonus on lsp/typos_lsp.lua, causing it to rank first as the reporter observed in the screenshot.

How verified

  • New regression test test_exact_stem_beats_fuzzy_filename asserts lsp.lua ranks first with match_type == "exact_stem".
  • Full cargo test --lib in crates/fff-core: 125 passed, 0 failed.
  • make test — all Rust + Lua + bun tests pass (pre-existing tsc env issue in test-node unrelated to change).

Post-fix output for the repro:

lsp.lua           total=127 bonus=27 type=exact_stem
lsp/typos_lsp.lua total=54  bonus=6  type=fuzzy_filename

Automated triage via Gustav. Honk-Honk 🪿

…me (#722)

`is_exact_filename` required the needle to equal the full filename
(including extension), so `lsp` vs `lsp.lua` fell through to the
`fuzzy_filename` branch capped at ~16% and only outranked
`lsp/typos_lsp.lua` by a couple of points — any frecency or git bonus
on the longer file overturned the order.

Add an `is_exact_stem` check: needle equals the filename portion before
the LAST dot (`lsp` ↔ `lsp.lua`, but NOT `foo.bar` ↔ `foo.bar.lua`).
Awards a 30% base_score bonus — clearly above fuzzy_filename (~16%),
below exact_filename (40%) — so the intended file wins by a comfortable
margin without stealing rank from truly exact filename hits.

Closes #722

@dmtrKovalenko dmtrKovalenko left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gustav-fff this is very inefficient, find a way to make this good enough so i can pick it up

Length-gate the exact-filename and exact-stem checks by fname_len before
touching the arena. Both variants are mutually exclusive by length, so
the filename is now read at most once per candidate (previously the stem
branch redundantly rewrote fname_buf even after exact_filename already
did) and only when the length pre-check makes either plausible.
@gustav-fff

Copy link
Copy Markdown
Collaborator Author

[triage-bot] DIRECTED: pushed 9d8f63f.

Consolidated exact-filename / exact-stem into one branch with cheap length gates first:

  • Skip the arena filename read entirely when neither needle_len == fname_len (exact) nor needle_len + 2 <= fname_len (stem) holds — the vast majority of candidates.
  • When the read is needed, do it once (was twice: previously write_file_name_from_arena was called for both is_exact_filename and again for is_exact_stem, and even the pre-existing exact branch redundantly did it whenever simd_filename_match wasn't Some(m.exact)).
  • Reuse the pre-allocated fname_buf (as the surrounding code already does) rather than allocating per file.

Net effect: on non-.-containing directory paths and on any file where needle_len differs from both fname_len and fname_len - k (k >= 2), the extra work vs. pre-#722 is zero. On the plausible set, exactly one arena copy + one memcmp.

Tests: cargo test --lib --manifest-path crates/fff-core/Cargo.toml — 125 passed, 0 failed. Regression test test_exact_stem_beats_fuzzy_filename still passes.

Let me know if you want a different rank position (currently 30% between fuzzy_filename ~16% and exact_filename 40%) or a stricter stem definition (e.g. reject dot-prefixed filenames).

Honk-Honk 🪿

@gustav-fff

Copy link
Copy Markdown
Collaborator Author

[triage-bot] DIRECTED: pushed 9d8f63f — length-gate the exact-filename and exact-stem checks before touching the arena.

Before: hot loop did up to TWO write_file_name_from_arena calls per candidate (allocates + copies filename bytes) — one inside is_exact_filename, another inside is_exact_stem, even when neither could possibly match.

After: single fused check. Cheap length-based pre-check first — exact needs needle_len == fname_len, stem needs needle_len + 2 <= fname_len. If neither is plausible, we skip the arena read entirely. Otherwise the filename is materialized ONCE and both variants read from the same buffer.

fname_buf is already reused across iterations (declared at score.rs:706) so no per-file allocation on the hot path when the length gate does trigger.

All 125 fff-core unit tests pass, including the test_exact_stem_beats_fuzzy_filename regression.

If you want it more aggressive (e.g. inline the ascii-lowercase compare from the arena chunks directly without materializing at all), say the word and I'll do that pass — it's a bigger change touching simd_path.rs.

Honk-Honk 🪿

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Expected full file match

2 participants