Merge #9130
9130: Prefix/suffix parameter inlay hint hiding heuristic is more strict r=Veykril a=Veykril Instead of just plainly checking prefix/suffix of the argument string to the parameter name we only check for prefixes and suffixes if they are split apart via an underscore meaning, with the argument `foo`, it will be hidden for the parameter name `foo_bar` but not for `foobar`. bors r+ Closes https://github.com/rust-analyzer/rust-analyzer/issues/8878 Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
commit
7f9c4a59d9
1 changed files with 61 additions and 74 deletions
|
@ -150,17 +150,11 @@ fn get_param_name_hints(
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let args = match &expr {
|
let (callable, arg_list) = get_callable(sema, &expr)?;
|
||||||
ast::Expr::CallExpr(expr) => expr.arg_list()?.args(),
|
|
||||||
ast::Expr::MethodCallExpr(expr) => expr.arg_list()?.args(),
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let callable = get_callable(sema, &expr)?;
|
|
||||||
let hints = callable
|
let hints = callable
|
||||||
.params(sema.db)
|
.params(sema.db)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.zip(args)
|
.zip(arg_list.args())
|
||||||
.filter_map(|((param, _ty), arg)| {
|
.filter_map(|((param, _ty), arg)| {
|
||||||
let param_name = match param? {
|
let param_name = match param? {
|
||||||
Either::Left(_) => "self".to_string(),
|
Either::Left(_) => "self".to_string(),
|
||||||
|
@ -171,7 +165,7 @@ fn get_param_name_hints(
|
||||||
};
|
};
|
||||||
Some((param_name, arg))
|
Some((param_name, arg))
|
||||||
})
|
})
|
||||||
.filter(|(param_name, arg)| should_show_param_name_hint(sema, &callable, param_name, &arg))
|
.filter(|(param_name, arg)| !should_hide_param_name_hint(sema, &callable, param_name, &arg))
|
||||||
.map(|(param_name, arg)| InlayHint {
|
.map(|(param_name, arg)| InlayHint {
|
||||||
range: arg.syntax().text_range(),
|
range: arg.syntax().text_range(),
|
||||||
kind: InlayKind::ParameterHint,
|
kind: InlayKind::ParameterHint,
|
||||||
|
@ -289,15 +283,9 @@ fn should_not_display_type_hint(
|
||||||
for node in bind_pat.syntax().ancestors() {
|
for node in bind_pat.syntax().ancestors() {
|
||||||
match_ast! {
|
match_ast! {
|
||||||
match node {
|
match node {
|
||||||
ast::LetStmt(it) => {
|
ast::LetStmt(it) => return it.ty().is_some(),
|
||||||
return it.ty().is_some()
|
ast::Param(it) => return it.ty().is_some(),
|
||||||
},
|
ast::MatchArm(_it) => return pat_is_enum_variant(db, bind_pat, pat_ty),
|
||||||
ast::Param(it) => {
|
|
||||||
return it.ty().is_some()
|
|
||||||
},
|
|
||||||
ast::MatchArm(_it) => {
|
|
||||||
return pat_is_enum_variant(db, bind_pat, pat_ty);
|
|
||||||
},
|
|
||||||
ast::IfExpr(it) => {
|
ast::IfExpr(it) => {
|
||||||
return it.condition().and_then(|condition| condition.pat()).is_some()
|
return it.condition().and_then(|condition| condition.pat()).is_some()
|
||||||
&& pat_is_enum_variant(db, bind_pat, pat_ty);
|
&& pat_is_enum_variant(db, bind_pat, pat_ty);
|
||||||
|
@ -322,76 +310,66 @@ fn should_not_display_type_hint(
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_show_param_name_hint(
|
fn should_hide_param_name_hint(
|
||||||
sema: &Semantics<RootDatabase>,
|
sema: &Semantics<RootDatabase>,
|
||||||
callable: &hir::Callable,
|
callable: &hir::Callable,
|
||||||
param_name: &str,
|
param_name: &str,
|
||||||
argument: &ast::Expr,
|
argument: &ast::Expr,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
// hide when:
|
||||||
|
// - the parameter name is a suffix of the function's name
|
||||||
|
// - the argument is an enum whose name is equal to the parameter
|
||||||
|
// - exact argument<->parameter match(ignoring leading underscore) or argument is a prefix/suffix
|
||||||
|
// of parameter with _ splitting it off
|
||||||
|
// - param starts with `ra_fixture`
|
||||||
|
// - param is a well known name in an unary function
|
||||||
|
|
||||||
let param_name = param_name.trim_start_matches('_');
|
let param_name = param_name.trim_start_matches('_');
|
||||||
let fn_name = match callable.kind() {
|
if param_name.is_empty() {
|
||||||
hir::CallableKind::Function(it) => Some(it.name(sema.db).to_string()),
|
|
||||||
hir::CallableKind::TupleStruct(_)
|
|
||||||
| hir::CallableKind::TupleEnumVariant(_)
|
|
||||||
| hir::CallableKind::Closure => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
if param_name.is_empty()
|
|
||||||
|| Some(param_name) == fn_name.as_ref().map(|s| s.trim_start_matches('_'))
|
|
||||||
|| is_argument_similar_to_param_name(sema, argument, param_name)
|
|
||||||
|| is_param_name_similar_to_fn_name(param_name, callable, fn_name.as_ref())
|
|
||||||
|| param_name.starts_with("ra_fixture")
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// avoid displaying hints for common functions like map, filter, etc.
|
|
||||||
// or other obvious words used in std
|
|
||||||
!(callable.n_params() == 1 && is_obvious_param(param_name))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_argument_similar_to_param_name(
|
|
||||||
sema: &Semantics<RootDatabase>,
|
|
||||||
argument: &ast::Expr,
|
|
||||||
param_name: &str,
|
|
||||||
) -> bool {
|
|
||||||
if is_enum_name_similar_to_param_name(sema, argument, param_name) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let fn_name = match callable.kind() {
|
||||||
|
hir::CallableKind::Function(it) => Some(it.name(sema.db).to_string()),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
let fn_name = fn_name.as_deref();
|
||||||
|
is_param_name_suffix_of_fn_name(param_name, callable, fn_name)
|
||||||
|
|| is_enum_name_similar_to_param_name(sema, argument, param_name)
|
||||||
|
|| is_argument_similar_to_param_name(argument, param_name)
|
||||||
|
|| param_name.starts_with("ra_fixture")
|
||||||
|
|| (callable.n_params() == 1 && is_obvious_param(param_name))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_argument_similar_to_param_name(argument: &ast::Expr, param_name: &str) -> bool {
|
||||||
match get_string_representation(argument) {
|
match get_string_representation(argument) {
|
||||||
None => false,
|
None => false,
|
||||||
Some(argument_string) => {
|
Some(argument) => {
|
||||||
let num_leading_underscores =
|
let mut res = false;
|
||||||
argument_string.bytes().take_while(|&c| c == b'_').count();
|
if let Some(first) = argument.bytes().skip_while(|&c| c == b'_').position(|c| c == b'_')
|
||||||
|
{
|
||||||
// Does the argument name begin with the parameter name? Ignore leading underscores.
|
res |= param_name == argument[..first].trim_start_matches('_');
|
||||||
let mut arg_bytes = argument_string.bytes().skip(num_leading_underscores);
|
|
||||||
let starts_with_pattern = param_name.bytes().all(
|
|
||||||
|expected| matches!(arg_bytes.next(), Some(actual) if expected.eq_ignore_ascii_case(&actual)),
|
|
||||||
);
|
|
||||||
|
|
||||||
if starts_with_pattern {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
if let Some(last) =
|
||||||
// Does the argument name end with the parameter name?
|
argument.bytes().rev().skip_while(|&c| c == b'_').position(|c| c == b'_')
|
||||||
let mut arg_bytes = argument_string.bytes().skip(num_leading_underscores);
|
{
|
||||||
param_name.bytes().rev().all(
|
res |= param_name == argument[last..].trim_end_matches('_');
|
||||||
|expected| matches!(arg_bytes.next_back(), Some(actual) if expected.eq_ignore_ascii_case(&actual)),
|
}
|
||||||
)
|
res |= argument == param_name;
|
||||||
|
res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_param_name_similar_to_fn_name(
|
/// Hide the parameter name of an unary function if it is a `_` - prefixed suffix of the function's name, or equal.
|
||||||
|
///
|
||||||
|
/// `fn strip_suffix(suffix)` will be hidden.
|
||||||
|
/// `fn stripsuffix(suffix)` will not be hidden.
|
||||||
|
fn is_param_name_suffix_of_fn_name(
|
||||||
param_name: &str,
|
param_name: &str,
|
||||||
callable: &Callable,
|
callable: &Callable,
|
||||||
fn_name: Option<&String>,
|
fn_name: Option<&str>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// if it's the only parameter, don't show it if:
|
|
||||||
// - is the same as the function name, or
|
|
||||||
// - the function ends with '_' + param_name
|
|
||||||
|
|
||||||
match (callable.n_params(), fn_name) {
|
match (callable.n_params(), fn_name) {
|
||||||
(1, Some(function)) => {
|
(1, Some(function)) => {
|
||||||
function == param_name
|
function == param_name
|
||||||
|
@ -424,7 +402,7 @@ fn get_string_representation(expr: &ast::Expr) -> Option<String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::Expr::FieldExpr(field_expr) => Some(field_expr.name_ref()?.to_string()),
|
ast::Expr::FieldExpr(field_expr) => Some(field_expr.name_ref()?.to_string()),
|
||||||
ast::Expr::PathExpr(path_expr) => Some(path_expr.to_string()),
|
ast::Expr::PathExpr(path_expr) => Some(path_expr.path()?.segment()?.to_string()),
|
||||||
ast::Expr::PrefixExpr(prefix_expr) => get_string_representation(&prefix_expr.expr()?),
|
ast::Expr::PrefixExpr(prefix_expr) => get_string_representation(&prefix_expr.expr()?),
|
||||||
ast::Expr::RefExpr(ref_expr) => get_string_representation(&ref_expr.expr()?),
|
ast::Expr::RefExpr(ref_expr) => get_string_representation(&ref_expr.expr()?),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -432,15 +410,24 @@ fn get_string_representation(expr: &ast::Expr) -> Option<String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_obvious_param(param_name: &str) -> bool {
|
fn is_obvious_param(param_name: &str) -> bool {
|
||||||
|
// avoid displaying hints for common functions like map, filter, etc.
|
||||||
|
// or other obvious words used in std
|
||||||
let is_obvious_param_name =
|
let is_obvious_param_name =
|
||||||
matches!(param_name, "predicate" | "value" | "pat" | "rhs" | "other");
|
matches!(param_name, "predicate" | "value" | "pat" | "rhs" | "other");
|
||||||
param_name.len() == 1 || is_obvious_param_name
|
param_name.len() == 1 || is_obvious_param_name
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_callable(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Callable> {
|
fn get_callable(
|
||||||
|
sema: &Semantics<RootDatabase>,
|
||||||
|
expr: &ast::Expr,
|
||||||
|
) -> Option<(hir::Callable, ast::ArgList)> {
|
||||||
match expr {
|
match expr {
|
||||||
ast::Expr::CallExpr(expr) => sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db),
|
ast::Expr::CallExpr(expr) => {
|
||||||
ast::Expr::MethodCallExpr(expr) => sema.resolve_method_call_as_callable(expr),
|
sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db).zip(expr.arg_list())
|
||||||
|
}
|
||||||
|
ast::Expr::MethodCallExpr(expr) => {
|
||||||
|
sema.resolve_method_call_as_callable(expr).zip(expr.arg_list())
|
||||||
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue