aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ide/src/inlay_hints.rs127
1 files changed, 57 insertions, 70 deletions
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 85f887737..d6dfa0183 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -150,17 +150,11 @@ fn get_param_name_hints(
150 return None; 150 return None;
151 } 151 }
152 152
153 let args = match &expr { 153 let (callable, arg_list) = get_callable(sema, &expr)?;
154 ast::Expr::CallExpr(expr) => expr.arg_list()?.args(),
155 ast::Expr::MethodCallExpr(expr) => expr.arg_list()?.args(),
156 _ => return None,
157 };
158
159 let callable = get_callable(sema, &expr)?;
160 let hints = callable 154 let hints = callable
161 .params(sema.db) 155 .params(sema.db)
162 .into_iter() 156 .into_iter()
163 .zip(args) 157 .zip(arg_list.args())
164 .filter_map(|((param, _ty), arg)| { 158 .filter_map(|((param, _ty), arg)| {
165 let param_name = match param? { 159 let param_name = match param? {
166 Either::Left(_) => "self".to_string(), 160 Either::Left(_) => "self".to_string(),
@@ -171,7 +165,7 @@ fn get_param_name_hints(
171 }; 165 };
172 Some((param_name, arg)) 166 Some((param_name, arg))
173 }) 167 })
174 .filter(|(param_name, arg)| should_show_param_name_hint(sema, &callable, param_name, &arg)) 168 .filter(|(param_name, arg)| !should_hide_param_name_hint(sema, &callable, param_name, &arg))
175 .map(|(param_name, arg)| InlayHint { 169 .map(|(param_name, arg)| InlayHint {
176 range: arg.syntax().text_range(), 170 range: arg.syntax().text_range(),
177 kind: InlayKind::ParameterHint, 171 kind: InlayKind::ParameterHint,
@@ -289,15 +283,9 @@ fn should_not_display_type_hint(
289 for node in bind_pat.syntax().ancestors() { 283 for node in bind_pat.syntax().ancestors() {
290 match_ast! { 284 match_ast! {
291 match node { 285 match node {
292 ast::LetStmt(it) => { 286 ast::LetStmt(it) => return it.ty().is_some(),
293 return it.ty().is_some() 287 ast::Param(it) => return it.ty().is_some(),
294 }, 288 ast::MatchArm(_it) => return pat_is_enum_variant(db, bind_pat, pat_ty),
295 ast::Param(it) => {
296 return it.ty().is_some()
297 },
298 ast::MatchArm(_it) => {
299 return pat_is_enum_variant(db, bind_pat, pat_ty);
300 },
301 ast::IfExpr(it) => { 289 ast::IfExpr(it) => {
302 return it.condition().and_then(|condition| condition.pat()).is_some() 290 return it.condition().and_then(|condition| condition.pat()).is_some()
303 && pat_is_enum_variant(db, bind_pat, pat_ty); 291 && pat_is_enum_variant(db, bind_pat, pat_ty);
@@ -322,76 +310,66 @@ fn should_not_display_type_hint(
322 false 310 false
323} 311}
324 312
325fn should_show_param_name_hint( 313fn should_hide_param_name_hint(
326 sema: &Semantics<RootDatabase>, 314 sema: &Semantics<RootDatabase>,
327 callable: &hir::Callable, 315 callable: &hir::Callable,
328 param_name: &str, 316 param_name: &str,
329 argument: &ast::Expr, 317 argument: &ast::Expr,
330) -> bool { 318) -> bool {
319 // hide when:
320 // - the parameter name is a suffix of the function's name
321 // - the argument is an enum whose name is equal to the parameter
322 // - exact argument<->parameter match(ignoring leading underscore) or argument is a prefix/suffix
323 // of parameter with _ splitting it off
324 // - param starts with `ra_fixture`
325 // - param is a well known name in an unary function
326
331 let param_name = param_name.trim_start_matches('_'); 327 let param_name = param_name.trim_start_matches('_');
328 if param_name.is_empty() {
329 return true;
330 }
331
332 let fn_name = match callable.kind() { 332 let fn_name = match callable.kind() {
333 hir::CallableKind::Function(it) => Some(it.name(sema.db).to_string()), 333 hir::CallableKind::Function(it) => Some(it.name(sema.db).to_string()),
334 hir::CallableKind::TupleStruct(_) 334 _ => None,
335 | hir::CallableKind::TupleEnumVariant(_)
336 | hir::CallableKind::Closure => None,
337 }; 335 };
338 336 let fn_name = fn_name.as_deref();
339 if param_name.is_empty() 337 is_param_name_suffix_of_fn_name(param_name, callable, fn_name)
340 || Some(param_name) == fn_name.as_ref().map(|s| s.trim_start_matches('_')) 338 || is_enum_name_similar_to_param_name(sema, argument, param_name)
341 || is_argument_similar_to_param_name(sema, argument, param_name) 339 || is_argument_similar_to_param_name(argument, param_name)
342 || is_param_name_similar_to_fn_name(param_name, callable, fn_name.as_ref())
343 || param_name.starts_with("ra_fixture") 340 || param_name.starts_with("ra_fixture")
344 { 341 || (callable.n_params() == 1 && is_obvious_param(param_name))
345 return false;
346 }
347
348 // avoid displaying hints for common functions like map, filter, etc.
349 // or other obvious words used in std
350 !(callable.n_params() == 1 && is_obvious_param(param_name))
351} 342}
352 343
353fn is_argument_similar_to_param_name( 344fn is_argument_similar_to_param_name(argument: &ast::Expr, param_name: &str) -> bool {
354 sema: &Semantics<RootDatabase>,
355 argument: &ast::Expr,
356 param_name: &str,
357) -> bool {
358 if is_enum_name_similar_to_param_name(sema, argument, param_name) {
359 return true;
360 }
361 match get_string_representation(argument) { 345 match get_string_representation(argument) {
362 None => false, 346 None => false,
363 Some(argument_string) => { 347 Some(argument) => {
364 let num_leading_underscores = 348 let mut res = false;
365 argument_string.bytes().take_while(|&c| c == b'_').count(); 349 if let Some(first) = argument.bytes().skip_while(|&c| c == b'_').position(|c| c == b'_')
366 350 {
367 // Does the argument name begin with the parameter name? Ignore leading underscores. 351 res |= param_name == argument[..first].trim_start_matches('_');
368 let mut arg_bytes = argument_string.bytes().skip(num_leading_underscores);
369 let starts_with_pattern = param_name.bytes().all(
370 |expected| matches!(arg_bytes.next(), Some(actual) if expected.eq_ignore_ascii_case(&actual)),
371 );
372
373 if starts_with_pattern {
374 return true;
375 } 352 }
376 353 if let Some(last) =
377 // Does the argument name end with the parameter name? 354 argument.bytes().rev().skip_while(|&c| c == b'_').position(|c| c == b'_')
378 let mut arg_bytes = argument_string.bytes().skip(num_leading_underscores); 355 {
379 param_name.bytes().rev().all( 356 res |= param_name == argument[last..].trim_end_matches('_');
380 |expected| matches!(arg_bytes.next_back(), Some(actual) if expected.eq_ignore_ascii_case(&actual)), 357 }
381 ) 358 res |= argument == param_name;
359 res
382 } 360 }
383 } 361 }
384} 362}
385 363
386fn is_param_name_similar_to_fn_name( 364/// Hide the parameter name of an unary function if it is a `_` - prefixed suffix of the function's name, or equal.
365///
366/// `fn strip_suffix(suffix)` will be hidden.
367/// `fn stripsuffix(suffix)` will not be hidden.
368fn is_param_name_suffix_of_fn_name(
387 param_name: &str, 369 param_name: &str,
388 callable: &Callable, 370 callable: &Callable,
389 fn_name: Option<&String>, 371 fn_name: Option<&str>,
390) -> bool { 372) -> bool {
391 // if it's the only parameter, don't show it if:
392 // - is the same as the function name, or
393 // - the function ends with '_' + param_name
394
395 match (callable.n_params(), fn_name) { 373 match (callable.n_params(), fn_name) {
396 (1, Some(function)) => { 374 (1, Some(function)) => {
397 function == param_name 375 function == param_name
@@ -424,7 +402,7 @@ fn get_string_representation(expr: &ast::Expr) -> Option<String> {
424 } 402 }
425 } 403 }
426 ast::Expr::FieldExpr(field_expr) => Some(field_expr.name_ref()?.to_string()), 404 ast::Expr::FieldExpr(field_expr) => Some(field_expr.name_ref()?.to_string()),
427 ast::Expr::PathExpr(path_expr) => Some(path_expr.to_string()), 405 ast::Expr::PathExpr(path_expr) => Some(path_expr.path()?.segment()?.to_string()),
428 ast::Expr::PrefixExpr(prefix_expr) => get_string_representation(&prefix_expr.expr()?), 406 ast::Expr::PrefixExpr(prefix_expr) => get_string_representation(&prefix_expr.expr()?),
429 ast::Expr::RefExpr(ref_expr) => get_string_representation(&ref_expr.expr()?), 407 ast::Expr::RefExpr(ref_expr) => get_string_representation(&ref_expr.expr()?),
430 _ => None, 408 _ => None,
@@ -432,15 +410,24 @@ fn get_string_representation(expr: &ast::Expr) -> Option<String> {
432} 410}
433 411
434fn is_obvious_param(param_name: &str) -> bool { 412fn is_obvious_param(param_name: &str) -> bool {
413 // avoid displaying hints for common functions like map, filter, etc.
414 // or other obvious words used in std
435 let is_obvious_param_name = 415 let is_obvious_param_name =
436 matches!(param_name, "predicate" | "value" | "pat" | "rhs" | "other"); 416 matches!(param_name, "predicate" | "value" | "pat" | "rhs" | "other");
437 param_name.len() == 1 || is_obvious_param_name 417 param_name.len() == 1 || is_obvious_param_name
438} 418}
439 419
440fn get_callable(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Callable> { 420fn get_callable(
421 sema: &Semantics<RootDatabase>,
422 expr: &ast::Expr,
423) -> Option<(hir::Callable, ast::ArgList)> {
441 match expr { 424 match expr {
442 ast::Expr::CallExpr(expr) => sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db), 425 ast::Expr::CallExpr(expr) => {
443 ast::Expr::MethodCallExpr(expr) => sema.resolve_method_call_as_callable(expr), 426 sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db).zip(expr.arg_list())
427 }
428 ast::Expr::MethodCallExpr(expr) => {
429 sema.resolve_method_call_as_callable(expr).zip(expr.arg_list())
430 }
444 _ => None, 431 _ => None,
445 } 432 }
446} 433}