aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/cli/src/main.rs4
-rw-r--r--crates/libeditor/src/lib.rs2
-rw-r--r--crates/libeditor/src/symbols.rs82
-rw-r--r--crates/libeditor/tests/test.rs26
-rw-r--r--crates/libsyntax2/src/ast/generated.rs18
-rw-r--r--crates/libsyntax2/src/ast/mod.rs5
-rw-r--r--crates/libsyntax2/src/grammar.ron1
-rw-r--r--crates/server/src/conv.rs1
-rw-r--r--crates/server/src/main_loop/handlers.rs31
9 files changed, 131 insertions, 39 deletions
diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs
index b6e9139c7..fc28691fc 100644
--- a/crates/cli/src/main.rs
+++ b/crates/cli/src/main.rs
@@ -10,7 +10,7 @@ use std::{
10}; 10};
11use clap::{App, Arg, SubCommand}; 11use clap::{App, Arg, SubCommand};
12use tools::collect_tests; 12use tools::collect_tests;
13use libeditor::{File, syntax_tree, file_symbols}; 13use libeditor::{File, syntax_tree, file_structure};
14 14
15type Result<T> = ::std::result::Result<T, failure::Error>; 15type Result<T> = ::std::result::Result<T, failure::Error>;
16 16
@@ -51,7 +51,7 @@ fn main() -> Result<()> {
51 } 51 }
52 ("symbols", _) => { 52 ("symbols", _) => {
53 let file = file()?; 53 let file = file()?;
54 for s in file_symbols(&file) { 54 for s in file_structure(&file) {
55 println!("{:?}", s); 55 println!("{:?}", s);
56 } 56 }
57 } 57 }
diff --git a/crates/libeditor/src/lib.rs b/crates/libeditor/src/lib.rs
index e5933cbd6..b40db2c66 100644
--- a/crates/libeditor/src/lib.rs
+++ b/crates/libeditor/src/lib.rs
@@ -19,7 +19,7 @@ pub use libsyntax2::{File, TextRange, TextUnit};
19pub use self::{ 19pub use self::{
20 line_index::{LineIndex, LineCol}, 20 line_index::{LineIndex, LineCol},
21 extend_selection::extend_selection, 21 extend_selection::extend_selection,
22 symbols::{FileSymbol, file_symbols}, 22 symbols::{StructureNode, file_structure, FileSymbol, file_symbols},
23 edit::{EditBuilder, Edit, AtomEdit}, 23 edit::{EditBuilder, Edit, AtomEdit},
24 code_actions::{flip_comma}, 24 code_actions::{flip_comma},
25}; 25};
diff --git a/crates/libeditor/src/symbols.rs b/crates/libeditor/src/symbols.rs
index f1d5222f4..43f4164da 100644
--- a/crates/libeditor/src/symbols.rs
+++ b/crates/libeditor/src/symbols.rs
@@ -4,22 +4,58 @@ use libsyntax2::{
4 ast::{self, NameOwner}, 4 ast::{self, NameOwner},
5 algo::{ 5 algo::{
6 visit::{visitor, Visitor}, 6 visit::{visitor, Visitor},
7 walk::{walk, WalkEvent}, 7 walk::{walk, WalkEvent, preorder},
8 }, 8 },
9 SyntaxKind::*,
9}; 10};
10use TextRange; 11use TextRange;
11 12
12#[derive(Debug)] 13#[derive(Debug)]
13pub struct FileSymbol { 14pub struct StructureNode {
14 pub parent: Option<usize>, 15 pub parent: Option<usize>,
15 pub name: SmolStr, 16 pub label: String,
16 pub name_range: TextRange, 17 pub navigation_range: TextRange,
17 pub node_range: TextRange, 18 pub node_range: TextRange,
18 pub kind: SyntaxKind, 19 pub kind: SyntaxKind,
19} 20}
20 21
22#[derive(Debug)]
23pub struct FileSymbol {
24 pub name: SmolStr,
25 pub node_range: TextRange,
26 pub kind: SyntaxKind,
27}
21 28
22pub fn file_symbols(file: &ast::File) -> Vec<FileSymbol> { 29pub fn file_symbols(file: &ast::File) -> Vec<FileSymbol> {
30 let syntax = file.syntax();
31 preorder(syntax.as_ref())
32 .filter_map(to_symbol)
33 .collect()
34}
35
36fn to_symbol(node: SyntaxNodeRef) -> Option<FileSymbol> {
37 fn decl<'a, N: NameOwner<&'a SyntaxRoot>>(node: N) -> Option<FileSymbol> {
38 let name = node.name()?;
39 Some(FileSymbol {
40 name: name.text(),
41 node_range: node.syntax().range(),
42 kind: node.syntax().kind(),
43 })
44 }
45 visitor()
46 .visit(decl::<ast::FnDef<_>>)
47 .visit(decl::<ast::StructDef<_>>)
48 .visit(decl::<ast::EnumDef<_>>)
49 .visit(decl::<ast::TraitDef<_>>)
50 .visit(decl::<ast::Module<_>>)
51 .visit(decl::<ast::TypeDef<_>>)
52 .visit(decl::<ast::ConstDef<_>>)
53 .visit(decl::<ast::StaticDef<_>>)
54 .accept(node)?
55}
56
57
58pub fn file_structure(file: &ast::File) -> Vec<StructureNode> {
23 let mut res = Vec::new(); 59 let mut res = Vec::new();
24 let mut stack = Vec::new(); 60 let mut stack = Vec::new();
25 let syntax = file.syntax(); 61 let syntax = file.syntax();
@@ -27,7 +63,7 @@ pub fn file_symbols(file: &ast::File) -> Vec<FileSymbol> {
27 for event in walk(syntax.as_ref()) { 63 for event in walk(syntax.as_ref()) {
28 match event { 64 match event {
29 WalkEvent::Enter(node) => { 65 WalkEvent::Enter(node) => {
30 match to_symbol(node) { 66 match structure_node(node) {
31 Some(mut symbol) => { 67 Some(mut symbol) => {
32 symbol.parent = stack.last().map(|&n| n); 68 symbol.parent = stack.last().map(|&n| n);
33 stack.push(res.len()); 69 stack.push(res.len());
@@ -37,7 +73,7 @@ pub fn file_symbols(file: &ast::File) -> Vec<FileSymbol> {
37 } 73 }
38 } 74 }
39 WalkEvent::Exit(node) => { 75 WalkEvent::Exit(node) => {
40 if to_symbol(node).is_some() { 76 if structure_node(node).is_some() {
41 stack.pop().unwrap(); 77 stack.pop().unwrap();
42 } 78 }
43 } 79 }
@@ -46,13 +82,13 @@ pub fn file_symbols(file: &ast::File) -> Vec<FileSymbol> {
46 res 82 res
47} 83}
48 84
49fn to_symbol(node: SyntaxNodeRef) -> Option<FileSymbol> { 85fn structure_node(node: SyntaxNodeRef) -> Option<StructureNode> {
50 fn decl<'a, N: NameOwner<&'a SyntaxRoot>>(node: N) -> Option<FileSymbol> { 86 fn decl<'a, N: NameOwner<&'a SyntaxRoot>>(node: N) -> Option<StructureNode> {
51 let name = node.name()?; 87 let name = node.name()?;
52 Some(FileSymbol { 88 Some(StructureNode {
53 parent: None, 89 parent: None,
54 name: name.text(), 90 label: name.text().to_string(),
55 name_range: name.syntax().range(), 91 navigation_range: name.syntax().range(),
56 node_range: node.syntax().range(), 92 node_range: node.syntax().range(),
57 kind: node.syntax().kind(), 93 kind: node.syntax().kind(),
58 }) 94 })
@@ -67,5 +103,29 @@ fn to_symbol(node: SyntaxNodeRef) -> Option<FileSymbol> {
67 .visit(decl::<ast::TypeDef<_>>) 103 .visit(decl::<ast::TypeDef<_>>)
68 .visit(decl::<ast::ConstDef<_>>) 104 .visit(decl::<ast::ConstDef<_>>)
69 .visit(decl::<ast::StaticDef<_>>) 105 .visit(decl::<ast::StaticDef<_>>)
106 .visit(|im: ast::ImplItem<_>| {
107 let mut label = String::new();
108 let brace = im.syntax().children()
109 .find(|it| {
110 let stop = it.kind() == L_CURLY;
111 if !stop {
112 label.push_str(&it.text());
113 }
114 stop
115 })?;
116 let navigation_range = TextRange::from_to(
117 im.syntax().range().start(),
118 brace.range().start(),
119 );
120
121 let node = StructureNode {
122 parent: None,
123 label,
124 navigation_range,
125 node_range: im.syntax().range(),
126 kind: im.syntax().kind(),
127 };
128 Some(node)
129 })
70 .accept(node)? 130 .accept(node)?
71} 131}
diff --git a/crates/libeditor/tests/test.rs b/crates/libeditor/tests/test.rs
index fab2e4ad3..97fa30e1f 100644
--- a/crates/libeditor/tests/test.rs
+++ b/crates/libeditor/tests/test.rs
@@ -9,7 +9,7 @@ use itertools::Itertools;
9use libsyntax2::AstNode; 9use libsyntax2::AstNode;
10use libeditor::{ 10use libeditor::{
11 File, TextUnit, TextRange, 11 File, TextUnit, TextRange,
12 highlight, runnables, extend_selection, file_symbols, flip_comma, 12 highlight, runnables, extend_selection, file_structure, flip_comma,
13}; 13};
14 14
15#[test] 15#[test]
@@ -66,7 +66,7 @@ fn test_foo() {}
66} 66}
67 67
68#[test] 68#[test]
69fn symbols() { 69fn test_structure() {
70 let file = file(r#" 70 let file = file(r#"
71struct Foo { 71struct Foo {
72 x: i32 72 x: i32
@@ -80,16 +80,22 @@ enum E { X, Y(i32) }
80type T = (); 80type T = ();
81static S: i32 = 92; 81static S: i32 = 92;
82const C: i32 = 92; 82const C: i32 = 92;
83
84impl E {}
85
86impl fmt::Debug for E {}
83"#); 87"#);
84 let symbols = file_symbols(&file); 88 let symbols = file_structure(&file);
85 dbg_eq( 89 dbg_eq(
86 r#"[FileSymbol { parent: None, name: "Foo", name_range: [8; 11), node_range: [1; 26), kind: STRUCT_DEF }, 90 r#"[StructureNode { parent: None, label: "Foo", navigation_range: [8; 11), node_range: [1; 26), kind: STRUCT_DEF },
87 FileSymbol { parent: None, name: "m", name_range: [32; 33), node_range: [28; 53), kind: MODULE }, 91 StructureNode { parent: None, label: "m", navigation_range: [32; 33), node_range: [28; 53), kind: MODULE },
88 FileSymbol { parent: Some(1), name: "bar", name_range: [43; 46), node_range: [40; 51), kind: FN_DEF }, 92 StructureNode { parent: Some(1), label: "bar", navigation_range: [43; 46), node_range: [40; 51), kind: FN_DEF },
89 FileSymbol { parent: None, name: "E", name_range: [60; 61), node_range: [55; 75), kind: ENUM_DEF }, 93 StructureNode { parent: None, label: "E", navigation_range: [60; 61), node_range: [55; 75), kind: ENUM_DEF },
90 FileSymbol { parent: None, name: "T", name_range: [81; 82), node_range: [76; 88), kind: TYPE_DEF }, 94 StructureNode { parent: None, label: "T", navigation_range: [81; 82), node_range: [76; 88), kind: TYPE_DEF },
91 FileSymbol { parent: None, name: "S", name_range: [96; 97), node_range: [89; 108), kind: STATIC_DEF }, 95 StructureNode { parent: None, label: "S", navigation_range: [96; 97), node_range: [89; 108), kind: STATIC_DEF },
92 FileSymbol { parent: None, name: "C", name_range: [115; 116), node_range: [109; 127), kind: CONST_DEF }]"#, 96 StructureNode { parent: None, label: "C", navigation_range: [115; 116), node_range: [109; 127), kind: CONST_DEF },
97 StructureNode { parent: None, label: "impl E ", navigation_range: [129; 136), node_range: [129; 138), kind: IMPL_ITEM },
98 StructureNode { parent: None, label: "impl fmt::Debug for E ", navigation_range: [140; 162), node_range: [140; 164), kind: IMPL_ITEM }]"#,
93 &symbols, 99 &symbols,
94 ) 100 )
95} 101}
diff --git a/crates/libsyntax2/src/ast/generated.rs b/crates/libsyntax2/src/ast/generated.rs
index b347a05b4..13668b803 100644
--- a/crates/libsyntax2/src/ast/generated.rs
+++ b/crates/libsyntax2/src/ast/generated.rs
@@ -86,6 +86,24 @@ impl<R: TreeRoot> AstNode<R> for FnDef<R> {
86impl<R: TreeRoot> ast::NameOwner<R> for FnDef<R> {} 86impl<R: TreeRoot> ast::NameOwner<R> for FnDef<R> {}
87impl<R: TreeRoot> FnDef<R> {} 87impl<R: TreeRoot> FnDef<R> {}
88 88
89// ImplItem
90#[derive(Debug, Clone, Copy)]
91pub struct ImplItem<R: TreeRoot = Arc<SyntaxRoot>> {
92 syntax: SyntaxNode<R>,
93}
94
95impl<R: TreeRoot> AstNode<R> for ImplItem<R> {
96 fn cast(syntax: SyntaxNode<R>) -> Option<Self> {
97 match syntax.kind() {
98 IMPL_ITEM => Some(ImplItem { syntax }),
99 _ => None,
100 }
101 }
102 fn syntax(&self) -> &SyntaxNode<R> { &self.syntax }
103}
104
105impl<R: TreeRoot> ImplItem<R> {}
106
89// Module 107// Module
90#[derive(Debug, Clone, Copy)] 108#[derive(Debug, Clone, Copy)]
91pub struct Module<R: TreeRoot = Arc<SyntaxRoot>> { 109pub struct Module<R: TreeRoot = Arc<SyntaxRoot>> {
diff --git a/crates/libsyntax2/src/ast/mod.rs b/crates/libsyntax2/src/ast/mod.rs
index 18a9f5d17..f001d340e 100644
--- a/crates/libsyntax2/src/ast/mod.rs
+++ b/crates/libsyntax2/src/ast/mod.rs
@@ -10,8 +10,9 @@ use {
10}; 10};
11pub use self::generated::*; 11pub use self::generated::*;
12 12
13pub trait AstNode<R: TreeRoot>: Sized { 13pub trait AstNode<R: TreeRoot> {
14 fn cast(syntax: SyntaxNode<R>) -> Option<Self>; 14 fn cast(syntax: SyntaxNode<R>) -> Option<Self>
15 where Self: Sized;
15 fn syntax(&self) -> &SyntaxNode<R>; 16 fn syntax(&self) -> &SyntaxNode<R>;
16} 17}
17 18
diff --git a/crates/libsyntax2/src/grammar.ron b/crates/libsyntax2/src/grammar.ron
index ef56761fd..ebd7d3943 100644
--- a/crates/libsyntax2/src/grammar.ron
+++ b/crates/libsyntax2/src/grammar.ron
@@ -229,6 +229,7 @@ Grammar(
229 "ConstDef": ( traits: ["NameOwner"] ), 229 "ConstDef": ( traits: ["NameOwner"] ),
230 "StaticDef": ( traits: ["NameOwner"] ), 230 "StaticDef": ( traits: ["NameOwner"] ),
231 "TypeDef": ( traits: ["NameOwner"] ), 231 "TypeDef": ( traits: ["NameOwner"] ),
232 "ImplItem": (),
232 "Name": (), 233 "Name": (),
233 "NameRef": (), 234 "NameRef": (),
234 }, 235 },
diff --git a/crates/server/src/conv.rs b/crates/server/src/conv.rs
index 3aa255e6a..3ddce5fb3 100644
--- a/crates/server/src/conv.rs
+++ b/crates/server/src/conv.rs
@@ -36,6 +36,7 @@ impl Conv for SyntaxKind {
36 SyntaxKind::TYPE_DEF => SymbolKind::TypeParameter, 36 SyntaxKind::TYPE_DEF => SymbolKind::TypeParameter,
37 SyntaxKind::STATIC_DEF => SymbolKind::Constant, 37 SyntaxKind::STATIC_DEF => SymbolKind::Constant,
38 SyntaxKind::CONST_DEF => SymbolKind::Constant, 38 SyntaxKind::CONST_DEF => SymbolKind::Constant,
39 SyntaxKind::IMPL_ITEM => SymbolKind::Object,
39 _ => SymbolKind::Variable, 40 _ => SymbolKind::Variable,
40 } 41 }
41 } 42 }
diff --git a/crates/server/src/main_loop/handlers.rs b/crates/server/src/main_loop/handlers.rs
index bfdfcb51e..8789ef0d2 100644
--- a/crates/server/src/main_loop/handlers.rs
+++ b/crates/server/src/main_loop/handlers.rs
@@ -48,29 +48,34 @@ pub fn handle_document_symbol(
48 let file = world.file_syntax(&path)?; 48 let file = world.file_syntax(&path)?;
49 let line_index = world.file_line_index(&path)?; 49 let line_index = world.file_line_index(&path)?;
50 50
51 let mut res: Vec<DocumentSymbol> = Vec::new(); 51 let mut parents: Vec<(DocumentSymbol, Option<usize>)> = Vec::new();
52 52
53 for symbol in libeditor::file_symbols(&file) { 53 for symbol in libeditor::file_structure(&file) {
54 let name = symbol.name.to_string();
55 let doc_symbol = DocumentSymbol { 54 let doc_symbol = DocumentSymbol {
56 name: name.clone(), 55 name: symbol.label,
57 detail: Some(name), 56 detail: Some("".to_string()),
58 kind: symbol.kind.conv(), 57 kind: symbol.kind.conv(),
59 deprecated: None, 58 deprecated: None,
60 range: symbol.node_range.conv_with(&line_index), 59 range: symbol.node_range.conv_with(&line_index),
61 selection_range: symbol.name_range.conv_with(&line_index), 60 selection_range: symbol.navigation_range.conv_with(&line_index),
62 children: None, 61 children: None,
63 }; 62 };
64 if let Some(idx) = symbol.parent { 63 parents.push((doc_symbol, symbol.parent));
65 let children = &mut res[idx].children; 64 }
66 if children.is_none() { 65 let mut res = Vec::new();
67 *children = Some(Vec::new()); 66 while let Some((node, parent)) = parents.pop() {
67 match parent {
68 None => res.push(node),
69 Some(i) => {
70 let children = &mut parents[i].0.children;
71 if children.is_none() {
72 *children = Some(Vec::new());
73 }
74 children.as_mut().unwrap().push(node);
68 } 75 }
69 children.as_mut().unwrap().push(doc_symbol);
70 } else {
71 res.push(doc_symbol);
72 } 76 }
73 } 77 }
78
74 Ok(Some(req::DocumentSymbolResponse::Nested(res))) 79 Ok(Some(req::DocumentSymbolResponse::Nested(res)))
75} 80}
76 81