Skip to content

Commit afb7f03

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 a616c4d commit afb7f03

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
@@ -127,9 +127,9 @@ text-size = "1.1.1"
127127
tracing = "0.1.40"
128128
tracing-tree = "0.3.0"
129129
tracing-subscriber = { version = "0.3.18", default-features = false, features = [
130-
"registry",
131-
"fmt",
132-
"tracing-log",
130+
"registry",
131+
"fmt",
132+
"tracing-log",
133133
] }
134134
triomphe = { version = "0.1.10", default-features = false, features = ["std"] }
135135
xshell = "0.2.5"

crates/ide-completion/src/item.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,9 @@ pub struct CompletionRelevance {
166166
pub postfix_match: Option<CompletionRelevancePostfixMatch>,
167167
/// This is set for type inference results
168168
pub is_definite: bool,
169+
/// Any other bonuses we want to add,
170+
/// eg. bonus for good behavior!
171+
pub bonus_score: u32,
169172
}
170173

171174
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
@@ -231,6 +234,7 @@ impl CompletionRelevance {
231234
postfix_match,
232235
is_definite,
233236
is_item_from_notable_trait,
237+
bonus_score,
234238
} = self;
235239

236240
// lower rank private things
@@ -275,7 +279,8 @@ impl CompletionRelevance {
275279
if is_definite {
276280
score += 10;
277281
}
278-
score
282+
283+
score + bonus_score
279284
}
280285

281286
/// 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
}
@@ -1177,6 +1181,7 @@ fn main() { let _: m::Spam = S$0 }
11771181
is_private_editable: false,
11781182
postfix_match: None,
11791183
is_definite: false,
1184+
bonus_score: 0,
11801185
},
11811186
trigger_call_info: true,
11821187
},
@@ -1204,6 +1209,7 @@ fn main() { let _: m::Spam = S$0 }
12041209
is_private_editable: false,
12051210
postfix_match: None,
12061211
is_definite: false,
1212+
bonus_score: 0,
12071213
},
12081214
trigger_call_info: true,
12091215
},
@@ -1283,6 +1289,7 @@ fn foo() { A { the$0 } }
12831289
is_private_editable: false,
12841290
postfix_match: None,
12851291
is_definite: false,
1292+
bonus_score: 0,
12861293
},
12871294
},
12881295
]
@@ -2005,6 +2012,60 @@ fn main() {
20052012
);
20062013
}
20072014

2015+
#[test]
2016+
fn colon_complete_preferred_order_relevances() {
2017+
check_relevance(
2018+
r#"
2019+
struct A;
2020+
struct ABuilder;
2021+
impl A {
2022+
fn foo(&self) {}
2023+
fn new_1(input: u32) -> A { A }
2024+
fn new_2() -> Self { A }
2025+
fn aaaabuilder() -> ABuilder { A }
2026+
}
2027+
2028+
fn test() {
2029+
let a = A::$0;
2030+
}
2031+
"#,
2032+
// preference:
2033+
// fn with no param that returns itself
2034+
// builder like fn
2035+
// fn with param that returns itself
2036+
expect![[r#"
2037+
fn new_2() [type_could_unify]
2038+
fn aaaabuilder() [type_could_unify]
2039+
fn new_1(…) [type_could_unify]
2040+
me foo(…) [type_could_unify]
2041+
"#]],
2042+
);
2043+
2044+
// Generic
2045+
check_relevance(
2046+
r#"
2047+
struct A<T>{item: T}
2048+
struct ABuilder;
2049+
impl A {
2050+
fn foo(&self) {}
2051+
fn new_1<T>(input: u32, l: T) -> A<T> { A }
2052+
fn new_2<T>() -> Self { A { item: <_>::default()} }
2053+
fn aaaabuilder<T>() -> ABuilder<T> { A }
2054+
}
2055+
2056+
fn test() {
2057+
let a = A::$0;
2058+
}
2059+
"#,
2060+
expect![[r#"
2061+
fn new_2() [type_could_unify]
2062+
fn aaaabuilder() [type_could_unify]
2063+
fn new_1(…) [type_could_unify]
2064+
me foo(…) [type_could_unify]
2065+
"#]],
2066+
);
2067+
}
2068+
20082069
#[test]
20092070
fn struct_field_method_ref() {
20102071
check_kinds(
@@ -2099,6 +2160,7 @@ fn foo() {
20992160
is_private_editable: false,
21002161
postfix_match: None,
21012162
is_definite: false,
2163+
bonus_score: 0,
21022164
},
21032165
},
21042166
]
@@ -2136,6 +2198,19 @@ fn main() {
21362198
),
21372199
lookup: "foo",
21382200
detail: "fn() -> S",
2201+
relevance: CompletionRelevance {
2202+
exact_name_match: false,
2203+
type_match: None,
2204+
is_local: false,
2205+
is_item_from_trait: false,
2206+
is_name_already_imported: false,
2207+
requires_import: false,
2208+
is_op_method: false,
2209+
is_private_editable: false,
2210+
postfix_match: None,
2211+
is_definite: false,
2212+
bonus_score: 30,
2213+
},
21392214
ref_match: "&@92",
21402215
},
21412216
]

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ fn render(
109109
exact_name_match: compute_exact_name_match(completion, &call),
110110
is_op_method,
111111
is_item_from_notable_trait,
112+
bonus_score: calculate_bonus(&ctx, func, db),
112113
..ctx.completion_relevance()
113114
});
114115

@@ -157,6 +158,34 @@ fn render(
157158
item
158159
}
159160

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

0 commit comments

Comments
 (0)