search: Escape seeded buffer search query in regex mode (#57748)
## What When `seed_search_query_from_cursor` is enabled and regex mode is active, the selected text is used verbatim as the regex pattern. This means selecting text like `z.d` seeds the regex `z.d`, where `.` acts as a wildcard — matching `zed`, `zXd`, etc. — rather than only the literal string the user selected. Fixes #57253. ## Why it was broken `search_suggested()` in `BufferSearchBar` retrieves the raw selection text from `query_suggestion()` and passes it directly to `search()`. There was no escaping step, even when `default_options` had `SearchOptions::REGEX` set. ## Fix Run the seeded suggestion through `regex::escape()` before passing it to `search()` when regex mode is enabled. This mirrors the identical pattern already used in `crates/vim/src/normal/search.rs:485`. ```rust // Before let search = self .query_suggestion(seed_query_override, window, cx) .map(|suggestion| { self.search(&suggestion, Some(self.default_options), true, window, cx) }); // After let search = self .query_suggestion(seed_query_override, window, cx) .map(|suggestion| { let suggestion = if self.default_options.contains(SearchOptions::REGEX) { regex::escape(&suggestion) } else { suggestion }; self.search(&suggestion, Some(self.default_options), true, window, cx) }); ``` ## Test Added `test_seeded_query_is_escaped_in_regex_mode` in `buffer_search.rs`. It creates a buffer with `"z.d\nzed\n"`, enables regex mode, selects `z.d`, seeds the search, and asserts: - The query text becomes `z\.d` (escaped), not `z.d` - Exactly 1 match is highlighted (the literal `z.d`), not 2 The `regex` crate was already a workspace dependency and is already used in the `search` crate transitively; this adds it explicitly to `crates/search/Cargo.toml`. Release Notes: - Fixed regex search seeded from cursor/selection matching more than the selected text when the selection contained regex special characters (e.g. `.`, `*`, `(`) --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
This commit is contained in:
1 parent
ca267a68a3
commit
3d7c8e732e
1 file changed
+49
@@ -1043,6 +1043,11 @@ impl BufferSearchBar {
|
||||
let search = self
|
||||
.query_suggestion(seed_query_override, window, cx)
|
||||
.map(|suggestion| {
|
||||
let suggestion = if self.default_options.contains(SearchOptions::REGEX) {
|
||||
regex::escape(&suggestion)
|
||||
} else {
|
||||
suggestion
|
||||
};
|
||||
self.search(&suggestion, Some(self.default_options), true, window, cx)
|
||||
});
|
||||
|
||||
@@ -4167,6 +4172,50 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_seeded_query_is_escaped_in_regex_mode(cx: &mut TestAppContext) {
|
||||
init_globals(cx);
|
||||
let buffer = cx.new(|cx| Buffer::local("z.d\nzed\n", cx));
|
||||
let cx = cx.add_empty_window();
|
||||
let editor =
|
||||
cx.new_window_entity(|window, cx| Editor::for_buffer(buffer.clone(), None, window, cx));
|
||||
let search_bar = cx.new_window_entity(|window, cx| {
|
||||
let mut search_bar = BufferSearchBar::new(None, window, cx);
|
||||
search_bar.set_active_pane_item(Some(&editor), window, cx);
|
||||
search_bar.show(window, cx);
|
||||
search_bar
|
||||
});
|
||||
|
||||
// Select "z.d" on the first line.
|
||||
editor.update_in(cx, |editor, window, cx| {
|
||||
editor.change_selections(SelectionEffects::no_scroll(), window, cx, |s| {
|
||||
s.select_ranges([Point::new(0, 0)..Point::new(0, 3)])
|
||||
});
|
||||
});
|
||||
|
||||
search_bar.update_in(cx, |search_bar, window, cx| {
|
||||
search_bar.toggle_search_option(SearchOptions::REGEX, window, cx);
|
||||
search_bar.search_suggested(Some(SeedQuerySetting::Selection), window, cx);
|
||||
});
|
||||
cx.run_until_parked();
|
||||
|
||||
search_bar.read_with(cx, |search_bar, cx| {
|
||||
assert_eq!(
|
||||
search_bar.query(cx),
|
||||
r"z\.d",
|
||||
"seeded regex query must be escaped"
|
||||
);
|
||||
});
|
||||
|
||||
editor.update(cx, |editor, cx| {
|
||||
assert_eq!(
|
||||
editor.search_background_highlights(cx).len(),
|
||||
1,
|
||||
"only the literal 'z.d' should match, not 'zed'"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn update_search_settings(search_settings: SearchSettings, cx: &mut TestAppContext) {
|
||||
cx.update(|cx| {
|
||||
SettingsStore::update_global(cx, |store, cx| {
|
||||
|
||||
Reference in new issue
Block a user