diff options
-rw-r--r-- | crates/cli/src/main.rs | 4 | ||||
-rw-r--r-- | crates/libeditor/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/libeditor/src/symbols.rs | 82 | ||||
-rw-r--r-- | crates/libeditor/tests/test.rs | 26 | ||||
-rw-r--r-- | crates/libsyntax2/src/ast/generated.rs | 18 | ||||
-rw-r--r-- | crates/libsyntax2/src/ast/mod.rs | 5 | ||||
-rw-r--r-- | crates/libsyntax2/src/grammar.ron | 1 | ||||
-rw-r--r-- | crates/server/src/conv.rs | 1 | ||||
-rw-r--r-- | crates/server/src/main_loop/handlers.rs | 31 |
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 | }; |
11 | use clap::{App, Arg, SubCommand}; | 11 | use clap::{App, Arg, SubCommand}; |
12 | use tools::collect_tests; | 12 | use tools::collect_tests; |
13 | use libeditor::{File, syntax_tree, file_symbols}; | 13 | use libeditor::{File, syntax_tree, file_structure}; |
14 | 14 | ||
15 | type Result<T> = ::std::result::Result<T, failure::Error>; | 15 | type 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}; | |||
19 | pub use self::{ | 19 | pub 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 | }; |
10 | use TextRange; | 11 | use TextRange; |
11 | 12 | ||
12 | #[derive(Debug)] | 13 | #[derive(Debug)] |
13 | pub struct FileSymbol { | 14 | pub 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)] | ||
23 | pub struct FileSymbol { | ||
24 | pub name: SmolStr, | ||
25 | pub node_range: TextRange, | ||
26 | pub kind: SyntaxKind, | ||
27 | } | ||
21 | 28 | ||
22 | pub fn file_symbols(file: &ast::File) -> Vec<FileSymbol> { | 29 | pub 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 | |||
36 | fn 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 | |||
58 | pub 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 | ||
49 | fn to_symbol(node: SyntaxNodeRef) -> Option<FileSymbol> { | 85 | fn 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; | |||
9 | use libsyntax2::AstNode; | 9 | use libsyntax2::AstNode; |
10 | use libeditor::{ | 10 | use 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] |
69 | fn symbols() { | 69 | fn test_structure() { |
70 | let file = file(r#" | 70 | let file = file(r#" |
71 | struct Foo { | 71 | struct Foo { |
72 | x: i32 | 72 | x: i32 |
@@ -80,16 +80,22 @@ enum E { X, Y(i32) } | |||
80 | type T = (); | 80 | type T = (); |
81 | static S: i32 = 92; | 81 | static S: i32 = 92; |
82 | const C: i32 = 92; | 82 | const C: i32 = 92; |
83 | |||
84 | impl E {} | ||
85 | |||
86 | impl 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> { | |||
86 | impl<R: TreeRoot> ast::NameOwner<R> for FnDef<R> {} | 86 | impl<R: TreeRoot> ast::NameOwner<R> for FnDef<R> {} |
87 | impl<R: TreeRoot> FnDef<R> {} | 87 | impl<R: TreeRoot> FnDef<R> {} |
88 | 88 | ||
89 | // ImplItem | ||
90 | #[derive(Debug, Clone, Copy)] | ||
91 | pub struct ImplItem<R: TreeRoot = Arc<SyntaxRoot>> { | ||
92 | syntax: SyntaxNode<R>, | ||
93 | } | ||
94 | |||
95 | impl<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 | |||
105 | impl<R: TreeRoot> ImplItem<R> {} | ||
106 | |||
89 | // Module | 107 | // Module |
90 | #[derive(Debug, Clone, Copy)] | 108 | #[derive(Debug, Clone, Copy)] |
91 | pub struct Module<R: TreeRoot = Arc<SyntaxRoot>> { | 109 | pub 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 | }; |
11 | pub use self::generated::*; | 11 | pub use self::generated::*; |
12 | 12 | ||
13 | pub trait AstNode<R: TreeRoot>: Sized { | 13 | pub 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 | ||