summaryrefslogtreecommitdiff
path: root/stag/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'stag/src/main.rs')
-rw-r--r--stag/src/main.rs810
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 @@
1use std::collections::VecDeque;
2
3use serde::Deserialize;
4use serde::Serialize;
5use tree_sitter::Parser;
6use tree_sitter_graph::ast::File;
7use tree_sitter_graph::functions::{Function, Functions};
8use tree_sitter_graph::graph::Value;
9use tree_sitter_graph::ExecutionConfig;
10use tree_sitter_graph::ExecutionError;
11use tree_sitter_graph::Identifier;
12use tree_sitter_graph::NoCancellation;
13use tree_sitter_graph::Variables;
14
15mod text_range;
16use text_range::TextRange;
17
18use petgraph::{graph::NodeIndex, visit::EdgeRef, Direction, Graph};
19
20fn 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
60fn 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
74fn 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
92fn 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
96fn is_scope(node: &tree_sitter_graph::graph::GraphNode) -> bool {
97 is_string_attr(node, "kind", "scope")
98}
99
100fn is_import(node: &tree_sitter_graph::graph::GraphNode) -> bool {
101 is_string_attr(node, "kind", "import")
102}
103
104fn is_def(node: &tree_sitter_graph::graph::GraphNode) -> bool {
105 is_string_attr(node, "kind", "def")
106}
107
108fn is_ref(node: &tree_sitter_graph::graph::GraphNode) -> bool {
109 is_string_attr(node, "kind", "ref")
110}
111
112pub struct ScopeShorthand;
113
114impl 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
141pub struct DefShorthand;
142
143impl 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
179pub struct RefShortHand;
180
181impl 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
214pub struct CoverRanges;
215
216impl 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
237fn 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
256pub struct NodeRange;
257
258impl 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)]
272pub struct LocalDef {
273 pub range: TextRange,
274 pub text: String,
275 pub symbol: Option<String>,
276}
277
278impl 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)]
294pub struct LocalImport {
295 pub range: TextRange,
296 pub text: String,
297}
298
299impl 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)]
311pub struct Reference {
312 pub range: TextRange,
313 pub text: String,
314 pub symbol: Option<String>,
315}
316
317impl 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)]
329pub struct LocalScope {
330 pub range: TextRange,
331}
332
333impl LocalScope {
334 pub fn new(range: TextRange) -> Self {
335 Self { range }
336 }
337}
338
339pub struct ScopeStack<'a> {
340 pub scope_graph: &'a ScopeGraph,
341 pub start: Option<NodeIndex<u32>>,
342}
343
344impl<'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)]
365pub 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
379impl 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)]
398pub 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)]
417pub 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
427impl 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
737fn 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}