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.rs440
1 files changed, 0 insertions, 440 deletions
diff --git a/crates/ra_ide/src/display/structure.rs b/crates/ra_ide/src/display/structure.rs
deleted file mode 100644
index c22a5d17b..000000000
--- a/crates/ra_ide/src/display/structure.rs
+++ /dev/null
@@ -1,440 +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 expect::{expect, Expect};
177
178 use super::*;
179
180 fn check(ra_fixture: &str, expect: Expect) {
181 let file = SourceFile::parse(ra_fixture).ok().unwrap();
182 let structure = file_structure(&file);
183 expect.assert_debug_eq(&structure)
184 }
185
186 #[test]
187 fn test_file_structure() {
188 check(
189 r#"
190struct Foo {
191 x: i32
192}
193
194mod m {
195 fn bar1() {}
196 fn bar2<T>(t: T) -> T {}
197 fn bar3<A,
198 B>(a: A,
199 b: B) -> Vec<
200 u32
201 > {}
202}
203
204enum E { X, Y(i32) }
205type T = ();
206static S: i32 = 92;
207const C: i32 = 92;
208
209impl E {}
210
211impl fmt::Debug for E {}
212
213macro_rules! mc {
214 () => {}
215}
216
217#[macro_export]
218macro_rules! mcexp {
219 () => {}
220}
221
222/// Doc comment
223macro_rules! mcexp {
224 () => {}
225}
226
227#[deprecated]
228fn obsolete() {}
229
230#[deprecated(note = "for awhile")]
231fn very_obsolete() {}
232"#,
233 expect![[r#"
234 [
235 StructureNode {
236 parent: None,
237 label: "Foo",
238 navigation_range: 8..11,
239 node_range: 1..26,
240 kind: STRUCT_DEF,
241 detail: None,
242 deprecated: false,
243 },
244 StructureNode {
245 parent: Some(
246 0,
247 ),
248 label: "x",
249 navigation_range: 18..19,
250 node_range: 18..24,
251 kind: RECORD_FIELD_DEF,
252 detail: Some(
253 "i32",
254 ),
255 deprecated: false,
256 },
257 StructureNode {
258 parent: None,
259 label: "m",
260 navigation_range: 32..33,
261 node_range: 28..158,
262 kind: MODULE,
263 detail: None,
264 deprecated: false,
265 },
266 StructureNode {
267 parent: Some(
268 2,
269 ),
270 label: "bar1",
271 navigation_range: 43..47,
272 node_range: 40..52,
273 kind: FN_DEF,
274 detail: Some(
275 "fn()",
276 ),
277 deprecated: false,
278 },
279 StructureNode {
280 parent: Some(
281 2,
282 ),
283 label: "bar2",
284 navigation_range: 60..64,
285 node_range: 57..81,
286 kind: FN_DEF,
287 detail: Some(
288 "fn<T>(t: T) -> T",
289 ),
290 deprecated: false,
291 },
292 StructureNode {
293 parent: Some(
294 2,
295 ),
296 label: "bar3",
297 navigation_range: 89..93,
298 node_range: 86..156,
299 kind: FN_DEF,
300 detail: Some(
301 "fn<A, B>(a: A, b: B) -> Vec< u32 >",
302 ),
303 deprecated: false,
304 },
305 StructureNode {
306 parent: None,
307 label: "E",
308 navigation_range: 165..166,
309 node_range: 160..180,
310 kind: ENUM_DEF,
311 detail: None,
312 deprecated: false,
313 },
314 StructureNode {
315 parent: Some(
316 6,
317 ),
318 label: "X",
319 navigation_range: 169..170,
320 node_range: 169..170,
321 kind: ENUM_VARIANT,
322 detail: None,
323 deprecated: false,
324 },
325 StructureNode {
326 parent: Some(
327 6,
328 ),
329 label: "Y",
330 navigation_range: 172..173,
331 node_range: 172..178,
332 kind: ENUM_VARIANT,
333 detail: None,
334 deprecated: false,
335 },
336 StructureNode {
337 parent: None,
338 label: "T",
339 navigation_range: 186..187,
340 node_range: 181..193,
341 kind: TYPE_ALIAS_DEF,
342 detail: Some(
343 "()",
344 ),
345 deprecated: false,
346 },
347 StructureNode {
348 parent: None,
349 label: "S",
350 navigation_range: 201..202,
351 node_range: 194..213,
352 kind: STATIC_DEF,
353 detail: Some(
354 "i32",
355 ),
356 deprecated: false,
357 },
358 StructureNode {
359 parent: None,
360 label: "C",
361 navigation_range: 220..221,
362 node_range: 214..232,
363 kind: CONST_DEF,
364 detail: Some(
365 "i32",
366 ),
367 deprecated: false,
368 },
369 StructureNode {
370 parent: None,
371 label: "impl E",
372 navigation_range: 239..240,
373 node_range: 234..243,
374 kind: IMPL_DEF,
375 detail: None,
376 deprecated: false,
377 },
378 StructureNode {
379 parent: None,
380 label: "impl fmt::Debug for E",
381 navigation_range: 265..266,
382 node_range: 245..269,
383 kind: IMPL_DEF,
384 detail: None,
385 deprecated: false,
386 },
387 StructureNode {
388 parent: None,
389 label: "mc",
390 navigation_range: 284..286,
391 node_range: 271..303,
392 kind: MACRO_CALL,
393 detail: None,
394 deprecated: false,
395 },
396 StructureNode {
397 parent: None,
398 label: "mcexp",
399 navigation_range: 334..339,
400 node_range: 305..356,
401 kind: MACRO_CALL,
402 detail: None,
403 deprecated: false,
404 },
405 StructureNode {
406 parent: None,
407 label: "mcexp",
408 navigation_range: 387..392,
409 node_range: 358..409,
410 kind: MACRO_CALL,
411 detail: None,
412 deprecated: false,
413 },
414 StructureNode {
415 parent: None,
416 label: "obsolete",
417 navigation_range: 428..436,
418 node_range: 411..441,
419 kind: FN_DEF,
420 detail: Some(
421 "fn()",
422 ),
423 deprecated: true,
424 },
425 StructureNode {
426 parent: None,
427 label: "very_obsolete",
428 navigation_range: 481..494,
429 node_range: 443..499,
430 kind: FN_DEF,
431 detail: Some(
432 "fn()",
433 ),
434 deprecated: true,
435 },
436 ]
437 "#]],
438 );
439 }
440}