aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis/src/imp.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_analysis/src/imp.rs')
-rw-r--r--crates/ra_analysis/src/imp.rs211
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::{
8use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase}; 8use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase};
9use ra_editor::{self, find_node_at_offset, LocalEdit, Severity}; 9use ra_editor::{self, find_node_at_offset, LocalEdit, Severity};
10use ra_syntax::{ 10use 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
18use crate::{ 18use 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
516impl 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}