Skip to content

Commit 25f2c77

Browse files
committed
feat: completion list suggests methods in the order we most likely want
When typing `MyType::` the completion items' order could be re-ordered based on how likely we want to select those: * Constructors: `new` like functions to be able to create the type, * Builder Methods: any builder methods available, * Constructors that take args: Any other function that creates `Self`, * Regular methods & associated functions (no change there) ![image](https://github.com/rust-lang/rust-analyzer/assets/1546896/54593b91-07b3-455a-8a71-8d203d4eaf4a) In this photo, the order is: * `new` constructor is first * `new_builder` second is a builder method * `aaaanew` is a constructor that takes arguments, is third and is irrespective of its alphabetical order among names. I've dropped my previous idea of highlighting these functions in the rustdoc (rust-lang/rust#107926)
1 parent dd07f1f commit 25f2c77

File tree

4 files changed

+114
-5
lines changed

4 files changed

+114
-5
lines changed

Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,9 @@ text-size = "1.1.1"
117117
tracing = "0.1.40"
118118
tracing-tree = "0.3.0"
119119
tracing-subscriber = { version = "0.3.18", default-features = false, features = [
120-
"registry",
121-
"fmt",
122-
"tracing-log",
120+
"registry",
121+
"fmt",
122+
"tracing-log",
123123
] }
124124
triomphe = { version = "0.1.10", default-features = false, features = ["std"] }
125125
xshell = "0.2.5"

crates/ide-completion/src/item.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,9 @@ pub struct CompletionRelevance {
164164
pub postfix_match: Option<CompletionRelevancePostfixMatch>,
165165
/// This is set for type inference results
166166
pub is_definite: bool,
167+
/// Any other bonuses we want to add,
168+
/// eg. bonus for good behavior!
169+
pub bonus_score: u32,
167170
}
168171

169172
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
@@ -228,6 +231,7 @@ impl CompletionRelevance {
228231
is_private_editable,
229232
postfix_match,
230233
is_definite,
234+
bonus_score,
231235
} = self;
232236

233237
// lower rank private things
@@ -269,7 +273,8 @@ impl CompletionRelevance {
269273
if is_definite {
270274
score += 10;
271275
}
272-
score
276+
277+
score + bonus_score
273278
}
274279

275280
/// Returns true when the score for this threshold is above

crates/ide-completion/src/render.rs

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use ide_db::{
1717
imports::import_assets::LocatedImport,
1818
RootDatabase, SnippetCap, SymbolKind,
1919
};
20-
use syntax::{AstNode, SmolStr, SyntaxKind, TextRange};
20+
use syntax::{AstNode, SmolStr, SyntaxKind, SyntaxToken, TextRange};
2121
use text_edit::TextEdit;
2222

2323
use crate::{
@@ -72,6 +72,10 @@ impl<'a> RenderContext<'a> {
7272
self.completion.db
7373
}
7474

75+
fn token(&self) -> &SyntaxToken {
76+
&self.completion.token
77+
}
78+
7579
fn source_range(&self) -> TextRange {
7680
self.completion.source_range()
7781
}
@@ -1176,6 +1180,7 @@ fn main() { let _: m::Spam = S$0 }
11761180
is_private_editable: false,
11771181
postfix_match: None,
11781182
is_definite: false,
1183+
bonus_score: 0,
11791184
},
11801185
trigger_call_info: true,
11811186
},
@@ -1202,6 +1207,7 @@ fn main() { let _: m::Spam = S$0 }
12021207
is_private_editable: false,
12031208
postfix_match: None,
12041209
is_definite: false,
1210+
bonus_score: 0,
12051211
},
12061212
trigger_call_info: true,
12071213
},
@@ -1280,6 +1286,7 @@ fn foo() { A { the$0 } }
12801286
is_private_editable: false,
12811287
postfix_match: None,
12821288
is_definite: false,
1289+
bonus_score: 0,
12831290
},
12841291
},
12851292
]
@@ -2002,6 +2009,60 @@ fn main() {
20022009
);
20032010
}
20042011

2012+
#[test]
2013+
fn colon_complete_preferred_order_relevances() {
2014+
check_relevance(
2015+
r#"
2016+
struct A;
2017+
struct ABuilder;
2018+
impl A {
2019+
fn foo(&self) {}
2020+
fn new_1(input: u32) -> A { A }
2021+
fn new_2() -> Self { A }
2022+
fn aaaabuilder() -> ABuilder { A }
2023+
}
2024+
2025+
fn test() {
2026+
let a = A::$0;
2027+
}
2028+
"#,
2029+
// preference:
2030+
// fn with no param that returns itself
2031+
// builder like fn
2032+
// fn with param that returns itself
2033+
expect![[r#"
2034+
fn new_2() [type_could_unify]
2035+
fn aaaabuilder() [type_could_unify]
2036+
fn new_1(…) [type_could_unify]
2037+
me foo(…) [type_could_unify]
2038+
"#]],
2039+
);
2040+
2041+
// Generic
2042+
check_relevance(
2043+
r#"
2044+
struct A<T>{item: T}
2045+
struct ABuilder;
2046+
impl A {
2047+
fn foo(&self) {}
2048+
fn new_1<T>(input: u32, l: T) -> A<T> { A }
2049+
fn new_2<T>() -> Self { A { item: <_>::default()} }
2050+
fn aaaabuilder<T>() -> ABuilder<T> { A }
2051+
}
2052+
2053+
fn test() {
2054+
let a = A::$0;
2055+
}
2056+
"#,
2057+
expect![[r#"
2058+
fn new_2() [type_could_unify]
2059+
fn aaaabuilder() [type_could_unify]
2060+
fn new_1(…) [type_could_unify]
2061+
me foo(…) [type_could_unify]
2062+
"#]],
2063+
);
2064+
}
2065+
20052066
#[test]
20062067
fn struct_field_method_ref() {
20072068
check_kinds(
@@ -2095,6 +2156,7 @@ fn foo() {
20952156
is_private_editable: false,
20962157
postfix_match: None,
20972158
is_definite: false,
2159+
bonus_score: 0,
20982160
},
20992161
},
21002162
]
@@ -2132,6 +2194,19 @@ fn main() {
21322194
),
21332195
lookup: "foo",
21342196
detail: "fn() -> S",
2197+
relevance: CompletionRelevance {
2198+
exact_name_match: false,
2199+
type_match: None,
2200+
is_local: false,
2201+
is_item_from_trait: false,
2202+
is_name_already_imported: false,
2203+
requires_import: false,
2204+
is_op_method: false,
2205+
is_private_editable: false,
2206+
postfix_match: None,
2207+
is_definite: false,
2208+
bonus_score: 30,
2209+
},
21352210
ref_match: "&@92",
21362211
},
21372212
]

crates/ide-completion/src/render/function.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ fn render(
105105
},
106106
exact_name_match: compute_exact_name_match(completion, &call),
107107
is_op_method,
108+
bonus_score: calculate_bonus(&ctx, func, db),
108109
..ctx.completion_relevance()
109110
});
110111

@@ -153,6 +154,34 @@ fn render(
153154
item
154155
}
155156

157+
/// When typing `::` of a type, the preferred order is:
158+
/// * Constructors: new like functions to be able to create the type,
159+
/// * Builder Methods,
160+
/// * Constructors that take args: Any other function that creates Self
161+
/// * Regular methods & Associated functions
162+
///
163+
fn calculate_bonus(ctx: &RenderContext<'_>, func: hir::Function, db: &dyn HirDatabase) -> u32 {
164+
if ctx.token().kind() != syntax::SyntaxKind::COLON2 || func.self_param(db).is_some() {
165+
return 0;
166+
}
167+
168+
let mut bonus = 0;
169+
170+
let has_args = !func.assoc_fn_params(db).is_empty();
171+
let ret_type = func.ret_type(db);
172+
if !has_args && !ret_type.is_unit() {
173+
// fn() -> A
174+
bonus += 30;
175+
} else if has_args && !ret_type.is_unit() {
176+
// fn(..) -> A
177+
bonus += 20;
178+
} else if !has_args && ret_type.display(db).to_string().ends_with("Builder") {
179+
// -> [..]Builder
180+
bonus += 10;
181+
}
182+
bonus
183+
}
184+
156185
pub(super) fn add_call_parens<'b>(
157186
builder: &'b mut Builder,
158187
ctx: &CompletionContext<'_>,

0 commit comments

Comments
 (0)