mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-14 17:07:26 +00:00
6393: Remove repetitive inlay hints (take 2) r=matklad a=lnicola 6399: Keep generic annotations when qualifying things r=matklad a=Veykril The `qualify_path` assists currently eats up already annotated generics in all but one cases which can be annoying if one already pre-fills generics of a type before it's been qualified. Co-authored-by: Matthew Sanetra <matthewsanetra@gmail.com> Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
commit
d021dbeb4f
2 changed files with 275 additions and 15 deletions
|
@ -56,12 +56,14 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
|
|||
ImportCandidate::QualifierStart(_) => {
|
||||
mark::hit!(qualify_path_qualifier_start);
|
||||
let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?;
|
||||
let segment = path.segment()?;
|
||||
QualifyCandidate::QualifierStart(segment)
|
||||
let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?);
|
||||
QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list())
|
||||
}
|
||||
ImportCandidate::UnqualifiedName(_) => {
|
||||
mark::hit!(qualify_path_unqualified_name);
|
||||
QualifyCandidate::UnqualifiedName
|
||||
let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?;
|
||||
let generics = path.segment()?.generic_arg_list();
|
||||
QualifyCandidate::UnqualifiedName(generics)
|
||||
}
|
||||
ImportCandidate::TraitAssocItem(_) => {
|
||||
mark::hit!(qualify_path_trait_assoc_item);
|
||||
|
@ -96,22 +98,25 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
|
|||
}
|
||||
|
||||
enum QualifyCandidate<'db> {
|
||||
QualifierStart(ast::PathSegment),
|
||||
UnqualifiedName,
|
||||
QualifierStart(ast::PathSegment, Option<ast::GenericArgList>),
|
||||
UnqualifiedName(Option<ast::GenericArgList>),
|
||||
TraitAssocItem(ast::Path, ast::PathSegment),
|
||||
TraitMethod(&'db RootDatabase, ast::MethodCallExpr),
|
||||
}
|
||||
|
||||
impl QualifyCandidate<'_> {
|
||||
fn qualify(&self, mut replacer: impl FnMut(String), import: hir::ModPath, item: hir::ItemInNs) {
|
||||
let import = mod_path_to_ast(&import);
|
||||
match self {
|
||||
QualifyCandidate::QualifierStart(segment) => {
|
||||
let import = mod_path_to_ast(&import);
|
||||
replacer(format!("{}::{}", import, segment));
|
||||
QualifyCandidate::QualifierStart(segment, generics) => {
|
||||
let generics = generics.as_ref().map_or_else(String::new, ToString::to_string);
|
||||
replacer(format!("{}{}::{}", import, generics, segment));
|
||||
}
|
||||
QualifyCandidate::UnqualifiedName(generics) => {
|
||||
let generics = generics.as_ref().map_or_else(String::new, ToString::to_string);
|
||||
replacer(format!("{}{}", import.to_string(), generics));
|
||||
}
|
||||
QualifyCandidate::UnqualifiedName => replacer(mod_path_to_ast(&import).to_string()),
|
||||
QualifyCandidate::TraitAssocItem(qualifier, segment) => {
|
||||
let import = mod_path_to_ast(&import);
|
||||
replacer(format!("<{} as {}>::{}", qualifier, import, segment));
|
||||
}
|
||||
&QualifyCandidate::TraitMethod(db, ref mcall_expr) => {
|
||||
|
@ -124,25 +129,27 @@ impl QualifyCandidate<'_> {
|
|||
db: &RootDatabase,
|
||||
mcall_expr: &ast::MethodCallExpr,
|
||||
mut replacer: impl FnMut(String),
|
||||
import: hir::ModPath,
|
||||
import: ast::Path,
|
||||
item: hir::ItemInNs,
|
||||
) -> Option<()> {
|
||||
let receiver = mcall_expr.receiver()?;
|
||||
let trait_method_name = mcall_expr.name_ref()?;
|
||||
let generics =
|
||||
mcall_expr.generic_arg_list().as_ref().map_or_else(String::new, ToString::to_string);
|
||||
let arg_list = mcall_expr.arg_list().map(|arg_list| arg_list.args());
|
||||
let trait_ = item_as_trait(item)?;
|
||||
let method = find_trait_method(db, trait_, &trait_method_name)?;
|
||||
if let Some(self_access) = method.self_param(db).map(|sp| sp.access(db)) {
|
||||
let import = mod_path_to_ast(&import);
|
||||
let receiver = match self_access {
|
||||
hir::Access::Shared => make::expr_ref(receiver, false),
|
||||
hir::Access::Exclusive => make::expr_ref(receiver, true),
|
||||
hir::Access::Owned => receiver,
|
||||
};
|
||||
replacer(format!(
|
||||
"{}::{}{}",
|
||||
"{}::{}{}{}",
|
||||
import,
|
||||
trait_method_name,
|
||||
generics,
|
||||
match arg_list.clone() {
|
||||
Some(args) => make::arg_list(iter::once(receiver).chain(args)),
|
||||
None => make::arg_list(iter::once(receiver)),
|
||||
|
@ -1045,4 +1052,153 @@ fn main() {
|
|||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keep_generic_annotations() {
|
||||
check_assist(
|
||||
qualify_path,
|
||||
r"
|
||||
//- /lib.rs crate:dep
|
||||
pub mod generic { pub struct Thing<'a, T>(&'a T); }
|
||||
|
||||
//- /main.rs crate:main deps:dep
|
||||
fn foo() -> Thin<|>g<'static, ()> {}
|
||||
|
||||
fn main() {}
|
||||
",
|
||||
r"
|
||||
fn foo() -> dep::generic::Thing<'static, ()> {}
|
||||
|
||||
fn main() {}
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keep_generic_annotations_leading_colon() {
|
||||
check_assist(
|
||||
qualify_path,
|
||||
r"
|
||||
//- /lib.rs crate:dep
|
||||
pub mod generic { pub struct Thing<'a, T>(&'a T); }
|
||||
|
||||
//- /main.rs crate:main deps:dep
|
||||
fn foo() -> Thin<|>g::<'static, ()> {}
|
||||
|
||||
fn main() {}
|
||||
",
|
||||
r"
|
||||
fn foo() -> dep::generic::Thing::<'static, ()> {}
|
||||
|
||||
fn main() {}
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn associated_struct_const_generic() {
|
||||
check_assist(
|
||||
qualify_path,
|
||||
r"
|
||||
mod test_mod {
|
||||
pub struct TestStruct<T> {}
|
||||
impl<T> TestStruct<T> {
|
||||
const TEST_CONST: u8 = 42;
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
TestStruct::<()>::TEST_CONST<|>
|
||||
}
|
||||
",
|
||||
r"
|
||||
mod test_mod {
|
||||
pub struct TestStruct<T> {}
|
||||
impl<T> TestStruct<T> {
|
||||
const TEST_CONST: u8 = 42;
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
test_mod::TestStruct::<()>::TEST_CONST
|
||||
}
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn associated_trait_const_generic() {
|
||||
check_assist(
|
||||
qualify_path,
|
||||
r"
|
||||
mod test_mod {
|
||||
pub trait TestTrait {
|
||||
const TEST_CONST: u8;
|
||||
}
|
||||
pub struct TestStruct<T> {}
|
||||
impl<T> TestTrait for TestStruct<T> {
|
||||
const TEST_CONST: u8 = 42;
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
test_mod::TestStruct::<()>::TEST_CONST<|>
|
||||
}
|
||||
",
|
||||
r"
|
||||
mod test_mod {
|
||||
pub trait TestTrait {
|
||||
const TEST_CONST: u8;
|
||||
}
|
||||
pub struct TestStruct<T> {}
|
||||
impl<T> TestTrait for TestStruct<T> {
|
||||
const TEST_CONST: u8 = 42;
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
<test_mod::TestStruct::<()> as test_mod::TestTrait>::TEST_CONST
|
||||
}
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trait_method_generic() {
|
||||
check_assist(
|
||||
qualify_path,
|
||||
r"
|
||||
mod test_mod {
|
||||
pub trait TestTrait {
|
||||
fn test_method<T>(&self);
|
||||
}
|
||||
pub struct TestStruct {}
|
||||
impl TestTrait for TestStruct {
|
||||
fn test_method<T>(&self) {}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let test_struct = test_mod::TestStruct {};
|
||||
test_struct.test_meth<|>od::<()>()
|
||||
}
|
||||
",
|
||||
r"
|
||||
mod test_mod {
|
||||
pub trait TestTrait {
|
||||
fn test_method<T>(&self);
|
||||
}
|
||||
pub struct TestStruct {}
|
||||
impl TestTrait for TestStruct {
|
||||
fn test_method<T>(&self) {}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let test_struct = test_mod::TestStruct {};
|
||||
test_mod::TestTrait::test_method::<()>(&test_struct)
|
||||
}
|
||||
",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use assists::utils::FamousDefs;
|
||||
use either::Either;
|
||||
use hir::{known, HirDisplay, Semantics};
|
||||
use hir::{known, Callable, HirDisplay, Semantics};
|
||||
use ide_db::RootDatabase;
|
||||
use stdx::to_lower_snake_case;
|
||||
use syntax::{
|
||||
|
@ -170,7 +170,7 @@ fn get_param_name_hints(
|
|||
};
|
||||
Some((param_name, arg))
|
||||
})
|
||||
.filter(|(param_name, arg)| should_show_param_name_hint(sema, &callable, ¶m_name, &arg))
|
||||
.filter(|(param_name, arg)| should_show_param_name_hint(sema, &callable, param_name, &arg))
|
||||
.map(|(param_name, arg)| InlayHint {
|
||||
range: arg.syntax().text_range(),
|
||||
kind: InlayKind::ParameterHint,
|
||||
|
@ -334,9 +334,11 @@ fn should_show_param_name_hint(
|
|||
| 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;
|
||||
|
@ -364,6 +366,26 @@ fn is_argument_similar_to_param_name(
|
|||
}
|
||||
}
|
||||
|
||||
fn is_param_name_similar_to_fn_name(
|
||||
param_name: &str,
|
||||
callable: &Callable,
|
||||
fn_name: Option<&String>,
|
||||
) -> 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) {
|
||||
(1, Some(function)) => {
|
||||
function == param_name
|
||||
|| (function.len() > param_name.len()
|
||||
&& function.ends_with(param_name)
|
||||
&& function[..function.len() - param_name.len()].ends_with('_'))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_enum_name_similar_to_param_name(
|
||||
sema: &Semantics<RootDatabase>,
|
||||
argument: &ast::Expr,
|
||||
|
@ -456,6 +478,88 @@ fn main() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn param_name_similar_to_fn_name_still_hints() {
|
||||
check_with_config(
|
||||
InlayHintsConfig {
|
||||
parameter_hints: true,
|
||||
type_hints: false,
|
||||
chaining_hints: false,
|
||||
max_length: None,
|
||||
},
|
||||
r#"
|
||||
fn max(x: i32, y: i32) -> i32 { x + y }
|
||||
fn main() {
|
||||
let _x = max(
|
||||
4,
|
||||
//^ x
|
||||
4,
|
||||
//^ y
|
||||
);
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn param_name_similar_to_fn_name() {
|
||||
check_with_config(
|
||||
InlayHintsConfig {
|
||||
parameter_hints: true,
|
||||
type_hints: false,
|
||||
chaining_hints: false,
|
||||
max_length: None,
|
||||
},
|
||||
r#"
|
||||
fn param_with_underscore(with_underscore: i32) -> i32 { with_underscore }
|
||||
fn main() {
|
||||
let _x = param_with_underscore(
|
||||
4,
|
||||
);
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn param_name_same_as_fn_name() {
|
||||
check_with_config(
|
||||
InlayHintsConfig {
|
||||
parameter_hints: true,
|
||||
type_hints: false,
|
||||
chaining_hints: false,
|
||||
max_length: None,
|
||||
},
|
||||
r#"
|
||||
fn foo(foo: i32) -> i32 { foo }
|
||||
fn main() {
|
||||
let _x = foo(
|
||||
4,
|
||||
);
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn never_hide_param_when_multiple_params() {
|
||||
check_with_config(
|
||||
InlayHintsConfig {
|
||||
parameter_hints: true,
|
||||
type_hints: false,
|
||||
chaining_hints: false,
|
||||
max_length: None,
|
||||
},
|
||||
r#"
|
||||
fn foo(bar: i32, baz: i32) -> i32 { bar + baz }
|
||||
fn main() {
|
||||
let _x = foo(
|
||||
4,
|
||||
//^ bar
|
||||
8,
|
||||
//^ baz
|
||||
);
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hints_disabled() {
|
||||
check_with_config(
|
||||
|
|
Loading…
Reference in a new issue