Skip to main content

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}