From e6a4383bb475b866b67df6bb83ecbdf823d73667 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 8 Jan 2019 18:16:26 +0300 Subject: move call-info to a separate file --- crates/ra_analysis/src/call_info.rs | 119 ++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 crates/ra_analysis/src/call_info.rs (limited to 'crates/ra_analysis/src/call_info.rs') diff --git a/crates/ra_analysis/src/call_info.rs b/crates/ra_analysis/src/call_info.rs new file mode 100644 index 000000000..a31a54ef6 --- /dev/null +++ b/crates/ra_analysis/src/call_info.rs @@ -0,0 +1,119 @@ +use ra_db::{SyntaxDatabase, Cancelable}; +use ra_syntax::{ + AstNode, SyntaxNode, TextUnit, TextRange, + SyntaxKind::FN_DEF, + ast::{self, ArgListOwner}, +}; +use ra_editor::find_node_at_offset; +use hir::FnSignatureInfo; + +use crate::{FilePosition, db::RootDatabase}; + +/// Computes parameter information for the given call expression. +pub(crate) fn call_info( + db: &RootDatabase, + position: FilePosition, +) -> Cancelable)>> { + let file = db.source_file(position.file_id); + let syntax = file.syntax(); + + // Find the calling expression and it's NameRef + let calling_node = ctry!(FnCallNode::with_node(syntax, position.offset)); + let name_ref = ctry!(calling_node.name_ref()); + + // Resolve the function's NameRef (NOTE: this isn't entirely accurate). + let file_symbols = db.index_resolve(name_ref)?; + for symbol in file_symbols { + if symbol.ptr.kind() == FN_DEF { + let fn_file = db.source_file(symbol.file_id); + let fn_def = symbol.ptr.resolve(&fn_file); + let fn_def = ast::FnDef::cast(&fn_def).unwrap(); + let descr = ctry!(hir::source_binder::function_from_source( + db, + symbol.file_id, + fn_def + )?); + if let Some(descriptor) = descr.signature_info(db) { + // If we have a calling expression let's find which argument we are on + let mut current_parameter = None; + + let num_params = descriptor.params.len(); + let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some(); + + if num_params == 1 { + if !has_self { + current_parameter = Some(0); + } + } else if num_params > 1 { + // Count how many parameters into the call we are. + // TODO: This is best effort for now and should be fixed at some point. + // It may be better to see where we are in the arg_list and then check + // where offset is in that list (or beyond). + // Revisit this after we get documentation comments in. + if let Some(ref arg_list) = calling_node.arg_list() { + let start = arg_list.syntax().range().start(); + + let range_search = TextRange::from_to(start, position.offset); + let mut commas: usize = arg_list + .syntax() + .text() + .slice(range_search) + .to_string() + .matches(',') + .count(); + + // If we have a method call eat the first param since it's just self. + if has_self { + commas += 1; + } + + current_parameter = Some(commas); + } + } + + return Ok(Some((descriptor, current_parameter))); + } + } + } + + Ok(None) +} + +enum FnCallNode<'a> { + CallExpr(&'a ast::CallExpr), + MethodCallExpr(&'a ast::MethodCallExpr), +} + +impl<'a> FnCallNode<'a> { + pub fn with_node(syntax: &'a SyntaxNode, offset: TextUnit) -> Option> { + if let Some(expr) = find_node_at_offset::(syntax, offset) { + return Some(FnCallNode::CallExpr(expr)); + } + if let Some(expr) = find_node_at_offset::(syntax, offset) { + return Some(FnCallNode::MethodCallExpr(expr)); + } + None + } + + pub fn name_ref(&self) -> Option<&'a ast::NameRef> { + match *self { + FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()?.kind() { + ast::ExprKind::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?, + _ => return None, + }), + + FnCallNode::MethodCallExpr(call_expr) => call_expr + .syntax() + .children() + .filter_map(ast::NameRef::cast) + .nth(0), + } + } + + pub fn arg_list(&self) -> Option<&'a ast::ArgList> { + match *self { + FnCallNode::CallExpr(expr) => expr.arg_list(), + FnCallNode::MethodCallExpr(expr) => expr.arg_list(), + } + } +} -- cgit v1.2.3