diff options
author | Akshay <[email protected]> | 2024-01-16 21:18:52 +0000 |
---|---|---|
committer | Akshay <[email protected]> | 2024-01-16 21:18:52 +0000 |
commit | be60a9f66d1c01b15ddc3a56bca0416ce8dee0fd (patch) | |
tree | 159f9afd776f4881f1312d5b36384b8d0533904b /stag | |
parent | f99715d8805a18dc7c16f17275a85a0724e01db9 (diff) |
begin work on stag
Diffstat (limited to 'stag')
-rw-r--r-- | stag/Cargo.toml | 16 | ||||
-rw-r--r-- | stag/src/main.rs | 810 | ||||
-rw-r--r-- | stag/src/scopes.scm | 463 | ||||
-rw-r--r-- | stag/src/stag.scm | 36 | ||||
-rw-r--r-- | stag/src/text_range.rs | 121 |
5 files changed, 1446 insertions, 0 deletions
diff --git a/stag/Cargo.toml b/stag/Cargo.toml new file mode 100644 index 0000000..24f07e0 --- /dev/null +++ b/stag/Cargo.toml | |||
@@ -0,0 +1,16 @@ | |||
1 | [package] | ||
2 | name = "stag" | ||
3 | version = "0.1.0" | ||
4 | edition = "2021" | ||
5 | |||
6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
7 | |||
8 | [dependencies] | ||
9 | petgraph = { version = "0.6.4", default-features = false, features = ["serde-1"] } | ||
10 | serde = {version = "1.0.188", features = ["derive"]} | ||
11 | once_cell = "1.19.0" | ||
12 | thiserror = "1.0.56" | ||
13 | tree-sitter-graph = { path = "../tree-sitter-graph/"} | ||
14 | tree-sitter-rust = "0.20.4" | ||
15 | tree-sitter = "0.20.10" | ||
16 | |||
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 | } | ||
diff --git a/stag/src/scopes.scm b/stag/src/scopes.scm new file mode 100644 index 0000000..8a5c385 --- /dev/null +++ b/stag/src/scopes.scm | |||
@@ -0,0 +1,463 @@ | |||
1 | ;; see tree-sitter-rust/src/grammar.json for an exhaustive list of productions | ||
2 | |||
3 | ;; scopes | ||
4 | (block) @local.scope ; { ... } | ||
5 | (function_item) @local.scope | ||
6 | (declaration_list) @local.scope ; mod { ... } | ||
7 | |||
8 | ;; impl items can define types and lifetimes: | ||
9 | ;; | ||
10 | ;; impl<'a, T> Trait for Struct { .. } | ||
11 | ;; | ||
12 | ;; in order to constrain those to the impl block, | ||
13 | ;; we add a local scope here: | ||
14 | (impl_item) @local.scope | ||
15 | (struct_item) @local.scope | ||
16 | (enum_item) @local.scope | ||
17 | (union_item) @local.scope | ||
18 | (type_item) @local.scope | ||
19 | (trait_item) @local.scope | ||
20 | |||
21 | ;; let expressions create scopes | ||
22 | (if_expression | ||
23 | [(let_condition) | ||
24 | (let_chain)]) @local.scope | ||
25 | |||
26 | ;; each match arm can bind variables with | ||
27 | ;; patterns, without creating a block scope; | ||
28 | ;; | ||
29 | ;; match _ { | ||
30 | ;; (a, b) => a, | ||
31 | ;; } | ||
32 | ;; | ||
33 | ;; The bindings for a, b are constrained to | ||
34 | ;; the match arm. | ||
35 | (match_arm) @local.scope | ||
36 | |||
37 | ;; loop labels are defs that are available only | ||
38 | ;; within the scope they create: | ||
39 | ;; | ||
40 | ;; 'outer: loop { | ||
41 | ;; let x = 2; | ||
42 | ;; }; | ||
43 | ;; let y = 2; | ||
44 | ;; | ||
45 | ;; Produces a scope graph like so: | ||
46 | ;; | ||
47 | ;; { | ||
48 | ;; defs: [ y ], | ||
49 | ;; scopes: [ | ||
50 | ;; { | ||
51 | ;; defs: [ 'outer ], | ||
52 | ;; scopes: [ | ||
53 | ;; { | ||
54 | ;; defs: [ x ] | ||
55 | ;; } | ||
56 | ;; ] | ||
57 | ;; } | ||
58 | ;; ] | ||
59 | ;; } | ||
60 | ;; | ||
61 | (loop_expression) @local.scope | ||
62 | (for_expression) @local.scope | ||
63 | (while_expression) @local.scope | ||
64 | |||
65 | |||
66 | ;; defs | ||
67 | |||
68 | ;; let x = ...; | ||
69 | (let_declaration | ||
70 | pattern: (identifier) @local.definition.variable) | ||
71 | |||
72 | ;; if let x = ...; | ||
73 | ;; while let x = ...; | ||
74 | (let_condition | ||
75 | . | ||
76 | (identifier) @local.definition.variable) | ||
77 | |||
78 | ;; let (a, b, ...) = ..; | ||
79 | ;; if let (a, b, ...) = {} | ||
80 | ;; while let (a, b, ...) = {} | ||
81 | ;; match _ { (a, b) => { .. } } | ||
82 | (tuple_pattern (identifier) @local.definition.variable) | ||
83 | |||
84 | ;; Some(a) | ||
85 | (tuple_struct_pattern | ||
86 | type: (_) | ||
87 | (identifier) @local.definition.variable) | ||
88 | |||
89 | ;; let S { field: a } = ..; | ||
90 | (struct_pattern | ||
91 | (field_pattern | ||
92 | (identifier) @local.definition.variable)) | ||
93 | |||
94 | ;; let S { a, b } = ..; | ||
95 | (struct_pattern | ||
96 | (field_pattern | ||
97 | (shorthand_field_identifier) @local.definition.variable)) | ||
98 | |||
99 | ;; (mut x: T) | ||
100 | (mut_pattern (identifier) @local.definition.variable) | ||
101 | |||
102 | ;; (ref x: T) | ||
103 | (ref_pattern (identifier) @local.definition.variable) | ||
104 | |||
105 | ;; const x = ...; | ||
106 | (const_item (identifier) @local.definition.const) | ||
107 | |||
108 | ;; static x = ...; | ||
109 | (static_item (identifier) @local.definition.const) | ||
110 | |||
111 | ;; fn _(x: _) | ||
112 | (parameters | ||
113 | (parameter | ||
114 | pattern: (identifier) @local.definition.variable)) | ||
115 | ;; fn _(self) | ||
116 | (parameters | ||
117 | (self_parameter | ||
118 | (self) @local.definition.variable)) | ||
119 | |||
120 | ;; type parameters | ||
121 | (type_parameters | ||
122 | (type_identifier) @local.definition.typedef) | ||
123 | (type_parameters | ||
124 | (lifetime) @local.definition.lifetime) | ||
125 | (constrained_type_parameter | ||
126 | left: (type_identifier) @local.definition.typedef) | ||
127 | |||
128 | ;; |x| { ... } | ||
129 | ;; no type | ||
130 | (closure_parameters (identifier) @local.definition.variable) | ||
131 | |||
132 | ;; |x: T| { ... } | ||
133 | ;; with type | ||
134 | (closure_parameters | ||
135 | (parameter | ||
136 | (identifier) @local.definition.variable)) | ||
137 | |||
138 | ;;fn x(..) | ||
139 | (function_item (identifier) @hoist.definition.function) | ||
140 | |||
141 | ;; 'outer: loop { .. } | ||
142 | (loop_expression | ||
143 | (loop_label) @local.definition.label) | ||
144 | |||
145 | ;; `for` exprs create two defs: a label (if any) and the | ||
146 | ;; loop variable | ||
147 | (for_expression . (identifier) @local.definition.variable) | ||
148 | (for_expression (loop_label) @local.definition.label) | ||
149 | |||
150 | ;; 'label: while cond { .. } | ||
151 | (while_expression | ||
152 | (loop_label) @local.definition.label) | ||
153 | |||
154 | ;; type definitions | ||
155 | (struct_item (type_identifier) @hoist.definition.struct) | ||
156 | (enum_item (type_identifier) @hoist.definition.enum) | ||
157 | (union_item (type_identifier) @hoist.definition.union) | ||
158 | (type_item . (type_identifier) @hoist.definition.typedef) | ||
159 | (trait_item (type_identifier) @hoist.definition.interface) | ||
160 | |||
161 | ;; struct and union fields | ||
162 | (field_declaration_list | ||
163 | (field_declaration | ||
164 | (field_identifier) @local.definition.field)) | ||
165 | |||
166 | ;; enum variants | ||
167 | (enum_variant_list | ||
168 | (enum_variant | ||
169 | (identifier) @local.definition.enumerator)) | ||
170 | |||
171 | ;; mod x; | ||
172 | (mod_item (identifier) @local.definition.module) | ||
173 | |||
174 | ;; use statements | ||
175 | |||
176 | ;; use item; | ||
177 | (use_declaration | ||
178 | (identifier) @local.import) | ||
179 | |||
180 | ;; use path as item; | ||
181 | (use_as_clause | ||
182 | alias: (identifier) @local.import) | ||
183 | |||
184 | ;; use path::item; | ||
185 | (use_declaration | ||
186 | (scoped_identifier | ||
187 | name: (identifier) @local.import)) | ||
188 | |||
189 | ;; use module::{member1, member2, member3}; | ||
190 | (use_list | ||
191 | (identifier) @local.import) | ||
192 | (use_list | ||
193 | (scoped_identifier | ||
194 | name: (identifier) @local.import)) | ||
195 | |||
196 | |||
197 | ;; refs | ||
198 | |||
199 | ;; !x | ||
200 | (unary_expression (identifier) @local.reference) | ||
201 | |||
202 | ;; &x | ||
203 | (reference_expression (identifier) @local.reference) | ||
204 | |||
205 | ;; (x) | ||
206 | (parenthesized_expression (identifier) @local.reference) | ||
207 | |||
208 | ;; x? | ||
209 | (try_expression (identifier) @local.reference) | ||
210 | |||
211 | ;; a = b | ||
212 | (assignment_expression (identifier) @local.reference) | ||
213 | |||
214 | ;; a op b | ||
215 | (binary_expression (identifier) @local.reference) | ||
216 | |||
217 | ;; a op= b | ||
218 | (compound_assignment_expr (identifier) @local.reference) | ||
219 | |||
220 | ;; a as b | ||
221 | (type_cast_expression (identifier) @local.reference) | ||
222 | |||
223 | ;; a() | ||
224 | (call_expression (identifier) @local.reference) | ||
225 | |||
226 | ;; Self::foo() | ||
227 | ;; | ||
228 | ;; `foo` can be resolved | ||
229 | (call_expression | ||
230 | (scoped_identifier | ||
231 | (identifier) @_self_type | ||
232 | (identifier) @local.reference) | ||
233 | (#match? @_self_type "Self")) | ||
234 | |||
235 | ;; self.foo() | ||
236 | ;; | ||
237 | ;; `foo` can be resolved | ||
238 | (call_expression | ||
239 | (field_expression | ||
240 | (self) | ||
241 | (field_identifier) @local.reference)) | ||
242 | |||
243 | ;; return a | ||
244 | (return_expression (identifier) @local.reference) | ||
245 | |||
246 | ;; break a | ||
247 | (break_expression (identifier) @local.reference) | ||
248 | |||
249 | ;; break 'label | ||
250 | (break_expression (loop_label) @local.reference) | ||
251 | |||
252 | ;; continue 'label; | ||
253 | (continue_expression (loop_label) @local.reference) | ||
254 | |||
255 | ;; yield x; | ||
256 | (yield_expression (identifier) @local.reference) | ||
257 | |||
258 | ;; await a | ||
259 | (await_expression (identifier) @local.reference) | ||
260 | |||
261 | ;; (a, b) | ||
262 | (tuple_expression (identifier) @local.reference) | ||
263 | |||
264 | ;; a[] | ||
265 | (index_expression (identifier) @local.reference) | ||
266 | |||
267 | ;; ident; | ||
268 | (expression_statement (identifier) @local.reference) | ||
269 | |||
270 | ;; a..b | ||
271 | (range_expression (identifier) @local.reference) | ||
272 | |||
273 | ;; [ident; N] | ||
274 | (array_expression (identifier) @local.reference) | ||
275 | |||
276 | ;; path::to::item | ||
277 | ;; | ||
278 | ;; `path` is a ref | ||
279 | (scoped_identifier | ||
280 | path: (identifier) @local.reference) | ||
281 | |||
282 | ;; rhs of let decls | ||
283 | (let_declaration | ||
284 | value: (identifier) @local.reference) | ||
285 | |||
286 | ;; type T = [T; N] | ||
287 | ;; | ||
288 | ;; N is a ident ref | ||
289 | (array_type | ||
290 | length: (identifier) @local.reference) | ||
291 | |||
292 | ;; S { _ } | ||
293 | (struct_expression | ||
294 | (type_identifier) @local.reference) | ||
295 | |||
296 | ;; S { a } | ||
297 | (struct_expression | ||
298 | (field_initializer_list | ||
299 | (shorthand_field_initializer | ||
300 | (identifier) @local.reference))) | ||
301 | |||
302 | ;; S { a: value } | ||
303 | (struct_expression | ||
304 | (field_initializer_list | ||
305 | (field_initializer | ||
306 | (identifier) @local.reference))) | ||
307 | |||
308 | ;; S { ..a } | ||
309 | (struct_expression | ||
310 | (field_initializer_list | ||
311 | (base_field_initializer | ||
312 | (identifier) @local.reference))) | ||
313 | |||
314 | ;; if a {} | ||
315 | (if_expression (identifier) @local.reference) | ||
316 | |||
317 | ;; for pattern in value {} | ||
318 | ;; | ||
319 | ;; `value` is a ref | ||
320 | (for_expression | ||
321 | value: (identifier) @local.reference) | ||
322 | |||
323 | ;; while a {} | ||
324 | (while_expression (identifier) @local.reference) | ||
325 | |||
326 | ;; if let _ = a {} | ||
327 | ;; | ||
328 | ;; the ident following the `=` is a ref | ||
329 | ;; the ident preceding the `=` is a def | ||
330 | ;; while let _ = a {} | ||
331 | (let_condition | ||
332 | "=" | ||
333 | (identifier) @local.reference) | ||
334 | |||
335 | |||
336 | ;; match a | ||
337 | (match_expression (identifier) @local.reference) | ||
338 | |||
339 | ;; match _ { | ||
340 | ;; pattern => a, | ||
341 | ;; } | ||
342 | ;; | ||
343 | ;; this `a` is somehow not any expression form | ||
344 | (match_arm (identifier) @local.reference) | ||
345 | |||
346 | ;; a.b | ||
347 | ;; | ||
348 | ;; `b` is ignored | ||
349 | (field_expression | ||
350 | (identifier) @local.reference) | ||
351 | |||
352 | ;; { stmt; foo } | ||
353 | (block | ||
354 | (identifier) @local.reference) | ||
355 | |||
356 | ;; arguments to method calls or function calls | ||
357 | (arguments | ||
358 | (identifier) @local.reference) | ||
359 | |||
360 | ;; impl S { .. } | ||
361 | (impl_item (type_identifier) @local.reference) | ||
362 | |||
363 | ;; where T: ... | ||
364 | (where_predicate | ||
365 | left: (type_identifier) @local.reference) | ||
366 | |||
367 | ;; trait bounds | ||
368 | (trait_bounds | ||
369 | (type_identifier) @local.reference) | ||
370 | (trait_bounds | ||
371 | (lifetime) @local.reference) | ||
372 | |||
373 | ;; idents in macros | ||
374 | (token_tree | ||
375 | (identifier) @local.reference) | ||
376 | |||
377 | ;; types | ||
378 | |||
379 | ;; (T, U) | ||
380 | (tuple_type | ||
381 | (type_identifier) @local.reference) | ||
382 | |||
383 | ;; &T | ||
384 | (reference_type | ||
385 | (type_identifier) @local.reference) | ||
386 | |||
387 | ;; &'a T | ||
388 | (reference_type | ||
389 | (lifetime) @local.reference) | ||
390 | |||
391 | ;; &'a self | ||
392 | (self_parameter | ||
393 | (lifetime) @local.reference) | ||
394 | |||
395 | ;; *mut T | ||
396 | ;; *const T | ||
397 | (pointer_type | ||
398 | (type_identifier) @local.reference) | ||
399 | |||
400 | ;; A<_> | ||
401 | (generic_type | ||
402 | (type_identifier) @local.reference) | ||
403 | |||
404 | ;; _<V> | ||
405 | (type_arguments | ||
406 | (type_identifier) @local.reference) | ||
407 | (type_arguments | ||
408 | (lifetime) @local.reference) | ||
409 | |||
410 | ;; T<U = V> | ||
411 | ;; | ||
412 | ;; U is ignored | ||
413 | ;; V is a ref | ||
414 | (type_binding | ||
415 | name: (_) | ||
416 | type: (type_identifier) @local.reference) | ||
417 | |||
418 | ;; [T] | ||
419 | (array_type | ||
420 | (type_identifier) @local.reference) | ||
421 | |||
422 | ;; type T = U; | ||
423 | ;; | ||
424 | ;; T is a def | ||
425 | ;; U is a ref | ||
426 | (type_item | ||
427 | name: (_) | ||
428 | type: (type_identifier) @local.reference) | ||
429 | |||
430 | (function_item | ||
431 | return_type: (type_identifier) @local.reference) | ||
432 | |||
433 | ;; type refs in params | ||
434 | ;; | ||
435 | ;; fn _(_: T) | ||
436 | (parameters | ||
437 | (parameter | ||
438 | type: (type_identifier) @local.reference)) | ||
439 | |||
440 | ;; dyn T | ||
441 | (dynamic_type | ||
442 | (type_identifier) @local.reference) | ||
443 | |||
444 | ;; <T>::call() | ||
445 | (bracketed_type | ||
446 | (type_identifier) @local.reference) | ||
447 | |||
448 | ;; T as Trait | ||
449 | (qualified_type | ||
450 | (type_identifier) @local.reference) | ||
451 | |||
452 | ;; module::T | ||
453 | ;; | ||
454 | ;; `module` is a def | ||
455 | ;; `T` is a ref | ||
456 | (scoped_type_identifier | ||
457 | path: (identifier) @local.reference) | ||
458 | |||
459 | ;; struct _ { field: Type } | ||
460 | ;; `Type` is a ref | ||
461 | (field_declaration | ||
462 | name: (_) | ||
463 | type: (type_identifier) @local.reference) | ||
diff --git a/stag/src/stag.scm b/stag/src/stag.scm new file mode 100644 index 0000000..b6271b8 --- /dev/null +++ b/stag/src/stag.scm | |||
@@ -0,0 +1,36 @@ | |||
1 | [ | ||
2 | (block) | ||
3 | (declaration_list) | ||
4 | (impl_item) | ||
5 | (struct_item) | ||
6 | (enum_item) | ||
7 | (union_item) | ||
8 | (type_item) | ||
9 | (trait_item) | ||
10 | (if_expression | ||
11 | [(let_condition) | ||
12 | (let_chain)]) | ||
13 | ] @cap | ||
14 | { | ||
15 | (scope (range @cap)) | ||
16 | } | ||
17 | |||
18 | (function_item | ||
19 | (parameters) @params | ||
20 | (block) @body) | ||
21 | { | ||
22 | (scope (cover @params @body)) | ||
23 | } | ||
24 | |||
25 | |||
26 | (let_declaration | ||
27 | pattern: (identifier) @cap) | ||
28 | { | ||
29 | (def @cap) | ||
30 | } | ||
31 | |||
32 | |||
33 | (binary_expression (identifier) @c) { | ||
34 | (ref @c) | ||
35 | } | ||
36 | |||
diff --git a/stag/src/text_range.rs b/stag/src/text_range.rs new file mode 100644 index 0000000..5b1ec67 --- /dev/null +++ b/stag/src/text_range.rs | |||
@@ -0,0 +1,121 @@ | |||
1 | use std::cmp::{Ord, Ordering}; | ||
2 | |||
3 | use serde::{Deserialize, Serialize}; | ||
4 | |||
5 | /// A singular position in a text document | ||
6 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] | ||
7 | pub struct Point { | ||
8 | /// The byte index | ||
9 | pub byte: usize, | ||
10 | |||
11 | /// 0-indexed line number | ||
12 | pub line: usize, | ||
13 | |||
14 | /// Position within the line | ||
15 | pub column: usize, | ||
16 | } | ||
17 | |||
18 | impl PartialOrd for Point { | ||
19 | fn partial_cmp(&self, other: &Self) -> Option<Ordering> { | ||
20 | Some(self.cmp(other)) | ||
21 | } | ||
22 | } | ||
23 | |||
24 | impl Ord for Point { | ||
25 | fn cmp(&self, other: &Self) -> Ordering { | ||
26 | self.byte.cmp(&other.byte) | ||
27 | } | ||
28 | } | ||
29 | |||
30 | impl Point { | ||
31 | pub fn new(byte: usize, line: usize, column: usize) -> Self { | ||
32 | Self { byte, line, column } | ||
33 | } | ||
34 | |||
35 | pub fn from_byte(byte: usize, line_end_indices: &[u32]) -> Self { | ||
36 | let line = line_end_indices | ||
37 | .iter() | ||
38 | .position(|&line_end_byte| (line_end_byte as usize) > byte) | ||
39 | .unwrap_or(0); | ||
40 | |||
41 | let column = line | ||
42 | .checked_sub(1) | ||
43 | .and_then(|idx| line_end_indices.get(idx)) | ||
44 | .map(|&prev_line_end| byte.saturating_sub(prev_line_end as usize)) | ||
45 | .unwrap_or(byte); | ||
46 | |||
47 | Self::new(byte, line, column) | ||
48 | } | ||
49 | } | ||
50 | |||
51 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] | ||
52 | pub struct TextRange { | ||
53 | pub start: Point, | ||
54 | pub end: Point, | ||
55 | } | ||
56 | |||
57 | impl PartialOrd for TextRange { | ||
58 | fn partial_cmp(&self, other: &Self) -> Option<Ordering> { | ||
59 | Some(self.cmp(other)) | ||
60 | } | ||
61 | } | ||
62 | |||
63 | impl Ord for TextRange { | ||
64 | fn cmp(&self, other: &Self) -> Ordering { | ||
65 | let compare_start_byte = self.start.byte.cmp(&other.start.byte); | ||
66 | let compare_size = self.size().cmp(&other.size()); | ||
67 | |||
68 | compare_start_byte.then(compare_size) | ||
69 | } | ||
70 | } | ||
71 | |||
72 | impl TextRange { | ||
73 | pub fn new(start: Point, end: Point) -> Self { | ||
74 | assert!(start <= end); | ||
75 | Self { start, end } | ||
76 | } | ||
77 | |||
78 | pub fn contains(&self, other: &TextRange) -> bool { | ||
79 | // (self.start ... [other.start ... other.end] ... self.end) | ||
80 | self.start <= other.start && other.end <= self.end | ||
81 | } | ||
82 | |||
83 | #[allow(unused)] | ||
84 | pub fn contains_strict(&self, other: TextRange) -> bool { | ||
85 | // (self.start ... (other.start ... other.end) ... self.end) | ||
86 | self.start < other.start && other.end <= self.end | ||
87 | } | ||
88 | |||
89 | pub fn size(&self) -> usize { | ||
90 | self.end.byte.saturating_sub(self.start.byte) | ||
91 | } | ||
92 | |||
93 | pub fn from_byte_range(range: std::ops::Range<usize>, line_end_indices: &[u32]) -> Self { | ||
94 | let start = Point::from_byte(range.start, line_end_indices); | ||
95 | let end = Point::from_byte(range.end, line_end_indices); | ||
96 | Self::new(start, end) | ||
97 | } | ||
98 | } | ||
99 | |||
100 | impl From<tree_sitter::Range> for TextRange { | ||
101 | fn from(r: tree_sitter::Range) -> Self { | ||
102 | Self { | ||
103 | start: Point { | ||
104 | byte: r.start_byte, | ||
105 | line: r.start_point.row, | ||
106 | column: r.start_point.column, | ||
107 | }, | ||
108 | end: Point { | ||
109 | byte: r.end_byte, | ||
110 | line: r.end_point.row, | ||
111 | column: r.end_point.column, | ||
112 | }, | ||
113 | } | ||
114 | } | ||
115 | } | ||
116 | |||
117 | impl From<TextRange> for std::ops::Range<usize> { | ||
118 | fn from(r: TextRange) -> std::ops::Range<usize> { | ||
119 | r.start.byte..r.end.byte | ||
120 | } | ||
121 | } | ||