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