intelligent ctags powered by tree-sitter and lexical scope-graphs
at main 7.0 kB view raw
1use std::fmt; 2 3use crate::stag::{EdgeKind, Node}; 4use crate::text_range::TextRange; 5 6use petgraph::{ 7 graph::{Graph, NodeIndex}, 8 visit::EdgeRef, 9 Direction, 10}; 11 12pub struct ScopeDebug { 13 range: TextRange, 14 defs: Vec<DefDebug>, 15 imports: Vec<ImportDebug>, 16 scopes: Vec<ScopeDebug>, 17} 18 19struct DefDebug { 20 name: String, 21 range: TextRange, 22 context: String, 23 refs: Vec<RefDebug>, 24} 25 26struct RefDebug { 27 context: String, 28} 29 30struct ImportDebug { 31 name: String, 32 range: TextRange, 33 context: String, 34 refs: Vec<RefDebug>, 35} 36 37impl 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 52impl 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 67impl 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<Node, 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<Node, 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 162impl 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 179impl 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 193impl 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 207impl fmt::Debug for RefDebug { 208 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 209 write!(f, "`{}`", self.context) 210 } 211} 212 213fn 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}