bacon/search/
search_pattern.rs

1use crate::*;
2
3pub struct Pattern {
4    pub pattern: String, // might change later
5}
6
7impl Pattern {
8    // Current limitations:
9    // - a match can't span over more than 2 lines. This is probably fine.
10    pub fn search_lines<'i, I>(
11        &self,
12        lines: I,
13    ) -> Vec<Found>
14    where
15        I: IntoIterator<Item = &'i Line>,
16    {
17        let lines = lines.into_iter();
18        let pattern = &self.pattern;
19        let len = pattern.len();
20        let mut founds = Vec::new();
21        let mut previous_line: Option<&Line> = None;
22        for (line_idx, line) in lines.enumerate() {
23            if line.is_continuation() {
24                if let Some(previous_line) = previous_line {
25                    // we check for a match broken by wrapping
26                    if !previous_line.content.strings.is_empty() && !line.content.strings.is_empty()
27                    {
28                        let previous_line_string_idx = previous_line.content.strings.len() - 1;
29                        let previous_last_raw =
30                            &previous_line.content.strings[previous_line_string_idx].raw;
31                        if let Some(cut) = find_cut_pattern(
32                            pattern,
33                            previous_last_raw,
34                            &line.content.strings[0].raw,
35                        ) {
36                            let found = Found {
37                                line_idx: line_idx - 1,
38                                trange: TRange {
39                                    string_idx: previous_line_string_idx,
40                                    start_byte_in_string: previous_last_raw.len() - cut,
41                                    end_byte_in_string: previous_last_raw.len(),
42                                },
43                                continued: Some(TRange {
44                                    string_idx: 0,
45                                    start_byte_in_string: 0,
46                                    end_byte_in_string: len - cut,
47                                }),
48                            };
49                            founds.push(found);
50                        }
51                    }
52                }
53            }
54            previous_line = Some(line);
55            for (string_idx, tstring) in line.content.strings.iter().enumerate() {
56                let mut offset = 0;
57                while offset + len < tstring.raw.len() {
58                    let haystack = &tstring.raw[offset..];
59                    let Some(pos) = haystack.find(pattern) else {
60                        break;
61                    };
62                    let found = Found {
63                        line_idx,
64                        trange: TRange {
65                            string_idx,
66                            start_byte_in_string: pos + offset,
67                            end_byte_in_string: pos + offset + pattern.len(),
68                        },
69                        continued: None,
70                    };
71                    founds.push(found);
72                    offset += pos + pattern.len();
73                }
74            }
75        }
76        founds
77    }
78}
79
80fn find_cut_pattern(
81    pattern: &str,
82    a: &str,
83    b: &str,
84) -> Option<usize> {
85    let len = pattern.len();
86    (1..len).find(|&i| a.ends_with(&pattern[..i]) && b.starts_with(&pattern[i..]))
87}