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}