aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_editor/src/structure.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_editor/src/structure.rs')
-rw-r--r--crates/ra_editor/src/structure.rs129
1 files changed, 129 insertions, 0 deletions
diff --git a/crates/ra_editor/src/structure.rs b/crates/ra_editor/src/structure.rs
new file mode 100644
index 000000000..2292b1ddf
--- /dev/null
+++ b/crates/ra_editor/src/structure.rs
@@ -0,0 +1,129 @@
1use crate::TextRange;
2
3use ra_syntax::{
4 algo::visit::{visitor, Visitor},
5 ast::{self, NameOwner},
6 AstNode, SourceFileNode, SyntaxKind, SyntaxNodeRef, WalkEvent,
7};
8
9#[derive(Debug, Clone)]
10pub struct StructureNode {
11 pub parent: Option<usize>,
12 pub label: String,
13 pub navigation_range: TextRange,
14 pub node_range: TextRange,
15 pub kind: SyntaxKind,
16}
17
18pub fn file_structure(file: &SourceFileNode) -> Vec<StructureNode> {
19 let mut res = Vec::new();
20 let mut stack = Vec::new();
21
22 for event in file.syntax().preorder() {
23 match event {
24 WalkEvent::Enter(node) => {
25 if let Some(mut symbol) = structure_node(node) {
26 symbol.parent = stack.last().map(|&n| n);
27 stack.push(res.len());
28 res.push(symbol);
29 }
30 }
31 WalkEvent::Leave(node) => {
32 if structure_node(node).is_some() {
33 stack.pop().unwrap();
34 }
35 }
36 }
37 }
38 res
39}
40
41fn structure_node(node: SyntaxNodeRef) -> Option<StructureNode> {
42 fn decl<'a, N: NameOwner<'a>>(node: N) -> Option<StructureNode> {
43 let name = node.name()?;
44 Some(StructureNode {
45 parent: None,
46 label: name.text().to_string(),
47 navigation_range: name.syntax().range(),
48 node_range: node.syntax().range(),
49 kind: node.syntax().kind(),
50 })
51 }
52
53 visitor()
54 .visit(decl::<ast::FnDef>)
55 .visit(decl::<ast::StructDef>)
56 .visit(decl::<ast::NamedFieldDef>)
57 .visit(decl::<ast::EnumDef>)
58 .visit(decl::<ast::TraitDef>)
59 .visit(decl::<ast::Module>)
60 .visit(decl::<ast::TypeDef>)
61 .visit(decl::<ast::ConstDef>)
62 .visit(decl::<ast::StaticDef>)
63 .visit(|im: ast::ImplItem| {
64 let target_type = im.target_type()?;
65 let target_trait = im.target_trait();
66 let label = match target_trait {
67 None => format!("impl {}", target_type.syntax().text()),
68 Some(t) => format!(
69 "impl {} for {}",
70 t.syntax().text(),
71 target_type.syntax().text(),
72 ),
73 };
74
75 let node = StructureNode {
76 parent: None,
77 label,
78 navigation_range: target_type.syntax().range(),
79 node_range: im.syntax().range(),
80 kind: im.syntax().kind(),
81 };
82 Some(node)
83 })
84 .accept(node)?
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90 use test_utils::assert_eq_dbg;
91
92 #[test]
93 fn test_file_structure() {
94 let file = SourceFileNode::parse(
95 r#"
96struct Foo {
97 x: i32
98}
99
100mod m {
101 fn bar() {}
102}
103
104enum E { X, Y(i32) }
105type T = ();
106static S: i32 = 92;
107const C: i32 = 92;
108
109impl E {}
110
111impl fmt::Debug for E {}
112"#,
113 );
114 let structure = file_structure(&file);
115 assert_eq_dbg(
116 r#"[StructureNode { parent: None, label: "Foo", navigation_range: [8; 11), node_range: [1; 26), kind: STRUCT_DEF },
117 StructureNode { parent: Some(0), label: "x", navigation_range: [18; 19), node_range: [18; 24), kind: NAMED_FIELD_DEF },
118 StructureNode { parent: None, label: "m", navigation_range: [32; 33), node_range: [28; 53), kind: MODULE },
119 StructureNode { parent: Some(2), label: "bar", navigation_range: [43; 46), node_range: [40; 51), kind: FN_DEF },
120 StructureNode { parent: None, label: "E", navigation_range: [60; 61), node_range: [55; 75), kind: ENUM_DEF },
121 StructureNode { parent: None, label: "T", navigation_range: [81; 82), node_range: [76; 88), kind: TYPE_DEF },
122 StructureNode { parent: None, label: "S", navigation_range: [96; 97), node_range: [89; 108), kind: STATIC_DEF },
123 StructureNode { parent: None, label: "C", navigation_range: [115; 116), node_range: [109; 127), kind: CONST_DEF },
124 StructureNode { parent: None, label: "impl E", navigation_range: [134; 135), node_range: [129; 138), kind: IMPL_ITEM },
125 StructureNode { parent: None, label: "impl fmt::Debug for E", navigation_range: [160; 161), node_range: [140; 164), kind: IMPL_ITEM }]"#,
126 &structure,
127 )
128 }
129}