diff options
author | Aleksey Kladov <[email protected]> | 2020-07-16 20:51:44 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2020-07-16 20:51:44 +0100 |
commit | a5ae8b8b92748e1b876002799d160136a7836212 (patch) | |
tree | d6d8bfd99ef8c33eba4e679d4556c84aaf69bebd /crates/ra_ide | |
parent | edc0190f7a2a701ef7c8534b053070212798cd8b (diff) |
Inlay hints use callables
Diffstat (limited to 'crates/ra_ide')
-rw-r--r-- | crates/ra_ide/src/display/function_signature.rs | 96 | ||||
-rw-r--r-- | crates/ra_ide/src/inlay_hints.rs | 76 |
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) |
5 | use std::convert::From; | 5 | use std::convert::From; |
6 | 6 | ||
7 | use hir::{Docs, Documentation, HasSource, HirDisplay}; | 7 | use hir::Documentation; |
8 | use ra_ide_db::RootDatabase; | ||
9 | use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; | 8 | use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; |
10 | use stdx::split_delim; | 9 | use stdx::split_delim; |
11 | 10 | ||
@@ -14,8 +13,6 @@ use crate::display::{generic_parameters, where_predicates}; | |||
14 | #[derive(Debug)] | 13 | #[derive(Debug)] |
15 | pub(crate) enum CallableKind { | 14 | pub(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 | ||
59 | impl 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 | |||
150 | impl From<&'_ ast::FnDef> for FunctionSignature { | 56 | impl 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 @@ | |||
1 | use hir::{Adt, HirDisplay, Semantics, Type}; | 1 | use hir::{Adt, Callable, HirDisplay, Semantics, Type}; |
2 | use ra_ide_db::RootDatabase; | 2 | use ra_ide_db::RootDatabase; |
3 | use ra_prof::profile; | 3 | use ra_prof::profile; |
4 | use ra_syntax::{ | 4 | use ra_syntax::{ |
@@ -7,7 +7,9 @@ use ra_syntax::{ | |||
7 | }; | 7 | }; |
8 | use stdx::to_lower_snake_case; | 8 | use stdx::to_lower_snake_case; |
9 | 9 | ||
10 | use crate::{display::function_signature::FunctionSignature, FileId}; | 10 | use crate::FileId; |
11 | use ast::NameOwner; | ||
12 | use either::Either; | ||
11 | 13 | ||
12 | #[derive(Clone, Debug, PartialEq, Eq)] | 14 | #[derive(Clone, Debug, PartialEq, Eq)] |
13 | pub struct InlayHintsConfig { | 15 | pub 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, ¶m_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 | ||
251 | fn should_show_param_name_hint( | 256 | fn 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 | ||
277 | fn is_argument_similar_to_param_name( | 280 | fn 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 | ||
321 | fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<FunctionSignature> { | 324 | fn 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) { |