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}