From c390e92fdd25ced46c589bfbff94e4b0bc4d9c38 Mon Sep 17 00:00:00 2001 From: imtsuki Date: Wed, 15 Jan 2020 01:02:01 +0800 Subject: Add inlay parameter name hints for function calls Signed-off-by: imtsuki --- crates/ra_ide/src/display/function_signature.rs | 22 ++++ crates/ra_ide/src/inlay_hints.rs | 141 +++++++++++++++++++++++- crates/ra_lsp_server/src/main_loop/handlers.rs | 1 + crates/ra_lsp_server/src/req.rs | 1 + 4 files changed, 163 insertions(+), 2 deletions(-) (limited to 'crates') diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs index 324ad9552..b0eb1f194 100644 --- a/crates/ra_ide/src/display/function_signature.rs +++ b/crates/ra_ide/src/display/function_signature.rs @@ -34,6 +34,8 @@ pub struct FunctionSignature { pub generic_parameters: Vec, /// Parameters of the function pub parameters: Vec, + /// Parameter names of the function + pub parameter_names: Vec, /// Optional return type pub ret_type: Option, /// Where predicates @@ -75,6 +77,7 @@ impl FunctionSignature { name: node.name().map(|n| n.text().to_string()), ret_type: node.name().map(|n| n.text().to_string()), parameters: params, + parameter_names: vec![], generic_parameters: generic_parameters(&node), where_predicates: where_predicates(&node), doc: None, @@ -114,6 +117,7 @@ impl FunctionSignature { name: Some(name), ret_type: None, parameters: params, + parameter_names: vec![], generic_parameters: vec![], where_predicates: vec![], doc: None, @@ -134,6 +138,7 @@ impl FunctionSignature { name: node.name().map(|n| n.text().to_string()), ret_type: None, parameters: params, + parameter_names: vec![], generic_parameters: vec![], where_predicates: vec![], doc: None, @@ -157,6 +162,22 @@ impl From<&'_ ast::FnDef> for FunctionSignature { res } + fn param_name_list(node: &ast::FnDef) -> Vec { + let mut res = vec![]; + if let Some(param_list) = node.param_list() { + if let Some(self_param) = param_list.self_param() { + res.push(self_param.syntax().text().to_string()) + } + + res.extend( + param_list + .params() + .map(|param| param.pat().unwrap().syntax().text().to_string()), + ); + } + res + } + FunctionSignature { kind: CallableKind::Function, visibility: node.visibility().map(|n| n.syntax().text().to_string()), @@ -166,6 +187,7 @@ impl From<&'_ ast::FnDef> for FunctionSignature { .and_then(|r| r.type_ref()) .map(|n| n.syntax().text().to_string()), parameters: param_list(node), + parameter_names: param_name_list(node), generic_parameters: generic_parameters(node), where_predicates: where_predicates(node), // docs are processed separately diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index 977aafc51..83e4588c1 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs @@ -4,15 +4,16 @@ use hir::{HirDisplay, SourceAnalyzer}; use once_cell::unsync::Lazy; use ra_prof::profile; use ra_syntax::{ - ast::{self, AstNode, TypeAscriptionOwner}, + ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner}, match_ast, SmolStr, SourceFile, SyntaxKind, SyntaxNode, TextRange, }; -use crate::{db::RootDatabase, FileId}; +use crate::{db::RootDatabase, FileId, FunctionSignature}; #[derive(Debug, PartialEq, Eq)] pub enum InlayKind { TypeHint, + ParameterHint, } #[derive(Debug)] @@ -87,10 +88,79 @@ fn get_inlay_hints( .collect(), ) }, + ast::CallExpr(it) => { + get_param_name_hints(db, &analyzer, ast::Expr::from(it)) + }, + ast::MethodCallExpr(it) => { + get_param_name_hints(db, &analyzer, ast::Expr::from(it)) + }, _ => None, } } } +fn get_param_name_hints( + db: &RootDatabase, + analyzer: &SourceAnalyzer, + expr: ast::Expr, +) -> Option> { + let args = match &expr { + ast::Expr::CallExpr(expr) => Some(expr.arg_list()?.args()), + ast::Expr::MethodCallExpr(expr) => Some(expr.arg_list()?.args()), + _ => None, + }?; + + let mut parameters = get_fn_signature(db, analyzer, &expr)?.parameter_names.into_iter(); + + if let ast::Expr::MethodCallExpr(_) = &expr { + parameters.next(); + }; + + let hints = parameters + .zip(args) + .filter_map(|(param, arg)| { + if arg.syntax().kind() == SyntaxKind::LITERAL { + Some((arg.syntax().text_range(), param)) + } else { + None + } + }) + .map(|(range, param_name)| InlayHint { + range, + kind: InlayKind::ParameterHint, + label: param_name.into(), + }) + .collect(); + + Some(hints) +} + +fn get_fn_signature( + db: &RootDatabase, + analyzer: &SourceAnalyzer, + expr: &ast::Expr, +) -> Option { + match expr { + ast::Expr::CallExpr(expr) => { + // FIXME: Type::as_callable is broken for closures + let callable_def = analyzer.type_of(db, &expr.expr()?)?.as_callable()?; + match callable_def { + hir::CallableDef::FunctionId(it) => { + let fn_def = it.into(); + Some(FunctionSignature::from_hir(db, fn_def)) + } + hir::CallableDef::StructId(it) => FunctionSignature::from_struct(db, it.into()), + hir::CallableDef::EnumVariantId(it) => { + FunctionSignature::from_enum_variant(db, it.into()) + } + } + } + ast::Expr::MethodCallExpr(expr) => { + let fn_def = analyzer.resolve_method_call(&expr)?; + Some(FunctionSignature::from_hir(db, fn_def)) + } + _ => None, + } +} fn get_pat_type_hints( db: &RootDatabase, @@ -605,4 +675,71 @@ fn main() { "### ); } + + #[test] + fn function_call_parameter_hint() { + let (analysis, file_id) = single_file( + r#" +struct Test {} + +impl Test { + fn method(&self, param: i32) -> i32 { + param * 2 + } +} + +fn test_func(foo: i32, bar: i32, msg: &str, _: i32, last: i32) -> i32 { + foo + bar +} + +fn main() { + let not_literal = 1; + let _: i32 = test_func(1, 2, "hello", 3, not_literal); + let t: Test = Test {}; + t.method(123); + Test::method(&t, 3456); +}"#, + ); + + assert_debug_snapshot!(analysis.inlay_hints(file_id, None).unwrap(), @r###" + [ + InlayHint { + range: [207; 218), + kind: TypeHint, + label: "i32", + }, + InlayHint { + range: [251; 252), + kind: ParameterHint, + label: "foo", + }, + InlayHint { + range: [254; 255), + kind: ParameterHint, + label: "bar", + }, + InlayHint { + range: [257; 264), + kind: ParameterHint, + label: "msg", + }, + InlayHint { + range: [266; 267), + kind: ParameterHint, + label: "_", + }, + InlayHint { + range: [322; 325), + kind: ParameterHint, + label: "param", + }, + InlayHint { + range: [349; 353), + kind: ParameterHint, + label: "param", + }, + ] + "### + ); + } } diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index a592f0a12..a9a8538b7 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs @@ -952,6 +952,7 @@ pub fn handle_inlay_hints( range: api_type.range.conv_with(&line_index), kind: match api_type.kind { ra_ide::InlayKind::TypeHint => InlayKind::TypeHint, + ra_ide::InlayKind::ParameterHint => InlayKind::ParameterHint, }, }) .collect()) diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs index 8098ff31d..dc327f53d 100644 --- a/crates/ra_lsp_server/src/req.rs +++ b/crates/ra_lsp_server/src/req.rs @@ -197,6 +197,7 @@ pub struct InlayHintsParams { #[derive(Debug, PartialEq, Eq, Deserialize, Serialize)] pub enum InlayKind { TypeHint, + ParameterHint, } #[derive(Debug, Deserialize, Serialize)] -- cgit v1.2.3 From d78a3cb6385789a5af66cd186651d19e974470fb Mon Sep 17 00:00:00 2001 From: imtsuki Date: Wed, 15 Jan 2020 01:32:03 +0800 Subject: Update test snapshot Signed-off-by: imtsuki --- crates/ra_ide/src/inlay_hints.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'crates') diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index 83e4588c1..8cb0c70dd 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs @@ -729,12 +729,12 @@ fn main() { label: "_", }, InlayHint { - range: [322; 325), + range: [323; 326), kind: ParameterHint, label: "param", }, InlayHint { - range: [349; 353), + range: [350; 354), kind: ParameterHint, label: "param", }, -- cgit v1.2.3 From d854ad8f279e5a3a4b3908bf5b0afdaba3d37bc9 Mon Sep 17 00:00:00 2001 From: imtsuki Date: Wed, 15 Jan 2020 09:30:19 +0800 Subject: FnSignature: use unwrap_or_default for parameter_name_list Signed-off-by: imtsuki --- crates/ra_ide/src/display/function_signature.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'crates') diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs index b0eb1f194..ddc53a52b 100644 --- a/crates/ra_ide/src/display/function_signature.rs +++ b/crates/ra_ide/src/display/function_signature.rs @@ -169,11 +169,9 @@ impl From<&'_ ast::FnDef> for FunctionSignature { res.push(self_param.syntax().text().to_string()) } - res.extend( - param_list - .params() - .map(|param| param.pat().unwrap().syntax().text().to_string()), - ); + res.extend(param_list.params().map(|param| { + param.pat().map(|pat| pat.syntax().text().to_string()).unwrap_or_default() + })); } res } -- cgit v1.2.3