diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs index 030790e470..06fdc641f8 100644 --- a/crates/ide/src/signature_help.rs +++ b/crates/ide/src/signature_help.rs @@ -4,11 +4,15 @@ use either::Either; use hir::{HasAttrs, HirDisplay, Semantics}; use ide_db::{ - active_parameter::{callable_for_token, generics_for_token}, + active_parameter::{callable_for_node, generics_for_token}, base_db::FilePosition, }; use stdx::format_to; -use syntax::{algo, AstNode, Direction, TextRange, TextSize}; +use syntax::{ + algo, + ast::{self, HasArgList}, + AstNode, Direction, SyntaxToken, TextRange, TextSize, +}; use crate::RootDatabase; @@ -65,8 +69,8 @@ pub(crate) fn signature_help(db: &RootDatabase, position: FilePosition) -> Optio .and_then(|tok| algo::skip_trivia_token(tok, Direction::Prev))?; let token = sema.descend_into_macros_single(token); - if let Some((callable, active_parameter)) = callable_for_token(&sema, token.clone()) { - return Some(signature_help_for_callable(db, callable, active_parameter)); + if let Some(help) = signature_help_for_call(&sema, &token) { + return Some(help); } if let Some((generic_def, active_parameter)) = generics_for_token(&sema, token.clone()) { @@ -76,14 +80,39 @@ pub(crate) fn signature_help(db: &RootDatabase, position: FilePosition) -> Optio None } -fn signature_help_for_callable( - db: &RootDatabase, - callable: hir::Callable, - active_parameter: Option, -) -> SignatureHelp { +fn signature_help_for_call( + sema: &Semantics, + token: &SyntaxToken, +) -> Option { + // Find the calling expression and its NameRef + let mut node = token.parent()?; + let calling_node = loop { + if let Some(callable) = ast::CallableExpr::cast(node.clone()) { + if callable + .arg_list() + .map_or(false, |it| it.syntax().text_range().contains(token.text_range().start())) + { + break callable; + } + } + + // Stop at multi-line expressions, since the signature of the outer call is not very + // helpful inside them. + if let Some(expr) = ast::Expr::cast(node.clone()) { + if expr.syntax().text().contains_char('\n') { + return None; + } + } + + node = node.parent()?; + }; + + let (callable, active_parameter) = callable_for_node(sema, &calling_node, token)?; + let mut res = SignatureHelp { doc: None, signature: String::new(), parameters: vec![], active_parameter }; + let db = sema.db; match callable.kind() { hir::CallableKind::Function(func) => { res.doc = func.docs(db).map(|it| it.into()); @@ -134,7 +163,7 @@ fn signature_help_for_callable( } hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {} } - res + Some(res) } fn signature_help_for_generics( @@ -786,6 +815,46 @@ fn main() { ) } + #[test] + fn test_multiline_argument() { + check( + r#" +fn callee(a: u8, b: u8) {} +fn main() { + callee(match 0 { + 0 => 1,$0 + }) +}"#, + expect![[r#""#]], + ); + check( + r#" +fn callee(a: u8, b: u8) {} +fn main() { + callee(match 0 { + 0 => 1, + },$0) +}"#, + expect![[r#" + fn callee(a: u8, b: u8) + ----- ^^^^^ + "#]], + ); + check( + r#" +fn callee(a: u8, b: u8) {} +fn main() { + callee($0match 0 { + 0 => 1, + }) +}"#, + expect![[r#" + fn callee(a: u8, b: u8) + ^^^^^ ----- + "#]], + ); + } + #[test] fn test_generics_simple() { check( diff --git a/crates/ide_db/src/active_parameter.rs b/crates/ide_db/src/active_parameter.rs index 67b819c5a5..ed0c010f83 100644 --- a/crates/ide_db/src/active_parameter.rs +++ b/crates/ide_db/src/active_parameter.rs @@ -43,13 +43,21 @@ pub fn callable_for_token( sema: &Semantics, token: SyntaxToken, ) -> Option<(hir::Callable, Option)> { - // Find the calling expression and it's NameRef + // Find the calling expression and its NameRef let parent = token.parent()?; let calling_node = parent.ancestors().filter_map(ast::CallableExpr::cast).find(|it| { it.arg_list() .map_or(false, |it| it.syntax().text_range().contains(token.text_range().start())) })?; + callable_for_node(sema, &calling_node, &token) +} + +pub fn callable_for_node( + sema: &Semantics, + calling_node: &ast::CallableExpr, + token: &SyntaxToken, +) -> Option<(hir::Callable, Option)> { let callable = match &calling_node { ast::CallableExpr::Call(call) => { let expr = call.expr()?;