aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_ide/src/inlay_hints.rs74
-rw-r--r--crates/stdx/src/lib.rs14
2 files changed, 70 insertions, 18 deletions
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index 98483df32..b391f903a 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -9,6 +9,7 @@ use ra_syntax::{
9}; 9};
10 10
11use crate::{FileId, FunctionSignature}; 11use crate::{FileId, FunctionSignature};
12use stdx::to_lower_snake_case;
12 13
13#[derive(Clone, Debug, PartialEq, Eq)] 14#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct InlayHintsConfig { 15pub struct InlayHintsConfig {
@@ -144,7 +145,7 @@ fn get_param_name_hints(
144 .iter() 145 .iter()
145 .skip(n_params_to_skip) 146 .skip(n_params_to_skip)
146 .zip(args) 147 .zip(args)
147 .filter(|(param, arg)| should_show_param_hint(&fn_signature, param, &arg)) 148 .filter(|(param, arg)| should_show_param_name_hint(sema, &fn_signature, param, &arg))
148 .map(|(param_name, arg)| InlayHint { 149 .map(|(param_name, arg)| InlayHint {
149 range: arg.syntax().text_range(), 150 range: arg.syntax().text_range(),
150 kind: InlayKind::ParameterHint, 151 kind: InlayKind::ParameterHint,
@@ -181,7 +182,7 @@ fn get_bind_pat_hints(
181 182
182fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::BindPat, pat_ty: &Type) -> bool { 183fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::BindPat, pat_ty: &Type) -> bool {
183 if let Some(Adt::Enum(enum_data)) = pat_ty.as_adt() { 184 if let Some(Adt::Enum(enum_data)) = pat_ty.as_adt() {
184 let pat_text = bind_pat.syntax().to_string(); 185 let pat_text = bind_pat.to_string();
185 enum_data 186 enum_data
186 .variants(db) 187 .variants(db)
187 .into_iter() 188 .into_iter()
@@ -198,7 +199,7 @@ fn should_not_display_type_hint(db: &RootDatabase, bind_pat: &ast::BindPat, pat_
198 } 199 }
199 200
200 if let Some(Adt::Struct(s)) = pat_ty.as_adt() { 201 if let Some(Adt::Struct(s)) = pat_ty.as_adt() {
201 if s.fields(db).is_empty() && s.name(db).to_string() == bind_pat.syntax().to_string() { 202 if s.fields(db).is_empty() && s.name(db).to_string() == bind_pat.to_string() {
202 return true; 203 return true;
203 } 204 }
204 } 205 }
@@ -230,15 +231,16 @@ fn should_not_display_type_hint(db: &RootDatabase, bind_pat: &ast::BindPat, pat_
230 false 231 false
231} 232}
232 233
233fn should_show_param_hint( 234fn should_show_param_name_hint(
235 sema: &Semantics<RootDatabase>,
234 fn_signature: &FunctionSignature, 236 fn_signature: &FunctionSignature,
235 param_name: &str, 237 param_name: &str,
236 argument: &ast::Expr, 238 argument: &ast::Expr,
237) -> bool { 239) -> bool {
240 let param_name = param_name.trim_start_matches('_');
238 if param_name.is_empty() 241 if param_name.is_empty()
239 || is_argument_similar_to_param(argument, param_name) 242 || Some(param_name) == fn_signature.name.as_ref().map(|s| s.trim_start_matches('_'))
240 || Some(param_name.trim_start_matches('_')) 243 || is_argument_similar_to_param_name(sema, argument, param_name)
241 == fn_signature.name.as_ref().map(|s| s.trim_start_matches('_'))
242 { 244 {
243 return false; 245 return false;
244 } 246 }
@@ -254,20 +256,42 @@ fn should_show_param_hint(
254 parameters_len != 1 || !is_obvious_param(param_name) 256 parameters_len != 1 || !is_obvious_param(param_name)
255} 257}
256 258
257fn is_argument_similar_to_param(argument: &ast::Expr, param_name: &str) -> bool { 259fn is_argument_similar_to_param_name(
258 let argument_string = remove_ref(argument.clone()).syntax().to_string(); 260 sema: &Semantics<RootDatabase>,
259 let param_name = param_name.trim_start_matches('_'); 261 argument: &ast::Expr,
260 let argument_string = argument_string.trim_start_matches('_'); 262 param_name: &str,
261 argument_string.starts_with(&param_name) || argument_string.ends_with(&param_name) 263) -> bool {
264 if is_enum_name_similar_to_param_name(sema, argument, param_name) {
265 return true;
266 }
267 match get_string_representation(argument) {
268 None => false,
269 Some(repr) => {
270 let argument_string = repr.trim_start_matches('_');
271 argument_string.starts_with(param_name) || argument_string.ends_with(param_name)
272 }
273 }
274}
275
276fn is_enum_name_similar_to_param_name(
277 sema: &Semantics<RootDatabase>,
278 argument: &ast::Expr,
279 param_name: &str,
280) -> bool {
281 match sema.type_of_expr(argument).and_then(|t| t.as_adt()) {
282 Some(Adt::Enum(e)) => to_lower_snake_case(&e.name(sema.db).to_string()) == param_name,
283 _ => false,
284 }
262} 285}
263 286
264fn remove_ref(expr: ast::Expr) -> ast::Expr { 287fn get_string_representation(expr: &ast::Expr) -> Option<String> {
265 if let ast::Expr::RefExpr(ref_expr) = &expr { 288 match expr {
266 if let Some(inner) = ref_expr.expr() { 289 ast::Expr::MethodCallExpr(method_call_expr) => {
267 return inner; 290 Some(method_call_expr.name_ref()?.to_string())
268 } 291 }
292 ast::Expr::RefExpr(ref_expr) => get_string_representation(&ref_expr.expr()?),
293 _ => Some(expr.to_string()),
269 } 294 }
270 expr
271} 295}
272 296
273fn is_obvious_param(param_name: &str) -> bool { 297fn is_obvious_param(param_name: &str) -> bool {
@@ -1073,6 +1097,12 @@ struct TestVarContainer {
1073 test_var: i32, 1097 test_var: i32,
1074} 1098}
1075 1099
1100impl TestVarContainer {
1101 fn test_var(&self) -> i32 {
1102 self.test_var
1103 }
1104}
1105
1076struct Test {} 1106struct Test {}
1077 1107
1078impl Test { 1108impl Test {
@@ -1098,10 +1128,15 @@ struct Param {}
1098fn different_order(param: &Param) {} 1128fn different_order(param: &Param) {}
1099fn different_order_mut(param: &mut Param) {} 1129fn different_order_mut(param: &mut Param) {}
1100fn has_underscore(_param: bool) {} 1130fn has_underscore(_param: bool) {}
1131fn enum_matches_param_name(completion_kind: CompletionKind) {}
1101 1132
1102fn twiddle(twiddle: bool) {} 1133fn twiddle(twiddle: bool) {}
1103fn doo(_doo: bool) {} 1134fn doo(_doo: bool) {}
1104 1135
1136enum CompletionKind {
1137 Keyword,
1138}
1139
1105fn main() { 1140fn main() {
1106 let container: TestVarContainer = TestVarContainer { test_var: 42 }; 1141 let container: TestVarContainer = TestVarContainer { test_var: 42 };
1107 let test: Test = Test {}; 1142 let test: Test = Test {};
@@ -1114,18 +1149,21 @@ fn main() {
1114 let test_var: i32 = 55; 1149 let test_var: i32 = 55;
1115 test_processed.no_hints_expected(22, test_var); 1150 test_processed.no_hints_expected(22, test_var);
1116 test_processed.no_hints_expected(33, container.test_var); 1151 test_processed.no_hints_expected(33, container.test_var);
1152 test_processed.no_hints_expected(44, container.test_var());
1117 test_processed.frob(false); 1153 test_processed.frob(false);
1118 1154
1119 twiddle(true); 1155 twiddle(true);
1120 doo(true); 1156 doo(true);
1121 1157
1122 let param_begin: Param = Param {}; 1158 let mut param_begin: Param = Param {};
1123 different_order(&param_begin); 1159 different_order(&param_begin);
1124 different_order(&mut param_begin); 1160 different_order(&mut param_begin);
1125 1161
1126 let param: bool = true; 1162 let param: bool = true;
1127 has_underscore(param); 1163 has_underscore(param);
1128 1164
1165 enum_matches_param_name(CompletionKind::Keyword);
1166
1129 let a: f64 = 7.0; 1167 let a: f64 = 7.0;
1130 let b: f64 = 4.0; 1168 let b: f64 = 4.0;
1131 let _: f64 = a.div_euclid(b); 1169 let _: f64 = a.div_euclid(b);
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index 01cdf452c..0f34ce70e 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -102,3 +102,17 @@ pub fn timeit(label: &'static str) -> impl Drop {
102 102
103 Guard { label, start: Instant::now() } 103 Guard { label, start: Instant::now() }
104} 104}
105
106pub fn to_lower_snake_case(s: &str) -> String {
107 let mut buf = String::with_capacity(s.len());
108 let mut prev = false;
109 for c in s.chars() {
110 if c.is_ascii_uppercase() && prev {
111 buf.push('_')
112 }
113 prev = true;
114
115 buf.push(c.to_ascii_lowercase());
116 }
117 buf
118}