diff options
author | Canop <cano.petrole@gmail.com> | 2021-05-09 17:45:11 +0200 |
---|---|---|
committer | Canop <cano.petrole@gmail.com> | 2021-05-09 17:45:11 +0200 |
commit | 88410cdf95881aaa1df67c4bddd171a9b61c0be7 (patch) | |
tree | 7e0b0537fd5d5a8b7ee262e05fc42a4137378b84 /src | |
parent | 8932923694f5890a7ad0cb8e90679e4246d8617a (diff) |
match line count in selection on content searches
when a content search has been done, opening a file with a
compatible command (like the standard `:edit`) opens on the
first line with a match
Diffstat (limited to 'src')
-rw-r--r-- | src/browser/browser_state.rs | 7 | ||||
-rw-r--r-- | src/content_search/mod.rs | 17 | ||||
-rw-r--r-- | src/display/displayable_tree.rs | 3 | ||||
-rw-r--r-- | src/pattern/composite_pattern.rs | 27 | ||||
-rw-r--r-- | src/pattern/content_pattern.rs | 12 | ||||
-rw-r--r-- | src/pattern/content_regex_pattern.rs | 24 | ||||
-rw-r--r-- | src/pattern/exact_pattern.rs | 34 | ||||
-rw-r--r-- | src/pattern/pattern.rs | 13 |
8 files changed, 134 insertions, 3 deletions
diff --git a/src/browser/browser_state.rs b/src/browser/browser_state.rs index e41b93a..33fc2c4 100644 --- a/src/browser/browser_state.rs +++ b/src/browser/browser_state.rs @@ -175,7 +175,12 @@ impl PanelState for BrowserState { } fn selection(&self) -> Option<Selection<'_>> { - Some(self.displayed_tree().selected_line().as_selection()) + let tree = self.displayed_tree(); + let mut selection = tree.selected_line().as_selection(); + selection.line = tree.options.pattern.pattern + .get_match_line_count(&selection.path) + .unwrap_or(0); + Some(selection) } fn tree_options(&self) -> TreeOptions { diff --git a/src/content_search/mod.rs b/src/content_search/mod.rs index 9e2ca51..362672b 100644 --- a/src/content_search/mod.rs +++ b/src/content_search/mod.rs @@ -9,6 +9,7 @@ pub use { content_match::ContentMatch, content_search_result::ContentSearchResult, needle::Needle, + std::io::{ BufRead, BufReader}, }; use { @@ -53,3 +54,19 @@ pub fn is_path_binary<P: AsRef<Path>>(path: P) -> bool { _ => true, } } + +pub fn line_count_at_pos<P: AsRef<Path>>(path: P, pos: usize) -> io::Result<usize> { + let mut reader = BufReader::new(File::open(path)?); + let mut line = String::new(); + let mut line_count = 1; + let mut bytes_count = 0; + while reader.read_line(&mut line)? > 0 { + bytes_count += line.len(); + if bytes_count >= pos { + return Ok(line_count); + } + line_count += 1; + line.clear(); + } + Err(io::Error::new(io::ErrorKind::UnexpectedEof, "too short".to_string())) +} diff --git a/src/display/displayable_tree.rs b/src/display/displayable_tree.rs index ca8e9ab..28ab478 100644 --- a/src/display/displayable_tree.rs +++ b/src/display/displayable_tree.rs @@ -567,7 +567,8 @@ impl<'a, 's, 't> DisplayableTree<'a, 's, 't> { } if cw.allowed > 8 && pattern_object.content { - let extract = tree.options.pattern.pattern.search_content(&line.path, cw.allowed - 2); + let extract = tree.options.pattern.pattern + .search_content(&line.path, cw.allowed - 2); if let Some(extract) = extract { self.write_content_extract(cw, extract, selected)?; } diff --git a/src/pattern/composite_pattern.rs b/src/pattern/composite_pattern.rs index 4d093f5..e573b6a 100644 --- a/src/pattern/composite_pattern.rs +++ b/src/pattern/composite_pattern.rs @@ -137,6 +137,33 @@ impl CompositePattern { }) } + pub fn get_match_line_count( + &self, + candidate: &Path, + ) -> Option<usize> { + use PatternOperator::*; + let composite_result: Option<Option<usize>> = self.expr.eval( + // score evaluation + |pat| pat.get_match_line_count(candidate), + // operator + |op, a, b| match (op, a, b) { + (Not, Some(_), _) => None, + (_, Some(ma), _) => Some(ma), + (_, None, Some(omb)) => omb, + _ => None, + }, + |op, a| match (op, a) { + (Or, Some(_)) => true, + _ => false, + }, + ); + composite_result + .unwrap_or_else(||{ + warn!("unexpectedly missing result "); + None + }) + } + pub fn has_real_scores(&self) -> bool { self.expr.iter_atoms() .fold(false, |r, p| match p { diff --git a/src/pattern/content_pattern.rs b/src/pattern/content_pattern.rs index be7014a..3521eec 100644 --- a/src/pattern/content_pattern.rs +++ b/src/pattern/content_pattern.rs @@ -61,6 +61,18 @@ impl ContentExactPattern { } } + /// get the line of the first match, if any + pub fn get_match_line_count( + &self, + path: &Path, + ) -> Option<usize> { + if let Ok(ContentSearchResult::Found { pos }) = self.needle.search(path) { + line_count_at_pos(path, pos).ok() + } else { + None + } + } + pub fn get_content_match( &self, path: &Path, diff --git a/src/pattern/content_regex_pattern.rs b/src/pattern/content_regex_pattern.rs index 9ca9858..3a27767 100644 --- a/src/pattern/content_regex_pattern.rs +++ b/src/pattern/content_regex_pattern.rs @@ -86,6 +86,30 @@ impl ContentRegexPattern { Ok(None) } + /// get the line of the first match, if any + pub fn try_get_match_line_count( + &self, + path: &Path, + ) -> io::Result<Option<usize>> { + let mut line_count = 1; + for line in BufReader::new(File::open(path)?).lines() { + let line = line?; + if self.rex.is_match(line.as_str()) { + return Ok(Some(line_count)); + } + line_count += 1; + } + Ok(None) + } + /// get the line of the first match, if any + pub fn get_match_line_count( + &self, + path: &Path, + ) -> Option<usize> { + self.try_get_match_line_count(path) + .unwrap_or(None) + } + pub fn get_content_match( &self, path: &Path, diff --git a/src/pattern/exact_pattern.rs b/src/pattern/exact_pattern.rs index ae2fae1..f129e9f 100644 --- a/src/pattern/exact_pattern.rs +++ b/src/pattern/exact_pattern.rs @@ -5,7 +5,12 @@ use { super::NameMatch, smallvec::SmallVec, - std::fmt, + std::{ + fmt, + fs::File, + io::{self, BufRead, BufReader}, + path::Path, + }, }; // weights used in match score computing @@ -87,6 +92,33 @@ impl ExactPattern { }) } + /// get the line of the first match, if any + /// (not used today, we use content_pattern to search in files) + pub fn try_get_match_line_count( + &self, + path: &Path, + ) -> io::Result<Option<usize>> { + let mut line_count = 1; // first line in text editors is 1 + for line in BufReader::new(File::open(path)?).lines() { + let line = line?; + if line.contains(&self.pattern) { + return Ok(Some(line_count)); + } + line_count = 1; + } + Ok(None) + } + + /// get the line of the first match, if any + /// (not used today, we use content_pattern to search in files) + pub fn get_match_line_count( + &self, + path: &Path, + ) -> Option<usize> { + self.try_get_match_line_count(path) + .unwrap_or(None) + } + /// compute the score of the best match pub fn score_of(&self, candidate: &str) -> Option<i32> { candidate diff --git a/src/pattern/pattern.rs b/src/pattern/pattern.rs index d479995..36feb21 100644 --- a/src/pattern/pattern.rs +++ b/src/pattern/pattern.rs @@ -142,6 +142,19 @@ impl Pattern { } } + /// get the line of the first match, if any + pub fn get_match_line_count( + &self, + path: &Path, + ) -> Option<usize> { + match self { + Self::ContentExact(cp) => cp.get_match_line_count(path), + Self::ContentRegex(cp) => cp.get_match_line_count(path), + Self::Composite(cp) => cp.get_match_line_count(path), + _ => None, + } + } + pub fn score_of(&self, candidate: Candidate) -> Option<i32> { match self { Self::NameExact(ep) => ep.score_of(&candidate.name), |