redox_core/buffer/text_buffer/search.rs
1//! Plain-text search helpers for `TextBuffer`.
2//!
3//! These helpers intentionally implement only literal substring and same-line
4//! character search semantics for now. Regex-aware search can layer on later
5//! for stuff like `:s/` commands.
6
7use super::TextBuffer;
8use crate::buffer::Pos;
9
10impl TextBuffer {
11 /// Find the next occurrence of `needle` after `pos` on the same line.
12 pub fn find_char_after_on_line(&self, pos: Pos, needle: char) -> Option<Pos> {
13 let pos = self.clamp_pos(pos);
14 let line = self.clamp_line(pos.line);
15 let line_text = self.line_slice(line);
16
17 for (col, ch) in line_text
18 .chars()
19 .enumerate()
20 .skip(pos.col.saturating_add(1))
21 {
22 if ch == needle {
23 return Some(Pos::new(line, col));
24 }
25 }
26
27 None
28 }
29
30 /// Find all non-overlapping literal matches of `needle` in the buffer.
31 ///
32 /// Returned ranges are half-open `(start, end)` position pairs.
33 pub fn find_matches(&self, needle: &str) -> Vec<(Pos, Pos)> {
34 if needle.is_empty() {
35 return Vec::new();
36 }
37
38 // TODO: this is inefficient as hell since it's converting the whole buffer to a string.
39 // Fine for now, but fix it when I add regex-based searching.
40 let source = self.to_string();
41 source
42 .match_indices(needle)
43 .map(|(start_byte, matched)| {
44 let start_char = self.rope().byte_to_char(start_byte);
45 let end_char = self
46 .rope()
47 .byte_to_char(start_byte.saturating_add(matched.len()));
48 (self.char_to_pos(start_char), self.char_to_pos(end_char))
49 })
50 .collect()
51 }
52}