diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-01-03 09:59:24 +0000 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-01-03 09:59:24 +0000 |
commit | e92f2ffe270c6a9fca312fb8a53cd0da0dd01fde (patch) | |
tree | 7f72fee435e82c750b321a52c551010d1b7afd09 /crates/ra_analysis/src/imp.rs | |
parent | f673529a3355cdd00062c873cd4fdd100f60a243 (diff) | |
parent | 8d6185350953391d0dd405cf790a69bb65d88a36 (diff) |
Merge #410
410: Detangle symbols r=matklad a=matklad
Previously, we used `FileSymbol` both to represent bytes which are stored in the index and as an API of `ra_analysis`. Mixing internal storage format and an API is not a really bright idea, so we introduce `NavigationTarget` to handle API part.
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_analysis/src/imp.rs')
-rw-r--r-- | crates/ra_analysis/src/imp.rs | 211 |
1 files changed, 150 insertions, 61 deletions
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs index ff13247de..b812c3441 100644 --- a/crates/ra_analysis/src/imp.rs +++ b/crates/ra_analysis/src/imp.rs | |||
@@ -8,11 +8,11 @@ use hir::{ | |||
8 | use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase}; | 8 | use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase}; |
9 | use ra_editor::{self, find_node_at_offset, LocalEdit, Severity}; | 9 | use ra_editor::{self, find_node_at_offset, LocalEdit, Severity}; |
10 | use ra_syntax::{ | 10 | use ra_syntax::{ |
11 | algo::find_covering_node, | 11 | algo::{find_covering_node, visit::{visitor, Visitor}}, |
12 | ast::{self, ArgListOwner, Expr, FnDef, NameOwner}, | 12 | ast::{self, ArgListOwner, Expr, FnDef, NameOwner}, |
13 | AstNode, SourceFileNode, | 13 | AstNode, SourceFileNode, |
14 | SyntaxKind::*, | 14 | SyntaxKind::*, |
15 | SyntaxNodeRef, TextRange, TextUnit, | 15 | SyntaxNode, SyntaxNodeRef, TextRange, TextUnit, |
16 | }; | 16 | }; |
17 | 17 | ||
18 | use crate::{ | 18 | use crate::{ |
@@ -116,12 +116,13 @@ impl db::RootDatabase { | |||
116 | }; | 116 | }; |
117 | let decl = decl.borrowed(); | 117 | let decl = decl.borrowed(); |
118 | let decl_name = decl.name().unwrap(); | 118 | let decl_name = decl.name().unwrap(); |
119 | let symbol = FileSymbol { | 119 | Ok(vec![NavigationTarget { |
120 | file_id, | ||
120 | name: decl_name.text(), | 121 | name: decl_name.text(), |
121 | node_range: decl_name.syntax().range(), | 122 | range: decl_name.syntax().range(), |
122 | kind: MODULE, | 123 | kind: MODULE, |
123 | }; | 124 | ptr: None, |
124 | Ok(vec![NavigationTarget { file_id, symbol }]) | 125 | }]) |
125 | } | 126 | } |
126 | /// Returns `Vec` for the same reason as `parent_module` | 127 | /// Returns `Vec` for the same reason as `parent_module` |
127 | pub(crate) fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { | 128 | pub(crate) fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { |
@@ -153,14 +154,13 @@ impl db::RootDatabase { | |||
153 | let scope = fn_descr.scopes(self); | 154 | let scope = fn_descr.scopes(self); |
154 | // First try to resolve the symbol locally | 155 | // First try to resolve the symbol locally |
155 | if let Some(entry) = scope.resolve_local_name(name_ref) { | 156 | if let Some(entry) = scope.resolve_local_name(name_ref) { |
156 | rr.add_resolution( | 157 | rr.resolves_to.push(NavigationTarget { |
157 | position.file_id, | 158 | file_id: position.file_id, |
158 | FileSymbol { | 159 | name: entry.name().to_string().into(), |
159 | name: entry.name().to_string().into(), | 160 | range: entry.ptr().range(), |
160 | node_range: entry.ptr().range(), | 161 | kind: NAME, |
161 | kind: NAME, | 162 | ptr: None, |
162 | }, | 163 | }); |
163 | ); | ||
164 | return Ok(Some(rr)); | 164 | return Ok(Some(rr)); |
165 | }; | 165 | }; |
166 | } | 166 | } |
@@ -182,12 +182,14 @@ impl db::RootDatabase { | |||
182 | Some(name) => name.to_string().into(), | 182 | Some(name) => name.to_string().into(), |
183 | None => "".into(), | 183 | None => "".into(), |
184 | }; | 184 | }; |
185 | let symbol = FileSymbol { | 185 | let symbol = NavigationTarget { |
186 | file_id, | ||
186 | name, | 187 | name, |
187 | node_range: TextRange::offset_len(0.into(), 0.into()), | 188 | range: TextRange::offset_len(0.into(), 0.into()), |
188 | kind: MODULE, | 189 | kind: MODULE, |
190 | ptr: None, | ||
189 | }; | 191 | }; |
190 | rr.add_resolution(file_id, symbol); | 192 | rr.resolves_to.push(symbol); |
191 | return Ok(Some(rr)); | 193 | return Ok(Some(rr)); |
192 | } | 194 | } |
193 | } | 195 | } |
@@ -253,8 +255,7 @@ impl db::RootDatabase { | |||
253 | } | 255 | } |
254 | } | 256 | } |
255 | pub(crate) fn doc_text_for(&self, nav: NavigationTarget) -> Cancelable<Option<String>> { | 257 | pub(crate) fn doc_text_for(&self, nav: NavigationTarget) -> Cancelable<Option<String>> { |
256 | let file = self.source_file(nav.file_id); | 258 | let result = match (nav.description(self), nav.docs(self)) { |
257 | let result = match (nav.symbol.description(&file), nav.symbol.docs(&file)) { | ||
258 | (Some(desc), Some(docs)) => { | 259 | (Some(desc), Some(docs)) => { |
259 | Some("```rust\n".to_string() + &*desc + "\n```\n\n" + &*docs) | 260 | Some("```rust\n".to_string() + &*desc + "\n```\n\n" + &*docs) |
260 | } | 261 | } |
@@ -362,52 +363,52 @@ impl db::RootDatabase { | |||
362 | // Resolve the function's NameRef (NOTE: this isn't entirely accurate). | 363 | // Resolve the function's NameRef (NOTE: this isn't entirely accurate). |
363 | let file_symbols = self.index_resolve(name_ref)?; | 364 | let file_symbols = self.index_resolve(name_ref)?; |
364 | for (fn_file_id, fs) in file_symbols { | 365 | for (fn_file_id, fs) in file_symbols { |
365 | if fs.kind == FN_DEF { | 366 | if fs.ptr.kind() == FN_DEF { |
366 | let fn_file = self.source_file(fn_file_id); | 367 | let fn_file = self.source_file(fn_file_id); |
367 | if let Some(fn_def) = find_node_at_offset(fn_file.syntax(), fs.node_range.start()) { | 368 | let fn_def = fs.ptr.resolve(&fn_file); |
368 | let descr = ctry!(source_binder::function_from_source( | 369 | let fn_def = ast::FnDef::cast(fn_def.borrowed()).unwrap(); |
369 | self, fn_file_id, fn_def | 370 | let descr = ctry!(source_binder::function_from_source( |
370 | )?); | 371 | self, fn_file_id, fn_def |
371 | if let Some(descriptor) = descr.signature_info(self) { | 372 | )?); |
372 | // If we have a calling expression let's find which argument we are on | 373 | if let Some(descriptor) = descr.signature_info(self) { |
373 | let mut current_parameter = None; | 374 | // If we have a calling expression let's find which argument we are on |
374 | 375 | let mut current_parameter = None; | |
375 | let num_params = descriptor.params.len(); | 376 | |
376 | let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some(); | 377 | let num_params = descriptor.params.len(); |
377 | 378 | let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some(); | |
378 | if num_params == 1 { | 379 | |
379 | if !has_self { | 380 | if num_params == 1 { |
380 | current_parameter = Some(0); | 381 | if !has_self { |
381 | } | 382 | current_parameter = Some(0); |
382 | } else if num_params > 1 { | ||
383 | // Count how many parameters into the call we are. | ||
384 | // TODO: This is best effort for now and should be fixed at some point. | ||
385 | // It may be better to see where we are in the arg_list and then check | ||
386 | // where offset is in that list (or beyond). | ||
387 | // Revisit this after we get documentation comments in. | ||
388 | if let Some(ref arg_list) = calling_node.arg_list() { | ||
389 | let start = arg_list.syntax().range().start(); | ||
390 | |||
391 | let range_search = TextRange::from_to(start, position.offset); | ||
392 | let mut commas: usize = arg_list | ||
393 | .syntax() | ||
394 | .text() | ||
395 | .slice(range_search) | ||
396 | .to_string() | ||
397 | .matches(',') | ||
398 | .count(); | ||
399 | |||
400 | // If we have a method call eat the first param since it's just self. | ||
401 | if has_self { | ||
402 | commas += 1; | ||
403 | } | ||
404 | |||
405 | current_parameter = Some(commas); | ||
406 | } | ||
407 | } | 383 | } |
384 | } else if num_params > 1 { | ||
385 | // Count how many parameters into the call we are. | ||
386 | // TODO: This is best effort for now and should be fixed at some point. | ||
387 | // It may be better to see where we are in the arg_list and then check | ||
388 | // where offset is in that list (or beyond). | ||
389 | // Revisit this after we get documentation comments in. | ||
390 | if let Some(ref arg_list) = calling_node.arg_list() { | ||
391 | let start = arg_list.syntax().range().start(); | ||
392 | |||
393 | let range_search = TextRange::from_to(start, position.offset); | ||
394 | let mut commas: usize = arg_list | ||
395 | .syntax() | ||
396 | .text() | ||
397 | .slice(range_search) | ||
398 | .to_string() | ||
399 | .matches(',') | ||
400 | .count(); | ||
401 | |||
402 | // If we have a method call eat the first param since it's just self. | ||
403 | if has_self { | ||
404 | commas += 1; | ||
405 | } | ||
408 | 406 | ||
409 | return Ok(Some((descriptor, current_parameter))); | 407 | current_parameter = Some(commas); |
408 | } | ||
410 | } | 409 | } |
410 | |||
411 | return Ok(Some((descriptor, current_parameter))); | ||
411 | } | 412 | } |
412 | } | 413 | } |
413 | } | 414 | } |
@@ -511,3 +512,91 @@ impl<'a> FnCallNode<'a> { | |||
511 | } | 512 | } |
512 | } | 513 | } |
513 | } | 514 | } |
515 | |||
516 | impl NavigationTarget { | ||
517 | fn node(&self, db: &db::RootDatabase) -> Option<SyntaxNode> { | ||
518 | let source_file = db.source_file(self.file_id); | ||
519 | let source_file = source_file.syntax(); | ||
520 | let node = source_file | ||
521 | .descendants() | ||
522 | .find(|node| node.kind() == self.kind && node.range() == self.range)? | ||
523 | .owned(); | ||
524 | Some(node) | ||
525 | } | ||
526 | |||
527 | fn docs(&self, db: &db::RootDatabase) -> Option<String> { | ||
528 | let node = self.node(db)?; | ||
529 | let node = node.borrowed(); | ||
530 | fn doc_comments<'a, N: ast::DocCommentsOwner<'a>>(node: N) -> Option<String> { | ||
531 | let comments = node.doc_comment_text(); | ||
532 | if comments.is_empty() { | ||
533 | None | ||
534 | } else { | ||
535 | Some(comments) | ||
536 | } | ||
537 | } | ||
538 | |||
539 | visitor() | ||
540 | .visit(doc_comments::<ast::FnDef>) | ||
541 | .visit(doc_comments::<ast::StructDef>) | ||
542 | .visit(doc_comments::<ast::EnumDef>) | ||
543 | .visit(doc_comments::<ast::TraitDef>) | ||
544 | .visit(doc_comments::<ast::Module>) | ||
545 | .visit(doc_comments::<ast::TypeDef>) | ||
546 | .visit(doc_comments::<ast::ConstDef>) | ||
547 | .visit(doc_comments::<ast::StaticDef>) | ||
548 | .accept(node)? | ||
549 | } | ||
550 | |||
551 | /// Get a description of this node. | ||
552 | /// | ||
553 | /// e.g. `struct Name`, `enum Name`, `fn Name` | ||
554 | fn description(&self, db: &db::RootDatabase) -> Option<String> { | ||
555 | // TODO: After type inference is done, add type information to improve the output | ||
556 | let node = self.node(db)?; | ||
557 | let node = node.borrowed(); | ||
558 | // TODO: Refactor to be have less repetition | ||
559 | visitor() | ||
560 | .visit(|node: ast::FnDef| { | ||
561 | let mut string = "fn ".to_string(); | ||
562 | node.name()?.syntax().text().push_to(&mut string); | ||
563 | Some(string) | ||
564 | }) | ||
565 | .visit(|node: ast::StructDef| { | ||
566 | let mut string = "struct ".to_string(); | ||
567 | node.name()?.syntax().text().push_to(&mut string); | ||
568 | Some(string) | ||
569 | }) | ||
570 | .visit(|node: ast::EnumDef| { | ||
571 | let mut string = "enum ".to_string(); | ||
572 | node.name()?.syntax().text().push_to(&mut string); | ||
573 | Some(string) | ||
574 | }) | ||
575 | .visit(|node: ast::TraitDef| { | ||
576 | let mut string = "trait ".to_string(); | ||
577 | node.name()?.syntax().text().push_to(&mut string); | ||
578 | Some(string) | ||
579 | }) | ||
580 | .visit(|node: ast::Module| { | ||
581 | let mut string = "mod ".to_string(); | ||
582 | node.name()?.syntax().text().push_to(&mut string); | ||
583 | Some(string) | ||
584 | }) | ||
585 | .visit(|node: ast::TypeDef| { | ||
586 | let mut string = "type ".to_string(); | ||
587 | node.name()?.syntax().text().push_to(&mut string); | ||
588 | Some(string) | ||
589 | }) | ||
590 | .visit(|node: ast::ConstDef| { | ||
591 | let mut string = "const ".to_string(); | ||
592 | node.name()?.syntax().text().push_to(&mut string); | ||
593 | Some(string) | ||
594 | }) | ||
595 | .visit(|node: ast::StaticDef| { | ||
596 | let mut string = "static ".to_string(); | ||
597 | node.name()?.syntax().text().push_to(&mut string); | ||
598 | Some(string) | ||
599 | }) | ||
600 | .accept(node)? | ||
601 | } | ||
602 | } | ||