code_symbol_iter.rs

 1use project::DocumentSymbol;
 2use regex::Regex;
 3
 4#[derive(Debug, Clone)]
 5pub struct Entry {
 6    pub name: String,
 7    pub kind: lsp::SymbolKind,
 8    pub depth: u32,
 9    pub start_line: usize,
10    pub end_line: usize,
11}
12
13/// An iterator that filters document symbols based on a regex pattern.
14/// This iterator recursively traverses the document symbol tree, incrementing depth for child symbols.
15#[derive(Debug, Clone)]
16pub struct CodeSymbolIterator<'a> {
17    symbols: &'a [DocumentSymbol],
18    regex: Option<Regex>,
19    // Stack of (symbol, depth) pairs to process
20    pending_symbols: Vec<(&'a DocumentSymbol, u32)>,
21    current_index: usize,
22    current_depth: u32,
23}
24
25impl<'a> CodeSymbolIterator<'a> {
26    pub fn new(symbols: &'a [DocumentSymbol], regex: Option<Regex>) -> Self {
27        Self {
28            symbols,
29            regex,
30            pending_symbols: Vec::new(),
31            current_index: 0,
32            current_depth: 0,
33        }
34    }
35}
36
37impl Iterator for CodeSymbolIterator<'_> {
38    type Item = Entry;
39
40    fn next(&mut self) -> Option<Self::Item> {
41        if let Some((symbol, depth)) = self.pending_symbols.pop() {
42            for child in symbol.children.iter().rev() {
43                self.pending_symbols.push((child, depth + 1));
44            }
45
46            return Some(Entry {
47                name: symbol.name.clone(),
48                kind: symbol.kind,
49                depth,
50                start_line: symbol.range.start.0.row as usize,
51                end_line: symbol.range.end.0.row as usize,
52            });
53        }
54
55        while self.current_index < self.symbols.len() {
56            let regex = self.regex.as_ref();
57            let symbol = &self.symbols[self.current_index];
58            self.current_index += 1;
59
60            if regex.is_none_or(|regex| regex.is_match(&symbol.name)) {
61                // Push in reverse order to maintain traversal order
62                for child in symbol.children.iter().rev() {
63                    self.pending_symbols.push((child, self.current_depth + 1));
64                }
65
66                return Some(Entry {
67                    name: symbol.name.clone(),
68                    kind: symbol.kind,
69                    depth: self.current_depth,
70                    start_line: symbol.range.start.0.row as usize,
71                    end_line: symbol.range.end.0.row as usize,
72                });
73            } else {
74                // Even if parent doesn't match, push children to check them later
75                for child in symbol.children.iter().rev() {
76                    self.pending_symbols.push((child, self.current_depth + 1));
77                }
78
79                // Check if any pending children match our criteria
80                if let Some(result) = self.next() {
81                    return Some(result);
82                }
83            }
84        }
85
86        None
87    }
88}