diff options
-rw-r--r-- | crates/ra_analysis/src/imp.rs | 18 | ||||
-rw-r--r-- | crates/ra_analysis/src/lib.rs | 49 | ||||
-rw-r--r-- | crates/ra_analysis/src/symbol_index.rs | 123 | ||||
-rw-r--r-- | crates/ra_analysis/tests/tests.rs | 10 | ||||
-rw-r--r-- | crates/ra_editor/src/lib.rs | 4 | ||||
-rw-r--r-- | crates/ra_editor/src/structure.rs | 129 | ||||
-rw-r--r-- | crates/ra_editor/src/symbols.rs | 246 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/conv.rs | 11 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop/handlers.rs | 37 |
9 files changed, 329 insertions, 298 deletions
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs index ec7da437a..8071554a7 100644 --- a/crates/ra_analysis/src/imp.rs +++ b/crates/ra_analysis/src/imp.rs | |||
@@ -10,7 +10,7 @@ use hir::{ | |||
10 | self, FnSignatureInfo, Problem, source_binder, | 10 | self, FnSignatureInfo, Problem, source_binder, |
11 | }; | 11 | }; |
12 | use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase}; | 12 | use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase}; |
13 | use ra_editor::{self, FileSymbol, find_node_at_offset, LineIndex, LocalEdit, Severity}; | 13 | use ra_editor::{self, find_node_at_offset, LineIndex, LocalEdit, Severity}; |
14 | use ra_syntax::{ | 14 | use ra_syntax::{ |
15 | algo::find_covering_node, | 15 | algo::find_covering_node, |
16 | ast::{self, ArgListOwner, Expr, FnDef, NameOwner}, | 16 | ast::{self, ArgListOwner, Expr, FnDef, NameOwner}, |
@@ -21,11 +21,11 @@ use ra_syntax::{ | |||
21 | 21 | ||
22 | use crate::{ | 22 | use crate::{ |
23 | AnalysisChange, | 23 | AnalysisChange, |
24 | Cancelable, | 24 | Cancelable, NavigationTarget, |
25 | completion::{CompletionItem, completions}, | 25 | completion::{CompletionItem, completions}, |
26 | CrateId, db, Diagnostic, FileId, FilePosition, FileRange, FileSystemEdit, | 26 | CrateId, db, Diagnostic, FileId, FilePosition, FileRange, FileSystemEdit, |
27 | Query, ReferenceResolution, RootChange, SourceChange, SourceFileEdit, | 27 | Query, ReferenceResolution, RootChange, SourceChange, SourceFileEdit, |
28 | symbol_index::{LibrarySymbolsQuery, SymbolIndex, SymbolsDatabase}, | 28 | symbol_index::{LibrarySymbolsQuery, SymbolIndex, SymbolsDatabase, FileSymbol}, |
29 | }; | 29 | }; |
30 | 30 | ||
31 | #[derive(Debug, Default)] | 31 | #[derive(Debug, Default)] |
@@ -205,7 +205,7 @@ impl AnalysisImpl { | |||
205 | 205 | ||
206 | /// This returns `Vec` because a module may be included from several places. We | 206 | /// This returns `Vec` because a module may be included from several places. We |
207 | /// don't handle this case yet though, so the Vec has length at most one. | 207 | /// don't handle this case yet though, so the Vec has length at most one. |
208 | pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> { | 208 | pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<NavigationTarget>> { |
209 | let descr = match source_binder::module_from_position(&*self.db, position)? { | 209 | let descr = match source_binder::module_from_position(&*self.db, position)? { |
210 | None => return Ok(Vec::new()), | 210 | None => return Ok(Vec::new()), |
211 | Some(it) => it, | 211 | Some(it) => it, |
@@ -216,12 +216,12 @@ impl AnalysisImpl { | |||
216 | }; | 216 | }; |
217 | let decl = decl.borrowed(); | 217 | let decl = decl.borrowed(); |
218 | let decl_name = decl.name().unwrap(); | 218 | let decl_name = decl.name().unwrap(); |
219 | let sym = FileSymbol { | 219 | let symbol = FileSymbol { |
220 | name: decl_name.text(), | 220 | name: decl_name.text(), |
221 | node_range: decl_name.syntax().range(), | 221 | node_range: decl_name.syntax().range(), |
222 | kind: MODULE, | 222 | kind: MODULE, |
223 | }; | 223 | }; |
224 | Ok(vec![(file_id, sym)]) | 224 | Ok(vec![NavigationTarget { file_id, symbol }]) |
225 | } | 225 | } |
226 | /// Returns `Vec` for the same reason as `parent_module` | 226 | /// Returns `Vec` for the same reason as `parent_module` |
227 | pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { | 227 | pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { |
@@ -355,9 +355,9 @@ impl AnalysisImpl { | |||
355 | Ok(Some((binding, descr))) | 355 | Ok(Some((binding, descr))) |
356 | } | 356 | } |
357 | } | 357 | } |
358 | pub fn doc_text_for(&self, file_id: FileId, symbol: FileSymbol) -> Cancelable<Option<String>> { | 358 | pub fn doc_text_for(&self, nav: NavigationTarget) -> Cancelable<Option<String>> { |
359 | let file = self.db.source_file(file_id); | 359 | let file = self.db.source_file(nav.file_id); |
360 | let result = match (symbol.description(&file), symbol.docs(&file)) { | 360 | let result = match (nav.symbol.description(&file), nav.symbol.docs(&file)) { |
361 | (Some(desc), Some(docs)) => { | 361 | (Some(desc), Some(docs)) => { |
362 | Some("```rust\n".to_string() + &*desc + "\n```\n\n" + &*docs) | 362 | Some("```rust\n".to_string() + &*desc + "\n```\n\n" + &*docs) |
363 | } | 363 | } |
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs index 08ecb125a..9576453ab 100644 --- a/crates/ra_analysis/src/lib.rs +++ b/crates/ra_analysis/src/lib.rs | |||
@@ -23,22 +23,22 @@ mod syntax_highlighting; | |||
23 | use std::{fmt, sync::Arc}; | 23 | use std::{fmt, sync::Arc}; |
24 | 24 | ||
25 | use rustc_hash::FxHashMap; | 25 | use rustc_hash::FxHashMap; |
26 | use ra_syntax::{SourceFileNode, TextRange, TextUnit}; | 26 | use ra_syntax::{SourceFileNode, TextRange, TextUnit, SmolStr, SyntaxKind}; |
27 | use ra_text_edit::TextEdit; | 27 | use ra_text_edit::TextEdit; |
28 | use rayon::prelude::*; | 28 | use rayon::prelude::*; |
29 | use relative_path::RelativePathBuf; | 29 | use relative_path::RelativePathBuf; |
30 | 30 | ||
31 | use crate::{ | 31 | use crate::{ |
32 | imp::{AnalysisHostImpl, AnalysisImpl}, | 32 | imp::{AnalysisHostImpl, AnalysisImpl}, |
33 | symbol_index::SymbolIndex, | 33 | symbol_index::{SymbolIndex, FileSymbol}, |
34 | }; | 34 | }; |
35 | 35 | ||
36 | pub use crate::{ | 36 | pub use crate::{ |
37 | completion::{CompletionItem, CompletionItemKind, InsertText}, | 37 | completion::{CompletionItem, CompletionItemKind, InsertText}, |
38 | runnables::{Runnable, RunnableKind} | 38 | runnables::{Runnable, RunnableKind}, |
39 | }; | 39 | }; |
40 | pub use ra_editor::{ | 40 | pub use ra_editor::{ |
41 | FileSymbol, Fold, FoldKind, HighlightedRange, LineIndex, StructureNode, Severity | 41 | Fold, FoldKind, HighlightedRange, LineIndex, StructureNode, Severity |
42 | }; | 42 | }; |
43 | pub use hir::FnSignatureInfo; | 43 | pub use hir::FnSignatureInfo; |
44 | 44 | ||
@@ -242,6 +242,27 @@ impl Query { | |||
242 | } | 242 | } |
243 | } | 243 | } |
244 | 244 | ||
245 | #[derive(Debug)] | ||
246 | pub struct NavigationTarget { | ||
247 | file_id: FileId, | ||
248 | symbol: FileSymbol, | ||
249 | } | ||
250 | |||
251 | impl NavigationTarget { | ||
252 | pub fn name(&self) -> SmolStr { | ||
253 | self.symbol.name.clone() | ||
254 | } | ||
255 | pub fn kind(&self) -> SyntaxKind { | ||
256 | self.symbol.kind | ||
257 | } | ||
258 | pub fn file_id(&self) -> FileId { | ||
259 | self.file_id | ||
260 | } | ||
261 | pub fn range(&self) -> TextRange { | ||
262 | self.symbol.node_range | ||
263 | } | ||
264 | } | ||
265 | |||
245 | /// Result of "goto def" query. | 266 | /// Result of "goto def" query. |
246 | #[derive(Debug)] | 267 | #[derive(Debug)] |
247 | pub struct ReferenceResolution { | 268 | pub struct ReferenceResolution { |
@@ -250,7 +271,7 @@ pub struct ReferenceResolution { | |||
250 | /// client where the reference was. | 271 | /// client where the reference was. |
251 | pub reference_range: TextRange, | 272 | pub reference_range: TextRange, |
252 | /// What this reference resolves to. | 273 | /// What this reference resolves to. |
253 | pub resolves_to: Vec<(FileId, FileSymbol)>, | 274 | pub resolves_to: Vec<NavigationTarget>, |
254 | } | 275 | } |
255 | 276 | ||
256 | impl ReferenceResolution { | 277 | impl ReferenceResolution { |
@@ -262,7 +283,7 @@ impl ReferenceResolution { | |||
262 | } | 283 | } |
263 | 284 | ||
264 | fn add_resolution(&mut self, file_id: FileId, symbol: FileSymbol) { | 285 | fn add_resolution(&mut self, file_id: FileId, symbol: FileSymbol) { |
265 | self.resolves_to.push((file_id, symbol)) | 286 | self.resolves_to.push(NavigationTarget { file_id, symbol }) |
266 | } | 287 | } |
267 | } | 288 | } |
268 | 289 | ||
@@ -320,8 +341,14 @@ impl Analysis { | |||
320 | let file = self.imp.file_syntax(file_id); | 341 | let file = self.imp.file_syntax(file_id); |
321 | ra_editor::folding_ranges(&file) | 342 | ra_editor::folding_ranges(&file) |
322 | } | 343 | } |
323 | pub fn symbol_search(&self, query: Query) -> Cancelable<Vec<(FileId, FileSymbol)>> { | 344 | pub fn symbol_search(&self, query: Query) -> Cancelable<Vec<NavigationTarget>> { |
324 | self.imp.world_symbols(query) | 345 | let res = self |
346 | .imp | ||
347 | .world_symbols(query)? | ||
348 | .into_iter() | ||
349 | .map(|(file_id, symbol)| NavigationTarget { file_id, symbol }) | ||
350 | .collect(); | ||
351 | Ok(res) | ||
325 | } | 352 | } |
326 | pub fn approximately_resolve_symbol( | 353 | pub fn approximately_resolve_symbol( |
327 | &self, | 354 | &self, |
@@ -332,10 +359,10 @@ impl Analysis { | |||
332 | pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> { | 359 | pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> { |
333 | self.imp.find_all_refs(position) | 360 | self.imp.find_all_refs(position) |
334 | } | 361 | } |
335 | pub fn doc_text_for(&self, file_id: FileId, symbol: FileSymbol) -> Cancelable<Option<String>> { | 362 | pub fn doc_text_for(&self, nav: NavigationTarget) -> Cancelable<Option<String>> { |
336 | self.imp.doc_text_for(file_id, symbol) | 363 | self.imp.doc_text_for(nav) |
337 | } | 364 | } |
338 | pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> { | 365 | pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<NavigationTarget>> { |
339 | self.imp.parent_module(position) | 366 | self.imp.parent_module(position) |
340 | } | 367 | } |
341 | pub fn module_path(&self, position: FilePosition) -> Cancelable<Option<String>> { | 368 | pub fn module_path(&self, position: FilePosition) -> Cancelable<Option<String>> { |
diff --git a/crates/ra_analysis/src/symbol_index.rs b/crates/ra_analysis/src/symbol_index.rs index e5bdf0aa1..56a84a850 100644 --- a/crates/ra_analysis/src/symbol_index.rs +++ b/crates/ra_analysis/src/symbol_index.rs | |||
@@ -4,10 +4,11 @@ use std::{ | |||
4 | }; | 4 | }; |
5 | 5 | ||
6 | use fst::{self, Streamer}; | 6 | use fst::{self, Streamer}; |
7 | use ra_editor::{self, FileSymbol}; | ||
8 | use ra_syntax::{ | 7 | use ra_syntax::{ |
9 | SourceFileNode, | 8 | AstNode, SyntaxNodeRef, SourceFileNode, SmolStr, TextRange, |
9 | algo::visit::{visitor, Visitor}, | ||
10 | SyntaxKind::{self, *}, | 10 | SyntaxKind::{self, *}, |
11 | ast::{self, NameOwner, DocCommentsOwner}, | ||
11 | }; | 12 | }; |
12 | use ra_db::{SyntaxDatabase, SourceRootId}; | 13 | use ra_db::{SyntaxDatabase, SourceRootId}; |
13 | use rayon::prelude::*; | 14 | use rayon::prelude::*; |
@@ -65,8 +66,9 @@ impl SymbolIndex { | |||
65 | ) -> SymbolIndex { | 66 | ) -> SymbolIndex { |
66 | let mut symbols = files | 67 | let mut symbols = files |
67 | .flat_map(|(file_id, file)| { | 68 | .flat_map(|(file_id, file)| { |
68 | ra_editor::file_symbols(&file) | 69 | file.syntax() |
69 | .into_iter() | 70 | .descendants() |
71 | .filter_map(to_symbol) | ||
70 | .map(move |symbol| (symbol.name.as_str().to_lowercase(), (file_id, symbol))) | 72 | .map(move |symbol| (symbol.name.as_str().to_lowercase(), (file_id, symbol))) |
71 | .collect::<Vec<_>>() | 73 | .collect::<Vec<_>>() |
72 | }) | 74 | }) |
@@ -121,3 +123,116 @@ fn is_type(kind: SyntaxKind) -> bool { | |||
121 | _ => false, | 123 | _ => false, |
122 | } | 124 | } |
123 | } | 125 | } |
126 | |||
127 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
128 | pub(crate) struct FileSymbol { | ||
129 | pub(crate) name: SmolStr, | ||
130 | pub(crate) node_range: TextRange, | ||
131 | pub(crate) kind: SyntaxKind, | ||
132 | } | ||
133 | |||
134 | impl FileSymbol { | ||
135 | pub(crate) fn docs(&self, file: &SourceFileNode) -> Option<String> { | ||
136 | file.syntax() | ||
137 | .descendants() | ||
138 | .filter(|node| node.kind() == self.kind && node.range() == self.node_range) | ||
139 | .filter_map(|node: SyntaxNodeRef| { | ||
140 | fn doc_comments<'a, N: DocCommentsOwner<'a>>(node: N) -> Option<String> { | ||
141 | let comments = node.doc_comment_text(); | ||
142 | if comments.is_empty() { | ||
143 | None | ||
144 | } else { | ||
145 | Some(comments) | ||
146 | } | ||
147 | } | ||
148 | |||
149 | visitor() | ||
150 | .visit(doc_comments::<ast::FnDef>) | ||
151 | .visit(doc_comments::<ast::StructDef>) | ||
152 | .visit(doc_comments::<ast::EnumDef>) | ||
153 | .visit(doc_comments::<ast::TraitDef>) | ||
154 | .visit(doc_comments::<ast::Module>) | ||
155 | .visit(doc_comments::<ast::TypeDef>) | ||
156 | .visit(doc_comments::<ast::ConstDef>) | ||
157 | .visit(doc_comments::<ast::StaticDef>) | ||
158 | .accept(node)? | ||
159 | }) | ||
160 | .nth(0) | ||
161 | } | ||
162 | /// Get a description of this node. | ||
163 | /// | ||
164 | /// e.g. `struct Name`, `enum Name`, `fn Name` | ||
165 | pub(crate) fn description(&self, file: &SourceFileNode) -> Option<String> { | ||
166 | // TODO: After type inference is done, add type information to improve the output | ||
167 | file.syntax() | ||
168 | .descendants() | ||
169 | .filter(|node| node.kind() == self.kind && node.range() == self.node_range) | ||
170 | .filter_map(|node: SyntaxNodeRef| { | ||
171 | // TODO: Refactor to be have less repetition | ||
172 | visitor() | ||
173 | .visit(|node: ast::FnDef| { | ||
174 | let mut string = "fn ".to_string(); | ||
175 | node.name()?.syntax().text().push_to(&mut string); | ||
176 | Some(string) | ||
177 | }) | ||
178 | .visit(|node: ast::StructDef| { | ||
179 | let mut string = "struct ".to_string(); | ||
180 | node.name()?.syntax().text().push_to(&mut string); | ||
181 | Some(string) | ||
182 | }) | ||
183 | .visit(|node: ast::EnumDef| { | ||
184 | let mut string = "enum ".to_string(); | ||
185 | node.name()?.syntax().text().push_to(&mut string); | ||
186 | Some(string) | ||
187 | }) | ||
188 | .visit(|node: ast::TraitDef| { | ||
189 | let mut string = "trait ".to_string(); | ||
190 | node.name()?.syntax().text().push_to(&mut string); | ||
191 | Some(string) | ||
192 | }) | ||
193 | .visit(|node: ast::Module| { | ||
194 | let mut string = "mod ".to_string(); | ||
195 | node.name()?.syntax().text().push_to(&mut string); | ||
196 | Some(string) | ||
197 | }) | ||
198 | .visit(|node: ast::TypeDef| { | ||
199 | let mut string = "type ".to_string(); | ||
200 | node.name()?.syntax().text().push_to(&mut string); | ||
201 | Some(string) | ||
202 | }) | ||
203 | .visit(|node: ast::ConstDef| { | ||
204 | let mut string = "const ".to_string(); | ||
205 | node.name()?.syntax().text().push_to(&mut string); | ||
206 | Some(string) | ||
207 | }) | ||
208 | .visit(|node: ast::StaticDef| { | ||
209 | let mut string = "static ".to_string(); | ||
210 | node.name()?.syntax().text().push_to(&mut string); | ||
211 | Some(string) | ||
212 | }) | ||
213 | .accept(node)? | ||
214 | }) | ||
215 | .nth(0) | ||
216 | } | ||
217 | } | ||
218 | |||
219 | fn to_symbol(node: SyntaxNodeRef) -> Option<FileSymbol> { | ||
220 | fn decl<'a, N: NameOwner<'a>>(node: N) -> Option<FileSymbol> { | ||
221 | let name = node.name()?; | ||
222 | Some(FileSymbol { | ||
223 | name: name.text(), | ||
224 | node_range: node.syntax().range(), | ||
225 | kind: node.syntax().kind(), | ||
226 | }) | ||
227 | } | ||
228 | visitor() | ||
229 | .visit(decl::<ast::FnDef>) | ||
230 | .visit(decl::<ast::StructDef>) | ||
231 | .visit(decl::<ast::EnumDef>) | ||
232 | .visit(decl::<ast::TraitDef>) | ||
233 | .visit(decl::<ast::Module>) | ||
234 | .visit(decl::<ast::TypeDef>) | ||
235 | .visit(decl::<ast::ConstDef>) | ||
236 | .visit(decl::<ast::StaticDef>) | ||
237 | .accept(node)? | ||
238 | } | ||
diff --git a/crates/ra_analysis/tests/tests.rs b/crates/ra_analysis/tests/tests.rs index b61ead752..845fff3c6 100644 --- a/crates/ra_analysis/tests/tests.rs +++ b/crates/ra_analysis/tests/tests.rs | |||
@@ -25,7 +25,7 @@ fn approximate_resolve_works_in_items() { | |||
25 | assert_eq_dbg( | 25 | assert_eq_dbg( |
26 | r#"ReferenceResolution { | 26 | r#"ReferenceResolution { |
27 | reference_range: [23; 26), | 27 | reference_range: [23; 26), |
28 | resolves_to: [(FileId(1), FileSymbol { name: "Foo", node_range: [0; 11), kind: STRUCT_DEF })] | 28 | resolves_to: [NavigationTarget { file_id: FileId(1), symbol: FileSymbol { name: "Foo", node_range: [0; 11), kind: STRUCT_DEF } }] |
29 | }"#, | 29 | }"#, |
30 | &symbols, | 30 | &symbols, |
31 | ); | 31 | ); |
@@ -46,7 +46,7 @@ fn test_resolve_module() { | |||
46 | assert_eq_dbg( | 46 | assert_eq_dbg( |
47 | r#"ReferenceResolution { | 47 | r#"ReferenceResolution { |
48 | reference_range: [4; 7), | 48 | reference_range: [4; 7), |
49 | resolves_to: [(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })] | 49 | resolves_to: [NavigationTarget { file_id: FileId(2), symbol: FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE } }] |
50 | }"#, | 50 | }"#, |
51 | &symbols, | 51 | &symbols, |
52 | ); | 52 | ); |
@@ -64,7 +64,7 @@ fn test_resolve_module() { | |||
64 | assert_eq_dbg( | 64 | assert_eq_dbg( |
65 | r#"ReferenceResolution { | 65 | r#"ReferenceResolution { |
66 | reference_range: [4; 7), | 66 | reference_range: [4; 7), |
67 | resolves_to: [(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })] | 67 | resolves_to: [NavigationTarget { file_id: FileId(2), symbol: FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE } }] |
68 | }"#, | 68 | }"#, |
69 | &symbols, | 69 | &symbols, |
70 | ); | 70 | ); |
@@ -107,7 +107,7 @@ fn test_resolve_parent_module() { | |||
107 | ); | 107 | ); |
108 | let symbols = analysis.parent_module(pos).unwrap(); | 108 | let symbols = analysis.parent_module(pos).unwrap(); |
109 | assert_eq_dbg( | 109 | assert_eq_dbg( |
110 | r#"[(FileId(1), FileSymbol { name: "foo", node_range: [4; 7), kind: MODULE })]"#, | 110 | r#"[NavigationTarget { file_id: FileId(1), symbol: FileSymbol { name: "foo", node_range: [4; 7), kind: MODULE } }]"#, |
111 | &symbols, | 111 | &symbols, |
112 | ); | 112 | ); |
113 | } | 113 | } |
@@ -126,7 +126,7 @@ fn test_resolve_parent_module_for_inline() { | |||
126 | ); | 126 | ); |
127 | let symbols = analysis.parent_module(pos).unwrap(); | 127 | let symbols = analysis.parent_module(pos).unwrap(); |
128 | assert_eq_dbg( | 128 | assert_eq_dbg( |
129 | r#"[(FileId(1), FileSymbol { name: "bar", node_range: [18; 21), kind: MODULE })]"#, | 129 | r#"[NavigationTarget { file_id: FileId(1), symbol: FileSymbol { name: "bar", node_range: [18; 21), kind: MODULE } }]"#, |
130 | &symbols, | 130 | &symbols, |
131 | ); | 131 | ); |
132 | } | 132 | } |
diff --git a/crates/ra_editor/src/lib.rs b/crates/ra_editor/src/lib.rs index b03f9ea54..bfc745e58 100644 --- a/crates/ra_editor/src/lib.rs +++ b/crates/ra_editor/src/lib.rs | |||
@@ -3,7 +3,7 @@ mod extend_selection; | |||
3 | mod folding_ranges; | 3 | mod folding_ranges; |
4 | mod line_index; | 4 | mod line_index; |
5 | mod line_index_utils; | 5 | mod line_index_utils; |
6 | mod symbols; | 6 | mod structure; |
7 | #[cfg(test)] | 7 | #[cfg(test)] |
8 | mod test_utils; | 8 | mod test_utils; |
9 | mod typing; | 9 | mod typing; |
@@ -15,7 +15,7 @@ pub use self::{ | |||
15 | folding_ranges::{folding_ranges, Fold, FoldKind}, | 15 | folding_ranges::{folding_ranges, Fold, FoldKind}, |
16 | line_index::{LineCol, LineIndex}, | 16 | line_index::{LineCol, LineIndex}, |
17 | line_index_utils::translate_offset_with_edit, | 17 | line_index_utils::translate_offset_with_edit, |
18 | symbols::{file_structure, file_symbols, FileSymbol, StructureNode}, | 18 | structure::{file_structure, StructureNode}, |
19 | typing::{join_lines, on_enter, on_eq_typed}, | 19 | typing::{join_lines, on_enter, on_eq_typed}, |
20 | diagnostics::diagnostics | 20 | diagnostics::diagnostics |
21 | }; | 21 | }; |
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 @@ | |||
1 | use crate::TextRange; | ||
2 | |||
3 | use ra_syntax::{ | ||
4 | algo::visit::{visitor, Visitor}, | ||
5 | ast::{self, NameOwner}, | ||
6 | AstNode, SourceFileNode, SyntaxKind, SyntaxNodeRef, WalkEvent, | ||
7 | }; | ||
8 | |||
9 | #[derive(Debug, Clone)] | ||
10 | pub 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 | |||
18 | pub 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 | |||
41 | fn 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)] | ||
88 | mod 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#" | ||
96 | struct Foo { | ||
97 | x: i32 | ||
98 | } | ||
99 | |||
100 | mod m { | ||
101 | fn bar() {} | ||
102 | } | ||
103 | |||
104 | enum E { X, Y(i32) } | ||
105 | type T = (); | ||
106 | static S: i32 = 92; | ||
107 | const C: i32 = 92; | ||
108 | |||
109 | impl E {} | ||
110 | |||
111 | impl 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 | } | ||
diff --git a/crates/ra_editor/src/symbols.rs b/crates/ra_editor/src/symbols.rs deleted file mode 100644 index 9e25decfb..000000000 --- a/crates/ra_editor/src/symbols.rs +++ /dev/null | |||
@@ -1,246 +0,0 @@ | |||
1 | use crate::TextRange; | ||
2 | |||
3 | use ra_syntax::{ | ||
4 | algo::visit::{visitor, Visitor}, | ||
5 | ast::{self, DocCommentsOwner, NameOwner}, | ||
6 | AstNode, SourceFileNode, SmolStr, SyntaxKind, SyntaxNodeRef, WalkEvent, | ||
7 | }; | ||
8 | |||
9 | #[derive(Debug, Clone)] | ||
10 | pub 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 | |||
18 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
19 | pub struct FileSymbol { | ||
20 | pub name: SmolStr, | ||
21 | pub node_range: TextRange, | ||
22 | pub kind: SyntaxKind, | ||
23 | } | ||
24 | |||
25 | impl FileSymbol { | ||
26 | pub fn docs(&self, file: &SourceFileNode) -> Option<String> { | ||
27 | file.syntax() | ||
28 | .descendants() | ||
29 | .filter(|node| node.kind() == self.kind && node.range() == self.node_range) | ||
30 | .filter_map(|node: SyntaxNodeRef| { | ||
31 | fn doc_comments<'a, N: DocCommentsOwner<'a>>(node: N) -> Option<String> { | ||
32 | let comments = node.doc_comment_text(); | ||
33 | if comments.is_empty() { | ||
34 | None | ||
35 | } else { | ||
36 | Some(comments) | ||
37 | } | ||
38 | } | ||
39 | |||
40 | visitor() | ||
41 | .visit(doc_comments::<ast::FnDef>) | ||
42 | .visit(doc_comments::<ast::StructDef>) | ||
43 | .visit(doc_comments::<ast::EnumDef>) | ||
44 | .visit(doc_comments::<ast::TraitDef>) | ||
45 | .visit(doc_comments::<ast::Module>) | ||
46 | .visit(doc_comments::<ast::TypeDef>) | ||
47 | .visit(doc_comments::<ast::ConstDef>) | ||
48 | .visit(doc_comments::<ast::StaticDef>) | ||
49 | .accept(node)? | ||
50 | }) | ||
51 | .nth(0) | ||
52 | } | ||
53 | /// Get a description of this node. | ||
54 | /// | ||
55 | /// e.g. `struct Name`, `enum Name`, `fn Name` | ||
56 | pub fn description(&self, file: &SourceFileNode) -> Option<String> { | ||
57 | // TODO: After type inference is done, add type information to improve the output | ||
58 | file.syntax() | ||
59 | .descendants() | ||
60 | .filter(|node| node.kind() == self.kind && node.range() == self.node_range) | ||
61 | .filter_map(|node: SyntaxNodeRef| { | ||
62 | // TODO: Refactor to be have less repetition | ||
63 | visitor() | ||
64 | .visit(|node: ast::FnDef| { | ||
65 | let mut string = "fn ".to_string(); | ||
66 | node.name()?.syntax().text().push_to(&mut string); | ||
67 | Some(string) | ||
68 | }) | ||
69 | .visit(|node: ast::StructDef| { | ||
70 | let mut string = "struct ".to_string(); | ||
71 | node.name()?.syntax().text().push_to(&mut string); | ||
72 | Some(string) | ||
73 | }) | ||
74 | .visit(|node: ast::EnumDef| { | ||
75 | let mut string = "enum ".to_string(); | ||
76 | node.name()?.syntax().text().push_to(&mut string); | ||
77 | Some(string) | ||
78 | }) | ||
79 | .visit(|node: ast::TraitDef| { | ||
80 | let mut string = "trait ".to_string(); | ||
81 | node.name()?.syntax().text().push_to(&mut string); | ||
82 | Some(string) | ||
83 | }) | ||
84 | .visit(|node: ast::Module| { | ||
85 | let mut string = "mod ".to_string(); | ||
86 | node.name()?.syntax().text().push_to(&mut string); | ||
87 | Some(string) | ||
88 | }) | ||
89 | .visit(|node: ast::TypeDef| { | ||
90 | let mut string = "type ".to_string(); | ||
91 | node.name()?.syntax().text().push_to(&mut string); | ||
92 | Some(string) | ||
93 | }) | ||
94 | .visit(|node: ast::ConstDef| { | ||
95 | let mut string = "const ".to_string(); | ||
96 | node.name()?.syntax().text().push_to(&mut string); | ||
97 | Some(string) | ||
98 | }) | ||
99 | .visit(|node: ast::StaticDef| { | ||
100 | let mut string = "static ".to_string(); | ||
101 | node.name()?.syntax().text().push_to(&mut string); | ||
102 | Some(string) | ||
103 | }) | ||
104 | .accept(node)? | ||
105 | }) | ||
106 | .nth(0) | ||
107 | } | ||
108 | } | ||
109 | |||
110 | pub fn file_symbols(file: &SourceFileNode) -> Vec<FileSymbol> { | ||
111 | file.syntax().descendants().filter_map(to_symbol).collect() | ||
112 | } | ||
113 | |||
114 | fn to_symbol(node: SyntaxNodeRef) -> Option<FileSymbol> { | ||
115 | fn decl<'a, N: NameOwner<'a>>(node: N) -> Option<FileSymbol> { | ||
116 | let name = node.name()?; | ||
117 | Some(FileSymbol { | ||
118 | name: name.text(), | ||
119 | node_range: node.syntax().range(), | ||
120 | kind: node.syntax().kind(), | ||
121 | }) | ||
122 | } | ||
123 | visitor() | ||
124 | .visit(decl::<ast::FnDef>) | ||
125 | .visit(decl::<ast::StructDef>) | ||
126 | .visit(decl::<ast::EnumDef>) | ||
127 | .visit(decl::<ast::TraitDef>) | ||
128 | .visit(decl::<ast::Module>) | ||
129 | .visit(decl::<ast::TypeDef>) | ||
130 | .visit(decl::<ast::ConstDef>) | ||
131 | .visit(decl::<ast::StaticDef>) | ||
132 | .accept(node)? | ||
133 | } | ||
134 | |||
135 | pub fn file_structure(file: &SourceFileNode) -> Vec<StructureNode> { | ||
136 | let mut res = Vec::new(); | ||
137 | let mut stack = Vec::new(); | ||
138 | |||
139 | for event in file.syntax().preorder() { | ||
140 | match event { | ||
141 | WalkEvent::Enter(node) => { | ||
142 | if let Some(mut symbol) = structure_node(node) { | ||
143 | symbol.parent = stack.last().map(|&n| n); | ||
144 | stack.push(res.len()); | ||
145 | res.push(symbol); | ||
146 | } | ||
147 | } | ||
148 | WalkEvent::Leave(node) => { | ||
149 | if structure_node(node).is_some() { | ||
150 | stack.pop().unwrap(); | ||
151 | } | ||
152 | } | ||
153 | } | ||
154 | } | ||
155 | res | ||
156 | } | ||
157 | |||
158 | fn structure_node(node: SyntaxNodeRef) -> Option<StructureNode> { | ||
159 | fn decl<'a, N: NameOwner<'a>>(node: N) -> Option<StructureNode> { | ||
160 | let name = node.name()?; | ||
161 | Some(StructureNode { | ||
162 | parent: None, | ||
163 | label: name.text().to_string(), | ||
164 | navigation_range: name.syntax().range(), | ||
165 | node_range: node.syntax().range(), | ||
166 | kind: node.syntax().kind(), | ||
167 | }) | ||
168 | } | ||
169 | |||
170 | visitor() | ||
171 | .visit(decl::<ast::FnDef>) | ||
172 | .visit(decl::<ast::StructDef>) | ||
173 | .visit(decl::<ast::NamedFieldDef>) | ||
174 | .visit(decl::<ast::EnumDef>) | ||
175 | .visit(decl::<ast::TraitDef>) | ||
176 | .visit(decl::<ast::Module>) | ||
177 | .visit(decl::<ast::TypeDef>) | ||
178 | .visit(decl::<ast::ConstDef>) | ||
179 | .visit(decl::<ast::StaticDef>) | ||
180 | .visit(|im: ast::ImplItem| { | ||
181 | let target_type = im.target_type()?; | ||
182 | let target_trait = im.target_trait(); | ||
183 | let label = match target_trait { | ||
184 | None => format!("impl {}", target_type.syntax().text()), | ||
185 | Some(t) => format!( | ||
186 | "impl {} for {}", | ||
187 | t.syntax().text(), | ||
188 | target_type.syntax().text(), | ||
189 | ), | ||
190 | }; | ||
191 | |||
192 | let node = StructureNode { | ||
193 | parent: None, | ||
194 | label, | ||
195 | navigation_range: target_type.syntax().range(), | ||
196 | node_range: im.syntax().range(), | ||
197 | kind: im.syntax().kind(), | ||
198 | }; | ||
199 | Some(node) | ||
200 | }) | ||
201 | .accept(node)? | ||
202 | } | ||
203 | |||
204 | #[cfg(test)] | ||
205 | mod tests { | ||
206 | use super::*; | ||
207 | use test_utils::assert_eq_dbg; | ||
208 | |||
209 | #[test] | ||
210 | fn test_file_structure() { | ||
211 | let file = SourceFileNode::parse( | ||
212 | r#" | ||
213 | struct Foo { | ||
214 | x: i32 | ||
215 | } | ||
216 | |||
217 | mod m { | ||
218 | fn bar() {} | ||
219 | } | ||
220 | |||
221 | enum E { X, Y(i32) } | ||
222 | type T = (); | ||
223 | static S: i32 = 92; | ||
224 | const C: i32 = 92; | ||
225 | |||
226 | impl E {} | ||
227 | |||
228 | impl fmt::Debug for E {} | ||
229 | "#, | ||
230 | ); | ||
231 | let symbols = file_structure(&file); | ||
232 | assert_eq_dbg( | ||
233 | r#"[StructureNode { parent: None, label: "Foo", navigation_range: [8; 11), node_range: [1; 26), kind: STRUCT_DEF }, | ||
234 | StructureNode { parent: Some(0), label: "x", navigation_range: [18; 19), node_range: [18; 24), kind: NAMED_FIELD_DEF }, | ||
235 | StructureNode { parent: None, label: "m", navigation_range: [32; 33), node_range: [28; 53), kind: MODULE }, | ||
236 | StructureNode { parent: Some(2), label: "bar", navigation_range: [43; 46), node_range: [40; 51), kind: FN_DEF }, | ||
237 | StructureNode { parent: None, label: "E", navigation_range: [60; 61), node_range: [55; 75), kind: ENUM_DEF }, | ||
238 | StructureNode { parent: None, label: "T", navigation_range: [81; 82), node_range: [76; 88), kind: TYPE_DEF }, | ||
239 | StructureNode { parent: None, label: "S", navigation_range: [96; 97), node_range: [89; 108), kind: STATIC_DEF }, | ||
240 | StructureNode { parent: None, label: "C", navigation_range: [115; 116), node_range: [109; 127), kind: CONST_DEF }, | ||
241 | StructureNode { parent: None, label: "impl E", navigation_range: [134; 135), node_range: [129; 138), kind: IMPL_ITEM }, | ||
242 | StructureNode { parent: None, label: "impl fmt::Debug for E", navigation_range: [160; 161), node_range: [140; 164), kind: IMPL_ITEM }]"#, | ||
243 | &symbols, | ||
244 | ) | ||
245 | } | ||
246 | } | ||
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs index 618486250..1107ffc8b 100644 --- a/crates/ra_lsp_server/src/conv.rs +++ b/crates/ra_lsp_server/src/conv.rs | |||
@@ -2,7 +2,7 @@ use languageserver_types::{ | |||
2 | self, Location, Position, Range, SymbolKind, TextDocumentEdit, TextDocumentIdentifier, | 2 | self, Location, Position, Range, SymbolKind, TextDocumentEdit, TextDocumentIdentifier, |
3 | TextDocumentItem, TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier, InsertTextFormat, | 3 | TextDocumentItem, TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier, InsertTextFormat, |
4 | }; | 4 | }; |
5 | use ra_analysis::{FileId, FileSystemEdit, SourceChange, SourceFileEdit, FilePosition,FileRange, CompletionItem, CompletionItemKind, InsertText}; | 5 | use ra_analysis::{FileId, FileSystemEdit, SourceChange, SourceFileEdit, FilePosition,FileRange, CompletionItem, CompletionItemKind, InsertText, NavigationTarget}; |
6 | use ra_editor::{LineCol, LineIndex, translate_offset_with_edit}; | 6 | use ra_editor::{LineCol, LineIndex, translate_offset_with_edit}; |
7 | use ra_text_edit::{AtomTextEdit, TextEdit}; | 7 | use ra_text_edit::{AtomTextEdit, TextEdit}; |
8 | use ra_syntax::{SyntaxKind, TextRange, TextUnit}; | 8 | use ra_syntax::{SyntaxKind, TextRange, TextUnit}; |
@@ -322,6 +322,15 @@ impl TryConvWith for FileSystemEdit { | |||
322 | } | 322 | } |
323 | } | 323 | } |
324 | 324 | ||
325 | impl TryConvWith for &NavigationTarget { | ||
326 | type Ctx = ServerWorld; | ||
327 | type Output = Location; | ||
328 | fn try_conv_with(self, world: &ServerWorld) -> Result<Location> { | ||
329 | let line_index = world.analysis().file_line_index(self.file_id()); | ||
330 | to_location(self.file_id(), self.range(), &world, &line_index) | ||
331 | } | ||
332 | } | ||
333 | |||
325 | pub fn to_location( | 334 | pub fn to_location( |
326 | file_id: FileId, | 335 | file_id: FileId, |
327 | range: TextRange, | 336 | range: TextRange, |
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index 11825d74e..26b6c7d8a 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -188,12 +188,11 @@ pub fn handle_workspace_symbol( | |||
188 | 188 | ||
189 | fn exec_query(world: &ServerWorld, query: Query) -> Result<Vec<SymbolInformation>> { | 189 | fn exec_query(world: &ServerWorld, query: Query) -> Result<Vec<SymbolInformation>> { |
190 | let mut res = Vec::new(); | 190 | let mut res = Vec::new(); |
191 | for (file_id, symbol) in world.analysis().symbol_search(query)? { | 191 | for nav in world.analysis().symbol_search(query)? { |
192 | let line_index = world.analysis().file_line_index(file_id); | ||
193 | let info = SymbolInformation { | 192 | let info = SymbolInformation { |
194 | name: symbol.name.to_string(), | 193 | name: nav.name().into(), |
195 | kind: symbol.kind.conv(), | 194 | kind: nav.kind().conv(), |
196 | location: to_location(file_id, symbol.node_range, world, &line_index)?, | 195 | location: nav.try_conv_with(world)?, |
197 | container_name: None, | 196 | container_name: None, |
198 | deprecated: None, | 197 | deprecated: None, |
199 | }; | 198 | }; |
@@ -212,12 +211,11 @@ pub fn handle_goto_definition( | |||
212 | None => return Ok(None), | 211 | None => return Ok(None), |
213 | Some(it) => it, | 212 | Some(it) => it, |
214 | }; | 213 | }; |
215 | let mut res = Vec::new(); | 214 | let res = rr |
216 | for (file_id, symbol) in rr.resolves_to { | 215 | .resolves_to |
217 | let line_index = world.analysis().file_line_index(file_id); | 216 | .into_iter() |
218 | let location = to_location(file_id, symbol.node_range, &world, &line_index)?; | 217 | .map(|nav| nav.try_conv_with(&world)) |
219 | res.push(location) | 218 | .collect::<Result<Vec<_>>>()?; |
220 | } | ||
221 | Ok(Some(req::GotoDefinitionResponse::Array(res))) | 219 | Ok(Some(req::GotoDefinitionResponse::Array(res))) |
222 | } | 220 | } |
223 | 221 | ||
@@ -226,13 +224,12 @@ pub fn handle_parent_module( | |||
226 | params: req::TextDocumentPositionParams, | 224 | params: req::TextDocumentPositionParams, |
227 | ) -> Result<Vec<Location>> { | 225 | ) -> Result<Vec<Location>> { |
228 | let position = params.try_conv_with(&world)?; | 226 | let position = params.try_conv_with(&world)?; |
229 | let mut res = Vec::new(); | 227 | world |
230 | for (file_id, symbol) in world.analysis().parent_module(position)? { | 228 | .analysis() |
231 | let line_index = world.analysis().file_line_index(file_id); | 229 | .parent_module(position)? |
232 | let location = to_location(file_id, symbol.node_range, &world, &line_index)?; | 230 | .into_iter() |
233 | res.push(location); | 231 | .map(|nav| nav.try_conv_with(&world)) |
234 | } | 232 | .collect::<Result<Vec<_>>>() |
235 | Ok(res) | ||
236 | } | 233 | } |
237 | 234 | ||
238 | pub fn handle_runnables( | 235 | pub fn handle_runnables( |
@@ -517,8 +514,8 @@ pub fn handle_hover( | |||
517 | Some(it) => it, | 514 | Some(it) => it, |
518 | }; | 515 | }; |
519 | let mut result = Vec::new(); | 516 | let mut result = Vec::new(); |
520 | for (file_id, symbol) in rr.resolves_to { | 517 | for nav in rr.resolves_to { |
521 | if let Some(docs) = world.analysis().doc_text_for(file_id, symbol)? { | 518 | if let Some(docs) = world.analysis().doc_text_for(nav)? { |
522 | result.push(docs); | 519 | result.push(docs); |
523 | } | 520 | } |
524 | } | 521 | } |