Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions crates/file_finder/src/file_finder_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,55 @@ async fn test_complex_path(cx: &mut TestAppContext) {
});
}

#[gpui::test]
async fn test_tail_proximity_bonus_prefers_later_match(cx: &mut TestAppContext) {
let app_state = init_test(cx);

cx.update(|cx| {
let settings = *ProjectPanelSettings::get_global(cx);
ProjectPanelSettings::override_global(
ProjectPanelSettings {
hide_root: true,
..settings
},
cx,
);
});

app_state
.fs
.as_fake()
.insert_tree(
path!("/root"),
json!({
"zzz": { "zed": { "Cargo.toml": "" } },
"zed": { "zzz": { "Cargo.toml": "" } },
}),
)
.await;

let project = Project::test(app_state.fs.clone(), [path!("/root").as_ref()], cx).await;
let (picker, _, cx) = build_find_picker(project, cx);

picker
.update_in(cx, |picker, window, cx| {
picker
.delegate
.update_matches("zed cargo".to_string(), window, cx)
})
.await;

picker.update(cx, |picker, _| {
assert_eq!(
collect_search_matches(picker).search_paths_only(),
vec![
rel_path("zzz/zed/Cargo.toml").into(),
rel_path("zed/zzz/Cargo.toml").into(),
],
);
});
}

#[gpui::test]
async fn test_row_column_numbers_query_inside_file(cx: &mut TestAppContext) {
let app_state = init_test(cx);
Expand Down
17 changes: 16 additions & 1 deletion crates/fuzzy_nucleo/src/paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,19 @@ fn get_filename_match_bonus(
score as f64 / filename.len().max(1) as f64
}

const TAIL_REWARD_SCALE: f64 = 1.5;

#[inline]
fn get_tail_proximity_bonus(matched_chars: &[u32], haystack_len: u32) -> f64 {
debug_assert!(
haystack_len != 0 && matched_chars.len() != 0,
"This should only be called on a successful match."
);
let trailing: u32 = matched_chars.iter().map(|&i| haystack_len - 1 - i).sum();
let mean_trailing = trailing as f64 / matched_chars.len() as f64;
(1.0 / (1.0 + mean_trailing)) * TAIL_REWARD_SCALE
}

fn path_match_helper<'a>(
matcher: &mut nucleo::Matcher,
query: &Query,
Expand Down Expand Up @@ -193,7 +206,9 @@ fn path_match_helper<'a>(

let length_penalty = candidate_buf.len() as f64 * LENGTH_PENALTY;
let filename_bonus = get_filename_match_bonus(&candidate_buf, &query.pattern, matcher);
let positive = (score as f64 + filename_bonus) * case_penalty(case_mismatches);
let tail_proximity_bonus = get_tail_proximity_bonus(&matched_chars, haystack.len() as u32);
let positive =
(score as f64 + filename_bonus + tail_proximity_bonus) * case_penalty(case_mismatches);
let adjusted_score = positive - length_penalty;
let positions = positions_from_sorted(&candidate_buf, &matched_chars);

Expand Down