Skip to content

Commit d021dbe

Browse files
bors[bot]matthewsanetraVeykril
authored
Merge #6393 #6399
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 <[email protected]> Co-authored-by: Lukas Wirth <[email protected]>
3 parents 2bd26e6 + 05723cb + 042413c commit d021dbe

File tree

2 files changed

+275
-15
lines changed

2 files changed

+275
-15
lines changed

crates/assists/src/handlers/qualify_path.rs

Lines changed: 169 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,14 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
5656
ImportCandidate::QualifierStart(_) => {
5757
mark::hit!(qualify_path_qualifier_start);
5858
let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?;
59-
let segment = path.segment()?;
60-
QualifyCandidate::QualifierStart(segment)
59+
let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?);
60+
QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list())
6161
}
6262
ImportCandidate::UnqualifiedName(_) => {
6363
mark::hit!(qualify_path_unqualified_name);
64-
QualifyCandidate::UnqualifiedName
64+
let path = ast::Path::cast(import_assets.syntax_under_caret().clone())?;
65+
let generics = path.segment()?.generic_arg_list();
66+
QualifyCandidate::UnqualifiedName(generics)
6567
}
6668
ImportCandidate::TraitAssocItem(_) => {
6769
mark::hit!(qualify_path_trait_assoc_item);
@@ -96,22 +98,25 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
9698
}
9799

98100
enum QualifyCandidate<'db> {
99-
QualifierStart(ast::PathSegment),
100-
UnqualifiedName,
101+
QualifierStart(ast::PathSegment, Option<ast::GenericArgList>),
102+
UnqualifiedName(Option<ast::GenericArgList>),
101103
TraitAssocItem(ast::Path, ast::PathSegment),
102104
TraitMethod(&'db RootDatabase, ast::MethodCallExpr),
103105
}
104106

105107
impl QualifyCandidate<'_> {
106108
fn qualify(&self, mut replacer: impl FnMut(String), import: hir::ModPath, item: hir::ItemInNs) {
109+
let import = mod_path_to_ast(&import);
107110
match self {
108-
QualifyCandidate::QualifierStart(segment) => {
109-
let import = mod_path_to_ast(&import);
110-
replacer(format!("{}::{}", import, segment));
111+
QualifyCandidate::QualifierStart(segment, generics) => {
112+
let generics = generics.as_ref().map_or_else(String::new, ToString::to_string);
113+
replacer(format!("{}{}::{}", import, generics, segment));
114+
}
115+
QualifyCandidate::UnqualifiedName(generics) => {
116+
let generics = generics.as_ref().map_or_else(String::new, ToString::to_string);
117+
replacer(format!("{}{}", import.to_string(), generics));
111118
}
112-
QualifyCandidate::UnqualifiedName => replacer(mod_path_to_ast(&import).to_string()),
113119
QualifyCandidate::TraitAssocItem(qualifier, segment) => {
114-
let import = mod_path_to_ast(&import);
115120
replacer(format!("<{} as {}>::{}", qualifier, import, segment));
116121
}
117122
&QualifyCandidate::TraitMethod(db, ref mcall_expr) => {
@@ -124,25 +129,27 @@ impl QualifyCandidate<'_> {
124129
db: &RootDatabase,
125130
mcall_expr: &ast::MethodCallExpr,
126131
mut replacer: impl FnMut(String),
127-
import: hir::ModPath,
132+
import: ast::Path,
128133
item: hir::ItemInNs,
129134
) -> Option<()> {
130135
let receiver = mcall_expr.receiver()?;
131136
let trait_method_name = mcall_expr.name_ref()?;
137+
let generics =
138+
mcall_expr.generic_arg_list().as_ref().map_or_else(String::new, ToString::to_string);
132139
let arg_list = mcall_expr.arg_list().map(|arg_list| arg_list.args());
133140
let trait_ = item_as_trait(item)?;
134141
let method = find_trait_method(db, trait_, &trait_method_name)?;
135142
if let Some(self_access) = method.self_param(db).map(|sp| sp.access(db)) {
136-
let import = mod_path_to_ast(&import);
137143
let receiver = match self_access {
138144
hir::Access::Shared => make::expr_ref(receiver, false),
139145
hir::Access::Exclusive => make::expr_ref(receiver, true),
140146
hir::Access::Owned => receiver,
141147
};
142148
replacer(format!(
143-
"{}::{}{}",
149+
"{}::{}{}{}",
144150
import,
145151
trait_method_name,
152+
generics,
146153
match arg_list.clone() {
147154
Some(args) => make::arg_list(iter::once(receiver).chain(args)),
148155
None => make::arg_list(iter::once(receiver)),
@@ -1045,4 +1052,153 @@ fn main() {
10451052
",
10461053
);
10471054
}
1055+
1056+
#[test]
1057+
fn keep_generic_annotations() {
1058+
check_assist(
1059+
qualify_path,
1060+
r"
1061+
//- /lib.rs crate:dep
1062+
pub mod generic { pub struct Thing<'a, T>(&'a T); }
1063+
1064+
//- /main.rs crate:main deps:dep
1065+
fn foo() -> Thin<|>g<'static, ()> {}
1066+
1067+
fn main() {}
1068+
",
1069+
r"
1070+
fn foo() -> dep::generic::Thing<'static, ()> {}
1071+
1072+
fn main() {}
1073+
",
1074+
);
1075+
}
1076+
1077+
#[test]
1078+
fn keep_generic_annotations_leading_colon() {
1079+
check_assist(
1080+
qualify_path,
1081+
r"
1082+
//- /lib.rs crate:dep
1083+
pub mod generic { pub struct Thing<'a, T>(&'a T); }
1084+
1085+
//- /main.rs crate:main deps:dep
1086+
fn foo() -> Thin<|>g::<'static, ()> {}
1087+
1088+
fn main() {}
1089+
",
1090+
r"
1091+
fn foo() -> dep::generic::Thing::<'static, ()> {}
1092+
1093+
fn main() {}
1094+
",
1095+
);
1096+
}
1097+
1098+
#[test]
1099+
fn associated_struct_const_generic() {
1100+
check_assist(
1101+
qualify_path,
1102+
r"
1103+
mod test_mod {
1104+
pub struct TestStruct<T> {}
1105+
impl<T> TestStruct<T> {
1106+
const TEST_CONST: u8 = 42;
1107+
}
1108+
}
1109+
1110+
fn main() {
1111+
TestStruct::<()>::TEST_CONST<|>
1112+
}
1113+
",
1114+
r"
1115+
mod test_mod {
1116+
pub struct TestStruct<T> {}
1117+
impl<T> TestStruct<T> {
1118+
const TEST_CONST: u8 = 42;
1119+
}
1120+
}
1121+
1122+
fn main() {
1123+
test_mod::TestStruct::<()>::TEST_CONST
1124+
}
1125+
",
1126+
);
1127+
}
1128+
1129+
#[test]
1130+
fn associated_trait_const_generic() {
1131+
check_assist(
1132+
qualify_path,
1133+
r"
1134+
mod test_mod {
1135+
pub trait TestTrait {
1136+
const TEST_CONST: u8;
1137+
}
1138+
pub struct TestStruct<T> {}
1139+
impl<T> TestTrait for TestStruct<T> {
1140+
const TEST_CONST: u8 = 42;
1141+
}
1142+
}
1143+
1144+
fn main() {
1145+
test_mod::TestStruct::<()>::TEST_CONST<|>
1146+
}
1147+
",
1148+
r"
1149+
mod test_mod {
1150+
pub trait TestTrait {
1151+
const TEST_CONST: u8;
1152+
}
1153+
pub struct TestStruct<T> {}
1154+
impl<T> TestTrait for TestStruct<T> {
1155+
const TEST_CONST: u8 = 42;
1156+
}
1157+
}
1158+
1159+
fn main() {
1160+
<test_mod::TestStruct::<()> as test_mod::TestTrait>::TEST_CONST
1161+
}
1162+
",
1163+
);
1164+
}
1165+
1166+
#[test]
1167+
fn trait_method_generic() {
1168+
check_assist(
1169+
qualify_path,
1170+
r"
1171+
mod test_mod {
1172+
pub trait TestTrait {
1173+
fn test_method<T>(&self);
1174+
}
1175+
pub struct TestStruct {}
1176+
impl TestTrait for TestStruct {
1177+
fn test_method<T>(&self) {}
1178+
}
1179+
}
1180+
1181+
fn main() {
1182+
let test_struct = test_mod::TestStruct {};
1183+
test_struct.test_meth<|>od::<()>()
1184+
}
1185+
",
1186+
r"
1187+
mod test_mod {
1188+
pub trait TestTrait {
1189+
fn test_method<T>(&self);
1190+
}
1191+
pub struct TestStruct {}
1192+
impl TestTrait for TestStruct {
1193+
fn test_method<T>(&self) {}
1194+
}
1195+
}
1196+
1197+
fn main() {
1198+
let test_struct = test_mod::TestStruct {};
1199+
test_mod::TestTrait::test_method::<()>(&test_struct)
1200+
}
1201+
",
1202+
);
1203+
}
10481204
}

crates/ide/src/inlay_hints.rs

Lines changed: 106 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use assists::utils::FamousDefs;
22
use either::Either;
3-
use hir::{known, HirDisplay, Semantics};
3+
use hir::{known, Callable, HirDisplay, Semantics};
44
use ide_db::RootDatabase;
55
use stdx::to_lower_snake_case;
66
use syntax::{
@@ -170,7 +170,7 @@ fn get_param_name_hints(
170170
};
171171
Some((param_name, arg))
172172
})
173-
.filter(|(param_name, arg)| should_show_param_name_hint(sema, &callable, &param_name, &arg))
173+
.filter(|(param_name, arg)| should_show_param_name_hint(sema, &callable, param_name, &arg))
174174
.map(|(param_name, arg)| InlayHint {
175175
range: arg.syntax().text_range(),
176176
kind: InlayKind::ParameterHint,
@@ -334,9 +334,11 @@ fn should_show_param_name_hint(
334334
| hir::CallableKind::TupleEnumVariant(_)
335335
| hir::CallableKind::Closure => None,
336336
};
337+
337338
if param_name.is_empty()
338339
|| Some(param_name) == fn_name.as_ref().map(|s| s.trim_start_matches('_'))
339340
|| is_argument_similar_to_param_name(sema, argument, param_name)
341+
|| is_param_name_similar_to_fn_name(param_name, callable, fn_name.as_ref())
340342
|| param_name.starts_with("ra_fixture")
341343
{
342344
return false;
@@ -364,6 +366,26 @@ fn is_argument_similar_to_param_name(
364366
}
365367
}
366368

369+
fn is_param_name_similar_to_fn_name(
370+
param_name: &str,
371+
callable: &Callable,
372+
fn_name: Option<&String>,
373+
) -> bool {
374+
// if it's the only parameter, don't show it if:
375+
// - is the same as the function name, or
376+
// - the function ends with '_' + param_name
377+
378+
match (callable.n_params(), fn_name) {
379+
(1, Some(function)) => {
380+
function == param_name
381+
|| (function.len() > param_name.len()
382+
&& function.ends_with(param_name)
383+
&& function[..function.len() - param_name.len()].ends_with('_'))
384+
}
385+
_ => false,
386+
}
387+
}
388+
367389
fn is_enum_name_similar_to_param_name(
368390
sema: &Semantics<RootDatabase>,
369391
argument: &ast::Expr,
@@ -456,6 +478,88 @@ fn main() {
456478
);
457479
}
458480

481+
#[test]
482+
fn param_name_similar_to_fn_name_still_hints() {
483+
check_with_config(
484+
InlayHintsConfig {
485+
parameter_hints: true,
486+
type_hints: false,
487+
chaining_hints: false,
488+
max_length: None,
489+
},
490+
r#"
491+
fn max(x: i32, y: i32) -> i32 { x + y }
492+
fn main() {
493+
let _x = max(
494+
4,
495+
//^ x
496+
4,
497+
//^ y
498+
);
499+
}"#,
500+
);
501+
}
502+
503+
#[test]
504+
fn param_name_similar_to_fn_name() {
505+
check_with_config(
506+
InlayHintsConfig {
507+
parameter_hints: true,
508+
type_hints: false,
509+
chaining_hints: false,
510+
max_length: None,
511+
},
512+
r#"
513+
fn param_with_underscore(with_underscore: i32) -> i32 { with_underscore }
514+
fn main() {
515+
let _x = param_with_underscore(
516+
4,
517+
);
518+
}"#,
519+
);
520+
}
521+
522+
#[test]
523+
fn param_name_same_as_fn_name() {
524+
check_with_config(
525+
InlayHintsConfig {
526+
parameter_hints: true,
527+
type_hints: false,
528+
chaining_hints: false,
529+
max_length: None,
530+
},
531+
r#"
532+
fn foo(foo: i32) -> i32 { foo }
533+
fn main() {
534+
let _x = foo(
535+
4,
536+
);
537+
}"#,
538+
);
539+
}
540+
541+
#[test]
542+
fn never_hide_param_when_multiple_params() {
543+
check_with_config(
544+
InlayHintsConfig {
545+
parameter_hints: true,
546+
type_hints: false,
547+
chaining_hints: false,
548+
max_length: None,
549+
},
550+
r#"
551+
fn foo(bar: i32, baz: i32) -> i32 { bar + baz }
552+
fn main() {
553+
let _x = foo(
554+
4,
555+
//^ bar
556+
8,
557+
//^ baz
558+
);
559+
}"#,
560+
);
561+
}
562+
459563
#[test]
460564
fn hints_disabled() {
461565
check_with_config(

0 commit comments

Comments
 (0)