aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2020-07-16 20:51:44 +0100
committerAleksey Kladov <[email protected]>2020-07-16 20:51:44 +0100
commita5ae8b8b92748e1b876002799d160136a7836212 (patch)
treed6d8bfd99ef8c33eba4e679d4556c84aaf69bebd /crates/ra_ide
parentedc0190f7a2a701ef7c8534b053070212798cd8b (diff)
Inlay hints use callables
Diffstat (limited to 'crates/ra_ide')
-rw-r--r--crates/ra_ide/src/display/function_signature.rs96
-rw-r--r--crates/ra_ide/src/inlay_hints.rs76
2 files changed, 32 insertions, 140 deletions
diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs
index 77551117b..f6e11357f 100644
--- a/crates/ra_ide/src/display/function_signature.rs
+++ b/crates/ra_ide/src/display/function_signature.rs
@@ -4,8 +4,7 @@
4// rewritten (matklad 2020-05-07) 4// rewritten (matklad 2020-05-07)
5use std::convert::From; 5use std::convert::From;
6 6
7use hir::{Docs, Documentation, HasSource, HirDisplay}; 7use hir::Documentation;
8use ra_ide_db::RootDatabase;
9use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; 8use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
10use stdx::split_delim; 9use stdx::split_delim;
11 10
@@ -14,8 +13,6 @@ use crate::display::{generic_parameters, where_predicates};
14#[derive(Debug)] 13#[derive(Debug)]
15pub(crate) enum CallableKind { 14pub(crate) enum CallableKind {
16 Function, 15 Function,
17 StructConstructor,
18 VariantConstructor,
19} 16}
20 17
21/// Contains information about a function signature 18/// Contains information about a function signature
@@ -56,97 +53,6 @@ pub(crate) struct FunctionQualifier {
56 pub(crate) extern_abi: Option<String>, 53 pub(crate) extern_abi: Option<String>,
57} 54}
58 55
59impl FunctionSignature {
60 pub(crate) fn from_hir(db: &RootDatabase, function: hir::Function) -> Self {
61 let ast_node = function.source(db).value;
62 let mut res = FunctionSignature::from(&ast_node);
63 res.doc = function.docs(db);
64 res
65 }
66
67 pub(crate) fn from_struct(db: &RootDatabase, st: hir::Struct) -> Option<Self> {
68 let node: ast::StructDef = st.source(db).value;
69 if let ast::StructKind::Record(_) = node.kind() {
70 return None;
71 };
72
73 let mut params = vec![];
74 let mut parameter_types = vec![];
75 for field in st.fields(db).into_iter() {
76 let ty = field.signature_ty(db);
77 let raw_param = format!("{}", ty.display(db));
78
79 if let Some(param_type) = raw_param.split(':').nth(1).and_then(|it| it.get(1..)) {
80 parameter_types.push(param_type.to_string());
81 } else {
82 // useful when you have tuple struct
83 parameter_types.push(raw_param.clone());
84 }
85 params.push(raw_param);
86 }
87
88 Some(FunctionSignature {
89 kind: CallableKind::StructConstructor,
90 visibility: node.visibility().map(|n| n.syntax().text().to_string()),
91 // Do we need `const`?
92 qualifier: Default::default(),
93 name: node.name().map(|n| n.text().to_string()),
94 ret_type: node.name().map(|n| n.text().to_string()),
95 parameters: params,
96 parameter_names: vec![],
97 parameter_types,
98 generic_parameters: generic_parameters(&node),
99 where_predicates: where_predicates(&node),
100 doc: st.docs(db),
101 has_self_param: false,
102 })
103 }
104
105 pub(crate) fn from_enum_variant(db: &RootDatabase, variant: hir::EnumVariant) -> Option<Self> {
106 let node: ast::EnumVariant = variant.source(db).value;
107 match node.kind() {
108 ast::StructKind::Record(_) | ast::StructKind::Unit => return None,
109 _ => (),
110 };
111
112 let parent_name = variant.parent_enum(db).name(db).to_string();
113
114 let name = format!("{}::{}", parent_name, variant.name(db));
115
116 let mut params = vec![];
117 let mut parameter_types = vec![];
118 for field in variant.fields(db).into_iter() {
119 let ty = field.signature_ty(db);
120 let raw_param = format!("{}", ty.display(db));
121 if let Some(param_type) = raw_param.split(':').nth(1).and_then(|it| it.get(1..)) {
122 parameter_types.push(param_type.to_string());
123 } else {
124 // The unwrap_or_else is useful when you have tuple
125 parameter_types.push(raw_param);
126 }
127 let name = field.name(db);
128
129 params.push(format!("{}: {}", name, ty.display(db)));
130 }
131
132 Some(FunctionSignature {
133 kind: CallableKind::VariantConstructor,
134 visibility: None,
135 // Do we need `const`?
136 qualifier: Default::default(),
137 name: Some(name),
138 ret_type: None,
139 parameters: params,
140 parameter_names: vec![],
141 parameter_types,
142 generic_parameters: vec![],
143 where_predicates: vec![],
144 doc: variant.docs(db),
145 has_self_param: false,
146 })
147 }
148}
149
150impl From<&'_ ast::FnDef> for FunctionSignature { 56impl From<&'_ ast::FnDef> for FunctionSignature {
151 fn from(node: &ast::FnDef) -> FunctionSignature { 57 fn from(node: &ast::FnDef) -> FunctionSignature {
152 fn param_list(node: &ast::FnDef) -> (bool, Vec<String>, Vec<String>) { 58 fn param_list(node: &ast::FnDef) -> (bool, Vec<String>, Vec<String>) {
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index ae5695f61..cec3b04e8 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -1,4 +1,4 @@
1use hir::{Adt, HirDisplay, Semantics, Type}; 1use hir::{Adt, Callable, HirDisplay, Semantics, Type};
2use ra_ide_db::RootDatabase; 2use ra_ide_db::RootDatabase;
3use ra_prof::profile; 3use ra_prof::profile;
4use ra_syntax::{ 4use ra_syntax::{
@@ -7,7 +7,9 @@ use ra_syntax::{
7}; 7};
8use stdx::to_lower_snake_case; 8use stdx::to_lower_snake_case;
9 9
10use crate::{display::function_signature::FunctionSignature, FileId}; 10use crate::FileId;
11use ast::NameOwner;
12use either::Either;
11 13
12#[derive(Clone, Debug, PartialEq, Eq)] 14#[derive(Clone, Debug, PartialEq, Eq)]
13pub struct InlayHintsConfig { 15pub struct InlayHintsConfig {
@@ -150,23 +152,26 @@ fn get_param_name_hints(
150 _ => return None, 152 _ => return None,
151 }; 153 };
152 154
153 let fn_signature = get_fn_signature(sema, &expr)?; 155 let callable = get_callable(sema, &expr)?;
154 let n_params_to_skip = 156 let hints = callable
155 if fn_signature.has_self_param && matches!(&expr, ast::Expr::MethodCallExpr(_)) { 157 .params(sema.db)
156 1 158 .into_iter()
157 } else {
158 0
159 };
160 let hints = fn_signature
161 .parameter_names
162 .iter()
163 .skip(n_params_to_skip)
164 .zip(args) 159 .zip(args)
165 .filter(|(param, arg)| should_show_param_name_hint(sema, &fn_signature, param, &arg)) 160 .filter_map(|((param, _ty), arg)| match param? {
161 Either::Left(self_param) => Some((self_param.to_string(), arg)),
162 Either::Right(pat) => {
163 let param_name = match pat {
164 ast::Pat::BindPat(it) => it.name()?.to_string(),
165 it => it.to_string(),
166 };
167 Some((param_name, arg))
168 }
169 })
170 .filter(|(param_name, arg)| should_show_param_name_hint(sema, &callable, &param_name, &arg))
166 .map(|(param_name, arg)| InlayHint { 171 .map(|(param_name, arg)| InlayHint {
167 range: arg.syntax().text_range(), 172 range: arg.syntax().text_range(),
168 kind: InlayKind::ParameterHint, 173 kind: InlayKind::ParameterHint,
169 label: param_name.into(), 174 label: param_name.to_string().into(),
170 }); 175 });
171 176
172 acc.extend(hints); 177 acc.extend(hints);
@@ -250,28 +255,26 @@ fn should_not_display_type_hint(db: &RootDatabase, bind_pat: &ast::BindPat, pat_
250 255
251fn should_show_param_name_hint( 256fn should_show_param_name_hint(
252 sema: &Semantics<RootDatabase>, 257 sema: &Semantics<RootDatabase>,
253 fn_signature: &FunctionSignature, 258 callable: &Callable,
254 param_name: &str, 259 param_name: &str,
255 argument: &ast::Expr, 260 argument: &ast::Expr,
256) -> bool { 261) -> bool {
257 let param_name = param_name.trim_start_matches('_'); 262 let param_name = param_name.trim_start_matches('_');
263 let fn_name = match callable.kind() {
264 hir::CallableKind::Function(it) => Some(it.name(sema.db).to_string()),
265 hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => None,
266 };
258 if param_name.is_empty() 267 if param_name.is_empty()
259 || Some(param_name) == fn_signature.name.as_ref().map(|s| s.trim_start_matches('_')) 268 || Some(param_name) == fn_name.as_ref().map(|s| s.trim_start_matches('_'))
260 || is_argument_similar_to_param_name(sema, argument, param_name) 269 || is_argument_similar_to_param_name(sema, argument, param_name)
261 || param_name.starts_with("ra_fixture") 270 || param_name.starts_with("ra_fixture")
262 { 271 {
263 return false; 272 return false;
264 } 273 }
265 274
266 let parameters_len = if fn_signature.has_self_param {
267 fn_signature.parameters.len() - 1
268 } else {
269 fn_signature.parameters.len()
270 };
271
272 // avoid displaying hints for common functions like map, filter, etc. 275 // avoid displaying hints for common functions like map, filter, etc.
273 // or other obvious words used in std 276 // or other obvious words used in std
274 !(parameters_len == 1 && is_obvious_param(param_name)) 277 !(callable.n_params() == 1 && is_obvious_param(param_name))
275} 278}
276 279
277fn is_argument_similar_to_param_name( 280fn is_argument_similar_to_param_name(
@@ -318,27 +321,10 @@ fn is_obvious_param(param_name: &str) -> bool {
318 param_name.len() == 1 || is_obvious_param_name 321 param_name.len() == 1 || is_obvious_param_name
319} 322}
320 323
321fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<FunctionSignature> { 324fn get_callable(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<Callable> {
322 match expr { 325 match expr {
323 ast::Expr::CallExpr(expr) => { 326 ast::Expr::CallExpr(expr) => sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db),
324 // FIXME: Type::as_callable is broken for closures 327 ast::Expr::MethodCallExpr(expr) => sema.resolve_method_call_as_callable(expr),
325 let callable = sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db)?;
326 match callable.kind() {
327 hir::CallableKind::Function(it) => {
328 Some(FunctionSignature::from_hir(sema.db, it.into()))
329 }
330 hir::CallableKind::TupleStruct(it) => {
331 FunctionSignature::from_struct(sema.db, it.into())
332 }
333 hir::CallableKind::TupleEnumVariant(it) => {
334 FunctionSignature::from_enum_variant(sema.db, it.into())
335 }
336 }
337 }
338 ast::Expr::MethodCallExpr(expr) => {
339 let fn_def = sema.resolve_method_call(&expr)?;
340 Some(FunctionSignature::from_hir(sema.db, fn_def))
341 }
342 _ => None, 328 _ => None,
343 } 329 }
344} 330}
@@ -360,7 +346,7 @@ mod tests {
360 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap(); 346 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap();
361 let actual = 347 let actual =
362 inlay_hints.into_iter().map(|it| (it.range, it.label.to_string())).collect::<Vec<_>>(); 348 inlay_hints.into_iter().map(|it| (it.range, it.label.to_string())).collect::<Vec<_>>();
363 assert_eq!(expected, actual); 349 assert_eq!(expected, actual, "\nExpected:\n{:#?}\n\nActual:\n{:#?}", expected, actual);
364 } 350 }
365 351
366 fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) { 352 fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {