From b4b59f87064374804422163f30b851b5a7a09c27 Mon Sep 17 00:00:00 2001 From: N8th8n8el Date: Sat, 10 Feb 2024 20:34:41 +0100 Subject: [PATCH] terminal: Fix non regex search to actually be non regex (#7330) Alacritty seems to support only regex search out of the box. This PR just escapes all special regex chars to make non regex search work as expected. Disclaimer: New to Rust. Release Notes: -Fixed text search not working correctly in terminal ([#4880](https://github.com/zed-industries/zed/issues/4880)) --- crates/terminal_view/src/terminal_view.rs | 63 ++++++++++++++++------- 1 file changed, 45 insertions(+), 18 deletions(-) diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index ae65c0aa2b..7ce5f1e514 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -46,6 +46,10 @@ use std::{ time::Duration, }; +const REGEX_SPECIAL_CHARS: &[char] = &[ + '\\', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '^', '$', +]; + const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500); ///Event to transmit the scroll from the element to the view @@ -436,21 +440,6 @@ impl TerminalView { .detach(); } - pub fn find_matches( - &mut self, - query: Arc, - cx: &mut ViewContext, - ) -> Task>> { - let searcher = regex_search_for_query(&query); - - if let Some(searcher) = searcher { - self.terminal - .update(cx, |term, cx| term.find_matches(searcher, cx)) - } else { - cx.background_executor().spawn(async { Vec::new() }) - } - } - pub fn terminal(&self) -> &Model { &self.terminal } @@ -656,6 +645,19 @@ fn possible_open_targets( possible_open_paths_metadata(fs, row, column, potential_abs_paths, cx) } +fn regex_to_literal(regex: &str) -> String { + regex + .chars() + .flat_map(|c| { + if REGEX_SPECIAL_CHARS.contains(&c) { + vec!['\\', c] + } else { + vec![c] + } + }) + .collect() +} + pub fn regex_search_for_query(query: &project::search::SearchQuery) -> Option { let query = query.as_str(); if query == "." { @@ -916,12 +918,27 @@ impl SearchableItem for TerminalView { /// Get all of the matches for this query, should be done on the background fn find_matches( &mut self, - query: Arc, + query: Arc, cx: &mut ViewContext, ) -> Task> { - if let Some(searcher) = regex_search_for_query(&query) { + let searcher = match &*query { + SearchQuery::Text { .. } => regex_search_for_query( + &(SearchQuery::text( + regex_to_literal(&query.as_str()), + query.whole_word(), + query.case_sensitive(), + query.include_ignored(), + query.files_to_include().to_vec(), + query.files_to_exclude().to_vec(), + ) + .unwrap()), + ), + SearchQuery::Regex { .. } => regex_search_for_query(&query), + }; + + if let Some(s) = searcher { self.terminal() - .update(cx, |term, cx| term.find_matches(searcher, cx)) + .update(cx, |term, cx| term.find_matches(s, cx)) } else { Task::ready(vec![]) } @@ -1212,4 +1229,14 @@ mod tests { project.update(cx, |project, cx| project.set_active_path(Some(p), cx)); }); } + + #[test] + fn escapes_only_special_characters() { + assert_eq!(regex_to_literal(r"test(\w)"), r"test\(\\w\)".to_string()); + } + + #[test] + fn empty_string_stays_empty() { + assert_eq!(regex_to_literal(""), "".to_string()); + } }