aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis/src/imp.rs
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2018-10-15 17:41:57 +0100
committerbors[bot] <bors[bot]@users.noreply.github.com>2018-10-15 17:41:57 +0100
commite031b65f93f73164a5729cf81ff60299708bc931 (patch)
tree1a891b75af3b436549381e8726c48ec5028a8341 /crates/ra_analysis/src/imp.rs
parent8cec1d986161298dbe9cc53f5477e87bfe5d0f47 (diff)
parentc9909f42ba4adf55b1e73e7118b48f1b10c80ac6 (diff)
Merge #110
110: Signature help r=matklad a=kjeremy @matklad Once this is in shape I would like to add tests. I think a separate PR should be done for returning documentation information and markdown. Fixes #102 Co-authored-by: Jeremy A. Kolb <[email protected]>
Diffstat (limited to 'crates/ra_analysis/src/imp.rs')
-rw-r--r--crates/ra_analysis/src/imp.rs112
1 files changed, 108 insertions, 4 deletions
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs
index 47bc0032b..aad54b977 100644
--- a/crates/ra_analysis/src/imp.rs
+++ b/crates/ra_analysis/src/imp.rs
@@ -12,19 +12,18 @@ use relative_path::RelativePath;
12use rustc_hash::FxHashSet; 12use rustc_hash::FxHashSet;
13use ra_editor::{self, FileSymbol, LineIndex, find_node_at_offset, LocalEdit, resolve_local_name}; 13use ra_editor::{self, FileSymbol, LineIndex, find_node_at_offset, LocalEdit, resolve_local_name};
14use ra_syntax::{ 14use ra_syntax::{
15 TextUnit, TextRange, SmolStr, File, AstNode, 15 TextUnit, TextRange, SmolStr, File, AstNode, SyntaxNodeRef,
16 SyntaxKind::*, 16 SyntaxKind::*,
17 ast::{self, NameOwner}, 17 ast::{self, NameOwner, ArgListOwner, Expr},
18}; 18};
19 19
20use { 20use {
21 FileId, FileResolver, Query, Diagnostic, SourceChange, SourceFileEdit, Position, FileSystemEdit, 21 FileId, FileResolver, Query, Diagnostic, SourceChange, SourceFileEdit, Position, FileSystemEdit,
22 JobToken, CrateGraph, CrateId, 22 JobToken, CrateGraph, CrateId,
23 roots::{SourceRoot, ReadonlySourceRoot, WritableSourceRoot}, 23 roots::{SourceRoot, ReadonlySourceRoot, WritableSourceRoot},
24 descriptors::{ModuleTreeDescriptor, Problem}, 24 descriptors::{FnDescriptor, ModuleTreeDescriptor, Problem},
25}; 25};
26 26
27
28#[derive(Clone, Debug)] 27#[derive(Clone, Debug)]
29pub(crate) struct FileResolverImp { 28pub(crate) struct FileResolverImp {
30 inner: Arc<FileResolver> 29 inner: Arc<FileResolver>
@@ -306,6 +305,68 @@ impl AnalysisImpl {
306 .collect() 305 .collect()
307 } 306 }
308 307
308 pub fn resolve_callable(&self, file_id: FileId, offset: TextUnit, token: &JobToken)
309 -> Option<(FnDescriptor, Option<usize>)> {
310
311 let root = self.root(file_id);
312 let file = root.syntax(file_id);
313 let syntax = file.syntax();
314
315 // Find the calling expression and it's NameRef
316 let calling_node = FnCallNode::with_node(syntax, offset)?;
317 let name_ref = calling_node.name_ref()?;
318
319 // Resolve the function's NameRef (NOTE: this isn't entirely accurate).
320 let file_symbols = self.index_resolve(name_ref, token);
321 for (_, fs) in file_symbols {
322 if fs.kind == FN_DEF {
323 if let Some(fn_def) = find_node_at_offset(syntax, fs.node_range.start()) {
324 if let Some(descriptor) = FnDescriptor::new(fn_def) {
325 // If we have a calling expression let's find which argument we are on
326 let mut current_parameter = None;
327
328 let num_params = descriptor.params.len();
329 let has_self = fn_def.param_list()
330 .and_then(|l| l.self_param())
331 .is_some();
332
333 if num_params == 1 {
334 if !has_self {
335 current_parameter = Some(1);
336 }
337 } else if num_params > 1 {
338 // Count how many parameters into the call we are.
339 // TODO: This is best effort for now and should be fixed at some point.
340 // It may be better to see where we are in the arg_list and then check
341 // where offset is in that list (or beyond).
342 // Revisit this after we get documentation comments in.
343 if let Some(ref arg_list) = calling_node.arg_list() {
344 let start = arg_list.syntax().range().start();
345
346 let range_search = TextRange::from_to(start, offset);
347 let mut commas: usize = arg_list.syntax().text()
348 .slice(range_search).to_string()
349 .matches(",")
350 .count();
351
352 // If we have a method call eat the first param since it's just self.
353 if has_self {
354 commas = commas + 1;
355 }
356
357 current_parameter = Some(commas);
358 }
359 }
360
361 return Some((descriptor, current_parameter));
362 }
363 }
364 }
365 }
366
367 None
368 }
369
309 fn index_resolve(&self, name_ref: ast::NameRef, token: &JobToken) -> Vec<(FileId, FileSymbol)> { 370 fn index_resolve(&self, name_ref: ast::NameRef, token: &JobToken) -> Vec<(FileId, FileSymbol)> {
310 let name = name_ref.text(); 371 let name = name_ref.text();
311 let mut query = Query::new(name.to_string()); 372 let mut query = Query::new(name.to_string());
@@ -355,3 +416,46 @@ impl CrateGraph {
355 Some(crate_id) 416 Some(crate_id)
356 } 417 }
357} 418}
419
420enum FnCallNode<'a> {
421 CallExpr(ast::CallExpr<'a>),
422 MethodCallExpr(ast::MethodCallExpr<'a>)
423}
424
425impl<'a> FnCallNode<'a> {
426 pub fn with_node(syntax: SyntaxNodeRef, offset: TextUnit) -> Option<FnCallNode> {
427 if let Some(expr) = find_node_at_offset::<ast::CallExpr>(syntax, offset) {
428 return Some(FnCallNode::CallExpr(expr));
429 }
430 if let Some(expr) = find_node_at_offset::<ast::MethodCallExpr>(syntax, offset) {
431 return Some(FnCallNode::MethodCallExpr(expr));
432 }
433 None
434 }
435
436 pub fn name_ref(&self) -> Option<ast::NameRef> {
437 match *self {
438 FnCallNode::CallExpr(call_expr) => {
439 Some(match call_expr.expr()? {
440 Expr::PathExpr(path_expr) => {
441 path_expr.path()?.segment()?.name_ref()?
442 },
443 _ => return None
444 })
445 },
446
447 FnCallNode::MethodCallExpr(call_expr) => {
448 call_expr.syntax().children()
449 .filter_map(ast::NameRef::cast)
450 .nth(0)
451 }
452 }
453 }
454
455 pub fn arg_list(&self) -> Option<ast::ArgList> {
456 match *self {
457 FnCallNode::CallExpr(expr) => expr.arg_list(),
458 FnCallNode::MethodCallExpr(expr) => expr.arg_list()
459 }
460 }
461} \ No newline at end of file