diff options
Diffstat (limited to 'stag/src/main.rs')
-rw-r--r-- | stag/src/main.rs | 810 |
1 files changed, 810 insertions, 0 deletions
diff --git a/stag/src/main.rs b/stag/src/main.rs new file mode 100644 index 0000000..eae6fe9 --- /dev/null +++ b/stag/src/main.rs | |||
@@ -0,0 +1,810 @@ | |||
1 | use std::collections::VecDeque; | ||
2 | |||
3 | use serde::Deserialize; | ||
4 | use serde::Serialize; | ||
5 | use tree_sitter::Parser; | ||
6 | use tree_sitter_graph::ast::File; | ||
7 | use tree_sitter_graph::functions::{Function, Functions}; | ||
8 | use tree_sitter_graph::graph::Value; | ||
9 | use tree_sitter_graph::ExecutionConfig; | ||
10 | use tree_sitter_graph::ExecutionError; | ||
11 | use tree_sitter_graph::Identifier; | ||
12 | use tree_sitter_graph::NoCancellation; | ||
13 | use tree_sitter_graph::Variables; | ||
14 | |||
15 | mod text_range; | ||
16 | use text_range::TextRange; | ||
17 | |||
18 | use petgraph::{graph::NodeIndex, visit::EdgeRef, Direction, Graph}; | ||
19 | |||
20 | fn main() { | ||
21 | let scopes = std::fs::read_to_string("src/stag.scm").unwrap(); | ||
22 | let src = r#"fn main() { | ||
23 | let a = 3; | ||
24 | let b = 4; | ||
25 | a + b | ||
26 | } | ||
27 | "#; | ||
28 | |||
29 | let mut parser = Parser::new(); | ||
30 | let language = tree_sitter_rust::language(); | ||
31 | parser.set_language(language).unwrap(); | ||
32 | let tree = parser.parse(src.as_bytes(), None).unwrap(); | ||
33 | |||
34 | let file = File::from_str(language, &scopes).unwrap(); | ||
35 | |||
36 | let mut functions = Functions::stdlib(); | ||
37 | functions.add(Identifier::from("scope"), ScopeShorthand); | ||
38 | functions.add(Identifier::from("def"), DefShorthand); | ||
39 | functions.add(Identifier::from("ref"), RefShortHand); | ||
40 | functions.add(Identifier::from("cover"), CoverRanges); | ||
41 | functions.add(Identifier::from("range"), NodeRange); | ||
42 | |||
43 | let mut globals = Variables::new(); | ||
44 | globals | ||
45 | .add(Identifier::from("filename"), "test.rs".into()) | ||
46 | .map_err(|_| ExecutionError::DuplicateVariable("filename".into())) | ||
47 | .unwrap(); | ||
48 | |||
49 | let config = ExecutionConfig::new(&functions, &globals); | ||
50 | |||
51 | let graph = file.execute(&tree, &src, &config, &NoCancellation).unwrap(); | ||
52 | |||
53 | let mut sg = ScopeGraph::new(tree.root_node().range().into()); | ||
54 | |||
55 | sg = build_scope_graph(graph, sg); | ||
56 | |||
57 | println!("{:#?}", sg); | ||
58 | } | ||
59 | |||
60 | fn range_to_value(value: &tree_sitter::Range) -> Value { | ||
61 | let start = Value::from(vec![ | ||
62 | Value::from(value.start_byte as u32), | ||
63 | Value::from(value.start_point.row as u32), | ||
64 | Value::from(value.start_point.column as u32), | ||
65 | ]); | ||
66 | let end = Value::from(vec![ | ||
67 | Value::from(value.end_byte as u32), | ||
68 | Value::from(value.end_point.row as u32), | ||
69 | Value::from(value.end_point.column as u32), | ||
70 | ]); | ||
71 | Value::from(vec![start, end]) | ||
72 | } | ||
73 | |||
74 | fn value_to_range(value: &Value) -> tree_sitter::Range { | ||
75 | let range = value.as_list().unwrap(); | ||
76 | let start = range[0].as_list().unwrap(); | ||
77 | let end = range[1].as_list().unwrap(); | ||
78 | tree_sitter::Range { | ||
79 | start_byte: start[0].as_integer().unwrap() as usize, | ||
80 | start_point: tree_sitter::Point { | ||
81 | row: start[1].as_integer().unwrap() as usize, | ||
82 | column: start[2].as_integer().unwrap() as usize, | ||
83 | }, | ||
84 | end_byte: end[0].as_integer().unwrap() as usize, | ||
85 | end_point: tree_sitter::Point { | ||
86 | row: end[1].as_integer().unwrap() as usize, | ||
87 | column: end[2].as_integer().unwrap() as usize, | ||
88 | }, | ||
89 | } | ||
90 | } | ||
91 | |||
92 | fn is_string_attr(node: &tree_sitter_graph::graph::GraphNode, key: &str, value: &str) -> bool { | ||
93 | matches!(node.attributes.get(&Identifier::from(key)).and_then(|v| v.as_str().ok()), Some(v) if v == value) | ||
94 | } | ||
95 | |||
96 | fn is_scope(node: &tree_sitter_graph::graph::GraphNode) -> bool { | ||
97 | is_string_attr(node, "kind", "scope") | ||
98 | } | ||
99 | |||
100 | fn is_import(node: &tree_sitter_graph::graph::GraphNode) -> bool { | ||
101 | is_string_attr(node, "kind", "import") | ||
102 | } | ||
103 | |||
104 | fn is_def(node: &tree_sitter_graph::graph::GraphNode) -> bool { | ||
105 | is_string_attr(node, "kind", "def") | ||
106 | } | ||
107 | |||
108 | fn is_ref(node: &tree_sitter_graph::graph::GraphNode) -> bool { | ||
109 | is_string_attr(node, "kind", "ref") | ||
110 | } | ||
111 | |||
112 | pub struct ScopeShorthand; | ||
113 | |||
114 | impl Function for ScopeShorthand { | ||
115 | fn call( | ||
116 | &self, | ||
117 | graph: &mut tree_sitter_graph::graph::Graph, | ||
118 | source: &str, | ||
119 | parameters: &mut dyn tree_sitter_graph::functions::Parameters, | ||
120 | ) -> Result<tree_sitter_graph::graph::Value, ExecutionError> { | ||
121 | let target_range = parameters.param()?; | ||
122 | if target_range.as_list().is_err() { | ||
123 | return Err(ExecutionError::ExpectedList(format!( | ||
124 | "`scope` expects list" | ||
125 | ))); | ||
126 | } | ||
127 | parameters.finish()?; | ||
128 | |||
129 | let graph_node = graph.add_graph_node(); | ||
130 | graph[graph_node] | ||
131 | .attributes | ||
132 | .add::<String>(Identifier::from("kind"), "scope".into()); | ||
133 | graph[graph_node] | ||
134 | .attributes | ||
135 | .add(Identifier::from("range"), target_range); | ||
136 | |||
137 | Ok(tree_sitter_graph::graph::Value::GraphNode(graph_node)) | ||
138 | } | ||
139 | } | ||
140 | |||
141 | pub struct DefShorthand; | ||
142 | |||
143 | impl Function for DefShorthand { | ||
144 | fn call( | ||
145 | &self, | ||
146 | graph: &mut tree_sitter_graph::graph::Graph, | ||
147 | source: &str, | ||
148 | parameters: &mut dyn tree_sitter_graph::functions::Parameters, | ||
149 | ) -> Result<tree_sitter_graph::graph::Value, ExecutionError> { | ||
150 | let target_node = parameters.param()?.into_syntax_node_ref()?; | ||
151 | let ts_node = graph[target_node]; | ||
152 | parameters.finish()?; | ||
153 | |||
154 | let graph_node = graph.add_graph_node(); | ||
155 | graph[graph_node] | ||
156 | .attributes | ||
157 | .add::<String>(Identifier::from("kind"), "def".into()); | ||
158 | graph[graph_node] | ||
159 | .attributes | ||
160 | .add::<String>(Identifier::from("scope"), "local".into()); | ||
161 | graph[graph_node].attributes.add::<String>( | ||
162 | Identifier::from("text"), | ||
163 | source[ts_node.byte_range()].to_string().into(), | ||
164 | ); | ||
165 | graph[graph_node] | ||
166 | .attributes | ||
167 | .add(Identifier::from("range"), range_to_value(&ts_node.range())); | ||
168 | graph[graph_node] | ||
169 | .attributes | ||
170 | .add::<tree_sitter_graph::graph::SyntaxNodeRef>( | ||
171 | Identifier::from("target"), | ||
172 | target_node.into(), | ||
173 | ); | ||
174 | |||
175 | Ok(tree_sitter_graph::graph::Value::GraphNode(graph_node)) | ||
176 | } | ||
177 | } | ||
178 | |||
179 | pub struct RefShortHand; | ||
180 | |||
181 | impl Function for RefShortHand { | ||
182 | fn call( | ||
183 | &self, | ||
184 | graph: &mut tree_sitter_graph::graph::Graph, | ||
185 | source: &str, | ||
186 | parameters: &mut dyn tree_sitter_graph::functions::Parameters, | ||
187 | ) -> Result<tree_sitter_graph::graph::Value, ExecutionError> { | ||
188 | let target_node = parameters.param()?.into_syntax_node_ref()?; | ||
189 | let ts_node = graph[target_node]; | ||
190 | parameters.finish()?; | ||
191 | |||
192 | let graph_node = graph.add_graph_node(); | ||
193 | graph[graph_node] | ||
194 | .attributes | ||
195 | .add::<String>(Identifier::from("kind"), "ref".into()); | ||
196 | graph[graph_node].attributes.add::<String>( | ||
197 | Identifier::from("text"), | ||
198 | source[ts_node.byte_range()].to_string().into(), | ||
199 | ); | ||
200 | graph[graph_node] | ||
201 | .attributes | ||
202 | .add(Identifier::from("range"), range_to_value(&ts_node.range())); | ||
203 | graph[graph_node] | ||
204 | .attributes | ||
205 | .add::<tree_sitter_graph::graph::SyntaxNodeRef>( | ||
206 | Identifier::from("target"), | ||
207 | target_node.into(), | ||
208 | ); | ||
209 | |||
210 | Ok(tree_sitter_graph::graph::Value::GraphNode(graph_node)) | ||
211 | } | ||
212 | } | ||
213 | |||
214 | pub struct CoverRanges; | ||
215 | |||
216 | impl Function for CoverRanges { | ||
217 | fn call( | ||
218 | &self, | ||
219 | graph: &mut tree_sitter_graph::graph::Graph, | ||
220 | _source: &str, | ||
221 | parameters: &mut dyn tree_sitter_graph::functions::Parameters, | ||
222 | ) -> Result<tree_sitter_graph::graph::Value, ExecutionError> { | ||
223 | let node_a = parameters.param()?.into_syntax_node_ref()?; | ||
224 | let node_b = parameters.param()?.into_syntax_node_ref()?; | ||
225 | let ts_node_a = graph[node_a]; | ||
226 | let ts_node_b = graph[node_b]; | ||
227 | |||
228 | let mut range = cover(ts_node_a.range(), ts_node_b.range()); | ||
229 | while let Ok(param) = parameters.param() { | ||
230 | range = cover(range, graph[param.into_syntax_node_ref()?].range()) | ||
231 | } | ||
232 | |||
233 | Ok(range_to_value(&range)) | ||
234 | } | ||
235 | } | ||
236 | |||
237 | fn cover(a: tree_sitter::Range, b: tree_sitter::Range) -> tree_sitter::Range { | ||
238 | let (start_byte, start_point) = if a.start_point < b.start_point { | ||
239 | (a.start_byte, a.start_point) | ||
240 | } else { | ||
241 | (b.start_byte, b.start_point) | ||
242 | }; | ||
243 | let (end_byte, end_point) = if a.end_point > b.end_point { | ||
244 | (a.end_byte, a.end_point) | ||
245 | } else { | ||
246 | (b.end_byte, b.end_point) | ||
247 | }; | ||
248 | tree_sitter::Range { | ||
249 | start_byte, | ||
250 | start_point, | ||
251 | end_byte, | ||
252 | end_point, | ||
253 | } | ||
254 | } | ||
255 | |||
256 | pub struct NodeRange; | ||
257 | |||
258 | impl Function for NodeRange { | ||
259 | fn call( | ||
260 | &self, | ||
261 | graph: &mut tree_sitter_graph::graph::Graph, | ||
262 | _source: &str, | ||
263 | parameters: &mut dyn tree_sitter_graph::functions::Parameters, | ||
264 | ) -> Result<tree_sitter_graph::graph::Value, ExecutionError> { | ||
265 | let target_node = parameters.param()?.into_syntax_node_ref()?; | ||
266 | let ts_node = graph[target_node]; | ||
267 | Ok(range_to_value(&ts_node.range())) | ||
268 | } | ||
269 | } | ||
270 | |||
271 | #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] | ||
272 | pub struct LocalDef { | ||
273 | pub range: TextRange, | ||
274 | pub text: String, | ||
275 | pub symbol: Option<String>, | ||
276 | } | ||
277 | |||
278 | impl LocalDef { | ||
279 | /// Initialize a new definition | ||
280 | pub fn new(range: TextRange, text: String, symbol: Option<String>) -> Self { | ||
281 | Self { | ||
282 | range, | ||
283 | text, | ||
284 | symbol, | ||
285 | } | ||
286 | } | ||
287 | |||
288 | pub fn name<'a>(&self, buffer: &'a [u8]) -> &'a [u8] { | ||
289 | &buffer[self.range.start.byte..self.range.end.byte] | ||
290 | } | ||
291 | } | ||
292 | |||
293 | #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] | ||
294 | pub struct LocalImport { | ||
295 | pub range: TextRange, | ||
296 | pub text: String, | ||
297 | } | ||
298 | |||
299 | impl LocalImport { | ||
300 | /// Initialize a new import | ||
301 | pub fn new(range: TextRange, text: String) -> Self { | ||
302 | Self { range, text } | ||
303 | } | ||
304 | |||
305 | pub fn name<'a>(&self, buffer: &'a [u8]) -> &'a [u8] { | ||
306 | &buffer[self.range.start.byte..self.range.end.byte] | ||
307 | } | ||
308 | } | ||
309 | |||
310 | #[derive(Debug, Clone, Serialize, Deserialize)] | ||
311 | pub struct Reference { | ||
312 | pub range: TextRange, | ||
313 | pub text: String, | ||
314 | pub symbol: Option<String>, | ||
315 | } | ||
316 | |||
317 | impl Reference { | ||
318 | /// Initialize a new reference | ||
319 | pub fn new(range: TextRange, text: String, symbol: Option<String>) -> Self { | ||
320 | Self { | ||
321 | range, | ||
322 | text, | ||
323 | symbol, | ||
324 | } | ||
325 | } | ||
326 | } | ||
327 | |||
328 | #[derive(Debug, Clone, Copy, Serialize, Deserialize)] | ||
329 | pub struct LocalScope { | ||
330 | pub range: TextRange, | ||
331 | } | ||
332 | |||
333 | impl LocalScope { | ||
334 | pub fn new(range: TextRange) -> Self { | ||
335 | Self { range } | ||
336 | } | ||
337 | } | ||
338 | |||
339 | pub struct ScopeStack<'a> { | ||
340 | pub scope_graph: &'a ScopeGraph, | ||
341 | pub start: Option<NodeIndex<u32>>, | ||
342 | } | ||
343 | |||
344 | impl<'a> Iterator for ScopeStack<'a> { | ||
345 | type Item = NodeIndex<u32>; | ||
346 | fn next(&mut self) -> Option<Self::Item> { | ||
347 | if let Some(start) = self.start { | ||
348 | let parent = self | ||
349 | .scope_graph | ||
350 | .graph | ||
351 | .edges_directed(start, Direction::Outgoing) | ||
352 | .find(|edge| *edge.weight() == EdgeKind::ScopeToScope) | ||
353 | .map(|edge| edge.target()); | ||
354 | let original = start; | ||
355 | self.start = parent; | ||
356 | Some(original) | ||
357 | } else { | ||
358 | None | ||
359 | } | ||
360 | } | ||
361 | } | ||
362 | |||
363 | /// The type of a node in the ScopeGraph | ||
364 | #[derive(Serialize, Deserialize, Debug, Clone)] | ||
365 | pub enum NodeKind { | ||
366 | /// A scope node | ||
367 | Scope(LocalScope), | ||
368 | |||
369 | /// A definition node | ||
370 | Def(LocalDef), | ||
371 | |||
372 | /// An import node | ||
373 | Import(LocalImport), | ||
374 | |||
375 | /// A reference node | ||
376 | Ref(Reference), | ||
377 | } | ||
378 | |||
379 | impl NodeKind { | ||
380 | /// Construct a scope node from a range | ||
381 | pub fn scope(range: TextRange) -> Self { | ||
382 | Self::Scope(LocalScope::new(range)) | ||
383 | } | ||
384 | |||
385 | /// Produce the range spanned by this node | ||
386 | pub fn range(&self) -> TextRange { | ||
387 | match self { | ||
388 | Self::Scope(l) => l.range, | ||
389 | Self::Def(d) => d.range, | ||
390 | Self::Ref(r) => r.range, | ||
391 | Self::Import(i) => i.range, | ||
392 | } | ||
393 | } | ||
394 | } | ||
395 | |||
396 | /// Describes the relation between two nodes in the ScopeGraph | ||
397 | #[derive(Serialize, Deserialize, PartialEq, Eq, Copy, Clone, Debug)] | ||
398 | pub enum EdgeKind { | ||
399 | /// The edge weight from a nested scope to its parent scope | ||
400 | ScopeToScope, | ||
401 | |||
402 | /// The edge weight from a definition to its definition scope | ||
403 | DefToScope, | ||
404 | |||
405 | /// The edge weight from an import to its definition scope | ||
406 | ImportToScope, | ||
407 | |||
408 | /// The edge weight from a reference to its definition | ||
409 | RefToDef, | ||
410 | |||
411 | /// The edge weight from a reference to its import | ||
412 | RefToImport, | ||
413 | } | ||
414 | |||
415 | /// A graph representation of scopes and names in a single syntax tree | ||
416 | #[derive(Debug, Serialize, Deserialize, Clone)] | ||
417 | pub struct ScopeGraph { | ||
418 | /// The raw graph | ||
419 | pub graph: Graph<NodeKind, EdgeKind>, | ||
420 | |||
421 | // Graphs do not have the concept of a `root`, but lexical scopes follow the syntax | ||
422 | // tree, and as a result, have a "root" node. The root_idx points to a scope node that | ||
423 | // encompasses the entire file: the file-global scope. | ||
424 | root_idx: NodeIndex, | ||
425 | } | ||
426 | |||
427 | impl ScopeGraph { | ||
428 | pub fn new(range: TextRange) -> Self { | ||
429 | let mut graph = Graph::new(); | ||
430 | let root_idx = graph.add_node(NodeKind::scope(range)); | ||
431 | Self { graph, root_idx } | ||
432 | } | ||
433 | |||
434 | pub fn get_node(&self, node_idx: NodeIndex) -> Option<&NodeKind> { | ||
435 | self.graph.node_weight(node_idx) | ||
436 | } | ||
437 | |||
438 | /// Insert a local scope into the scope-graph | ||
439 | pub fn insert_local_scope(&mut self, new: LocalScope) { | ||
440 | if let Some(parent_scope) = self.scope_by_range(new.range, self.root_idx) { | ||
441 | let new_scope = NodeKind::Scope(new); | ||
442 | let new_idx = self.graph.add_node(new_scope); | ||
443 | self.graph | ||
444 | .add_edge(new_idx, parent_scope, EdgeKind::ScopeToScope); | ||
445 | } | ||
446 | } | ||
447 | |||
448 | /// Insert a def into the scope-graph | ||
449 | pub fn insert_local_def(&mut self, new: LocalDef) { | ||
450 | if let Some(defining_scope) = self.scope_by_range(new.range, self.root_idx) { | ||
451 | let new_def = NodeKind::Def(new); | ||
452 | let new_idx = self.graph.add_node(new_def); | ||
453 | self.graph | ||
454 | .add_edge(new_idx, defining_scope, EdgeKind::DefToScope); | ||
455 | } | ||
456 | } | ||
457 | |||
458 | /// Insert a def into the scope-graph, at the parent scope of the defining scope | ||
459 | pub fn insert_hoisted_def(&mut self, new: LocalDef) { | ||
460 | if let Some(defining_scope) = self.scope_by_range(new.range, self.root_idx) { | ||
461 | let new_def = NodeKind::Def(new); | ||
462 | let new_idx = self.graph.add_node(new_def); | ||
463 | |||
464 | // if the parent scope exists, insert this def there, if not, | ||
465 | // insert into the defining scope | ||
466 | let target_scope = self.parent_scope(defining_scope).unwrap_or(defining_scope); | ||
467 | |||
468 | self.graph | ||
469 | .add_edge(new_idx, target_scope, EdgeKind::DefToScope); | ||
470 | } | ||
471 | } | ||
472 | |||
473 | /// Insert a def into the scope-graph, at the root scope | ||
474 | pub fn insert_global_def(&mut self, new: LocalDef) { | ||
475 | let new_def = NodeKind::Def(new); | ||
476 | let new_idx = self.graph.add_node(new_def); | ||
477 | self.graph | ||
478 | .add_edge(new_idx, self.root_idx, EdgeKind::DefToScope); | ||
479 | } | ||
480 | |||
481 | /// Insert an import into the scope-graph | ||
482 | pub fn insert_local_import(&mut self, new: LocalImport) { | ||
483 | if let Some(defining_scope) = self.scope_by_range(new.range, self.root_idx) { | ||
484 | let new_imp = NodeKind::Import(new); | ||
485 | let new_idx = self.graph.add_node(new_imp); | ||
486 | self.graph | ||
487 | .add_edge(new_idx, defining_scope, EdgeKind::ImportToScope); | ||
488 | } | ||
489 | } | ||
490 | |||
491 | /// Insert a ref into the scope-graph | ||
492 | pub fn insert_ref(&mut self, new: Reference) { | ||
493 | let mut possible_defs = vec![]; | ||
494 | let mut possible_imports = vec![]; | ||
495 | if let Some(local_scope_idx) = self.scope_by_range(new.range, self.root_idx) { | ||
496 | // traverse the scopes from the current-scope to the root-scope | ||
497 | for scope in self.scope_stack(local_scope_idx) { | ||
498 | // find candidate definitions in each scope | ||
499 | for local_def in self | ||
500 | .graph | ||
501 | .edges_directed(scope, Direction::Incoming) | ||
502 | .filter(|edge| *edge.weight() == EdgeKind::DefToScope) | ||
503 | .map(|edge| edge.source()) | ||
504 | { | ||
505 | if let NodeKind::Def(def) = &self.graph[local_def] { | ||
506 | if new.text == def.text { | ||
507 | match (def.symbol.as_ref(), new.symbol.as_ref()) { | ||
508 | // both contain symbols, but they don't belong to the same namepspace | ||
509 | (Some(d), Some(r)) if d != r => {} | ||
510 | |||
511 | // in all other cases, form an edge from the ref to def. | ||
512 | // an empty symbol belongs to all namespaces: | ||
513 | // * (None, None) | ||
514 | // * (None, Some(_)) | ||
515 | // * (Some(_), None) | ||
516 | // * (Some(_), Some(_)) if def.namespace == ref.namespace | ||
517 | _ => { | ||
518 | possible_defs.push(local_def); | ||
519 | } | ||
520 | }; | ||
521 | } | ||
522 | } | ||
523 | } | ||
524 | |||
525 | // find candidate imports in each scope | ||
526 | for local_import in self | ||
527 | .graph | ||
528 | .edges_directed(scope, Direction::Incoming) | ||
529 | .filter(|edge| *edge.weight() == EdgeKind::ImportToScope) | ||
530 | .map(|edge| edge.source()) | ||
531 | { | ||
532 | if let NodeKind::Import(import) = &self.graph[local_import] { | ||
533 | if new.text == import.text { | ||
534 | possible_imports.push(local_import); | ||
535 | } | ||
536 | } | ||
537 | } | ||
538 | } | ||
539 | } | ||
540 | |||
541 | if !possible_defs.is_empty() || !possible_imports.is_empty() { | ||
542 | let new_ref = NodeKind::Ref(new); | ||
543 | let ref_idx = self.graph.add_node(new_ref); | ||
544 | for def_idx in possible_defs { | ||
545 | self.graph.add_edge(ref_idx, def_idx, EdgeKind::RefToDef); | ||
546 | } | ||
547 | for imp_idx in possible_imports { | ||
548 | self.graph.add_edge(ref_idx, imp_idx, EdgeKind::RefToImport); | ||
549 | } | ||
550 | } | ||
551 | } | ||
552 | |||
553 | fn scope_stack(&self, start: NodeIndex) -> ScopeStack<'_> { | ||
554 | ScopeStack { | ||
555 | scope_graph: self, | ||
556 | start: Some(start), | ||
557 | } | ||
558 | } | ||
559 | |||
560 | // The smallest scope that encompasses `range`. Start at `start` and narrow down if possible. | ||
561 | fn scope_by_range(&self, range: TextRange, start: NodeIndex) -> Option<NodeIndex> { | ||
562 | let target_range = self.graph[start].range(); | ||
563 | if target_range.contains(&range) { | ||
564 | let mut child_scopes = self | ||
565 | .graph | ||
566 | .edges_directed(start, Direction::Incoming) | ||
567 | .filter(|edge| *edge.weight() == EdgeKind::ScopeToScope) | ||
568 | .map(|edge| edge.source()) | ||
569 | .collect::<Vec<_>>(); | ||
570 | |||
571 | child_scopes.sort_by_key(|s| self.graph[*s].range()); | ||
572 | let target_child_scope = child_scopes.binary_search_by(|x| { | ||
573 | if self.graph[*x].range().contains(&range) { | ||
574 | std::cmp::Ordering::Equal | ||
575 | } else { | ||
576 | self.graph[*x].range().cmp(&range) | ||
577 | } | ||
578 | }); | ||
579 | |||
580 | if let Some(t) = target_child_scope | ||
581 | .ok() | ||
582 | .and_then(|idx| child_scopes.get(idx)) | ||
583 | .and_then(|s| self.scope_by_range(range, *s)) | ||
584 | { | ||
585 | return Some(t); | ||
586 | } else { | ||
587 | return Some(start); | ||
588 | } | ||
589 | } | ||
590 | None | ||
591 | } | ||
592 | |||
593 | // Produce the parent scope of a given scope | ||
594 | fn parent_scope(&self, start: NodeIndex) -> Option<NodeIndex> { | ||
595 | if matches!(self.graph[start], NodeKind::Scope(_)) { | ||
596 | return self | ||
597 | .graph | ||
598 | .edges_directed(start, Direction::Outgoing) | ||
599 | .filter(|edge| *edge.weight() == EdgeKind::ScopeToScope) | ||
600 | .map(|edge| edge.target()) | ||
601 | .next(); | ||
602 | } | ||
603 | None | ||
604 | } | ||
605 | |||
606 | /// Produce a list of interesting ranges: ranges of defs and refs | ||
607 | pub fn hoverable_ranges(&self) -> Box<dyn Iterator<Item = TextRange> + '_> { | ||
608 | let iterator = | ||
609 | self.graph | ||
610 | .node_indices() | ||
611 | .filter_map(|node_idx| match &self.graph[node_idx] { | ||
612 | NodeKind::Scope(_) => None, | ||
613 | NodeKind::Def(d) => Some(d.range), | ||
614 | NodeKind::Ref(r) => Some(r.range), | ||
615 | NodeKind::Import(i) => Some(i.range), | ||
616 | }); | ||
617 | Box::new(iterator) | ||
618 | } | ||
619 | |||
620 | /// Produce possible definitions for a reference | ||
621 | pub fn definitions( | ||
622 | &self, | ||
623 | reference_node: NodeIndex, | ||
624 | ) -> Box<dyn Iterator<Item = NodeIndex> + '_> { | ||
625 | let iterator = self | ||
626 | .graph | ||
627 | .edges_directed(reference_node, Direction::Outgoing) | ||
628 | .filter(|edge| *edge.weight() == EdgeKind::RefToDef) | ||
629 | .map(|edge| edge.target()); | ||
630 | Box::new(iterator) | ||
631 | } | ||
632 | |||
633 | /// Produce possible imports for a reference | ||
634 | pub fn imports(&self, reference_node: NodeIndex) -> Box<dyn Iterator<Item = NodeIndex> + '_> { | ||
635 | let iterator = self | ||
636 | .graph | ||
637 | .edges_directed(reference_node, Direction::Outgoing) | ||
638 | .filter(|edge| *edge.weight() == EdgeKind::RefToImport) | ||
639 | .map(|edge| edge.target()); | ||
640 | Box::new(iterator) | ||
641 | } | ||
642 | |||
643 | /// Produce possible references for a definition/import node | ||
644 | pub fn references( | ||
645 | &self, | ||
646 | definition_node: NodeIndex, | ||
647 | ) -> Box<dyn Iterator<Item = NodeIndex> + '_> { | ||
648 | let iterator = self | ||
649 | .graph | ||
650 | .edges_directed(definition_node, Direction::Incoming) | ||
651 | .filter(|edge| { | ||
652 | *edge.weight() == EdgeKind::RefToDef || *edge.weight() == EdgeKind::RefToImport | ||
653 | }) | ||
654 | .map(|edge| edge.source()); | ||
655 | Box::new(iterator) | ||
656 | } | ||
657 | |||
658 | pub fn node_by_range(&self, start_byte: usize, end_byte: usize) -> Option<NodeIndex> { | ||
659 | self.graph | ||
660 | .node_indices() | ||
661 | .filter(|&idx| self.is_definition(idx) || self.is_reference(idx) || self.is_import(idx)) | ||
662 | .find(|&idx| { | ||
663 | let node = self.graph[idx].range(); | ||
664 | start_byte >= node.start.byte && end_byte <= node.end.byte | ||
665 | }) | ||
666 | } | ||
667 | |||
668 | /// The "value" of a definition is loosely characterized as | ||
669 | /// | ||
670 | /// - the body of a function block | ||
671 | /// - the body of a class | ||
672 | /// - the parameters list defining generic types | ||
673 | /// - the RHS of a value | ||
674 | /// | ||
675 | /// The heuristic used here is | ||
676 | /// - the smallest scope-node that encompasses the definition_node | ||
677 | /// - or the largest scope-node on the same line as the to the definition_node | ||
678 | pub fn value_of_definition(&self, def_idx: NodeIndex) -> Option<NodeIndex> { | ||
679 | let smallest_scope_node = self | ||
680 | .scope_by_range(self.graph[def_idx].range(), self.root_idx) | ||
681 | .filter(|&idx| { | ||
682 | self.graph[idx].range().start.line == self.graph[def_idx].range().start.line | ||
683 | }); | ||
684 | let largest_adjacent_node = self | ||
685 | .graph | ||
686 | .node_indices() | ||
687 | .filter(|&idx| match self.graph[idx] { | ||
688 | NodeKind::Scope(scope) => { | ||
689 | scope.range.start.line == self.graph[def_idx].range().start.line | ||
690 | } | ||
691 | _ => false, | ||
692 | }) | ||
693 | .max_by_key(|idx| self.graph[*idx].range().size()); | ||
694 | |||
695 | smallest_scope_node.or(largest_adjacent_node) | ||
696 | } | ||
697 | |||
698 | pub fn node_by_position(&self, line: usize, column: usize) -> Option<NodeIndex> { | ||
699 | self.graph | ||
700 | .node_indices() | ||
701 | .filter(|&idx| self.is_definition(idx) || self.is_reference(idx)) | ||
702 | .find(|&idx| { | ||
703 | let node = self.graph[idx].range(); | ||
704 | node.start.line == line | ||
705 | && node.end.line == line | ||
706 | && node.start.column <= column | ||
707 | && node.end.column >= column | ||
708 | }) | ||
709 | } | ||
710 | |||
711 | #[cfg(test)] | ||
712 | pub fn find_node_by_name(&self, src: &[u8], name: &[u8]) -> Option<NodeIndex> { | ||
713 | self.graph.node_indices().find(|idx| { | ||
714 | matches!( | ||
715 | &self.graph[*idx], | ||
716 | NodeKind::Def(d) if d.name(src) == name) | ||
717 | }) | ||
718 | } | ||
719 | |||
720 | pub fn is_definition(&self, node_idx: NodeIndex) -> bool { | ||
721 | matches!(self.graph[node_idx], NodeKind::Def(_)) | ||
722 | } | ||
723 | |||
724 | pub fn is_reference(&self, node_idx: NodeIndex) -> bool { | ||
725 | matches!(self.graph[node_idx], NodeKind::Ref(_)) | ||
726 | } | ||
727 | |||
728 | pub fn is_scope(&self, node_idx: NodeIndex) -> bool { | ||
729 | matches!(self.graph[node_idx], NodeKind::Scope(_)) | ||
730 | } | ||
731 | |||
732 | pub fn is_import(&self, node_idx: NodeIndex) -> bool { | ||
733 | matches!(self.graph[node_idx], NodeKind::Import(_)) | ||
734 | } | ||
735 | } | ||
736 | |||
737 | fn build_scope_graph( | ||
738 | tsg: tree_sitter_graph::graph::Graph, | ||
739 | mut scope_graph: ScopeGraph, | ||
740 | ) -> ScopeGraph { | ||
741 | let nodes = tsg.iter_nodes().collect::<Vec<_>>(); | ||
742 | // insert scopes first | ||
743 | for node in nodes | ||
744 | .iter() | ||
745 | .map(|node_ref| &tsg[*node_ref]) | ||
746 | .filter(|node| is_scope(node)) | ||
747 | { | ||
748 | let range = value_to_range(node.attributes.get(&Identifier::from("range")).unwrap()); | ||
749 | let scope = LocalScope::new(range.into()); | ||
750 | scope_graph.insert_local_scope(scope); | ||
751 | } | ||
752 | |||
753 | for node in nodes | ||
754 | .iter() | ||
755 | .map(|node_ref| &tsg[*node_ref]) | ||
756 | .filter(|node| is_import(node)) | ||
757 | { | ||
758 | let range = value_to_range(node.attributes.get(&Identifier::from("range")).unwrap()); | ||
759 | let text = node | ||
760 | .attributes | ||
761 | .get(&Identifier::from("text")) | ||
762 | .and_then(|id| id.clone().into_string().ok()) | ||
763 | .expect("import without text"); | ||
764 | let import = LocalImport::new(range.into(), text); | ||
765 | scope_graph.insert_local_import(import); | ||
766 | } | ||
767 | |||
768 | for node in nodes | ||
769 | .iter() | ||
770 | .map(|node_ref| &tsg[*node_ref]) | ||
771 | .filter(|node| is_def(node)) | ||
772 | { | ||
773 | let range = value_to_range(node.attributes.get(&Identifier::from("range")).unwrap()); | ||
774 | let symbol = node | ||
775 | .attributes | ||
776 | .get(&Identifier::from("symbol")) | ||
777 | .and_then(|id| id.clone().into_string().ok()); | ||
778 | let text = node | ||
779 | .attributes | ||
780 | .get(&Identifier::from("text")) | ||
781 | .and_then(|id| id.clone().into_string().ok()) | ||
782 | .expect("def without text"); | ||
783 | let local_def = LocalDef::new(range.into(), text, symbol); | ||
784 | |||
785 | // TODO: fix scoping here | ||
786 | scope_graph.insert_local_def(local_def); | ||
787 | } | ||
788 | |||
789 | for node in nodes | ||
790 | .iter() | ||
791 | .map(|node_ref| &tsg[*node_ref]) | ||
792 | .filter(|node| is_ref(node)) | ||
793 | { | ||
794 | let range = value_to_range(node.attributes.get(&Identifier::from("range")).unwrap()); | ||
795 | let symbol = node | ||
796 | .attributes | ||
797 | .get(&Identifier::from("symbol")) | ||
798 | .and_then(|id| id.clone().into_string().ok()); | ||
799 | let text = node | ||
800 | .attributes | ||
801 | .get(&Identifier::from("text")) | ||
802 | .and_then(|id| id.clone().into_string().ok()) | ||
803 | .expect("ref without text"); | ||
804 | let ref_ = Reference::new(range.into(), text, symbol); | ||
805 | |||
806 | scope_graph.insert_ref(ref_); | ||
807 | } | ||
808 | |||
809 | scope_graph | ||
810 | } | ||