diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_ide_api/src/navigation_target.rs | 11 | ||||
-rw-r--r-- | crates/ra_ide_api/src/symbol_index.rs | 62 | ||||
-rw-r--r-- | crates/ra_ide_api/tests/test/main.rs | 58 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop/handlers.rs | 2 |
4 files changed, 116 insertions, 17 deletions
diff --git a/crates/ra_ide_api/src/navigation_target.rs b/crates/ra_ide_api/src/navigation_target.rs index a2e4b6506..bcacbe6fc 100644 --- a/crates/ra_ide_api/src/navigation_target.rs +++ b/crates/ra_ide_api/src/navigation_target.rs | |||
@@ -19,6 +19,7 @@ pub struct NavigationTarget { | |||
19 | kind: SyntaxKind, | 19 | kind: SyntaxKind, |
20 | full_range: TextRange, | 20 | full_range: TextRange, |
21 | focus_range: Option<TextRange>, | 21 | focus_range: Option<TextRange>, |
22 | container_name: Option<SmolStr>, | ||
22 | } | 23 | } |
23 | 24 | ||
24 | impl NavigationTarget { | 25 | impl NavigationTarget { |
@@ -26,6 +27,10 @@ impl NavigationTarget { | |||
26 | &self.name | 27 | &self.name |
27 | } | 28 | } |
28 | 29 | ||
30 | pub fn container_name(&self) -> Option<&SmolStr> { | ||
31 | self.container_name.as_ref() | ||
32 | } | ||
33 | |||
29 | pub fn kind(&self) -> SyntaxKind { | 34 | pub fn kind(&self) -> SyntaxKind { |
30 | self.kind | 35 | self.kind |
31 | } | 36 | } |
@@ -53,6 +58,7 @@ impl NavigationTarget { | |||
53 | kind: symbol.ptr.kind(), | 58 | kind: symbol.ptr.kind(), |
54 | full_range: symbol.ptr.range(), | 59 | full_range: symbol.ptr.range(), |
55 | focus_range: None, | 60 | focus_range: None, |
61 | container_name: symbol.container_name.map(|v| v.clone()), | ||
56 | } | 62 | } |
57 | } | 63 | } |
58 | 64 | ||
@@ -67,6 +73,7 @@ impl NavigationTarget { | |||
67 | full_range: ptr.range(), | 73 | full_range: ptr.range(), |
68 | focus_range: None, | 74 | focus_range: None, |
69 | kind: NAME, | 75 | kind: NAME, |
76 | container_name: None, | ||
70 | } | 77 | } |
71 | } | 78 | } |
72 | 79 | ||
@@ -170,6 +177,9 @@ impl NavigationTarget { | |||
170 | if let Some(focus_range) = self.focus_range() { | 177 | if let Some(focus_range) = self.focus_range() { |
171 | buf.push_str(&format!(" {:?}", focus_range)) | 178 | buf.push_str(&format!(" {:?}", focus_range)) |
172 | } | 179 | } |
180 | if let Some(container_name) = self.container_name() { | ||
181 | buf.push_str(&format!(" {:?}", container_name)) | ||
182 | } | ||
173 | buf | 183 | buf |
174 | } | 184 | } |
175 | 185 | ||
@@ -192,6 +202,7 @@ impl NavigationTarget { | |||
192 | full_range: node.range(), | 202 | full_range: node.range(), |
193 | focus_range, | 203 | focus_range, |
194 | // ptr: Some(LocalSyntaxPtr::new(node)), | 204 | // ptr: Some(LocalSyntaxPtr::new(node)), |
205 | container_name: None, | ||
195 | } | 206 | } |
196 | } | 207 | } |
197 | } | 208 | } |
diff --git a/crates/ra_ide_api/src/symbol_index.rs b/crates/ra_ide_api/src/symbol_index.rs index 15348124b..62d0979fe 100644 --- a/crates/ra_ide_api/src/symbol_index.rs +++ b/crates/ra_ide_api/src/symbol_index.rs | |||
@@ -32,6 +32,7 @@ use ra_syntax::{ | |||
32 | algo::{visit::{visitor, Visitor}, find_covering_node}, | 32 | algo::{visit::{visitor, Visitor}, find_covering_node}, |
33 | SyntaxKind::{self, *}, | 33 | SyntaxKind::{self, *}, |
34 | ast::{self, NameOwner}, | 34 | ast::{self, NameOwner}, |
35 | WalkEvent, | ||
35 | }; | 36 | }; |
36 | use ra_db::{ | 37 | use ra_db::{ |
37 | SourceRootId, SourceDatabase, | 38 | SourceRootId, SourceDatabase, |
@@ -62,17 +63,14 @@ pub(crate) trait SymbolsDatabase: hir::db::HirDatabase { | |||
62 | fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex> { | 63 | fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex> { |
63 | db.check_canceled(); | 64 | db.check_canceled(); |
64 | let source_file = db.parse(file_id); | 65 | let source_file = db.parse(file_id); |
65 | let mut symbols = source_file | 66 | |
66 | .syntax() | 67 | let mut symbols = source_file_to_file_symbols(&source_file, file_id); |
67 | .descendants() | ||
68 | .filter_map(to_symbol) | ||
69 | .map(move |(name, ptr)| FileSymbol { name, ptr, file_id }) | ||
70 | .collect::<Vec<_>>(); | ||
71 | 68 | ||
72 | for (name, text_range) in hir::source_binder::macro_symbols(db, file_id) { | 69 | for (name, text_range) in hir::source_binder::macro_symbols(db, file_id) { |
73 | let node = find_covering_node(source_file.syntax(), text_range); | 70 | let node = find_covering_node(source_file.syntax(), text_range); |
74 | let ptr = SyntaxNodePtr::new(node); | 71 | let ptr = SyntaxNodePtr::new(node); |
75 | symbols.push(FileSymbol { file_id, name, ptr }) | 72 | // TODO: Should we get container name for macro symbols? |
73 | symbols.push(FileSymbol { file_id, name, ptr, container_name: None }) | ||
76 | } | 74 | } |
77 | 75 | ||
78 | Arc::new(SymbolIndex::new(symbols)) | 76 | Arc::new(SymbolIndex::new(symbols)) |
@@ -158,13 +156,7 @@ impl SymbolIndex { | |||
158 | files: impl ParallelIterator<Item = (FileId, TreeArc<SourceFile>)>, | 156 | files: impl ParallelIterator<Item = (FileId, TreeArc<SourceFile>)>, |
159 | ) -> SymbolIndex { | 157 | ) -> SymbolIndex { |
160 | let symbols = files | 158 | let symbols = files |
161 | .flat_map(|(file_id, file)| { | 159 | .flat_map(|(file_id, file)| source_file_to_file_symbols(&file, file_id)) |
162 | file.syntax() | ||
163 | .descendants() | ||
164 | .filter_map(to_symbol) | ||
165 | .map(move |(name, ptr)| FileSymbol { name, ptr, file_id }) | ||
166 | .collect::<Vec<_>>() | ||
167 | }) | ||
168 | .collect::<Vec<_>>(); | 160 | .collect::<Vec<_>>(); |
169 | SymbolIndex::new(symbols) | 161 | SymbolIndex::new(symbols) |
170 | } | 162 | } |
@@ -208,6 +200,16 @@ fn is_type(kind: SyntaxKind) -> bool { | |||
208 | } | 200 | } |
209 | } | 201 | } |
210 | 202 | ||
203 | fn is_symbol_def(kind: SyntaxKind) -> bool { | ||
204 | match kind { | ||
205 | FN_DEF | STRUCT_DEF | ENUM_DEF | TRAIT_DEF | MODULE | TYPE_DEF | CONST_DEF | STATIC_DEF => { | ||
206 | true | ||
207 | } | ||
208 | |||
209 | _ => false, | ||
210 | } | ||
211 | } | ||
212 | |||
211 | /// The actual data that is stored in the index. It should be as compact as | 213 | /// The actual data that is stored in the index. It should be as compact as |
212 | /// possible. | 214 | /// possible. |
213 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 215 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
@@ -215,12 +217,40 @@ pub(crate) struct FileSymbol { | |||
215 | pub(crate) file_id: FileId, | 217 | pub(crate) file_id: FileId, |
216 | pub(crate) name: SmolStr, | 218 | pub(crate) name: SmolStr, |
217 | pub(crate) ptr: SyntaxNodePtr, | 219 | pub(crate) ptr: SyntaxNodePtr, |
220 | pub(crate) container_name: Option<SmolStr>, | ||
221 | } | ||
222 | |||
223 | fn source_file_to_file_symbols(source_file: &SourceFile, file_id: FileId) -> Vec<FileSymbol> { | ||
224 | let mut symbols = Vec::new(); | ||
225 | let mut stack = Vec::new(); | ||
226 | |||
227 | for event in source_file.syntax().preorder() { | ||
228 | match event { | ||
229 | WalkEvent::Enter(node) => { | ||
230 | if let Some(mut symbol) = to_file_symbol(node, file_id) { | ||
231 | symbol.container_name = stack.last().map(|v: &SmolStr| v.clone()); | ||
232 | |||
233 | stack.push(symbol.name.clone()); | ||
234 | symbols.push(symbol); | ||
235 | } | ||
236 | } | ||
237 | |||
238 | WalkEvent::Leave(node) => { | ||
239 | if is_symbol_def(node.kind()) { | ||
240 | stack.pop(); | ||
241 | } | ||
242 | } | ||
243 | } | ||
244 | } | ||
245 | |||
246 | symbols | ||
218 | } | 247 | } |
219 | 248 | ||
220 | fn to_symbol(node: &SyntaxNode) -> Option<(SmolStr, SyntaxNodePtr)> { | 249 | fn to_symbol(node: &SyntaxNode) -> Option<(SmolStr, SyntaxNodePtr)> { |
221 | fn decl<N: NameOwner>(node: &N) -> Option<(SmolStr, SyntaxNodePtr)> { | 250 | fn decl<N: NameOwner>(node: &N) -> Option<(SmolStr, SyntaxNodePtr)> { |
222 | let name = node.name()?.text().clone(); | 251 | let name = node.name()?.text().clone(); |
223 | let ptr = SyntaxNodePtr::new(node.syntax()); | 252 | let ptr = SyntaxNodePtr::new(node.syntax()); |
253 | |||
224 | Some((name, ptr)) | 254 | Some((name, ptr)) |
225 | } | 255 | } |
226 | visitor() | 256 | visitor() |
@@ -234,3 +264,7 @@ fn to_symbol(node: &SyntaxNode) -> Option<(SmolStr, SyntaxNodePtr)> { | |||
234 | .visit(decl::<ast::StaticDef>) | 264 | .visit(decl::<ast::StaticDef>) |
235 | .accept(node)? | 265 | .accept(node)? |
236 | } | 266 | } |
267 | |||
268 | fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option<FileSymbol> { | ||
269 | to_symbol(node).map(move |(name, ptr)| FileSymbol { name, ptr, file_id, container_name: None }) | ||
270 | } | ||
diff --git a/crates/ra_ide_api/tests/test/main.rs b/crates/ra_ide_api/tests/test/main.rs index 7d1695cfd..4126bd160 100644 --- a/crates/ra_ide_api/tests/test/main.rs +++ b/crates/ra_ide_api/tests/test/main.rs | |||
@@ -1,9 +1,9 @@ | |||
1 | use insta::assert_debug_snapshot_matches; | 1 | use insta::assert_debug_snapshot_matches; |
2 | use ra_ide_api::{ | 2 | use ra_ide_api::{ |
3 | mock_analysis::{single_file, single_file_with_position, MockAnalysis}, | 3 | mock_analysis::{single_file, single_file_with_position, MockAnalysis}, |
4 | AnalysisChange, CrateGraph, FileId, Query, | 4 | AnalysisChange, CrateGraph, FileId, Query, NavigationTarget, |
5 | }; | 5 | }; |
6 | use ra_syntax::TextRange; | 6 | use ra_syntax::{TextRange, SmolStr}; |
7 | 7 | ||
8 | #[test] | 8 | #[test] |
9 | fn test_unresolved_module_diagnostic() { | 9 | fn test_unresolved_module_diagnostic() { |
@@ -49,6 +49,11 @@ fn get_all_refs(text: &str) -> Vec<(FileId, TextRange)> { | |||
49 | analysis.find_all_refs(position).unwrap() | 49 | analysis.find_all_refs(position).unwrap() |
50 | } | 50 | } |
51 | 51 | ||
52 | fn get_symbols_matching(text: &str, query: &str) -> Vec<NavigationTarget> { | ||
53 | let (analysis, _) = single_file(text); | ||
54 | analysis.symbol_search(Query::new(query.into())).unwrap() | ||
55 | } | ||
56 | |||
52 | #[test] | 57 | #[test] |
53 | fn test_find_all_refs_for_local() { | 58 | fn test_find_all_refs_for_local() { |
54 | let code = r#" | 59 | let code = r#" |
@@ -91,6 +96,55 @@ fn test_find_all_refs_for_fn_param() { | |||
91 | } | 96 | } |
92 | 97 | ||
93 | #[test] | 98 | #[test] |
99 | fn test_world_symbols_with_no_container() { | ||
100 | { | ||
101 | let code = r#" | ||
102 | enum FooInner { } | ||
103 | "#; | ||
104 | |||
105 | let mut symbols = get_symbols_matching(code, "FooInner"); | ||
106 | |||
107 | let s = symbols.pop().unwrap(); | ||
108 | |||
109 | assert_eq!(s.name(), "FooInner"); | ||
110 | assert!(s.container_name().is_none()); | ||
111 | } | ||
112 | } | ||
113 | |||
114 | #[test] | ||
115 | fn test_world_symbols_include_container_name() { | ||
116 | { | ||
117 | let code = r#" | ||
118 | fn foo() { | ||
119 | enum FooInner { } | ||
120 | } | ||
121 | "#; | ||
122 | |||
123 | let mut symbols = get_symbols_matching(code, "FooInner"); | ||
124 | |||
125 | let s = symbols.pop().unwrap(); | ||
126 | |||
127 | assert_eq!(s.name(), "FooInner"); | ||
128 | assert_eq!(s.container_name(), Some(&SmolStr::new("foo"))); | ||
129 | } | ||
130 | |||
131 | { | ||
132 | let code = r#" | ||
133 | mod foo { | ||
134 | struct FooInner; | ||
135 | } | ||
136 | "#; | ||
137 | |||
138 | let mut symbols = get_symbols_matching(code, "FooInner"); | ||
139 | |||
140 | let s = symbols.pop().unwrap(); | ||
141 | |||
142 | assert_eq!(s.name(), "FooInner"); | ||
143 | assert_eq!(s.container_name(), Some(&SmolStr::new("foo"))); | ||
144 | } | ||
145 | } | ||
146 | |||
147 | #[test] | ||
94 | #[ignore] | 148 | #[ignore] |
95 | fn world_symbols_include_stuff_from_macros() { | 149 | fn world_symbols_include_stuff_from_macros() { |
96 | let (analysis, _) = single_file( | 150 | let (analysis, _) = single_file( |
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index 0cdb39c32..09d896c40 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -190,7 +190,7 @@ pub fn handle_workspace_symbol( | |||
190 | name: nav.name().to_string(), | 190 | name: nav.name().to_string(), |
191 | kind: nav.kind().conv(), | 191 | kind: nav.kind().conv(), |
192 | location: nav.try_conv_with(world)?, | 192 | location: nav.try_conv_with(world)?, |
193 | container_name: None, | 193 | container_name: nav.container_name().map(|v| v.to_string()), |
194 | deprecated: None, | 194 | deprecated: None, |
195 | }; | 195 | }; |
196 | res.push(info); | 196 | res.push(info); |