fix: fix issue with \ preceding pattern items

With the current implementation of pattern items, it's impossible to
search for strings like `\c` in buffers as, even when using `\\c`, the
`\c` suffix will be interpreted as a pattern item. This commit updates
the regex used to find the pattern items so as to ensure that the
preceding character is never a `\` and, if it is, it's considered that
the user is searching for a slash instead of trying to use a pattern
item.
This commit is contained in:
dinocosta 2025-08-18 23:37:57 +01:00
parent 09470b9d57
commit f7473c80ae
3 changed files with 28 additions and 14 deletions

2
Cargo.lock generated
View file

@ -14568,12 +14568,12 @@ dependencies = [
"client",
"collections",
"editor",
"fancy-regex 0.14.0",
"futures 0.3.31",
"gpui",
"language",
"menu",
"project",
"regex",
"schemars",
"serde",
"serde_json",

View file

@ -42,7 +42,7 @@ util.workspace = true
workspace.workspace = true
zed_actions.workspace = true
workspace-hack.workspace = true
regex.workspace = true
fancy-regex.workspace = true
[dev-dependencies]
client = { workspace = true, features = ["test-support"] }

View file

@ -13,6 +13,7 @@ use editor::{
DisplayPoint, Editor, EditorSettings,
actions::{Backtab, Tab},
};
use fancy_regex::Regex;
use futures::channel::oneshot;
use gpui::{
Action, App, ClickEvent, Context, Entity, EventEmitter, Focusable, InteractiveElement as _,
@ -24,7 +25,6 @@ use project::{
search::SearchQuery,
search_history::{SearchHistory, SearchHistoryCursor},
};
use regex::Regex;
use schemars::JsonSchema;
use serde::Deserialize;
use settings::Settings;
@ -54,8 +54,8 @@ const MAX_BUFFER_SEARCH_HISTORY_SIZE: usize = 50;
// TODO: Should this be updated to an HashMap so we can easily determine the
// search option for a given pattern?
static PATTERN_ITEMS: [(&str, &SearchOptions, bool); 2] = [
("\\c", &SearchOptions::CASE_SENSITIVE, false),
("\\C", &SearchOptions::CASE_SENSITIVE, true),
("c", &SearchOptions::CASE_SENSITIVE, false),
("C", &SearchOptions::CASE_SENSITIVE, true),
];
/// Opens the buffer search interface with the specified configuration.
@ -661,13 +661,13 @@ impl BufferSearchBar {
.detach_and_log_err(cx);
}
let pattern_items_regex = Regex::new(
&PATTERN_ITEMS
let pattern_items_regex = Regex::new(&format!(
r"(?<!\\)(\\[{}])",
PATTERN_ITEMS
.iter()
.map(|(pattern, _, _)| regex::escape(pattern))
.collect::<Vec<_>>()
.join("|"),
)
.map(|(pattern, _, _)| *pattern)
.collect::<String>()
))
.unwrap();
Self {
@ -1528,13 +1528,16 @@ impl BufferSearchBar {
let mut pattern_item_options = Vec::new();
let query = self.raw_query(cx);
// TODO: Maybe avoid so many unwrap/expect calls here.
self.pattern_items_regex
.captures_iter(&query)
.map(|capture| capture.extract())
.map(|(str, [])| {
.map(|capture| capture.unwrap())
.map(|capture| {
let pattern_item = capture.get(1).unwrap().as_str();
PATTERN_ITEMS
.iter()
.find(|(pattern, _, _)| *pattern == str)
.find(|(pattern, _, _)| pattern_item.ends_with(*pattern))
.expect("should only capture valid pattern items")
})
.for_each(|(_, search_option, value)| {
@ -3079,6 +3082,17 @@ mod tests {
"Should have case sensitivity enabled when all pattern items are removed and original search options are restored"
);
});
cx.simulate_input("\\\\c");
cx.run_until_parked();
search_bar.update(cx, |search_bar, cx| {
assert_eq!(search_bar.raw_query(cx), "test\\\\c");
assert_eq!(
search_bar.search_options,
SearchOptions::REGEX | SearchOptions::CASE_SENSITIVE,
"Should still have case sensitivity enabled when pattern item is preceded by another \\"
);
});
}
fn update_search_settings(search_settings: SearchSettings, cx: &mut TestAppContext) {