aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/display/structure.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/display/structure.rs')
-rw-r--r--crates/ra_ide/src/display/structure.rs438
1 files changed, 0 insertions, 438 deletions
diff --git a/crates/ra_ide/src/display/structure.rs b/crates/ra_ide/src/display/structure.rs
deleted file mode 100644
index aad5a8e4d..000000000
--- a/crates/ra_ide/src/display/structure.rs
+++ /dev/null
@@ -1,438 +0,0 @@
1use ra_syntax::{
2 ast::{self, AttrsOwner, NameOwner, TypeAscriptionOwner, TypeParamsOwner},
3 match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, WalkEvent,
4};
5
6#[derive(Debug, Clone)]
7pub struct StructureNode {
8 pub parent: Option<usize>,
9 pub label: String,
10 pub navigation_range: TextRange,
11 pub node_range: TextRange,
12 pub kind: SyntaxKind,
13 pub detail: Option<String>,
14 pub deprecated: bool,
15}
16
17// Feature: File Structure
18//
19// Provides a tree of the symbols defined in the file. Can be used to
20//
21// * fuzzy search symbol in a file (super useful)
22// * draw breadcrumbs to describe the context around the cursor
23// * draw outline of the file
24//
25// |===
26// | Editor | Shortcut
27//
28// | VS Code | kbd:[Ctrl+Shift+O]
29// |===
30pub fn file_structure(file: &SourceFile) -> Vec<StructureNode> {
31 let mut res = Vec::new();
32 let mut stack = Vec::new();
33
34 for event in file.syntax().preorder() {
35 match event {
36 WalkEvent::Enter(node) => {
37 if let Some(mut symbol) = structure_node(&node) {
38 symbol.parent = stack.last().copied();
39 stack.push(res.len());
40 res.push(symbol);
41 }
42 }
43 WalkEvent::Leave(node) => {
44 if structure_node(&node).is_some() {
45 stack.pop().unwrap();
46 }
47 }
48 }
49 }
50 res
51}
52
53fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
54 fn decl<N: NameOwner + AttrsOwner>(node: N) -> Option<StructureNode> {
55 decl_with_detail(node, None)
56 }
57
58 fn decl_with_ascription<N: NameOwner + AttrsOwner + TypeAscriptionOwner>(
59 node: N,
60 ) -> Option<StructureNode> {
61 let ty = node.ascribed_type();
62 decl_with_type_ref(node, ty)
63 }
64
65 fn decl_with_type_ref<N: NameOwner + AttrsOwner>(
66 node: N,
67 type_ref: Option<ast::TypeRef>,
68 ) -> Option<StructureNode> {
69 let detail = type_ref.map(|type_ref| {
70 let mut detail = String::new();
71 collapse_ws(type_ref.syntax(), &mut detail);
72 detail
73 });
74 decl_with_detail(node, detail)
75 }
76
77 fn decl_with_detail<N: NameOwner + AttrsOwner>(
78 node: N,
79 detail: Option<String>,
80 ) -> Option<StructureNode> {
81 let name = node.name()?;
82
83 Some(StructureNode {
84 parent: None,
85 label: name.text().to_string(),
86 navigation_range: name.syntax().text_range(),
87 node_range: node.syntax().text_range(),
88 kind: node.syntax().kind(),
89 detail,
90 deprecated: node.attrs().filter_map(|x| x.simple_name()).any(|x| x == "deprecated"),
91 })
92 }
93
94 fn collapse_ws(node: &SyntaxNode, output: &mut String) {
95 let mut can_insert_ws = false;
96 node.text().for_each_chunk(|chunk| {
97 for line in chunk.lines() {
98 let line = line.trim();
99 if line.is_empty() {
100 if can_insert_ws {
101 output.push(' ');
102 can_insert_ws = false;
103 }
104 } else {
105 output.push_str(line);
106 can_insert_ws = true;
107 }
108 }
109 })
110 }
111
112 match_ast! {
113 match node {
114 ast::FnDef(it) => {
115 let mut detail = String::from("fn");
116 if let Some(type_param_list) = it.type_param_list() {
117 collapse_ws(type_param_list.syntax(), &mut detail);
118 }
119 if let Some(param_list) = it.param_list() {
120 collapse_ws(param_list.syntax(), &mut detail);
121 }
122 if let Some(ret_type) = it.ret_type() {
123 detail.push_str(" ");
124 collapse_ws(ret_type.syntax(), &mut detail);
125 }
126
127 decl_with_detail(it, Some(detail))
128 },
129 ast::StructDef(it) => decl(it),
130 ast::EnumDef(it) => decl(it),
131 ast::EnumVariant(it) => decl(it),
132 ast::TraitDef(it) => decl(it),
133 ast::Module(it) => decl(it),
134 ast::TypeAliasDef(it) => {
135 let ty = it.type_ref();
136 decl_with_type_ref(it, ty)
137 },
138 ast::RecordFieldDef(it) => decl_with_ascription(it),
139 ast::ConstDef(it) => decl_with_ascription(it),
140 ast::StaticDef(it) => decl_with_ascription(it),
141 ast::ImplDef(it) => {
142 let target_type = it.target_type()?;
143 let target_trait = it.target_trait();
144 let label = match target_trait {
145 None => format!("impl {}", target_type.syntax().text()),
146 Some(t) => {
147 format!("impl {} for {}", t.syntax().text(), target_type.syntax().text(),)
148 }
149 };
150
151 let node = StructureNode {
152 parent: None,
153 label,
154 navigation_range: target_type.syntax().text_range(),
155 node_range: it.syntax().text_range(),
156 kind: it.syntax().kind(),
157 detail: None,
158 deprecated: false,
159 };
160 Some(node)
161 },
162 ast::MacroCall(it) => {
163 match it.path().and_then(|it| it.segment()).and_then(|it| it.name_ref()) {
164 Some(path_segment) if path_segment.text() == "macro_rules"
165 => decl(it),
166 _ => None,
167 }
168 },
169 _ => None,
170 }
171 }
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177 use insta::assert_debug_snapshot;
178
179 #[test]
180 fn test_file_structure() {
181 let file = SourceFile::parse(
182 r#"
183struct Foo {
184 x: i32
185}
186
187mod m {
188 fn bar1() {}
189 fn bar2<T>(t: T) -> T {}
190 fn bar3<A,
191 B>(a: A,
192 b: B) -> Vec<
193 u32
194 > {}
195}
196
197enum E { X, Y(i32) }
198type T = ();
199static S: i32 = 92;
200const C: i32 = 92;
201
202impl E {}
203
204impl fmt::Debug for E {}
205
206macro_rules! mc {
207 () => {}
208}
209
210#[macro_export]
211macro_rules! mcexp {
212 () => {}
213}
214
215/// Doc comment
216macro_rules! mcexp {
217 () => {}
218}
219
220#[deprecated]
221fn obsolete() {}
222
223#[deprecated(note = "for awhile")]
224fn very_obsolete() {}
225"#,
226 )
227 .ok()
228 .unwrap();
229 let structure = file_structure(&file);
230 assert_debug_snapshot!(structure,
231 @r###"
232 [
233 StructureNode {
234 parent: None,
235 label: "Foo",
236 navigation_range: 8..11,
237 node_range: 1..26,
238 kind: STRUCT_DEF,
239 detail: None,
240 deprecated: false,
241 },
242 StructureNode {
243 parent: Some(
244 0,
245 ),
246 label: "x",
247 navigation_range: 18..19,
248 node_range: 18..24,
249 kind: RECORD_FIELD_DEF,
250 detail: Some(
251 "i32",
252 ),
253 deprecated: false,
254 },
255 StructureNode {
256 parent: None,
257 label: "m",
258 navigation_range: 32..33,
259 node_range: 28..158,
260 kind: MODULE,
261 detail: None,
262 deprecated: false,
263 },
264 StructureNode {
265 parent: Some(
266 2,
267 ),
268 label: "bar1",
269 navigation_range: 43..47,
270 node_range: 40..52,
271 kind: FN_DEF,
272 detail: Some(
273 "fn()",
274 ),
275 deprecated: false,
276 },
277 StructureNode {
278 parent: Some(
279 2,
280 ),
281 label: "bar2",
282 navigation_range: 60..64,
283 node_range: 57..81,
284 kind: FN_DEF,
285 detail: Some(
286 "fn<T>(t: T) -> T",
287 ),
288 deprecated: false,
289 },
290 StructureNode {
291 parent: Some(
292 2,
293 ),
294 label: "bar3",
295 navigation_range: 89..93,
296 node_range: 86..156,
297 kind: FN_DEF,
298 detail: Some(
299 "fn<A, B>(a: A, b: B) -> Vec< u32 >",
300 ),
301 deprecated: false,
302 },
303 StructureNode {
304 parent: None,
305 label: "E",
306 navigation_range: 165..166,
307 node_range: 160..180,
308 kind: ENUM_DEF,
309 detail: None,
310 deprecated: false,
311 },
312 StructureNode {
313 parent: Some(
314 6,
315 ),
316 label: "X",
317 navigation_range: 169..170,
318 node_range: 169..170,
319 kind: ENUM_VARIANT,
320 detail: None,
321 deprecated: false,
322 },
323 StructureNode {
324 parent: Some(
325 6,
326 ),
327 label: "Y",
328 navigation_range: 172..173,
329 node_range: 172..178,
330 kind: ENUM_VARIANT,
331 detail: None,
332 deprecated: false,
333 },
334 StructureNode {
335 parent: None,
336 label: "T",
337 navigation_range: 186..187,
338 node_range: 181..193,
339 kind: TYPE_ALIAS_DEF,
340 detail: Some(
341 "()",
342 ),
343 deprecated: false,
344 },
345 StructureNode {
346 parent: None,
347 label: "S",
348 navigation_range: 201..202,
349 node_range: 194..213,
350 kind: STATIC_DEF,
351 detail: Some(
352 "i32",
353 ),
354 deprecated: false,
355 },
356 StructureNode {
357 parent: None,
358 label: "C",
359 navigation_range: 220..221,
360 node_range: 214..232,
361 kind: CONST_DEF,
362 detail: Some(
363 "i32",
364 ),
365 deprecated: false,
366 },
367 StructureNode {
368 parent: None,
369 label: "impl E",
370 navigation_range: 239..240,
371 node_range: 234..243,
372 kind: IMPL_DEF,
373 detail: None,
374 deprecated: false,
375 },
376 StructureNode {
377 parent: None,
378 label: "impl fmt::Debug for E",
379 navigation_range: 265..266,
380 node_range: 245..269,
381 kind: IMPL_DEF,
382 detail: None,
383 deprecated: false,
384 },
385 StructureNode {
386 parent: None,
387 label: "mc",
388 navigation_range: 284..286,
389 node_range: 271..303,
390 kind: MACRO_CALL,
391 detail: None,
392 deprecated: false,
393 },
394 StructureNode {
395 parent: None,
396 label: "mcexp",
397 navigation_range: 334..339,
398 node_range: 305..356,
399 kind: MACRO_CALL,
400 detail: None,
401 deprecated: false,
402 },
403 StructureNode {
404 parent: None,
405 label: "mcexp",
406 navigation_range: 387..392,
407 node_range: 358..409,
408 kind: MACRO_CALL,
409 detail: None,
410 deprecated: false,
411 },
412 StructureNode {
413 parent: None,
414 label: "obsolete",
415 navigation_range: 428..436,
416 node_range: 411..441,
417 kind: FN_DEF,
418 detail: Some(
419 "fn()",
420 ),
421 deprecated: true,
422 },
423 StructureNode {
424 parent: None,
425 label: "very_obsolete",
426 navigation_range: 481..494,
427 node_range: 443..499,
428 kind: FN_DEF,
429 detail: Some(
430 "fn()",
431 ),
432 deprecated: true,
433 },
434 ]
435 "###
436 );
437 }
438}