diff options
-rw-r--r-- | crates/ra_analysis/src/imp.rs | 38 | ||||
-rw-r--r-- | crates/ra_analysis/src/lib.rs | 5 | ||||
-rw-r--r-- | crates/ra_analysis/tests/tests.rs | 12 | ||||
-rw-r--r-- | crates/ra_editor/src/symbols.rs | 55 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop/handlers.rs | 35 | ||||
-rw-r--r-- | crates/ra_syntax/src/grammar.ron | 4 | ||||
-rw-r--r-- | crates/ra_syntax/src/syntax_kinds/generated.rs | 1 | ||||
-rw-r--r-- | crates/ra_syntax/src/syntax_kinds/generated.rs.tera | 1 |
8 files changed, 116 insertions, 35 deletions
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs index 975afc145..99bcf0666 100644 --- a/crates/ra_analysis/src/imp.rs +++ b/crates/ra_analysis/src/imp.rs | |||
@@ -144,7 +144,7 @@ impl AnalysisImpl { | |||
144 | } else { | 144 | } else { |
145 | let files = &self.db.source_root(WORKSPACE).files; | 145 | let files = &self.db.source_root(WORKSPACE).files; |
146 | 146 | ||
147 | /// Need to wrap Snapshot to provide `Clon` impl for `map_with` | 147 | /// Need to wrap Snapshot to provide `Clone` impl for `map_with` |
148 | struct Snap(salsa::Snapshot<db::RootDatabase>); | 148 | struct Snap(salsa::Snapshot<db::RootDatabase>); |
149 | impl Clone for Snap { | 149 | impl Clone for Snap { |
150 | fn clone(&self) -> Snap { | 150 | fn clone(&self) -> Snap { |
@@ -164,7 +164,7 @@ impl AnalysisImpl { | |||
164 | .sweep(salsa::SweepStrategy::default().discard_values()); | 164 | .sweep(salsa::SweepStrategy::default().discard_values()); |
165 | Ok(query.search(&buf)) | 165 | Ok(query.search(&buf)) |
166 | } | 166 | } |
167 | /// This return `Vec`: a module may be included from several places. We | 167 | /// This returns `Vec` because a module may be included from several places. We |
168 | /// don't handle this case yet though, so the Vec has length at most one. | 168 | /// don't handle this case yet though, so the Vec has length at most one. |
169 | pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> { | 169 | pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> { |
170 | let descr = match source_binder::module_from_position(&*self.db, position)? { | 170 | let descr = match source_binder::module_from_position(&*self.db, position)? { |
@@ -206,7 +206,7 @@ impl AnalysisImpl { | |||
206 | pub fn approximately_resolve_symbol( | 206 | pub fn approximately_resolve_symbol( |
207 | &self, | 207 | &self, |
208 | position: FilePosition, | 208 | position: FilePosition, |
209 | ) -> Cancelable<Vec<(FileId, FileSymbol)>> { | 209 | ) -> Cancelable<Option<(TextRange, Vec<(FileId, FileSymbol)>)>> { |
210 | let file = self.db.source_file(position.file_id); | 210 | let file = self.db.source_file(position.file_id); |
211 | let syntax = file.syntax(); | 211 | let syntax = file.syntax(); |
212 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) { | 212 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) { |
@@ -218,20 +218,22 @@ impl AnalysisImpl { | |||
218 | let scope = fn_descr.scope(&*self.db); | 218 | let scope = fn_descr.scope(&*self.db); |
219 | // First try to resolve the symbol locally | 219 | // First try to resolve the symbol locally |
220 | if let Some(entry) = scope.resolve_local_name(name_ref) { | 220 | if let Some(entry) = scope.resolve_local_name(name_ref) { |
221 | let mut vec = vec![]; | 221 | let vec = vec![( |
222 | vec.push(( | ||
223 | position.file_id, | 222 | position.file_id, |
224 | FileSymbol { | 223 | FileSymbol { |
225 | name: entry.name().clone(), | 224 | name: entry.name().clone(), |
226 | node_range: entry.ptr().range(), | 225 | node_range: entry.ptr().range(), |
227 | kind: NAME, | 226 | kind: NAME, |
228 | }, | 227 | }, |
229 | )); | 228 | )]; |
230 | return Ok(vec); | 229 | return Ok(Some((name_ref.syntax().range(), vec))); |
231 | }; | 230 | }; |
232 | } | 231 | } |
233 | // If that fails try the index based approach. | 232 | // If that fails try the index based approach. |
234 | return self.index_resolve(name_ref); | 233 | return Ok(Some(( |
234 | name_ref.syntax().range(), | ||
235 | self.index_resolve(name_ref)?, | ||
236 | ))); | ||
235 | } | 237 | } |
236 | if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) { | 238 | if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) { |
237 | if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { | 239 | if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { |
@@ -248,7 +250,7 @@ impl AnalysisImpl { | |||
248 | node_range: TextRange::offset_len(0.into(), 0.into()), | 250 | node_range: TextRange::offset_len(0.into(), 0.into()), |
249 | kind: MODULE, | 251 | kind: MODULE, |
250 | }; | 252 | }; |
251 | return Ok(vec![(file_id, symbol)]); | 253 | return Ok(Some((name.syntax().range(), vec![(file_id, symbol)]))); |
252 | } | 254 | } |
253 | } | 255 | } |
254 | _ => (), | 256 | _ => (), |
@@ -256,7 +258,10 @@ impl AnalysisImpl { | |||
256 | } | 258 | } |
257 | } | 259 | } |
258 | } | 260 | } |
259 | Ok(vec![]) | 261 | let range = |
262 | ctry!(ra_syntax::algo::find_leaf_at_offset(syntax, position.offset).left_biased()) | ||
263 | .range(); | ||
264 | Ok(Some((range, vec![]))) | ||
260 | } | 265 | } |
261 | 266 | ||
262 | pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> { | 267 | pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> { |
@@ -318,6 +323,19 @@ impl AnalysisImpl { | |||
318 | 323 | ||
319 | Ok(symbol.docs(&file)) | 324 | Ok(symbol.docs(&file)) |
320 | } | 325 | } |
326 | pub fn doc_text_for(&self, file_id: FileId, symbol: FileSymbol) -> Cancelable<Option<String>> { | ||
327 | let file = self.db.source_file(file_id); | ||
328 | let result = match (symbol.description(&file), symbol.docs(&file)) { | ||
329 | (Some(desc), Some(docs)) => { | ||
330 | Some("```rust\n".to_string() + &*desc + "\n```\n\n" + &*docs) | ||
331 | } | ||
332 | (Some(desc), None) => Some("```rust\n".to_string() + &*desc + "\n```"), | ||
333 | (None, Some(docs)) => Some(docs), | ||
334 | _ => None, | ||
335 | }; | ||
336 | |||
337 | Ok(result) | ||
338 | } | ||
321 | 339 | ||
322 | pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> { | 340 | pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> { |
323 | let syntax = self.db.source_file(file_id); | 341 | let syntax = self.db.source_file(file_id); |
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs index 4b8b10816..d33f3e4ca 100644 --- a/crates/ra_analysis/src/lib.rs +++ b/crates/ra_analysis/src/lib.rs | |||
@@ -236,7 +236,7 @@ impl Analysis { | |||
236 | pub fn approximately_resolve_symbol( | 236 | pub fn approximately_resolve_symbol( |
237 | &self, | 237 | &self, |
238 | position: FilePosition, | 238 | position: FilePosition, |
239 | ) -> Cancelable<Vec<(FileId, FileSymbol)>> { | 239 | ) -> Cancelable<Option<(TextRange, Vec<(FileId, FileSymbol)>)>> { |
240 | self.imp.approximately_resolve_symbol(position) | 240 | self.imp.approximately_resolve_symbol(position) |
241 | } | 241 | } |
242 | pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> { | 242 | pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> { |
@@ -249,6 +249,9 @@ impl Analysis { | |||
249 | ) -> Cancelable<Option<String>> { | 249 | ) -> Cancelable<Option<String>> { |
250 | self.imp.doc_comment_for(file_id, symbol) | 250 | self.imp.doc_comment_for(file_id, symbol) |
251 | } | 251 | } |
252 | pub fn doc_text_for(&self, file_id: FileId, symbol: FileSymbol) -> Cancelable<Option<String>> { | ||
253 | self.imp.doc_text_for(file_id, symbol) | ||
254 | } | ||
252 | pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> { | 255 | pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> { |
253 | self.imp.parent_module(position) | 256 | self.imp.parent_module(position) |
254 | } | 257 | } |
diff --git a/crates/ra_analysis/tests/tests.rs b/crates/ra_analysis/tests/tests.rs index 4ce2c5c85..05ad687ae 100644 --- a/crates/ra_analysis/tests/tests.rs +++ b/crates/ra_analysis/tests/tests.rs | |||
@@ -21,9 +21,9 @@ fn approximate_resolve_works_in_items() { | |||
21 | ", | 21 | ", |
22 | ); | 22 | ); |
23 | 23 | ||
24 | let symbols = analysis.approximately_resolve_symbol(pos).unwrap(); | 24 | let symbols = analysis.approximately_resolve_symbol(pos).unwrap().unwrap(); |
25 | assert_eq_dbg( | 25 | assert_eq_dbg( |
26 | r#"[(FileId(1), FileSymbol { name: "Foo", node_range: [0; 11), kind: STRUCT_DEF })]"#, | 26 | r#"([23; 26), [(FileId(1), FileSymbol { name: "Foo", node_range: [0; 11), kind: STRUCT_DEF })])"#, |
27 | &symbols, | 27 | &symbols, |
28 | ); | 28 | ); |
29 | } | 29 | } |
@@ -39,9 +39,9 @@ fn test_resolve_module() { | |||
39 | ", | 39 | ", |
40 | ); | 40 | ); |
41 | 41 | ||
42 | let symbols = analysis.approximately_resolve_symbol(pos).unwrap(); | 42 | let symbols = analysis.approximately_resolve_symbol(pos).unwrap().unwrap(); |
43 | assert_eq_dbg( | 43 | assert_eq_dbg( |
44 | r#"[(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })]"#, | 44 | r#"([4; 7), [(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })])"#, |
45 | &symbols, | 45 | &symbols, |
46 | ); | 46 | ); |
47 | 47 | ||
@@ -54,9 +54,9 @@ fn test_resolve_module() { | |||
54 | ", | 54 | ", |
55 | ); | 55 | ); |
56 | 56 | ||
57 | let symbols = analysis.approximately_resolve_symbol(pos).unwrap(); | 57 | let symbols = analysis.approximately_resolve_symbol(pos).unwrap().unwrap(); |
58 | assert_eq_dbg( | 58 | assert_eq_dbg( |
59 | r#"[(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })]"#, | 59 | r#"([4; 7), [(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })])"#, |
60 | &symbols, | 60 | &symbols, |
61 | ); | 61 | ); |
62 | } | 62 | } |
diff --git a/crates/ra_editor/src/symbols.rs b/crates/ra_editor/src/symbols.rs index 6d3b0514a..9e25decfb 100644 --- a/crates/ra_editor/src/symbols.rs +++ b/crates/ra_editor/src/symbols.rs | |||
@@ -50,6 +50,61 @@ impl FileSymbol { | |||
50 | }) | 50 | }) |
51 | .nth(0) | 51 | .nth(0) |
52 | } | 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 | } | ||
53 | } | 108 | } |
54 | 109 | ||
55 | pub fn file_symbols(file: &SourceFileNode) -> Vec<FileSymbol> { | 110 | pub fn file_symbols(file: &SourceFileNode) -> Vec<FileSymbol> { |
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index af21254e4..f18a1305d 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -204,7 +204,10 @@ pub fn handle_goto_definition( | |||
204 | ) -> Result<Option<req::GotoDefinitionResponse>> { | 204 | ) -> Result<Option<req::GotoDefinitionResponse>> { |
205 | let position = params.try_conv_with(&world)?; | 205 | let position = params.try_conv_with(&world)?; |
206 | let mut res = Vec::new(); | 206 | let mut res = Vec::new(); |
207 | for (file_id, symbol) in world.analysis().approximately_resolve_symbol(position)? { | 207 | for (file_id, symbol) in match world.analysis().approximately_resolve_symbol(position)? { |
208 | None => return Ok(None), | ||
209 | Some(it) => it.1, | ||
210 | } { | ||
208 | let line_index = world.analysis().file_line_index(file_id); | 211 | let line_index = world.analysis().file_line_index(file_id); |
209 | let location = to_location(file_id, symbol.node_range, &world, &line_index)?; | 212 | let location = to_location(file_id, symbol.node_range, &world, &line_index)?; |
210 | res.push(location) | 213 | res.push(location) |
@@ -504,26 +507,30 @@ pub fn handle_hover( | |||
504 | world: ServerWorld, | 507 | world: ServerWorld, |
505 | params: req::TextDocumentPositionParams, | 508 | params: req::TextDocumentPositionParams, |
506 | ) -> Result<Option<Hover>> { | 509 | ) -> Result<Option<Hover>> { |
510 | // TODO: Cut down on number of allocations | ||
507 | let position = params.try_conv_with(&world)?; | 511 | let position = params.try_conv_with(&world)?; |
508 | let line_index = world.analysis().file_line_index(position.file_id); | 512 | let line_index = world.analysis().file_line_index(position.file_id); |
509 | 513 | let (range, resolved) = match world.analysis().approximately_resolve_symbol(position)? { | |
510 | for (file_id, symbol) in world.analysis().approximately_resolve_symbol(position)? { | 514 | None => return Ok(None), |
511 | let range = symbol.node_range.conv_with(&line_index); | 515 | Some(it) => it, |
512 | let comment = world.analysis.doc_comment_for(file_id, symbol)?; | 516 | }; |
513 | 517 | let mut result = Vec::new(); | |
514 | if comment.is_some() { | 518 | for (file_id, symbol) in resolved { |
515 | let contents = HoverContents::Scalar(MarkedString::String(comment.unwrap())); | 519 | if let Some(docs) = world.analysis().doc_text_for(file_id, symbol)? { |
516 | 520 | result.push(docs); | |
517 | return Ok(Some(Hover { | ||
518 | contents, | ||
519 | range: Some(range), | ||
520 | })); | ||
521 | } | 521 | } |
522 | } | 522 | } |
523 | 523 | let range = range.conv_with(&line_index); | |
524 | if result.len() > 0 { | ||
525 | return Ok(Some(Hover { | ||
526 | contents: HoverContents::Scalar(MarkedString::String(result.join("\n\n---\n"))), | ||
527 | range: Some(range), | ||
528 | })); | ||
529 | } | ||
524 | Ok(None) | 530 | Ok(None) |
525 | } | 531 | } |
526 | 532 | ||
533 | /// Test doc comment | ||
527 | pub fn handle_prepare_rename( | 534 | pub fn handle_prepare_rename( |
528 | world: ServerWorld, | 535 | world: ServerWorld, |
529 | params: req::TextDocumentPositionParams, | 536 | params: req::TextDocumentPositionParams, |
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 53cd2118f..eed67637e 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron | |||
@@ -25,9 +25,9 @@ Grammar( | |||
25 | ["^", "CARET"], | 25 | ["^", "CARET"], |
26 | ["%", "PERCENT"], | 26 | ["%", "PERCENT"], |
27 | ], | 27 | ], |
28 | // TODO: Confirm surmision: the tokens which cannot be recorded in a single UTF-8 byte | 28 | // Tokens for which the longest match must be chosen (e.g. `..` is a DOTDOT, but `.` is a DOT) |
29 | multi_byte_tokens: [ | 29 | multi_byte_tokens: [ |
30 | [".", "DOT"], // Note: DOT is here because <TODO: REASON> | 30 | [".", "DOT"], |
31 | ["..", "DOTDOT"], | 31 | ["..", "DOTDOT"], |
32 | ["...", "DOTDOTDOT"], | 32 | ["...", "DOTDOTDOT"], |
33 | ["..=", "DOTDOTEQ"], | 33 | ["..=", "DOTDOTEQ"], |
diff --git a/crates/ra_syntax/src/syntax_kinds/generated.rs b/crates/ra_syntax/src/syntax_kinds/generated.rs index c43a8bf65..3a869ad34 100644 --- a/crates/ra_syntax/src/syntax_kinds/generated.rs +++ b/crates/ra_syntax/src/syntax_kinds/generated.rs | |||
@@ -560,4 +560,3 @@ impl SyntaxKind { | |||
560 | Some(tok) | 560 | Some(tok) |
561 | } | 561 | } |
562 | } | 562 | } |
563 | |||
diff --git a/crates/ra_syntax/src/syntax_kinds/generated.rs.tera b/crates/ra_syntax/src/syntax_kinds/generated.rs.tera index c37e57702..21f9444b1 100644 --- a/crates/ra_syntax/src/syntax_kinds/generated.rs.tera +++ b/crates/ra_syntax/src/syntax_kinds/generated.rs.tera | |||
@@ -75,4 +75,3 @@ impl SyntaxKind { | |||
75 | Some(tok) | 75 | Some(tok) |
76 | } | 76 | } |
77 | } | 77 | } |
78 | |||