on master help sign in
search buffer contents during global search (#5652)
1adb194Pascal Kuthe
helix-core/src/lib.rs+2
@@ -41,7 +41,9 @@ pub use helix_loader::find_workspace;
41pub fn find_first_non_whitespace_char(line: RopeSlice) -> Option<usize> {
41pub fn find_first_non_whitespace_char(line: RopeSlice) -> Option<usize> {
42 line.chars().position(|ch| !ch.is_whitespace())
42 line.chars().position(|ch| !ch.is_whitespace())
43}
43}
44+mod rope_reader;
44
45
46+pub use rope_reader::RopeReader;
45pub use ropey::{self, str_utils, Rope, RopeBuilder, RopeSlice};
47pub use ropey::{self, str_utils, Rope, RopeBuilder, RopeSlice};
46
48
47// pub use tendril::StrTendril as Tendril;
49// pub use tendril::StrTendril as Tendril;
helix-core/src/rope_reader.rsnew file+37
@@ -0,0 +1,37 @@
1+use std::io;
2+
3+use ropey::iter::Chunks;
4+use ropey::RopeSlice;
5+
6+pub struct RopeReader<'a> {
7+ current_chunk: &'a [u8],
8+ chunks: Chunks<'a>,
9+}
10+
11+impl<'a> RopeReader<'a> {
12+ pub fn new(rope: RopeSlice<'a>) -> RopeReader<'a> {
13+ RopeReader {
14+ current_chunk: &[],
15+ chunks: rope.chunks(),
16+ }
17+ }
18+}
19+
20+impl io::Read for RopeReader<'_> {
21+ fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize> {
22+ let buf_len = buf.len();
23+ loop {
24+ let read_bytes = self.current_chunk.read(buf)?;
25+ buf = &mut buf[read_bytes..];
26+ if buf.is_empty() {
27+ return Ok(buf_len);
28+ }
29+
30+ if let Some(next_chunk) = self.chunks.next() {
31+ self.current_chunk = next_chunk.as_bytes();
32+ } else {
33+ return Ok(buf_len - buf.len());
34+ }
35+ }
36+ }
37+}
helix-term/src/commands.rs+38−13
@@ -29,7 +29,7 @@ use helix_core::{
29 tree_sitter::Node,
29 tree_sitter::Node,
30 unicode::width::UnicodeWidthChar,
30 unicode::width::UnicodeWidthChar,
31 visual_offset_from_block, Deletion, LineEnding, Position, Range, Rope, RopeGraphemes,
31 visual_offset_from_block, Deletion, LineEnding, Position, Range, Rope, RopeGraphemes,
32− RopeSlice, Selection, SmallVec, Tendril, Transaction,
32+ RopeReader, RopeSlice, Selection, SmallVec, Tendril, Transaction,
33};
33};
34use helix_view::{
34use helix_view::{
35 clipboard::ClipboardType,
35 clipboard::ClipboardType,
@@ -2062,11 +2062,16 @@ fn global_search(cx: &mut Context) {
2062 .map(|comp| (0.., std::borrow::Cow::Owned(comp.clone())))
2062 .map(|comp| (0.., std::borrow::Cow::Owned(comp.clone())))
2063 .collect()
2063 .collect()
2064 },
2064 },
2065− move |_editor, regex, event| {
2065+ move |editor, regex, event| {
2066 if event != PromptEvent::Validate {
2066 if event != PromptEvent::Validate {
2067 return;
2067 return;
2068 }
2068 }
2069
2069
2070+ let documents: Vec<_> = editor
2071+ .documents()
2072+ .map(|doc| (doc.path(), doc.text()))
2073+ .collect();
2074+
2070 if let Ok(matcher) = RegexMatcherBuilder::new()
2075 if let Ok(matcher) = RegexMatcherBuilder::new()
2071 .case_smart(smart_case)
2076 .case_smart(smart_case)
2072 .build(regex.as_str())
2077 .build(regex.as_str())
@@ -2099,6 +2104,7 @@ fn global_search(cx: &mut Context) {
2099 let mut searcher = searcher.clone();
2104 let mut searcher = searcher.clone();
2100 let matcher = matcher.clone();
2105 let matcher = matcher.clone();
2101 let all_matches_sx = all_matches_sx.clone();
2106 let all_matches_sx = all_matches_sx.clone();
2107+ let documents = &documents;
2102 Box::new(move |entry: Result<DirEntry, ignore::Error>| -> WalkState {
2108 Box::new(move |entry: Result<DirEntry, ignore::Error>| -> WalkState {
2103 let entry = match entry {
2109 let entry = match entry {
2104 Ok(entry) => entry,
2110 Ok(entry) => entry,
@@ -2111,17 +2117,36 @@ fn global_search(cx: &mut Context) {
2111 _ => return WalkState::Continue,
2117 _ => return WalkState::Continue,
2112 };
2118 };
2113
2119
2114− let result = searcher.search_path(
2115− &matcher,
2116− entry.path(),
2117− sinks::UTF8(|line_num, _| {
2120+ let sink = sinks::UTF8(|line_num, _| {
2118− all_matches_sx
2121+ all_matches_sx
2119− .send(FileResult::new(entry.path(), line_num as usize - 1))
2122+ .send(FileResult::new(entry.path(), line_num as usize - 1))
2120− .unwrap();
2123+ .unwrap();
2121−
2124+
2122− Ok(true)
2125+ Ok(true)
2123− }),
2126+ });
2127+ let doc = documents.iter().find(|&(doc_path, _)| {
2128+ doc_path.map_or(false, |doc_path| doc_path == entry.path())
2129+ });
2130+
2131+ let result = if let Some((_, doc)) = doc {
2132+ // there is already a buffer for this file
2133+ // search the buffer instead of the file because it's faster
2134+ // and captures new edits without requireing a save
2135+ if searcher.multi_line_with_matcher(&matcher) {
2136+ // in this case a continous buffer is required
2137+ // convert the rope to a string
2138+ let text = doc.to_string();
2139+ searcher.search_slice(&matcher, text.as_bytes(), sink)
2140+ } else {
2141+ searcher.search_reader(
2142+ &matcher,
2143+ RopeReader::new(doc.slice(..)),
2144+ sink,
2145+ )
2146+ }
2147+ } else {
2148+ searcher.search_path(&matcher, entry.path(), sink)
2124− );
2149+ };
2125
2150
2126 if let Err(err) = result {
2151 if let Err(err) = result {
2127 log::error!(
2152 log::error!(