vtcode_core/tools/tree_sitter/
navigation.rs

1//! Code navigation capabilities using tree-sitter
2
3use crate::tools::tree_sitter::analyzer::{Position, SyntaxNode};
4use crate::tools::tree_sitter::languages::{SymbolInfo, SymbolKind};
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7
8/// Navigation result
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct NavigationResult {
11    pub target: NavigationTarget,
12    pub context: NavigationContext,
13    pub related_symbols: Vec<SymbolInfo>,
14}
15
16/// Navigation target
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub enum NavigationTarget {
19    Symbol(SymbolInfo),
20    Position(Position),
21    Range { start: Position, end: Position },
22}
23
24impl NavigationTarget {
25    /// Get the position associated with this navigation target
26    pub fn get_position(&self) -> &Position {
27        match self {
28            NavigationTarget::Symbol(symbol) => &symbol.position,
29            NavigationTarget::Position(pos) => pos,
30            NavigationTarget::Range { start, .. } => start,
31        }
32    }
33}
34
35/// Navigation context
36#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct NavigationContext {
38    pub current_scope: Option<String>,
39    pub parent_scopes: Vec<String>,
40    pub available_symbols: Vec<SymbolInfo>,
41    pub references: Vec<ReferenceInfo>,
42}
43
44/// Reference information
45#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct ReferenceInfo {
47    pub symbol: SymbolInfo,
48    pub reference_type: ReferenceType,
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize)]
52pub enum ReferenceType {
53    Definition,
54    Declaration,
55    Usage,
56    Call,
57    Inheritance,
58    Implementation,
59}
60
61/// Code navigator
62pub struct CodeNavigator {
63    symbol_map: HashMap<String, SymbolInfo>,
64    position_map: HashMap<Position, SymbolInfo>,
65}
66
67impl CodeNavigator {
68    pub fn new() -> Self {
69        Self {
70            symbol_map: HashMap::new(),
71            position_map: HashMap::new(),
72        }
73    }
74
75    /// Build navigation index from symbols
76    pub fn build_index(&mut self, symbols: &[SymbolInfo]) {
77        self.symbol_map.clear();
78        self.position_map.clear();
79
80        for symbol in symbols {
81            self.symbol_map.insert(symbol.name.clone(), symbol.clone());
82            self.position_map
83                .insert(symbol.position.clone(), symbol.clone());
84        }
85    }
86
87    /// Navigate to symbol definition
88    pub fn goto_definition(&self, symbol_name: &str) -> Option<NavigationResult> {
89        self.symbol_map.get(symbol_name).map(|symbol| {
90            let context = self.build_context(symbol);
91            NavigationResult {
92                target: NavigationTarget::Symbol(symbol.clone()),
93                context,
94                related_symbols: self.find_related_symbols(symbol),
95            }
96        })
97    }
98
99    /// Navigate to symbol at position
100    pub fn goto_position(&self, position: &Position) -> Option<NavigationResult> {
101        self.position_map.get(position).map(|symbol| {
102            let context = self.build_context(symbol);
103            NavigationResult {
104                target: NavigationTarget::Position(position.clone()),
105                context,
106                related_symbols: self.find_related_symbols(symbol),
107            }
108        })
109    }
110
111    /// Find all references to a symbol
112    pub fn find_references(&self, symbol_name: &str) -> Vec<ReferenceInfo> {
113        // Search through all symbols to find references to the given symbol name
114        self.symbol_map
115            .values()
116            .filter(|symbol| {
117                // Check if this symbol references the target symbol
118                // Since the SymbolInfo struct doesn't have a references field,
119                // we'll look for symbols with the same name in different scopes
120                symbol.name == symbol_name
121            })
122            .map(|symbol| ReferenceInfo {
123                symbol: symbol.clone(),
124                reference_type: ReferenceType::Usage, // Default to usage, could be refined
125            })
126            .collect()
127    }
128
129    /// Get symbol information at position
130    pub fn get_symbol_at_position(&self, position: &Position) -> Option<&SymbolInfo> {
131        self.position_map.get(position)
132    }
133
134    /// Get all symbols in scope
135    pub fn get_symbols_in_scope(&self, scope: Option<&str>) -> Vec<&SymbolInfo> {
136        self.symbol_map
137            .values()
138            .filter(|symbol| {
139                if let Some(scope_name) = scope {
140                    symbol
141                        .scope
142                        .as_ref()
143                        .map(|s| s == scope_name)
144                        .unwrap_or(false)
145                } else {
146                    symbol.scope.is_none() // Global scope
147                }
148            })
149            .collect()
150    }
151
152    /// Search for symbols by name pattern
153    pub fn search_symbols(
154        &self,
155        pattern: &str,
156        kind_filter: Option<&[SymbolKind]>,
157    ) -> Vec<&SymbolInfo> {
158        self.symbol_map
159            .values()
160            .filter(|symbol| {
161                symbol.name.contains(pattern)
162                    && (kind_filter.is_none() || kind_filter.unwrap().contains(&symbol.kind))
163            })
164            .collect()
165    }
166
167    /// Navigate to parent scope
168    pub fn goto_parent(&self, current_symbol: &SymbolInfo) -> Option<NavigationResult> {
169        current_symbol.scope.as_ref().and_then(|scope| {
170            self.symbol_map.get(scope).map(|parent_symbol| {
171                let context = self.build_context(parent_symbol);
172                NavigationResult {
173                    target: NavigationTarget::Symbol(parent_symbol.clone()),
174                    context,
175                    related_symbols: self.find_related_symbols(parent_symbol),
176                }
177            })
178        })
179    }
180
181    /// Navigate to child symbols
182    pub fn goto_children(&self, symbol: &SymbolInfo) -> Vec<NavigationResult> {
183        self.symbol_map
184            .values()
185            .filter(|child| {
186                child
187                    .scope
188                    .as_ref()
189                    .map(|s| s == &symbol.name)
190                    .unwrap_or(false)
191            })
192            .map(|child| {
193                let context = self.build_context(child);
194                NavigationResult {
195                    target: NavigationTarget::Symbol(child.clone()),
196                    context,
197                    related_symbols: self.find_related_symbols(child),
198                }
199            })
200            .collect()
201    }
202
203    /// Build navigation context for a symbol
204    fn build_context(&self, symbol: &SymbolInfo) -> NavigationContext {
205        let current_scope = symbol.scope.clone();
206        let parent_scopes = self.build_parent_scopes(symbol);
207        let available_symbols = self.get_symbols_in_scope(symbol.scope.as_deref());
208        let references = self.find_references(&symbol.name);
209
210        NavigationContext {
211            current_scope,
212            parent_scopes,
213            available_symbols: available_symbols.into_iter().cloned().collect(),
214            references,
215        }
216    }
217
218    /// Build parent scope chain
219    fn build_parent_scopes(&self, symbol: &SymbolInfo) -> Vec<String> {
220        let mut scopes = Vec::new();
221        let mut current_scope = symbol.scope.as_ref();
222
223        while let Some(scope) = current_scope {
224            scopes.push(scope.clone());
225            current_scope = self
226                .symbol_map
227                .get(scope)
228                .and_then(|parent| parent.scope.as_ref());
229        }
230
231        scopes
232    }
233
234    /// Find related symbols (implementations, overrides, etc.)
235    fn find_related_symbols(&self, symbol: &SymbolInfo) -> Vec<SymbolInfo> {
236        let mut related = Vec::new();
237
238        for other in self.symbol_map.values() {
239            if other.name == symbol.name && other.scope != symbol.scope {
240                // Same name in different scope - likely override or implementation
241                related.push(other.clone());
242            } else if other.scope == symbol.scope
243                && other.kind == symbol.kind
244                && other.name != symbol.name
245            {
246                // Same scope and kind but different name - sibling symbols
247                related.push(other.clone());
248            }
249        }
250
251        related
252    }
253}
254
255/// Navigation utilities
256pub struct NavigationUtils;
257
258impl NavigationUtils {
259    /// Find the smallest node containing a position
260    pub fn find_node_at_position<'a>(
261        node: &'a SyntaxNode,
262        position: &Position,
263    ) -> Option<&'a SyntaxNode> {
264        // Check if position is within this node
265        if position.byte_offset >= node.start_position.byte_offset
266            && position.byte_offset <= node.end_position.byte_offset
267        {
268            // Try children first (more specific)
269            for child in &node.children {
270                if let Some(found) = Self::find_node_at_position(child, position) {
271                    return Some(found);
272                }
273            }
274            // Return this node if no child contains the position
275            Some(node)
276        } else {
277            None
278        }
279    }
280
281    /// Get all nodes of a specific type
282    pub fn find_nodes_by_type<'a>(node: &'a SyntaxNode, node_type: &str) -> Vec<&'a SyntaxNode> {
283        let mut results = Vec::new();
284
285        if node.kind == node_type {
286            results.push(node);
287        }
288
289        for child in &node.children {
290            results.extend(Self::find_nodes_by_type(child, node_type));
291        }
292
293        results
294    }
295
296    /// Get the path from root to a specific node
297    pub fn get_node_path(root: &SyntaxNode, target: &SyntaxNode) -> Vec<String> {
298        fn traverse<'a>(
299            current: &'a SyntaxNode,
300            target: &SyntaxNode,
301            path: &mut Vec<String>,
302        ) -> bool {
303            path.push(current.kind.clone());
304            if std::ptr::eq(current, target) {
305                return true;
306            }
307            for child in &current.children {
308                if traverse(child, target, path) {
309                    return true;
310                }
311            }
312            path.pop();
313            false
314        }
315
316        let mut path = Vec::new();
317        if traverse(root, target, &mut path) {
318            path
319        } else {
320            Vec::new()
321        }
322    }
323
324    /// Calculate distance between two positions
325    pub fn calculate_distance(pos1: &Position, pos2: &Position) -> usize {
326        if pos1.byte_offset < pos2.byte_offset {
327            pos2.byte_offset - pos1.byte_offset
328        } else {
329            pos1.byte_offset - pos2.byte_offset
330        }
331    }
332
333    /// Find the nearest symbol to a position
334    pub fn find_nearest_symbol<'a>(
335        symbols: &'a [SymbolInfo],
336        position: &Position,
337    ) -> Option<&'a SymbolInfo> {
338        symbols
339            .iter()
340            .min_by_key(|symbol| Self::calculate_distance(&symbol.position, position))
341    }
342
343    /// Get scope hierarchy at a position
344    pub fn get_scope_hierarchy(node: &SyntaxNode, position: &Position) -> Vec<String> {
345        if let Some(target_node) = Self::find_node_at_position(node, position) {
346            Self::get_node_path(node, target_node)
347        } else {
348            Vec::new()
349        }
350    }
351}