diff options
-rw-r--r-- | crates/ide/src/inlay_hints.rs | 127 |
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 | ||
325 | fn should_show_param_name_hint( | 313 | fn 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 | ||
353 | fn is_argument_similar_to_param_name( | 344 | fn 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 | ||
386 | fn 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. | ||
368 | fn 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 | ||
434 | fn is_obvious_param(param_name: &str) -> bool { | 412 | fn 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 | ||
440 | fn get_callable(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Callable> { | 420 | fn 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 | } |