vtcode_core/tools/tree_sitter/
navigation.rs1use 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#[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#[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 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#[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#[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
61pub 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 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 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 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 pub fn find_references(&self, symbol_name: &str) -> Vec<ReferenceInfo> {
113 self.symbol_map
115 .values()
116 .filter(|symbol| {
117 symbol.name == symbol_name
121 })
122 .map(|symbol| ReferenceInfo {
123 symbol: symbol.clone(),
124 reference_type: ReferenceType::Usage, })
126 .collect()
127 }
128
129 pub fn get_symbol_at_position(&self, position: &Position) -> Option<&SymbolInfo> {
131 self.position_map.get(position)
132 }
133
134 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() }
148 })
149 .collect()
150 }
151
152 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 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 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 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 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 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 related.push(other.clone());
242 } else if other.scope == symbol.scope
243 && other.kind == symbol.kind
244 && other.name != symbol.name
245 {
246 related.push(other.clone());
248 }
249 }
250
251 related
252 }
253}
254
255pub struct NavigationUtils;
257
258impl NavigationUtils {
259 pub fn find_node_at_position<'a>(
261 node: &'a SyntaxNode,
262 position: &Position,
263 ) -> Option<&'a SyntaxNode> {
264 if position.byte_offset >= node.start_position.byte_offset
266 && position.byte_offset <= node.end_position.byte_offset
267 {
268 for child in &node.children {
270 if let Some(found) = Self::find_node_at_position(child, position) {
271 return Some(found);
272 }
273 }
274 Some(node)
276 } else {
277 None
278 }
279 }
280
281 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 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 ¤t.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 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 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 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}