intelligent ctags powered by tree-sitter and lexical scope-graphs

init

oppi.li 7ec3d6f2

+1
.gitignore
··· 1 + target/
+398
Cargo.lock
··· 1 + # This file is automatically @generated by Cargo. 2 + # It is not intended for manual editing. 3 + version = 3 4 + 5 + [[package]] 6 + name = "ahash" 7 + version = "0.4.8" 8 + source = "registry+https://github.com/rust-lang/crates.io-index" 9 + checksum = "0453232ace82dee0dd0b4c87a59bd90f7b53b314f3e0f61fe2ee7c8a16482289" 10 + 11 + [[package]] 12 + name = "aho-corasick" 13 + version = "1.1.2" 14 + source = "registry+https://github.com/rust-lang/crates.io-index" 15 + checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" 16 + dependencies = [ 17 + "memchr", 18 + ] 19 + 20 + [[package]] 21 + name = "cc" 22 + version = "1.0.83" 23 + source = "registry+https://github.com/rust-lang/crates.io-index" 24 + checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" 25 + dependencies = [ 26 + "libc", 27 + ] 28 + 29 + [[package]] 30 + name = "cfg-if" 31 + version = "1.0.0" 32 + source = "registry+https://github.com/rust-lang/crates.io-index" 33 + checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 34 + 35 + [[package]] 36 + name = "darling" 37 + version = "0.20.6" 38 + source = "registry+https://github.com/rust-lang/crates.io-index" 39 + checksum = "c376d08ea6aa96aafe61237c7200d1241cb177b7d3a542d791f2d118e9cbb955" 40 + dependencies = [ 41 + "darling_core", 42 + "darling_macro", 43 + ] 44 + 45 + [[package]] 46 + name = "darling_core" 47 + version = "0.20.6" 48 + source = "registry+https://github.com/rust-lang/crates.io-index" 49 + checksum = "33043dcd19068b8192064c704b3f83eb464f91f1ff527b44a4e2b08d9cdb8855" 50 + dependencies = [ 51 + "fnv", 52 + "ident_case", 53 + "proc-macro2", 54 + "quote", 55 + "strsim", 56 + "syn", 57 + ] 58 + 59 + [[package]] 60 + name = "darling_macro" 61 + version = "0.20.6" 62 + source = "registry+https://github.com/rust-lang/crates.io-index" 63 + checksum = "c5a91391accf613803c2a9bf9abccdbaa07c54b4244a5b64883f9c3c137c86be" 64 + dependencies = [ 65 + "darling_core", 66 + "quote", 67 + "syn", 68 + ] 69 + 70 + [[package]] 71 + name = "derive_builder" 72 + version = "0.20.0" 73 + source = "registry+https://github.com/rust-lang/crates.io-index" 74 + checksum = "0350b5cb0331628a5916d6c5c0b72e97393b8b6b03b47a9284f4e7f5a405ffd7" 75 + dependencies = [ 76 + "derive_builder_macro", 77 + ] 78 + 79 + [[package]] 80 + name = "derive_builder_core" 81 + version = "0.20.0" 82 + source = "registry+https://github.com/rust-lang/crates.io-index" 83 + checksum = "d48cda787f839151732d396ac69e3473923d54312c070ee21e9effcaa8ca0b1d" 84 + dependencies = [ 85 + "darling", 86 + "proc-macro2", 87 + "quote", 88 + "syn", 89 + ] 90 + 91 + [[package]] 92 + name = "derive_builder_macro" 93 + version = "0.20.0" 94 + source = "registry+https://github.com/rust-lang/crates.io-index" 95 + checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" 96 + dependencies = [ 97 + "derive_builder_core", 98 + "syn", 99 + ] 100 + 101 + [[package]] 102 + name = "dissimilar" 103 + version = "1.0.7" 104 + source = "registry+https://github.com/rust-lang/crates.io-index" 105 + checksum = "86e3bdc80eee6e16b2b6b0f87fbc98c04bee3455e35174c0de1a125d0688c632" 106 + 107 + [[package]] 108 + name = "equivalent" 109 + version = "1.0.1" 110 + source = "registry+https://github.com/rust-lang/crates.io-index" 111 + checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 112 + 113 + [[package]] 114 + name = "expect-test" 115 + version = "1.4.1" 116 + source = "registry+https://github.com/rust-lang/crates.io-index" 117 + checksum = "30d9eafeadd538e68fb28016364c9732d78e420b9ff8853fa5e4058861e9f8d3" 118 + dependencies = [ 119 + "dissimilar", 120 + "once_cell", 121 + ] 122 + 123 + [[package]] 124 + name = "fixedbitset" 125 + version = "0.4.2" 126 + source = "registry+https://github.com/rust-lang/crates.io-index" 127 + checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" 128 + 129 + [[package]] 130 + name = "fnv" 131 + version = "1.0.7" 132 + source = "registry+https://github.com/rust-lang/crates.io-index" 133 + checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 134 + 135 + [[package]] 136 + name = "hashbrown" 137 + version = "0.9.1" 138 + source = "registry+https://github.com/rust-lang/crates.io-index" 139 + checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" 140 + dependencies = [ 141 + "ahash", 142 + ] 143 + 144 + [[package]] 145 + name = "hashbrown" 146 + version = "0.14.3" 147 + source = "registry+https://github.com/rust-lang/crates.io-index" 148 + checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" 149 + 150 + [[package]] 151 + name = "ident_case" 152 + version = "1.0.1" 153 + source = "registry+https://github.com/rust-lang/crates.io-index" 154 + checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 155 + 156 + [[package]] 157 + name = "indexmap" 158 + version = "2.2.3" 159 + source = "registry+https://github.com/rust-lang/crates.io-index" 160 + checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" 161 + dependencies = [ 162 + "equivalent", 163 + "hashbrown 0.14.3", 164 + ] 165 + 166 + [[package]] 167 + name = "itoa" 168 + version = "1.0.10" 169 + source = "registry+https://github.com/rust-lang/crates.io-index" 170 + checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" 171 + 172 + [[package]] 173 + name = "libc" 174 + version = "0.2.153" 175 + source = "registry+https://github.com/rust-lang/crates.io-index" 176 + checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" 177 + 178 + [[package]] 179 + name = "log" 180 + version = "0.4.20" 181 + source = "registry+https://github.com/rust-lang/crates.io-index" 182 + checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" 183 + 184 + [[package]] 185 + name = "memchr" 186 + version = "2.7.1" 187 + source = "registry+https://github.com/rust-lang/crates.io-index" 188 + checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" 189 + 190 + [[package]] 191 + name = "once_cell" 192 + version = "1.19.0" 193 + source = "registry+https://github.com/rust-lang/crates.io-index" 194 + checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 195 + 196 + [[package]] 197 + name = "petgraph" 198 + version = "0.6.4" 199 + source = "registry+https://github.com/rust-lang/crates.io-index" 200 + checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" 201 + dependencies = [ 202 + "fixedbitset", 203 + "indexmap", 204 + "serde", 205 + "serde_derive", 206 + ] 207 + 208 + [[package]] 209 + name = "proc-macro2" 210 + version = "1.0.78" 211 + source = "registry+https://github.com/rust-lang/crates.io-index" 212 + checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" 213 + dependencies = [ 214 + "unicode-ident", 215 + ] 216 + 217 + [[package]] 218 + name = "quote" 219 + version = "1.0.35" 220 + source = "registry+https://github.com/rust-lang/crates.io-index" 221 + checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 222 + dependencies = [ 223 + "proc-macro2", 224 + ] 225 + 226 + [[package]] 227 + name = "regex" 228 + version = "1.10.3" 229 + source = "registry+https://github.com/rust-lang/crates.io-index" 230 + checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" 231 + dependencies = [ 232 + "aho-corasick", 233 + "memchr", 234 + "regex-automata", 235 + "regex-syntax", 236 + ] 237 + 238 + [[package]] 239 + name = "regex-automata" 240 + version = "0.4.5" 241 + source = "registry+https://github.com/rust-lang/crates.io-index" 242 + checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" 243 + dependencies = [ 244 + "aho-corasick", 245 + "memchr", 246 + "regex-syntax", 247 + ] 248 + 249 + [[package]] 250 + name = "regex-syntax" 251 + version = "0.8.2" 252 + source = "registry+https://github.com/rust-lang/crates.io-index" 253 + checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" 254 + 255 + [[package]] 256 + name = "ryu" 257 + version = "1.0.16" 258 + source = "registry+https://github.com/rust-lang/crates.io-index" 259 + checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" 260 + 261 + [[package]] 262 + name = "serde" 263 + version = "1.0.196" 264 + source = "registry+https://github.com/rust-lang/crates.io-index" 265 + checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" 266 + dependencies = [ 267 + "serde_derive", 268 + ] 269 + 270 + [[package]] 271 + name = "serde_derive" 272 + version = "1.0.196" 273 + source = "registry+https://github.com/rust-lang/crates.io-index" 274 + checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" 275 + dependencies = [ 276 + "proc-macro2", 277 + "quote", 278 + "syn", 279 + ] 280 + 281 + [[package]] 282 + name = "serde_json" 283 + version = "1.0.113" 284 + source = "registry+https://github.com/rust-lang/crates.io-index" 285 + checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" 286 + dependencies = [ 287 + "itoa", 288 + "ryu", 289 + "serde", 290 + ] 291 + 292 + [[package]] 293 + name = "smallvec" 294 + version = "1.13.1" 295 + source = "registry+https://github.com/rust-lang/crates.io-index" 296 + checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" 297 + 298 + [[package]] 299 + name = "stag" 300 + version = "0.1.0" 301 + dependencies = [ 302 + "derive_builder", 303 + "expect-test", 304 + "once_cell", 305 + "petgraph", 306 + "serde", 307 + "thiserror", 308 + "tree-sitter", 309 + "tree-sitter-graph", 310 + "tree-sitter-rust", 311 + ] 312 + 313 + [[package]] 314 + name = "string-interner" 315 + version = "0.12.2" 316 + source = "registry+https://github.com/rust-lang/crates.io-index" 317 + checksum = "383196d1876517ee6f9f0864d1fc1070331b803335d3c6daaa04bbcccd823c08" 318 + dependencies = [ 319 + "cfg-if", 320 + "hashbrown 0.9.1", 321 + ] 322 + 323 + [[package]] 324 + name = "strsim" 325 + version = "0.10.0" 326 + source = "registry+https://github.com/rust-lang/crates.io-index" 327 + checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 328 + 329 + [[package]] 330 + name = "syn" 331 + version = "2.0.48" 332 + source = "registry+https://github.com/rust-lang/crates.io-index" 333 + checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" 334 + dependencies = [ 335 + "proc-macro2", 336 + "quote", 337 + "unicode-ident", 338 + ] 339 + 340 + [[package]] 341 + name = "thiserror" 342 + version = "1.0.57" 343 + source = "registry+https://github.com/rust-lang/crates.io-index" 344 + checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" 345 + dependencies = [ 346 + "thiserror-impl", 347 + ] 348 + 349 + [[package]] 350 + name = "thiserror-impl" 351 + version = "1.0.57" 352 + source = "registry+https://github.com/rust-lang/crates.io-index" 353 + checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" 354 + dependencies = [ 355 + "proc-macro2", 356 + "quote", 357 + "syn", 358 + ] 359 + 360 + [[package]] 361 + name = "tree-sitter" 362 + version = "0.20.10" 363 + source = "registry+https://github.com/rust-lang/crates.io-index" 364 + checksum = "e747b1f9b7b931ed39a548c1fae149101497de3c1fc8d9e18c62c1a66c683d3d" 365 + dependencies = [ 366 + "cc", 367 + "regex", 368 + ] 369 + 370 + [[package]] 371 + name = "tree-sitter-graph" 372 + version = "0.11.0" 373 + dependencies = [ 374 + "log", 375 + "regex", 376 + "serde", 377 + "serde_json", 378 + "smallvec", 379 + "string-interner", 380 + "thiserror", 381 + "tree-sitter", 382 + ] 383 + 384 + [[package]] 385 + name = "tree-sitter-rust" 386 + version = "0.20.4" 387 + source = "registry+https://github.com/rust-lang/crates.io-index" 388 + checksum = "b0832309b0b2b6d33760ce5c0e818cb47e1d72b468516bfe4134408926fa7594" 389 + dependencies = [ 390 + "cc", 391 + "tree-sitter", 392 + ] 393 + 394 + [[package]] 395 + name = "unicode-ident" 396 + version = "1.0.12" 397 + source = "registry+https://github.com/rust-lang/crates.io-index" 398 + checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+20
Cargo.toml
··· 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 + [lib] 9 + 10 + [dependencies] 11 + petgraph = { version = "0.6.4", default-features = false, features = ["serde-1"] } 12 + serde = {version = "1.0.188", features = ["derive"]} 13 + once_cell = "1.19.0" 14 + thiserror = "1.0.56" 15 + tree-sitter-graph = { path = "../tree-sitter-graph/"} 16 + tree-sitter-rust = "0.20.4" 17 + tree-sitter = "0.20.10" 18 + derive_builder = "0.20.0" 19 + expect-test = "1.4.1" 20 +
+240
src/debug.rs
··· 1 + use std::fmt; 2 + 3 + use crate::text_range::TextRange; 4 + use crate::{EdgeKind, LocalDef, NodeKind}; 5 + 6 + use petgraph::{ 7 + graph::{Graph, NodeIndex}, 8 + visit::EdgeRef, 9 + Direction, 10 + }; 11 + 12 + pub struct ScopeDebug { 13 + range: TextRange, 14 + defs: Vec<DefDebug>, 15 + imports: Vec<ImportDebug>, 16 + scopes: Vec<ScopeDebug>, 17 + } 18 + 19 + struct DefDebug { 20 + name: String, 21 + range: TextRange, 22 + context: String, 23 + refs: Vec<RefDebug>, 24 + } 25 + 26 + struct RefDebug { 27 + context: String, 28 + } 29 + 30 + struct ImportDebug { 31 + name: String, 32 + range: TextRange, 33 + context: String, 34 + refs: Vec<RefDebug>, 35 + } 36 + 37 + impl DefDebug { 38 + fn new(range: TextRange, name: String, refs: Vec<TextRange>, src: &[u8]) -> Self { 39 + Self { 40 + name, 41 + range, 42 + context: context(range, src), 43 + refs: refs 44 + .into_iter() 45 + .map(|r| context(r, src)) 46 + .map(|context| RefDebug { context }) 47 + .collect(), 48 + } 49 + } 50 + } 51 + 52 + impl ImportDebug { 53 + fn new(range: TextRange, name: String, refs: Vec<TextRange>, src: &[u8]) -> Self { 54 + Self { 55 + name, 56 + range, 57 + context: context(range, src), 58 + refs: refs 59 + .into_iter() 60 + .map(|r| context(r, src)) 61 + .map(|context| RefDebug { context }) 62 + .collect(), 63 + } 64 + } 65 + } 66 + 67 + impl ScopeDebug { 68 + fn empty(range: TextRange) -> Self { 69 + Self { 70 + range, 71 + defs: Vec::new(), 72 + imports: Vec::new(), 73 + scopes: Vec::new(), 74 + } 75 + } 76 + 77 + fn build(&mut self, graph: &Graph<NodeKind, EdgeKind>, start: NodeIndex<u32>, src: &[u8]) { 78 + let mut defs = graph 79 + .edges_directed(start, Direction::Incoming) 80 + .filter(|edge| *edge.weight() == EdgeKind::DefToScope) 81 + .map(|edge| { 82 + let def_node = edge.source(); 83 + 84 + // range of this def 85 + let range = graph[def_node].range(); 86 + 87 + // text source of this def 88 + let text = std::str::from_utf8(&src[range.start.byte..range.end.byte]) 89 + .unwrap() 90 + .to_owned(); 91 + 92 + // all references of this def, sorted by range 93 + let mut refs = graph 94 + .edges_directed(def_node, Direction::Incoming) 95 + .filter(|edge| *edge.weight() == EdgeKind::RefToDef) 96 + .map(|edge| graph[edge.source()].range()) 97 + .collect::<Vec<_>>(); 98 + 99 + refs.sort(); 100 + 101 + DefDebug::new(range, text, refs, src) 102 + }) 103 + .collect::<Vec<_>>(); 104 + 105 + let mut imports = graph 106 + .edges_directed(start, Direction::Incoming) 107 + .filter(|edge| *edge.weight() == EdgeKind::ImportToScope) 108 + .map(|edge| { 109 + let imp_node = edge.source(); 110 + 111 + // range of this import 112 + let range = graph[imp_node].range(); 113 + 114 + // text source of this import 115 + let text = std::str::from_utf8(&src[range.start.byte..range.end.byte]) 116 + .unwrap() 117 + .to_owned(); 118 + 119 + // all references of this import, sorted by range 120 + let mut refs = graph 121 + .edges_directed(imp_node, Direction::Incoming) 122 + .filter(|edge| *edge.weight() == EdgeKind::RefToImport) 123 + .map(|edge| graph[edge.source()].range()) 124 + .collect::<Vec<_>>(); 125 + 126 + refs.sort(); 127 + 128 + ImportDebug::new(range, text, refs, src) 129 + }) 130 + .collect::<Vec<_>>(); 131 + 132 + let mut scopes = graph 133 + .edges_directed(start, Direction::Incoming) 134 + .filter(|edge| *edge.weight() == EdgeKind::ScopeToScope) 135 + .map(|edge| { 136 + let source_scope = edge.source(); 137 + let mut scope_debug = ScopeDebug::empty(graph[source_scope].range()); 138 + scope_debug.build(graph, source_scope, src); 139 + scope_debug 140 + }) 141 + .collect::<Vec<_>>(); 142 + 143 + // sort defs by their ranges 144 + defs.sort_by(|a, b| a.range.cmp(&b.range)); 145 + // sort imports by their ranges 146 + imports.sort_by(|a, b| a.range.cmp(&b.range)); 147 + // sort scopes by their ranges 148 + scopes.sort_by(|a, b| a.range.cmp(&b.range)); 149 + 150 + self.defs = defs; 151 + self.imports = imports; 152 + self.scopes = scopes; 153 + } 154 + 155 + pub fn new(graph: &Graph<NodeKind, EdgeKind>, start: NodeIndex<u32>, src: &[u8]) -> Self { 156 + let mut scope_debug = Self::empty(graph[start].range()); 157 + scope_debug.build(graph, start, src); 158 + scope_debug 159 + } 160 + } 161 + 162 + impl fmt::Debug for ScopeDebug { 163 + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 164 + if self.imports.is_empty() { 165 + f.debug_struct("scope") 166 + .field("definitions", &self.defs) 167 + .field("child scopes", &self.scopes) 168 + .finish() 169 + } else { 170 + f.debug_struct("scope") 171 + .field("definitions", &self.defs) 172 + .field("imports", &self.imports) 173 + .field("child scopes", &self.scopes) 174 + .finish() 175 + } 176 + } 177 + } 178 + 179 + impl fmt::Debug for DefDebug { 180 + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 181 + let mut s = f.debug_struct(&self.name); 182 + let d = s.field("context", &self.context); 183 + 184 + if self.refs.is_empty() { 185 + d 186 + } else { 187 + d.field(&format!("referenced in ({})", self.refs.len()), &self.refs) 188 + } 189 + .finish() 190 + } 191 + } 192 + 193 + impl fmt::Debug for ImportDebug { 194 + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 195 + let mut s = f.debug_struct(&self.name); 196 + let d = s.field("context", &self.context); 197 + 198 + if self.refs.is_empty() { 199 + d 200 + } else { 201 + d.field(&format!("referenced in ({})", self.refs.len()), &self.refs) 202 + } 203 + .finish() 204 + } 205 + } 206 + 207 + impl fmt::Debug for RefDebug { 208 + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 209 + write!(f, "`{}`", self.context) 210 + } 211 + } 212 + 213 + fn context(range: TextRange, src: &[u8]) -> String { 214 + // first new line before start 215 + let context_start = src 216 + .iter() 217 + .enumerate() 218 + .take(range.start.byte) 219 + .rev() 220 + .find_map(|(idx, &c)| (c == b'\n').then_some(idx)) 221 + .unwrap_or(range.start.byte - 1) 222 + .saturating_add(1); 223 + 224 + // first new line after end 225 + let context_end: usize = src 226 + .iter() 227 + .enumerate() 228 + .skip(range.end.byte) 229 + .find_map(|(idx, &c)| (c == b'\n').then_some(idx)) 230 + .unwrap_or(range.end.byte + 1) 231 + .saturating_sub(1); 232 + 233 + let from_utf8 = |bytes| std::str::from_utf8(bytes).unwrap(); 234 + format!( 235 + "{}§{}§{}", 236 + from_utf8(&src[context_start..range.start.byte]).trim_start(), 237 + from_utf8(&src[range.start.byte..range.end.byte]), 238 + from_utf8(&src[range.end.byte..=context_end]).trim_end() 239 + ) 240 + }
+28
src/error.rs
··· 1 + use thiserror::Error; 2 + 3 + #[derive(Error, Debug)] 4 + pub enum StagError { 5 + #[error("tree-sitter: {0}")] 6 + TreeSitter(#[from] tree_sitter::LanguageError), 7 + 8 + #[error("config error: {0}")] 9 + Config(#[from] ConfigError), 10 + } 11 + 12 + #[derive(Error, Debug)] 13 + pub enum ConfigError { 14 + #[error("no language configured")] 15 + MissingLanguage, 16 + 17 + #[error("no stag query configured")] 18 + MissingStagSource, 19 + 20 + #[error("no source file configured")] 21 + MissingSource, 22 + 23 + #[error("parse error: {0}")] 24 + Parse(#[from] tree_sitter_graph::ParseError), 25 + 26 + #[error("tree-sitter graph execution error: {0}")] 27 + TsgExecute(#[from] tree_sitter_graph::ExecutionError), 28 + }
+1414
src/lib.rs
··· 1 + mod text_range; 2 + use petgraph::{graph::NodeIndex, visit::EdgeRef, Direction, Graph}; 3 + use serde::{Deserialize, Serialize}; 4 + use text_range::TextRange; 5 + use tree_sitter_graph::{ 6 + ast::File, functions::Functions, ExecutionConfig, Identifier, NoCancellation, Variables, 7 + }; 8 + 9 + mod debug; 10 + mod error; 11 + mod stdlib; 12 + 13 + use error::{ConfigError, StagError}; 14 + 15 + #[derive(Default)] 16 + pub struct StagBuilder<'a> { 17 + language: Option<tree_sitter::Language>, 18 + stag_file: Option<&'a str>, 19 + filename: Option<&'a str>, 20 + source: Option<&'a str>, 21 + } 22 + 23 + impl<'a> StagBuilder<'a> { 24 + pub fn with_language(&mut self, language: tree_sitter::Language) -> &mut Self { 25 + self.language = Some(language); 26 + self 27 + } 28 + 29 + pub fn with_stag_file(&mut self, stag_file: &'a str) -> &mut Self { 30 + self.stag_file = Some(stag_file); 31 + self 32 + } 33 + 34 + pub fn with_source(&mut self, source: &'a str) -> &mut Self { 35 + self.source = Some(source); 36 + self 37 + } 38 + 39 + pub fn with_file_name(&mut self, filename: &'a str) -> &mut Self { 40 + self.filename = Some(filename); 41 + self 42 + } 43 + 44 + pub fn execute(&self) -> Result<ScopeGraph, StagError> { 45 + let mut parser = tree_sitter::Parser::new(); 46 + let language = self.language.ok_or(ConfigError::MissingLanguage)?; 47 + parser.set_language(language)?; 48 + 49 + let file = File::from_str( 50 + language, 51 + self.stag_file.ok_or(ConfigError::MissingStagSource)?, 52 + ) 53 + .map_err(ConfigError::from)?; 54 + 55 + let mut functions = Functions::stdlib(); 56 + functions.add(Identifier::from("scope"), stdlib::ScopeShorthand); 57 + functions.add(Identifier::from("def"), stdlib::DefShorthand); 58 + functions.add(Identifier::from("ref"), stdlib::RefShortHand); 59 + functions.add(Identifier::from("cover"), stdlib::CoverRanges); 60 + functions.add(Identifier::from("range"), stdlib::NodeRange); 61 + let globals = Variables::new(); 62 + let config = ExecutionConfig::new(&functions, &globals); 63 + 64 + let src = self.source.ok_or(ConfigError::MissingSource)?; 65 + let tree = parser.parse(src.as_bytes(), None).unwrap(); 66 + let graph = file 67 + .execute(&tree, &src, &config, &NoCancellation) 68 + .map_err(ConfigError::from)?; 69 + 70 + let mut sg = ScopeGraph::new(tree.root_node().range().into()); 71 + sg = build_scope_graph(graph, sg); 72 + Ok(sg) 73 + } 74 + } 75 + 76 + #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] 77 + pub struct LocalDef { 78 + pub range: TextRange, 79 + pub text: String, 80 + pub symbol: Option<String>, 81 + } 82 + 83 + impl LocalDef { 84 + /// Initialize a new definition 85 + pub fn new(range: TextRange, text: String, symbol: Option<String>) -> Self { 86 + Self { 87 + range, 88 + text, 89 + symbol, 90 + } 91 + } 92 + 93 + pub fn name<'a>(&self, buffer: &'a [u8]) -> &'a [u8] { 94 + &buffer[self.range.start.byte..self.range.end.byte] 95 + } 96 + } 97 + 98 + #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] 99 + pub struct LocalImport { 100 + pub range: TextRange, 101 + pub text: String, 102 + } 103 + 104 + impl LocalImport { 105 + /// Initialize a new import 106 + pub fn new(range: TextRange, text: String) -> Self { 107 + Self { range, text } 108 + } 109 + 110 + pub fn name<'a>(&self, buffer: &'a [u8]) -> &'a [u8] { 111 + &buffer[self.range.start.byte..self.range.end.byte] 112 + } 113 + } 114 + 115 + #[derive(Debug, Clone, Serialize, Deserialize)] 116 + pub struct Reference { 117 + pub range: TextRange, 118 + pub text: String, 119 + pub symbol: Option<String>, 120 + } 121 + 122 + impl Reference { 123 + /// Initialize a new reference 124 + pub fn new(range: TextRange, text: String, symbol: Option<String>) -> Self { 125 + Self { 126 + range, 127 + text, 128 + symbol, 129 + } 130 + } 131 + } 132 + 133 + #[derive(Debug, Clone, Copy, Serialize, Deserialize)] 134 + pub struct LocalScope { 135 + pub range: TextRange, 136 + } 137 + 138 + impl LocalScope { 139 + pub fn new(range: TextRange) -> Self { 140 + Self { range } 141 + } 142 + } 143 + 144 + pub struct ScopeStack<'a> { 145 + pub scope_graph: &'a ScopeGraph, 146 + pub start: Option<NodeIndex<u32>>, 147 + } 148 + 149 + impl<'a> Iterator for ScopeStack<'a> { 150 + type Item = NodeIndex<u32>; 151 + fn next(&mut self) -> Option<Self::Item> { 152 + if let Some(start) = self.start { 153 + let parent = self 154 + .scope_graph 155 + .graph 156 + .edges_directed(start, Direction::Outgoing) 157 + .find(|edge| *edge.weight() == EdgeKind::ScopeToScope) 158 + .map(|edge| edge.target()); 159 + let original = start; 160 + self.start = parent; 161 + Some(original) 162 + } else { 163 + None 164 + } 165 + } 166 + } 167 + 168 + /// The type of a node in the ScopeGraph 169 + #[derive(Serialize, Deserialize, Debug, Clone)] 170 + pub enum NodeKind { 171 + /// A scope node 172 + Scope(LocalScope), 173 + 174 + /// A definition node 175 + Def(LocalDef), 176 + 177 + /// An import node 178 + Import(LocalImport), 179 + 180 + /// A reference node 181 + Ref(Reference), 182 + } 183 + 184 + impl NodeKind { 185 + /// Construct a scope node from a range 186 + pub fn scope(range: TextRange) -> Self { 187 + Self::Scope(LocalScope::new(range)) 188 + } 189 + 190 + /// Produce the range spanned by this node 191 + pub fn range(&self) -> TextRange { 192 + match self { 193 + Self::Scope(l) => l.range, 194 + Self::Def(d) => d.range, 195 + Self::Ref(r) => r.range, 196 + Self::Import(i) => i.range, 197 + } 198 + } 199 + } 200 + 201 + /// Describes the relation between two nodes in the ScopeGraph 202 + #[derive(Serialize, Deserialize, PartialEq, Eq, Copy, Clone, Debug)] 203 + pub enum EdgeKind { 204 + /// The edge weight from a nested scope to its parent scope 205 + ScopeToScope, 206 + 207 + /// The edge weight from a definition to its definition scope 208 + DefToScope, 209 + 210 + /// The edge weight from an import to its definition scope 211 + ImportToScope, 212 + 213 + /// The edge weight from a reference to its definition 214 + RefToDef, 215 + 216 + /// The edge weight from a reference to its import 217 + RefToImport, 218 + } 219 + 220 + /// A graph representation of scopes and names in a single syntax tree 221 + #[derive(Debug, Serialize, Deserialize, Clone)] 222 + pub struct ScopeGraph { 223 + /// The raw graph 224 + pub graph: Graph<NodeKind, EdgeKind>, 225 + 226 + // Graphs do not have the concept of a `root`, but lexical scopes follow the syntax 227 + // tree, and as a result, have a "root" node. The root_idx points to a scope node that 228 + // encompasses the entire file: the file-global scope. 229 + root_idx: NodeIndex, 230 + } 231 + 232 + impl ScopeGraph { 233 + pub fn new(range: TextRange) -> Self { 234 + let mut graph = Graph::new(); 235 + let root_idx = graph.add_node(NodeKind::scope(range)); 236 + Self { graph, root_idx } 237 + } 238 + 239 + pub fn get_node(&self, node_idx: NodeIndex) -> Option<&NodeKind> { 240 + self.graph.node_weight(node_idx) 241 + } 242 + 243 + /// Insert a local scope into the scope-graph 244 + fn insert_local_scope(&mut self, new: LocalScope) { 245 + if let Some(parent_scope) = self.scope_by_range(new.range, self.root_idx) { 246 + let new_scope = NodeKind::Scope(new); 247 + let new_idx = self.graph.add_node(new_scope); 248 + self.graph 249 + .add_edge(new_idx, parent_scope, EdgeKind::ScopeToScope); 250 + } 251 + } 252 + 253 + /// Insert a def into the scope-graph 254 + fn insert_local_def(&mut self, new: LocalDef) { 255 + if let Some(defining_scope) = self.scope_by_range(new.range, self.root_idx) { 256 + let new_def = NodeKind::Def(new); 257 + let new_idx = self.graph.add_node(new_def); 258 + self.graph 259 + .add_edge(new_idx, defining_scope, EdgeKind::DefToScope); 260 + } 261 + } 262 + 263 + /// Insert a def into the scope-graph, at the root scope 264 + #[allow(unused)] 265 + fn insert_global_def(&mut self, new: LocalDef) { 266 + let new_def = NodeKind::Def(new); 267 + let new_idx = self.graph.add_node(new_def); 268 + self.graph 269 + .add_edge(new_idx, self.root_idx, EdgeKind::DefToScope); 270 + } 271 + 272 + /// Insert an import into the scope-graph 273 + fn insert_local_import(&mut self, new: LocalImport) { 274 + if let Some(defining_scope) = self.scope_by_range(new.range, self.root_idx) { 275 + let new_imp = NodeKind::Import(new); 276 + let new_idx = self.graph.add_node(new_imp); 277 + self.graph 278 + .add_edge(new_idx, defining_scope, EdgeKind::ImportToScope); 279 + } 280 + } 281 + 282 + /// Insert a ref into the scope-graph 283 + fn insert_ref(&mut self, new: Reference) { 284 + let mut possible_defs = vec![]; 285 + let mut possible_imports = vec![]; 286 + if let Some(local_scope_idx) = self.scope_by_range(new.range, self.root_idx) { 287 + // traverse the scopes from the current-scope to the root-scope 288 + for scope in self.scope_stack(local_scope_idx) { 289 + // find candidate definitions in each scope 290 + for local_def in self 291 + .graph 292 + .edges_directed(scope, Direction::Incoming) 293 + .filter(|edge| *edge.weight() == EdgeKind::DefToScope) 294 + .map(|edge| edge.source()) 295 + { 296 + if let NodeKind::Def(def) = &self.graph[local_def] { 297 + if new.text == def.text { 298 + match (def.symbol.as_ref(), new.symbol.as_ref()) { 299 + // both contain symbols, but they don't belong to the same namepspace 300 + (Some(d), Some(r)) if d != r => {} 301 + 302 + // in all other cases, form an edge from the ref to def. 303 + // an empty symbol belongs to all namespaces: 304 + // * (None, None) 305 + // * (None, Some(_)) 306 + // * (Some(_), None) 307 + // * (Some(_), Some(_)) if def.namespace == ref.namespace 308 + _ => { 309 + possible_defs.push(local_def); 310 + } 311 + }; 312 + } 313 + } 314 + } 315 + 316 + // find candidate imports in each scope 317 + for local_import in self 318 + .graph 319 + .edges_directed(scope, Direction::Incoming) 320 + .filter(|edge| *edge.weight() == EdgeKind::ImportToScope) 321 + .map(|edge| edge.source()) 322 + { 323 + if let NodeKind::Import(import) = &self.graph[local_import] { 324 + if new.text == import.text { 325 + possible_imports.push(local_import); 326 + } 327 + } 328 + } 329 + } 330 + } 331 + 332 + if !possible_defs.is_empty() || !possible_imports.is_empty() { 333 + let new_ref = NodeKind::Ref(new); 334 + let ref_idx = self.graph.add_node(new_ref); 335 + for def_idx in possible_defs { 336 + self.graph.add_edge(ref_idx, def_idx, EdgeKind::RefToDef); 337 + } 338 + for imp_idx in possible_imports { 339 + self.graph.add_edge(ref_idx, imp_idx, EdgeKind::RefToImport); 340 + } 341 + } 342 + } 343 + 344 + fn scope_stack(&self, start: NodeIndex) -> ScopeStack<'_> { 345 + ScopeStack { 346 + scope_graph: self, 347 + start: Some(start), 348 + } 349 + } 350 + 351 + // The smallest scope that encompasses `range`. Start at `start` and narrow down if possible. 352 + fn scope_by_range(&self, range: TextRange, start: NodeIndex) -> Option<NodeIndex> { 353 + let target_range = self.graph[start].range(); 354 + if target_range.contains(&range) { 355 + let mut child_scopes = self 356 + .graph 357 + .edges_directed(start, Direction::Incoming) 358 + .filter(|edge| *edge.weight() == EdgeKind::ScopeToScope) 359 + .map(|edge| edge.source()) 360 + .collect::<Vec<_>>(); 361 + 362 + child_scopes.sort_by_key(|s| self.graph[*s].range()); 363 + let target_child_scope = child_scopes.binary_search_by(|x| { 364 + if self.graph[*x].range().contains(&range) { 365 + std::cmp::Ordering::Equal 366 + } else { 367 + self.graph[*x].range().cmp(&range) 368 + } 369 + }); 370 + 371 + if let Some(t) = target_child_scope 372 + .ok() 373 + .and_then(|idx| child_scopes.get(idx)) 374 + .and_then(|s| self.scope_by_range(range, *s)) 375 + { 376 + return Some(t); 377 + } else { 378 + return Some(start); 379 + } 380 + } 381 + None 382 + } 383 + 384 + /// Produce a list of interesting ranges: ranges of defs and refs 385 + pub fn hoverable_ranges(&self) -> Box<dyn Iterator<Item = TextRange> + '_> { 386 + let iterator = 387 + self.graph 388 + .node_indices() 389 + .filter_map(|node_idx| match &self.graph[node_idx] { 390 + NodeKind::Scope(_) => None, 391 + NodeKind::Def(d) => Some(d.range), 392 + NodeKind::Ref(r) => Some(r.range), 393 + NodeKind::Import(i) => Some(i.range), 394 + }); 395 + Box::new(iterator) 396 + } 397 + 398 + /// Produce possible definitions for a reference 399 + pub fn definitions( 400 + &self, 401 + reference_node: NodeIndex, 402 + ) -> Box<dyn Iterator<Item = NodeIndex> + '_> { 403 + let iterator = self 404 + .graph 405 + .edges_directed(reference_node, Direction::Outgoing) 406 + .filter(|edge| *edge.weight() == EdgeKind::RefToDef) 407 + .map(|edge| edge.target()); 408 + Box::new(iterator) 409 + } 410 + 411 + /// Produce possible imports for a reference 412 + pub fn imports(&self, reference_node: NodeIndex) -> Box<dyn Iterator<Item = NodeIndex> + '_> { 413 + let iterator = self 414 + .graph 415 + .edges_directed(reference_node, Direction::Outgoing) 416 + .filter(|edge| *edge.weight() == EdgeKind::RefToImport) 417 + .map(|edge| edge.target()); 418 + Box::new(iterator) 419 + } 420 + 421 + /// Produce possible references for a definition/import node 422 + pub fn references( 423 + &self, 424 + definition_node: NodeIndex, 425 + ) -> Box<dyn Iterator<Item = NodeIndex> + '_> { 426 + let iterator = self 427 + .graph 428 + .edges_directed(definition_node, Direction::Incoming) 429 + .filter(|edge| { 430 + *edge.weight() == EdgeKind::RefToDef || *edge.weight() == EdgeKind::RefToImport 431 + }) 432 + .map(|edge| edge.source()); 433 + Box::new(iterator) 434 + } 435 + 436 + pub fn node_by_range(&self, start_byte: usize, end_byte: usize) -> Option<NodeIndex> { 437 + self.graph 438 + .node_indices() 439 + .filter(|&idx| self.is_definition(idx) || self.is_reference(idx) || self.is_import(idx)) 440 + .find(|&idx| { 441 + let node = self.graph[idx].range(); 442 + start_byte >= node.start.byte && end_byte <= node.end.byte 443 + }) 444 + } 445 + 446 + pub fn node_by_position(&self, line: usize, column: usize) -> Option<NodeIndex> { 447 + self.graph 448 + .node_indices() 449 + .filter(|&idx| self.is_definition(idx) || self.is_reference(idx)) 450 + .find(|&idx| { 451 + let node = self.graph[idx].range(); 452 + node.start.line == line 453 + && node.end.line == line 454 + && node.start.column <= column 455 + && node.end.column >= column 456 + }) 457 + } 458 + 459 + #[cfg(test)] 460 + pub fn find_node_by_name(&self, src: &[u8], name: &[u8]) -> Option<NodeIndex> { 461 + self.graph.node_indices().find(|idx| { 462 + matches!( 463 + &self.graph[*idx], 464 + NodeKind::Def(d) if d.name(src) == name) 465 + }) 466 + } 467 + 468 + #[cfg(test)] 469 + pub fn debug(&self, src: &[u8]) -> debug::ScopeDebug { 470 + let graph = &self.graph; 471 + let start = self.root_idx; 472 + debug::ScopeDebug::new(graph, start, src) 473 + } 474 + 475 + pub fn is_definition(&self, node_idx: NodeIndex) -> bool { 476 + matches!(self.graph[node_idx], NodeKind::Def(_)) 477 + } 478 + 479 + pub fn is_reference(&self, node_idx: NodeIndex) -> bool { 480 + matches!(self.graph[node_idx], NodeKind::Ref(_)) 481 + } 482 + 483 + pub fn is_scope(&self, node_idx: NodeIndex) -> bool { 484 + matches!(self.graph[node_idx], NodeKind::Scope(_)) 485 + } 486 + 487 + pub fn is_import(&self, node_idx: NodeIndex) -> bool { 488 + matches!(self.graph[node_idx], NodeKind::Import(_)) 489 + } 490 + } 491 + 492 + fn build_scope_graph( 493 + tsg: tree_sitter_graph::graph::Graph, 494 + mut scope_graph: ScopeGraph, 495 + ) -> ScopeGraph { 496 + let nodes = tsg.iter_nodes().collect::<Vec<_>>(); 497 + for node in nodes 498 + .iter() 499 + .map(|node_ref| &tsg[*node_ref]) 500 + .filter(|node| is_scope(node)) 501 + { 502 + let range = 503 + text_range::value_to_range(node.attributes.get(&Identifier::from("range")).unwrap()); 504 + let scope = LocalScope::new(range.into()); 505 + scope_graph.insert_local_scope(scope); 506 + } 507 + 508 + for node in nodes 509 + .iter() 510 + .map(|node_ref| &tsg[*node_ref]) 511 + .filter(|node| is_import(node)) 512 + { 513 + let range = 514 + text_range::value_to_range(node.attributes.get(&Identifier::from("range")).unwrap()); 515 + let text = node 516 + .attributes 517 + .get(&Identifier::from("text")) 518 + .and_then(|id| id.clone().into_string().ok()) 519 + .expect("import without text"); 520 + let import = LocalImport::new(range.into(), text); 521 + scope_graph.insert_local_import(import); 522 + } 523 + 524 + for node in nodes 525 + .iter() 526 + .map(|node_ref| &tsg[*node_ref]) 527 + .filter(|node| is_def(node)) 528 + { 529 + let range = 530 + text_range::value_to_range(node.attributes.get(&Identifier::from("range")).unwrap()); 531 + let symbol = node 532 + .attributes 533 + .get(&Identifier::from("symbol")) 534 + .and_then(|id| id.clone().into_string().ok()); 535 + let text = node 536 + .attributes 537 + .get(&Identifier::from("text")) 538 + .and_then(|id| id.clone().into_string().ok()) 539 + .expect("def without text"); 540 + let local_def = LocalDef::new(range.into(), text, symbol); 541 + 542 + // TODO: fix scoping here 543 + scope_graph.insert_local_def(local_def); 544 + } 545 + 546 + for node in nodes 547 + .iter() 548 + .map(|node_ref| &tsg[*node_ref]) 549 + .filter(|node| is_ref(node)) 550 + { 551 + let range = 552 + text_range::value_to_range(node.attributes.get(&Identifier::from("range")).unwrap()); 553 + let symbol = node 554 + .attributes 555 + .get(&Identifier::from("symbol")) 556 + .and_then(|id| id.clone().into_string().ok()); 557 + let text = node 558 + .attributes 559 + .get(&Identifier::from("text")) 560 + .and_then(|id| id.clone().into_string().ok()) 561 + .expect("ref without text"); 562 + let ref_ = Reference::new(range.into(), text, symbol); 563 + 564 + scope_graph.insert_ref(ref_); 565 + } 566 + 567 + scope_graph 568 + } 569 + 570 + fn is_string_attr(node: &tree_sitter_graph::graph::GraphNode, key: &str, value: &str) -> bool { 571 + matches!(node.attributes.get(&Identifier::from(key)).and_then(|v| v.as_str().ok()), Some(v) if v == value) 572 + } 573 + 574 + fn is_scope(node: &tree_sitter_graph::graph::GraphNode) -> bool { 575 + is_string_attr(node, "kind", "scope") 576 + } 577 + 578 + fn is_import(node: &tree_sitter_graph::graph::GraphNode) -> bool { 579 + is_string_attr(node, "kind", "import") 580 + } 581 + 582 + fn is_def(node: &tree_sitter_graph::graph::GraphNode) -> bool { 583 + is_string_attr(node, "kind", "def") 584 + } 585 + 586 + fn is_ref(node: &tree_sitter_graph::graph::GraphNode) -> bool { 587 + is_string_attr(node, "kind", "ref") 588 + } 589 + 590 + #[cfg(test)] 591 + mod tests { 592 + use super::*; 593 + use expect_test::{expect, Expect}; 594 + 595 + fn counts(src: &str) -> (usize, usize, usize, usize) { 596 + let sg = build_graph(src); 597 + let nodes = sg.graph.node_weights(); 598 + 599 + nodes.fold((0, 0, 0, 0), |(s, d, r, i), node| match node { 600 + NodeKind::Scope(_) => (s + 1, d, r, i), 601 + NodeKind::Def(_) => (s, d + 1, r, i), 602 + NodeKind::Ref(_) => (s, d, r + 1, i), 603 + NodeKind::Import(_) => (s, d, r, i + 1), 604 + }) 605 + } 606 + 607 + pub fn test_scopes(src: &str, expected: Expect) { 608 + let graph = build_graph(src); 609 + let observed = graph.debug(src.as_bytes()); 610 + expected.assert_debug_eq(&observed) 611 + } 612 + 613 + pub fn build_graph(src: &str) -> ScopeGraph { 614 + StagBuilder::default() 615 + .with_source(src) 616 + .with_stag_file(include_str!("stag.tsg")) 617 + .with_language(tree_sitter_rust::language()) 618 + .execute() 619 + .unwrap() 620 + } 621 + 622 + #[test] 623 + fn declare_const_and_static() { 624 + let src = r#" 625 + const a: () = (); 626 + static b: () = (); 627 + "#; 628 + 629 + let (_, def_count, _, _) = counts(src); 630 + 631 + // a, b 632 + assert_eq!(def_count, 2); 633 + } 634 + 635 + #[test] 636 + fn declare_let_statement() { 637 + let src = r#" 638 + fn main() { 639 + let a = (); 640 + let (b, c) = (); 641 + let S { d, e } = (); 642 + let S { field: f, g } = (); 643 + let S { h, .. } = (); 644 + let S { i, field: _ } = (); 645 + } 646 + "#; 647 + let (_, def_count, _, _) = counts(src); 648 + 649 + // main, a, b, c, d, e, f, g, h, i 650 + assert_eq!(def_count, 10); 651 + } 652 + 653 + #[test] 654 + fn declare_function_params() { 655 + let src = r#" 656 + fn f1(a: T) {} 657 + fn f2(b: T, c: T) {} 658 + fn f3((d, e): (T, U)) {} 659 + fn f4(S {f, g}: S) {} 660 + fn f5(S {h, ..}: S) {} 661 + fn f6(S { field: i }: S) {} 662 + "#; 663 + let (_, def_count, _, _) = counts(src); 664 + 665 + // f1, f2, f3, f4, f5, f6, a, b, c, d, e, f, g, h, i 666 + assert_eq!(def_count, 15); 667 + } 668 + 669 + #[test] 670 + fn declare_closure_params() { 671 + let src = r#" 672 + fn main() { 673 + let _ = |x| {}; 674 + let _ = |x, y| {}; 675 + let _ = |x: ()| {}; 676 + let _ = |(x, y): ()| {}; 677 + } 678 + "#; 679 + let (_, def_count, _, _) = counts(src); 680 + 681 + // main, 682 + // x, 683 + // x, y, 684 + // x, 685 + // x, y 686 + assert_eq!(def_count, 7); 687 + } 688 + 689 + #[test] 690 + fn declare_labels() { 691 + let src = r#" 692 + fn main() { 693 + 'loop: loop {}; 694 + 'loop: for _ in () {} 695 + 'loop: while true {} 696 + } 697 + "#; 698 + let (_, def_count, _, _) = counts(src); 699 + 700 + // main, 'loop x3 701 + assert_eq!(def_count, 4); 702 + } 703 + 704 + #[test] 705 + fn declare_types() { 706 + let src = r#" 707 + struct One { 708 + two: T, 709 + three: T, 710 + } 711 + 712 + enum Four { 713 + Five, 714 + Six(T), 715 + Seven { 716 + eight: T 717 + } 718 + } 719 + 720 + union Nine {} 721 + 722 + type Ten = (); 723 + "#; 724 + let (_, def_count, _, _) = counts(src); 725 + 726 + assert_eq!(def_count, 10); 727 + } 728 + 729 + #[test] 730 + fn declare_namespaces() { 731 + let src = r#" 732 + mod one {} 733 + pub mod two {} 734 + mod three { 735 + mod four {} 736 + } 737 + "#; 738 + let (_, def_count, _, _) = counts(src); 739 + 740 + assert_eq!(def_count, 4); 741 + } 742 + 743 + #[test] 744 + fn declare_let_expr() { 745 + let src = r#" 746 + if let a = () {} 747 + if let Some(a) = () {} 748 + 749 + while let a = () {} 750 + while let Some(a) = () {} 751 + "#; 752 + let (_, def_count, _, _) = counts(src); 753 + 754 + assert_eq!(def_count, 4); 755 + } 756 + 757 + #[test] 758 + fn refer_unary_expr() { 759 + let src = r#" 760 + fn main() { 761 + let a = 2; 762 + !a; 763 + -a; 764 + *a; 765 + } 766 + "#; 767 + let (_, _, ref_count, _) = counts(src); 768 + 769 + assert_eq!(ref_count, 3); 770 + } 771 + 772 + #[test] 773 + fn refer_binary_expr() { 774 + let src = r#" 775 + fn main() { 776 + let a = 2; 777 + let b = 3; 778 + a + b; 779 + a >> b; 780 + } 781 + "#; 782 + let (_, _, ref_count, _) = counts(src); 783 + 784 + assert_eq!(ref_count, 4); 785 + } 786 + 787 + #[test] 788 + fn refer_control_flow() { 789 + let src = r#" 790 + fn main() { 791 + let a = 2; 792 + 793 + // 1 794 + if a {} 795 + 796 + // 2 797 + if _ {} else if a {} 798 + 799 + // 3 800 + while a { 801 + // 4 802 + break a; 803 + } 804 + 805 + // 5 806 + a?; 807 + 808 + // 6 809 + return a; 810 + 811 + // 7 812 + a.await; 813 + 814 + // 8 815 + yield a; 816 + } 817 + "#; 818 + let (_, _, ref_count, _) = counts(src); 819 + 820 + assert_eq!(ref_count, 8); 821 + } 822 + 823 + #[test] 824 + fn refer_assignment() { 825 + let src = r#" 826 + fn main() { 827 + let mut a = 2; 828 + a += 2; 829 + a = 2; 830 + a *= 2; 831 + } 832 + "#; 833 + let (_, _, ref_count, _) = counts(src); 834 + 835 + assert_eq!(ref_count, 3); 836 + } 837 + 838 + #[test] 839 + fn refer_struct_expr() { 840 + let src = r#" 841 + fn main() { 842 + let a = 2; 843 + let b = 2; 844 + S { a, b }; 845 + S { ..a }; 846 + S { field: a, b }; 847 + } 848 + "#; 849 + let (_, _, ref_count, _) = counts(src); 850 + 851 + assert_eq!(ref_count, 5); 852 + } 853 + 854 + #[test] 855 + fn refer_dot() { 856 + let src = r#" 857 + fn main() { 858 + let a = S {}; 859 + 860 + a.b; 861 + a.foo(); 862 + } 863 + "#; 864 + let (_, _, ref_count, _) = counts(src); 865 + 866 + assert_eq!(ref_count, 2); 867 + } 868 + 869 + #[test] 870 + fn refer_arguments() { 871 + let src = r#" 872 + fn main() { 873 + let a = 2; 874 + let b = 3; 875 + foo(a, b); 876 + } 877 + "#; 878 + let (_, _, ref_count, _) = counts(src); 879 + 880 + assert_eq!(ref_count, 2); 881 + } 882 + 883 + #[test] 884 + fn function_params() { 885 + test_scopes( 886 + r#" 887 + fn foo(t: T, u: U) -> R {} 888 + "#, 889 + expect![[r#" 890 + scope { 891 + definitions: [ 892 + foo { 893 + context: "fn §foo§(t: T, u: U) -> R {}", 894 + }, 895 + ], 896 + child scopes: [ 897 + scope { 898 + definitions: [ 899 + t { 900 + context: "fn foo(§t§: T, u: U) -> R {}", 901 + }, 902 + u { 903 + context: "fn foo(t: T, §u§: U) -> R {}", 904 + }, 905 + ], 906 + child scopes: [], 907 + }, 908 + scope { 909 + definitions: [], 910 + child scopes: [], 911 + }, 912 + ], 913 + } 914 + "#]], 915 + ); 916 + } 917 + 918 + #[test] 919 + fn use_statements() { 920 + test_scopes( 921 + r#" 922 + mod intelligence; 923 + 924 + use bleep; 925 + use super::test_utils; 926 + use intelligence::language as lang; 927 + use crate::text_range::{TextRange, Point}; 928 + "#, 929 + expect![[r#" 930 + scope { 931 + definitions: [ 932 + intelligence { 933 + context: "mod §intelligence§;", 934 + referenced in (1): [ 935 + `use §intelligence§::language as lang;`, 936 + ], 937 + }, 938 + bleep { 939 + context: "use §bleep§;", 940 + }, 941 + test_utils { 942 + context: "use super::§test_utils§;", 943 + }, 944 + lang { 945 + context: "use intelligence::language as §lang§;", 946 + }, 947 + TextRange { 948 + context: "use crate::text_range::{§TextRange§, Point};", 949 + }, 950 + Point { 951 + context: "use crate::text_range::{TextRange, §Point§};", 952 + }, 953 + ], 954 + child scopes: [], 955 + } 956 + "#]], 957 + ) 958 + } 959 + 960 + #[test] 961 + fn lifetimes() { 962 + test_scopes( 963 + r#" 964 + impl<'a, T> Trait for Struct<'a, T> { 965 + fn foo<'b>(&'a self) -> &'b T { } 966 + } 967 + "#, 968 + expect![[r#" 969 + scope { 970 + definitions: [], 971 + child scopes: [ 972 + scope { 973 + definitions: [ 974 + 'a { 975 + context: "impl<§'a§, T> Trait for Struct<'a, T> {", 976 + referenced in (2): [ 977 + `impl<'a, T> Trait for Struct<§'a§, T> {`, 978 + `fn foo<'b>(&§'a§ self) -> &'b T { }`, 979 + ], 980 + }, 981 + T { 982 + context: "impl<'a, §T§> Trait for Struct<'a, T> {", 983 + referenced in (2): [ 984 + `impl<'a, T> Trait for Struct<'a, §T§> {`, 985 + `fn foo<'b>(&'a self) -> &'b §T§ { }`, 986 + ], 987 + }, 988 + ], 989 + child scopes: [ 990 + scope { 991 + definitions: [ 992 + foo { 993 + context: "fn §foo§<'b>(&'a self) -> &'b T { }", 994 + }, 995 + 'b { 996 + context: "fn foo<§'b§>(&'a self) -> &'b T { }", 997 + referenced in (1): [ 998 + `fn foo<'b>(&'a self) -> &§'b§ T { }`, 999 + ], 1000 + }, 1001 + ], 1002 + child scopes: [ 1003 + scope { 1004 + definitions: [ 1005 + self { 1006 + context: "fn foo<'b>(&'a §self§) -> &'b T { }", 1007 + }, 1008 + ], 1009 + child scopes: [], 1010 + }, 1011 + scope { 1012 + definitions: [], 1013 + child scopes: [], 1014 + }, 1015 + ], 1016 + }, 1017 + ], 1018 + }, 1019 + ], 1020 + } 1021 + "#]], 1022 + ) 1023 + } 1024 + 1025 + #[test] 1026 + fn generics_and_traits() { 1027 + test_scopes( 1028 + r#" 1029 + trait Foo {} 1030 + 1031 + fn foo<'a, 'b, T, U: Foo<T> + 'a>(t: T, u: U) 1032 + where T: Foo + 'b, 1033 + { } 1034 + "#, 1035 + expect![[r#" 1036 + scope { 1037 + definitions: [ 1038 + Foo { 1039 + context: "trait §Foo§ {}", 1040 + referenced in (2): [ 1041 + `fn foo<'a, 'b, T, U: §Foo§<T> + 'a>(t: T, u: U)`, 1042 + `where T: §Foo§ + 'b,`, 1043 + ], 1044 + }, 1045 + foo { 1046 + context: "fn §foo§<'a, 'b, T, U: Foo<T> + 'a>(t: T, u: U)", 1047 + }, 1048 + 'a { 1049 + context: "fn foo<§'a§, 'b, T, U: Foo<T> + 'a>(t: T, u: U)", 1050 + referenced in (1): [ 1051 + `fn foo<'a, 'b, T, U: Foo<T> + §'a§>(t: T, u: U)`, 1052 + ], 1053 + }, 1054 + 'b { 1055 + context: "fn foo<'a, §'b§, T, U: Foo<T> + 'a>(t: T, u: U)", 1056 + referenced in (1): [ 1057 + `where T: Foo + §'b§,`, 1058 + ], 1059 + }, 1060 + T { 1061 + context: "fn foo<'a, 'b, §T§, U: Foo<T> + 'a>(t: T, u: U)", 1062 + referenced in (3): [ 1063 + `fn foo<'a, 'b, T, U: Foo<§T§> + 'a>(t: T, u: U)`, 1064 + `fn foo<'a, 'b, T, U: Foo<T> + 'a>(t: §T§, u: U)`, 1065 + `where §T§: Foo + 'b,`, 1066 + ], 1067 + }, 1068 + U { 1069 + context: "fn foo<'a, 'b, T, §U§: Foo<T> + 'a>(t: T, u: U)", 1070 + referenced in (1): [ 1071 + `fn foo<'a, 'b, T, U: Foo<T> + 'a>(t: T, u: §U§)`, 1072 + ], 1073 + }, 1074 + ], 1075 + child scopes: [ 1076 + scope { 1077 + definitions: [], 1078 + child scopes: [ 1079 + scope { 1080 + definitions: [], 1081 + child scopes: [ 1082 + scope { 1083 + definitions: [], 1084 + child scopes: [], 1085 + }, 1086 + ], 1087 + }, 1088 + ], 1089 + }, 1090 + scope { 1091 + definitions: [ 1092 + t { 1093 + context: "fn foo<'a, 'b, T, U: Foo<T> + 'a>(§t§: T, u: U)", 1094 + }, 1095 + u { 1096 + context: "fn foo<'a, 'b, T, U: Foo<T> + 'a>(t: T, §u§: U)", 1097 + }, 1098 + ], 1099 + child scopes: [], 1100 + }, 1101 + scope { 1102 + definitions: [], 1103 + child scopes: [], 1104 + }, 1105 + ], 1106 + } 1107 + "#]], 1108 + ) 1109 + } 1110 + 1111 + #[test] 1112 + fn type_constructors() { 1113 + test_scopes( 1114 + r#" 1115 + struct Highlight {} 1116 + 1117 + enum Direction { Incoming, Outgoing } 1118 + 1119 + fn foo() -> Highlight { 1120 + Highlight { } 1121 + } 1122 + 1123 + fn bar() -> Direction { 1124 + Direction::Incoming 1125 + } 1126 + "#, 1127 + expect![[r#" 1128 + scope { 1129 + definitions: [ 1130 + Highlight { 1131 + context: "struct §Highlight§ {}", 1132 + referenced in (2): [ 1133 + `fn foo() -> §Highlight§ {`, 1134 + `§Highlight§ { }`, 1135 + ], 1136 + }, 1137 + Direction { 1138 + context: "enum §Direction§ { Incoming, Outgoing }", 1139 + referenced in (2): [ 1140 + `fn bar() -> §Direction§ {`, 1141 + `§Direction§::Incoming`, 1142 + ], 1143 + }, 1144 + foo { 1145 + context: "fn §foo§() -> Highlight {", 1146 + }, 1147 + bar { 1148 + context: "fn §bar§() -> Direction {", 1149 + }, 1150 + ], 1151 + child scopes: [ 1152 + scope { 1153 + definitions: [], 1154 + child scopes: [ 1155 + scope { 1156 + definitions: [], 1157 + child scopes: [], 1158 + }, 1159 + ], 1160 + }, 1161 + scope { 1162 + definitions: [], 1163 + child scopes: [ 1164 + scope { 1165 + definitions: [ 1166 + Incoming { 1167 + context: "enum Direction { §Incoming§, Outgoing }", 1168 + }, 1169 + Outgoing { 1170 + context: "enum Direction { Incoming, §Outgoing§ }", 1171 + }, 1172 + ], 1173 + child scopes: [], 1174 + }, 1175 + ], 1176 + }, 1177 + scope { 1178 + definitions: [], 1179 + child scopes: [], 1180 + }, 1181 + scope { 1182 + definitions: [], 1183 + child scopes: [], 1184 + }, 1185 + scope { 1186 + definitions: [], 1187 + child scopes: [], 1188 + }, 1189 + scope { 1190 + definitions: [], 1191 + child scopes: [], 1192 + }, 1193 + ], 1194 + } 1195 + "#]], 1196 + ) 1197 + } 1198 + 1199 + #[test] 1200 + fn macros() { 1201 + test_scopes( 1202 + r#" 1203 + fn main() { 1204 + let (a, b, c) = (); 1205 + // top-level tokens 1206 + assert_eq!(a, b + c); 1207 + 1208 + // nested tokens 1209 + println!("{}", if a { b } then { c }); 1210 + } 1211 + "#, 1212 + expect![[r#" 1213 + scope { 1214 + definitions: [ 1215 + main { 1216 + context: "fn §main§() {", 1217 + }, 1218 + ], 1219 + child scopes: [ 1220 + scope { 1221 + definitions: [], 1222 + child scopes: [], 1223 + }, 1224 + scope { 1225 + definitions: [ 1226 + a { 1227 + context: "let (§a§, b, c) = ();", 1228 + referenced in (2): [ 1229 + `assert_eq!(§a§, b + c);`, 1230 + `println!("{}", if §a§ { b } then { c });`, 1231 + ], 1232 + }, 1233 + b { 1234 + context: "let (a, §b§, c) = ();", 1235 + referenced in (2): [ 1236 + `assert_eq!(a, §b§ + c);`, 1237 + `println!("{}", if a { §b§ } then { c });`, 1238 + ], 1239 + }, 1240 + c { 1241 + context: "let (a, b, §c§) = ();", 1242 + referenced in (2): [ 1243 + `assert_eq!(a, b + §c§);`, 1244 + `println!("{}", if a { b } then { §c§ });`, 1245 + ], 1246 + }, 1247 + ], 1248 + child scopes: [], 1249 + }, 1250 + ], 1251 + } 1252 + "#]], 1253 + ) 1254 + } 1255 + 1256 + // Self::method and self.method can be raised as references 1257 + #[test] 1258 + fn handle_self_type_and_var() { 1259 + test_scopes( 1260 + r#" 1261 + struct MyStruct {} 1262 + 1263 + impl MyStruct { 1264 + fn foo() { 1265 + Self::foo() 1266 + } 1267 + 1268 + fn bar(&self) { 1269 + self.bar() 1270 + } 1271 + } 1272 + "#, 1273 + expect![[r#" 1274 + scope { 1275 + definitions: [ 1276 + MyStruct { 1277 + context: "struct §MyStruct§ {}", 1278 + referenced in (1): [ 1279 + `impl §MyStruct§ {`, 1280 + ], 1281 + }, 1282 + ], 1283 + child scopes: [ 1284 + scope { 1285 + definitions: [], 1286 + child scopes: [ 1287 + scope { 1288 + definitions: [], 1289 + child scopes: [], 1290 + }, 1291 + ], 1292 + }, 1293 + scope { 1294 + definitions: [], 1295 + child scopes: [ 1296 + scope { 1297 + definitions: [ 1298 + foo { 1299 + context: "fn §foo§() {", 1300 + referenced in (1): [ 1301 + `Self::§foo§()`, 1302 + ], 1303 + }, 1304 + bar { 1305 + context: "fn §bar§(&self) {", 1306 + referenced in (1): [ 1307 + `self.§bar§()`, 1308 + ], 1309 + }, 1310 + ], 1311 + child scopes: [ 1312 + scope { 1313 + definitions: [], 1314 + child scopes: [], 1315 + }, 1316 + scope { 1317 + definitions: [], 1318 + child scopes: [], 1319 + }, 1320 + scope { 1321 + definitions: [ 1322 + self { 1323 + context: "fn bar(&§self§) {", 1324 + }, 1325 + ], 1326 + child scopes: [], 1327 + }, 1328 + scope { 1329 + definitions: [], 1330 + child scopes: [], 1331 + }, 1332 + ], 1333 + }, 1334 + ], 1335 + }, 1336 + ], 1337 + } 1338 + "#]], 1339 + ) 1340 + } 1341 + 1342 + #[test] 1343 + fn let_else_1_65_support() { 1344 + test_scopes( 1345 + r#" 1346 + fn main() { 1347 + let a = 3; 1348 + if let b = a 1349 + && let c = b 1350 + && let d = c { 1351 + d 1352 + } else { 1353 + return; 1354 + } 1355 + } 1356 + "#, 1357 + expect![[r#" 1358 + scope { 1359 + definitions: [ 1360 + main { 1361 + context: "fn §main§() {", 1362 + }, 1363 + ], 1364 + child scopes: [ 1365 + scope { 1366 + definitions: [], 1367 + child scopes: [], 1368 + }, 1369 + scope { 1370 + definitions: [ 1371 + a { 1372 + context: "let §a§ = 3;", 1373 + referenced in (1): [ 1374 + `if let b = §a§`, 1375 + ], 1376 + }, 1377 + ], 1378 + child scopes: [ 1379 + scope { 1380 + definitions: [ 1381 + b { 1382 + context: "if let §b§ = a", 1383 + referenced in (1): [ 1384 + `&& let c = §b§`, 1385 + ], 1386 + }, 1387 + c { 1388 + context: "&& let §c§ = b", 1389 + referenced in (1): [ 1390 + `&& let d = §c§ {`, 1391 + ], 1392 + }, 1393 + d { 1394 + context: "&& let §d§ = c {", 1395 + }, 1396 + ], 1397 + child scopes: [], 1398 + }, 1399 + scope { 1400 + definitions: [], 1401 + child scopes: [], 1402 + }, 1403 + scope { 1404 + definitions: [], 1405 + child scopes: [], 1406 + }, 1407 + ], 1408 + }, 1409 + ], 1410 + } 1411 + "#]], 1412 + ) 1413 + } 1414 + }
+30
src/main.rs
··· 1 + use stag::StagBuilder; 2 + 3 + fn main() { 4 + let scopes = std::fs::read_to_string("src/stag.tsg").unwrap(); 5 + let src = r#" 6 + fn main() { 7 + let x = 2; 8 + let a = 5; 9 + if let _ = z { 10 + a[x]; 11 + } 12 + } 13 + "#; 14 + 15 + let sg = StagBuilder::default() 16 + .with_source(src) 17 + .with_stag_file(&scopes) 18 + .with_language(tree_sitter_rust::language()) 19 + .execute() 20 + .unwrap(); 21 + 22 + for edge in sg.graph.raw_edges() { 23 + let s = edge.source(); 24 + let t = edge.target(); 25 + let sn = &sg.graph[s]; 26 + let st = &sg.graph[t]; 27 + println!("{:?} -> {:?}", edge.source(), edge.target()); 28 + println!("{:#?} -> {:#?}", sn, st); 29 + } 30 + }
+463
src/scopes.scm
··· 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)
+550
src/stag.tsg
··· 1 + [(block) 2 + (declaration_list) 3 + (impl_item) 4 + 5 + ;; let expressions create scopes 6 + (if_expression 7 + [(let_condition) 8 + (let_chain)]) 9 + 10 + ;; each match arm can bind variables with 11 + ;; patterns, without creating a block scope; 12 + ;; 13 + ;; match _ { 14 + ;; (a, b) => a, 15 + ;; } 16 + ;; 17 + ;; The bindings for a, b are constrained to 18 + ;; the match arm. 19 + (match_arm) 20 + 21 + ;; loop labels are defs that are available only 22 + ;; within the scope they create: 23 + ;; 24 + ;; 'outer: loop { 25 + ;; let x = 2; 26 + ;; }; 27 + ;; let y = 2; 28 + ;; 29 + ;; Produces a scope graph like so: 30 + ;; 31 + ;; { 32 + ;; defs: [ y ], 33 + ;; scopes: [ 34 + ;; { 35 + ;; defs: [ 'outer ], 36 + ;; scopes: [ 37 + ;; { 38 + ;; defs: [ x ] 39 + ;; } 40 + ;; ] 41 + ;; } 42 + ;; ] 43 + ;; } 44 + ;; 45 + (loop_expression) 46 + (for_expression) 47 + (while_expression)] @cap 48 + { 49 + (scope (range @cap)) 50 + } 51 + 52 + 53 + (function_item 54 + (identifier) @i 55 + (parameters) @params 56 + (block) @body) 57 + { 58 + (def @i "function") 59 + (scope (cover @params @body)) 60 + } 61 + 62 + 63 + ;; impl items can define types and lifetimes: 64 + ;; 65 + ;; impl<'a, T> Trait for Struct { .. } 66 + ;; 67 + ;; in order to constrain those to the impl block, 68 + ;; we add a local scope here: 69 + [(struct_item (type_identifier) @i (type_parameters)? @t body: (_) @b) 70 + (union_item (type_identifier) @i (type_parameters)? @t body: (_) @b) 71 + (enum_item (type_identifier) @i (type_parameters)? @t body: (_) @b) 72 + (type_item (type_identifier) @i (type_parameters)? @t type: (_) @b) 73 + (trait_item (type_identifier) @i (type_parameters)? @t body: (_) @b)] 74 + { 75 + (def @i) 76 + (scope (cover @t @b)) 77 + (scope (range @b)) 78 + } 79 + 80 + 81 + ;; DEFS 82 + ;; ---- 83 + 84 + ;; let x = ...; 85 + (let_declaration pattern: (identifier) @cap) 86 + { 87 + (def @cap "variable") 88 + } 89 + 90 + ;; if let x = ...; 91 + ;; while let x = ...; 92 + (let_condition (identifier) @cap . "=") 93 + { 94 + (def @cap "variable") 95 + } 96 + 97 + ;; let (a, b, ...) = ..; 98 + ;; if let (a, b, ...) = {} 99 + ;; while let (a, b, ...) = {} 100 + ;; match _ { (a, b) => { .. } } 101 + (tuple_pattern (identifier) @cap) 102 + { 103 + (def @cap "variable") 104 + } 105 + 106 + ;; Some(a) 107 + (tuple_struct_pattern 108 + type: (_) 109 + (identifier) @cap) 110 + { 111 + (def @cap "variable") 112 + } 113 + 114 + ;; let S { field: a } = ..; 115 + (struct_pattern 116 + (field_pattern 117 + (identifier) @cap)) 118 + { 119 + (def @cap "variable") 120 + } 121 + 122 + 123 + [ 124 + ;; (mut x: T) 125 + (mut_pattern (identifier) @i) 126 + 127 + ;; (ref x: T) 128 + (ref_pattern (identifier) @i) 129 + 130 + ;; const x = ...; 131 + (const_item (identifier) @i) 132 + 133 + ;; static x = ...; 134 + (static_item (identifier) @i)] 135 + { 136 + (def @i "variable") 137 + } 138 + 139 + ;; fn _(x: _) 140 + (parameters 141 + (parameter 142 + pattern: (identifier) @cap)) 143 + { 144 + (def @cap) 145 + } 146 + 147 + ;; fn _(self) 148 + (parameters 149 + (self_parameter 150 + (self) @cap)) 151 + { 152 + (def @cap) 153 + } 154 + 155 + ;; type parameters 156 + (type_parameters 157 + (type_identifier) @cap) 158 + { 159 + (def @cap) 160 + } 161 + (type_parameters 162 + (lifetime) @cap) 163 + { 164 + (def @cap) 165 + } 166 + (constrained_type_parameter 167 + left: (type_identifier) @cap) 168 + { 169 + (def @cap) 170 + } 171 + 172 + ;; |x| { ... } 173 + ;; no type 174 + (closure_parameters (identifier) @cap) 175 + { 176 + (def @cap) 177 + } 178 + 179 + ;; |x: T| { ... } 180 + ;; with type 181 + (closure_parameters 182 + (parameter 183 + (identifier) @cap)) 184 + { 185 + (def @cap) 186 + } 187 + 188 + ;; 'outer: loop { .. } 189 + (loop_expression 190 + (loop_label) @cap) 191 + { 192 + (def @cap) 193 + } 194 + 195 + ;; `for` exprs create two defs: a label (if any) and the 196 + ;; loop variable 197 + (for_expression . (identifier) @cap) 198 + { 199 + (def @cap) 200 + } 201 + (for_expression (loop_label) @cap) 202 + { 203 + (def @cap) 204 + } 205 + 206 + ;; 'label: while cond { .. } 207 + (while_expression 208 + (loop_label) @cap) 209 + { 210 + (def @cap) 211 + } 212 + 213 + ;; struct and union fields 214 + (field_declaration_list 215 + (field_declaration 216 + (field_identifier) @cap)) 217 + { 218 + (def @cap) 219 + } 220 + 221 + ;; enum variants 222 + (enum_variant_list 223 + (enum_variant 224 + (identifier) @cap)) 225 + { 226 + (def @cap) 227 + } 228 + 229 + ;; mod x; 230 + (mod_item (identifier) @cap) 231 + { 232 + (def @cap) 233 + } 234 + 235 + 236 + ;; IMPORTS 237 + ;; ------- 238 + 239 + ;; use item; 240 + (use_declaration 241 + (identifier) @cap) 242 + { 243 + (def @cap) 244 + } 245 + 246 + ;; use path as item; 247 + (use_as_clause 248 + alias: (identifier) @cap) 249 + { 250 + (def @cap) 251 + } 252 + 253 + ;; use path::item; 254 + (use_declaration 255 + (scoped_identifier 256 + name: (identifier) @cap)) 257 + { 258 + (def @cap) 259 + } 260 + 261 + ;; use module::{member1, member2, member3}; 262 + (use_list 263 + (identifier) @cap) 264 + { 265 + (def @cap) 266 + } 267 + 268 + (use_list 269 + (scoped_identifier 270 + name: (identifier) @cap)) 271 + { 272 + (def @cap) 273 + } 274 + 275 + 276 + ;; REFS 277 + ;; ---- 278 + 279 + [ 280 + ;; !x 281 + (unary_expression (identifier) @cap) 282 + ;; &x 283 + (reference_expression (identifier) @cap) 284 + 285 + ;; (x) 286 + (parenthesized_expression (identifier) @cap) 287 + 288 + ;; x? 289 + (try_expression (identifier) @cap) 290 + 291 + ;; a = b 292 + (assignment_expression (identifier) @cap) 293 + 294 + ;; a op b 295 + (binary_expression (identifier) @cap) 296 + 297 + ;; a op= b 298 + (compound_assignment_expr (identifier) @cap) 299 + 300 + ;; a as b 301 + (type_cast_expression (identifier) @cap) 302 + 303 + ;; a() 304 + (call_expression (identifier) @cap) 305 + 306 + ;; return a 307 + (return_expression (identifier) @cap) 308 + 309 + ;; break a 310 + (break_expression (identifier) @cap) 311 + 312 + ;; break 'label 313 + (break_expression (loop_label) @cap) 314 + 315 + ;; continue 'label; 316 + (continue_expression (loop_label) @cap) 317 + 318 + ;; yield x; 319 + (yield_expression (identifier) @cap) 320 + 321 + ;; await a 322 + (await_expression (identifier) @cap) 323 + 324 + ;; (a, b) 325 + (tuple_expression (identifier) @cap) 326 + 327 + ;; a[] 328 + (index_expression (identifier) @cap) 329 + 330 + ;; ident; 331 + (expression_statement (identifier) @cap) 332 + 333 + ;; a..b 334 + (range_expression (identifier) @cap) 335 + 336 + ;; [ident; N] 337 + (array_expression (identifier) @cap) 338 + 339 + ;; path::to::item 340 + ;; 341 + ;; `path` is a ref 342 + (scoped_identifier 343 + path: (identifier) @cap) 344 + 345 + ;; rhs of let decls 346 + (let_declaration 347 + value: (identifier) @cap) 348 + 349 + ;; type T = [T; N] 350 + ;; 351 + ;; N is a ident ref 352 + (array_type 353 + length: (identifier) @cap) 354 + 355 + ;; S { _ } 356 + (struct_expression 357 + (type_identifier) @cap) 358 + 359 + ;; S { a } 360 + (struct_expression 361 + (field_initializer_list 362 + (shorthand_field_initializer 363 + (identifier) @cap))) 364 + 365 + ;; S { a: value } 366 + (struct_expression 367 + (field_initializer_list 368 + (field_initializer 369 + (identifier) @cap))) 370 + 371 + ;; S { ..a } 372 + (struct_expression 373 + (field_initializer_list 374 + (base_field_initializer 375 + (identifier) @cap))) 376 + 377 + ;; if a {} 378 + (if_expression (identifier) @cap) 379 + 380 + ;; for pattern in value {} 381 + ;; 382 + ;; `value` is a ref 383 + (for_expression 384 + value: (identifier) @cap) 385 + 386 + ;; while a {} 387 + (while_expression (identifier) @cap) 388 + 389 + ;; match a 390 + (match_expression (identifier) @cap) 391 + 392 + ;; match _ { 393 + ;; pattern => a, 394 + ;; } 395 + ;; 396 + ;; this `a` is somehow not any expression form 397 + (match_arm (identifier) @cap) 398 + 399 + ;; a.b 400 + ;; 401 + ;; `b` is ignored 402 + (field_expression 403 + (identifier) @cap) 404 + 405 + ;; { stmt; foo } 406 + (block 407 + (identifier) @cap) 408 + 409 + ;; arguments to method calls or function calls 410 + (arguments 411 + (identifier) @cap) 412 + 413 + ;; impl S { .. } 414 + (impl_item (type_identifier) @cap) 415 + 416 + ;; where T: ... 417 + (where_predicate 418 + left: (type_identifier) @cap) 419 + 420 + ;; trait bounds 421 + (trait_bounds 422 + (type_identifier) @cap) 423 + (trait_bounds 424 + (lifetime) @cap) 425 + 426 + ;; idents in macros 427 + (token_tree 428 + (identifier) @cap) 429 + 430 + 431 + ;; types 432 + 433 + ;; (T, U) 434 + (tuple_type 435 + (type_identifier) @cap) 436 + 437 + ;; &T 438 + (reference_type 439 + (type_identifier) @cap) 440 + 441 + ;; &'a T 442 + (reference_type 443 + (lifetime) @cap) 444 + 445 + ;; &'a self 446 + (self_parameter 447 + (lifetime) @cap) 448 + 449 + ;; *mut T 450 + ;; *const T 451 + (pointer_type 452 + (type_identifier) @cap) 453 + 454 + ;; A<_> 455 + (generic_type 456 + (type_identifier) @cap) 457 + 458 + ;; _<V> 459 + (type_arguments 460 + (type_identifier) @cap) 461 + (type_arguments 462 + (lifetime) @cap) 463 + 464 + ;; T<U = V> 465 + ;; 466 + ;; U is ignored 467 + ;; V is a ref 468 + (type_binding 469 + name: (_) 470 + type: (type_identifier) @cap) 471 + 472 + ;; [T] 473 + (array_type 474 + (type_identifier) @cap) 475 + 476 + ;; type T = U; 477 + ;; 478 + ;; T is a def 479 + ;; U is a ref 480 + (type_item 481 + name: (_) 482 + type: (type_identifier) @cap) 483 + 484 + (function_item 485 + return_type: (type_identifier) @cap) 486 + 487 + ;; type refs in params 488 + ;; 489 + ;; fn _(_: T) 490 + (parameters 491 + (parameter 492 + type: (type_identifier) @cap)) 493 + 494 + ;; dyn T 495 + (dynamic_type 496 + (type_identifier) @cap) 497 + 498 + ;; <T>::call() 499 + (bracketed_type 500 + (type_identifier) @cap) 501 + 502 + ;; T as Trait 503 + (qualified_type 504 + (type_identifier) @cap) 505 + 506 + ;; module::T 507 + ;; 508 + ;; `module` is a def 509 + ;; `T` is a ref 510 + (scoped_type_identifier 511 + path: (identifier) @cap) 512 + 513 + ;; struct _ { field: Type } 514 + ;; `Type` is a ref 515 + (field_declaration 516 + name: (_) 517 + type: (type_identifier) @cap) 518 + 519 + ;; Self::foo() 520 + ;; 521 + ;; `foo` can be resolved 522 + (call_expression 523 + (scoped_identifier 524 + (identifier) @_self_type 525 + (identifier) @cap) 526 + (#match? @_self_type "Self")) 527 + 528 + ;; self.foo() 529 + ;; 530 + ;; `foo` can be resolved 531 + (call_expression 532 + (field_expression 533 + (self) 534 + (field_identifier) @cap)) 535 + 536 + ;; if let _ = a {} 537 + ;; 538 + ;; the ident following the `=` is a ref 539 + ;; the ident preceding the `=` is a def 540 + ;; while let _ = a {} 541 + (let_condition 542 + "=" 543 + . 544 + (identifier) @cap) 545 + ] 546 + { 547 + (ref @cap) 548 + } 549 + 550 +
+197
src/stdlib.rs
··· 1 + use tree_sitter_graph::{functions::Function, ExecutionError, Identifier}; 2 + 3 + use crate::text_range; 4 + 5 + pub struct ScopeShorthand; 6 + 7 + #[allow(unused_must_use)] 8 + impl Function for ScopeShorthand { 9 + fn call( 10 + &self, 11 + graph: &mut tree_sitter_graph::graph::Graph, 12 + _source: &str, 13 + parameters: &mut dyn tree_sitter_graph::functions::Parameters, 14 + ) -> Result<tree_sitter_graph::graph::Value, ExecutionError> { 15 + let target_range = parameters.param()?; 16 + if target_range.as_list().is_err() { 17 + return Err(ExecutionError::ExpectedList(format!( 18 + "`scope` expects list" 19 + ))); 20 + } 21 + parameters.finish()?; 22 + 23 + let graph_node = graph.add_graph_node(); 24 + graph[graph_node] 25 + .attributes 26 + .add::<String>(Identifier::from("kind"), "scope".into()); 27 + graph[graph_node] 28 + .attributes 29 + .add(Identifier::from("range"), target_range); 30 + 31 + Ok(tree_sitter_graph::graph::Value::GraphNode(graph_node)) 32 + } 33 + } 34 + 35 + pub struct DefShorthand; 36 + 37 + #[allow(unused_must_use)] 38 + impl Function for DefShorthand { 39 + fn call( 40 + &self, 41 + graph: &mut tree_sitter_graph::graph::Graph, 42 + source: &str, 43 + parameters: &mut dyn tree_sitter_graph::functions::Parameters, 44 + ) -> Result<tree_sitter_graph::graph::Value, ExecutionError> { 45 + let target_node = parameters.param()?.into_syntax_node_ref()?; 46 + let ts_node = graph[target_node]; 47 + let symbol = parameters 48 + .param() 49 + .and_then(|p| p.as_str().map(ToOwned::to_owned)) 50 + .ok(); 51 + parameters.finish()?; 52 + 53 + let graph_node = graph.add_graph_node(); 54 + graph[graph_node] 55 + .attributes 56 + .add::<String>(Identifier::from("kind"), "def".into()); 57 + graph[graph_node] 58 + .attributes 59 + .add::<String>(Identifier::from("scope"), "local".into()); 60 + 61 + if let Some(s) = symbol { 62 + graph[graph_node] 63 + .attributes 64 + .add::<String>(Identifier::from("symbol"), s.into()); 65 + } 66 + graph[graph_node].attributes.add::<String>( 67 + Identifier::from("text"), 68 + source[ts_node.byte_range()].to_string().into(), 69 + ); 70 + graph[graph_node].attributes.add( 71 + Identifier::from("range"), 72 + text_range::range_to_value(&ts_node.range()), 73 + ); 74 + graph[graph_node] 75 + .attributes 76 + .add::<tree_sitter_graph::graph::SyntaxNodeRef>( 77 + Identifier::from("target"), 78 + target_node.into(), 79 + ); 80 + 81 + Ok(tree_sitter_graph::graph::Value::GraphNode(graph_node)) 82 + } 83 + } 84 + 85 + pub struct RefShortHand; 86 + 87 + #[allow(unused_must_use)] 88 + impl Function for RefShortHand { 89 + fn call( 90 + &self, 91 + graph: &mut tree_sitter_graph::graph::Graph, 92 + source: &str, 93 + parameters: &mut dyn tree_sitter_graph::functions::Parameters, 94 + ) -> Result<tree_sitter_graph::graph::Value, ExecutionError> { 95 + let target_node = parameters.param()?.into_syntax_node_ref()?; 96 + let ts_node = graph[target_node]; 97 + parameters.finish()?; 98 + 99 + let graph_node = graph.add_graph_node(); 100 + graph[graph_node] 101 + .attributes 102 + .add::<String>(Identifier::from("kind"), "ref".into()); 103 + graph[graph_node].attributes.add::<String>( 104 + Identifier::from("text"), 105 + source[ts_node.byte_range()].to_string().into(), 106 + ); 107 + graph[graph_node].attributes.add( 108 + Identifier::from("range"), 109 + text_range::range_to_value(&ts_node.range()), 110 + ); 111 + graph[graph_node] 112 + .attributes 113 + .add::<tree_sitter_graph::graph::SyntaxNodeRef>( 114 + Identifier::from("target"), 115 + target_node.into(), 116 + ); 117 + 118 + Ok(tree_sitter_graph::graph::Value::GraphNode(graph_node)) 119 + } 120 + } 121 + 122 + pub struct CoverRanges; 123 + 124 + impl Function for CoverRanges { 125 + fn call( 126 + &self, 127 + graph: &mut tree_sitter_graph::graph::Graph, 128 + _source: &str, 129 + parameters: &mut dyn tree_sitter_graph::functions::Parameters, 130 + ) -> Result<tree_sitter_graph::graph::Value, ExecutionError> { 131 + let p1 = parameters.param()?; 132 + let p2 = parameters.param()?; 133 + 134 + match (p1.is_null(), p2.is_null()) { 135 + (true, true) => panic!("all nulls"), 136 + (false, true) => { 137 + return Ok(text_range::range_to_value( 138 + &graph[p1.into_syntax_node_ref()?].range(), 139 + )) 140 + } 141 + (true, false) => { 142 + return Ok(text_range::range_to_value( 143 + &graph[p2.into_syntax_node_ref()?].range(), 144 + )) 145 + } 146 + (false, false) => { 147 + let node_a = p1.into_syntax_node_ref()?; 148 + let node_b = p2.into_syntax_node_ref()?; 149 + let ts_node_a = graph[node_a]; 150 + let ts_node_b = graph[node_b]; 151 + 152 + let mut range = cover(ts_node_a.range(), ts_node_b.range()); 153 + while let Ok(param) = parameters.param() { 154 + if !param.is_null() { 155 + range = cover(range, graph[param.into_syntax_node_ref()?].range()) 156 + } 157 + } 158 + 159 + Ok(text_range::range_to_value(&range)) 160 + } 161 + } 162 + } 163 + } 164 + 165 + fn cover(a: tree_sitter::Range, b: tree_sitter::Range) -> tree_sitter::Range { 166 + let (start_byte, start_point) = if a.start_point < b.start_point { 167 + (a.start_byte, a.start_point) 168 + } else { 169 + (b.start_byte, b.start_point) 170 + }; 171 + let (end_byte, end_point) = if a.end_point > b.end_point { 172 + (a.end_byte, a.end_point) 173 + } else { 174 + (b.end_byte, b.end_point) 175 + }; 176 + tree_sitter::Range { 177 + start_byte, 178 + start_point, 179 + end_byte, 180 + end_point, 181 + } 182 + } 183 + 184 + pub struct NodeRange; 185 + 186 + impl Function for NodeRange { 187 + fn call( 188 + &self, 189 + graph: &mut tree_sitter_graph::graph::Graph, 190 + _source: &str, 191 + parameters: &mut dyn tree_sitter_graph::functions::Parameters, 192 + ) -> Result<tree_sitter_graph::graph::Value, ExecutionError> { 193 + let target_node = parameters.param()?.into_syntax_node_ref()?; 194 + let ts_node = graph[target_node]; 195 + Ok(text_range::range_to_value(&ts_node.range())) 196 + } 197 + }
+153
src/text_range.rs
··· 1 + use serde::{Deserialize, Serialize}; 2 + use tree_sitter_graph::graph::Value; 3 + 4 + use std::cmp::{Ord, Ordering}; 5 + 6 + /// A singular position in a text document 7 + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] 8 + pub struct Point { 9 + /// The byte index 10 + pub byte: usize, 11 + 12 + /// 0-indexed line number 13 + pub line: usize, 14 + 15 + /// Position within the line 16 + pub column: usize, 17 + } 18 + 19 + impl PartialOrd for Point { 20 + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { 21 + Some(self.cmp(other)) 22 + } 23 + } 24 + 25 + impl Ord for Point { 26 + fn cmp(&self, other: &Self) -> Ordering { 27 + self.byte.cmp(&other.byte) 28 + } 29 + } 30 + 31 + impl Point { 32 + pub fn new(byte: usize, line: usize, column: usize) -> Self { 33 + Self { byte, line, column } 34 + } 35 + 36 + pub fn from_byte(byte: usize, line_end_indices: &[u32]) -> Self { 37 + let line = line_end_indices 38 + .iter() 39 + .position(|&line_end_byte| (line_end_byte as usize) > byte) 40 + .unwrap_or(0); 41 + 42 + let column = line 43 + .checked_sub(1) 44 + .and_then(|idx| line_end_indices.get(idx)) 45 + .map(|&prev_line_end| byte.saturating_sub(prev_line_end as usize)) 46 + .unwrap_or(byte); 47 + 48 + Self::new(byte, line, column) 49 + } 50 + } 51 + 52 + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] 53 + pub struct TextRange { 54 + pub start: Point, 55 + pub end: Point, 56 + } 57 + 58 + impl PartialOrd for TextRange { 59 + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { 60 + Some(self.cmp(other)) 61 + } 62 + } 63 + 64 + impl Ord for TextRange { 65 + fn cmp(&self, other: &Self) -> Ordering { 66 + let compare_start_byte = self.start.byte.cmp(&other.start.byte); 67 + let compare_size = self.size().cmp(&other.size()); 68 + 69 + compare_start_byte.then(compare_size) 70 + } 71 + } 72 + 73 + impl TextRange { 74 + pub fn new(start: Point, end: Point) -> Self { 75 + assert!(start <= end); 76 + Self { start, end } 77 + } 78 + 79 + pub fn contains(&self, other: &TextRange) -> bool { 80 + // (self.start ... [other.start ... other.end] ... self.end) 81 + self.start <= other.start && other.end <= self.end 82 + } 83 + 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 + } 122 + 123 + pub fn value_to_range(value: &Value) -> tree_sitter::Range { 124 + let range = value.as_list().unwrap(); 125 + let start = range[0].as_list().unwrap(); 126 + let end = range[1].as_list().unwrap(); 127 + tree_sitter::Range { 128 + start_byte: start[0].as_integer().unwrap() as usize, 129 + start_point: tree_sitter::Point { 130 + row: start[1].as_integer().unwrap() as usize, 131 + column: start[2].as_integer().unwrap() as usize, 132 + }, 133 + end_byte: end[0].as_integer().unwrap() as usize, 134 + end_point: tree_sitter::Point { 135 + row: end[1].as_integer().unwrap() as usize, 136 + column: end[2].as_integer().unwrap() as usize, 137 + }, 138 + } 139 + } 140 + 141 + pub fn range_to_value(value: &tree_sitter::Range) -> Value { 142 + let start = Value::from(vec![ 143 + Value::from(value.start_byte as u32), 144 + Value::from(value.start_point.row as u32), 145 + Value::from(value.start_point.column as u32), 146 + ]); 147 + let end = Value::from(vec![ 148 + Value::from(value.end_byte as u32), 149 + Value::from(value.end_point.row as u32), 150 + Value::from(value.end_point.column as u32), 151 + ]); 152 + Value::from(vec![start, end]) 153 + }