Skip to content

Commit

Permalink
feat: completion list suggests methods in the order we most likely want
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
mustakimali committed Dec 13, 2023
1 parent dd07f1f commit 25f2c77
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 5 deletions.
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,9 @@ text-size = "1.1.1"
tracing = "0.1.40"
tracing-tree = "0.3.0"
tracing-subscriber = { version = "0.3.18", default-features = false, features = [
"registry",
"fmt",
"tracing-log",
"registry",
"fmt",
"tracing-log",
] }
triomphe = { version = "0.1.10", default-features = false, features = ["std"] }
xshell = "0.2.5"
Expand Down
7 changes: 6 additions & 1 deletion crates/ide-completion/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,9 @@ pub struct CompletionRelevance {
pub postfix_match: Option<CompletionRelevancePostfixMatch>,
/// This is set for type inference results
pub is_definite: bool,
/// Any other bonuses we want to add,
/// eg. bonus for good behavior!
pub bonus_score: u32,
}

#[derive(Debug, Clone, Copy, Eq, PartialEq)]
Expand Down Expand Up @@ -228,6 +231,7 @@ impl CompletionRelevance {
is_private_editable,
postfix_match,
is_definite,
bonus_score,
} = self;

// lower rank private things
Expand Down Expand Up @@ -269,7 +273,8 @@ impl CompletionRelevance {
if is_definite {
score += 10;
}
score

score + bonus_score
}

/// Returns true when the score for this threshold is above
Expand Down
77 changes: 76 additions & 1 deletion crates/ide-completion/src/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use ide_db::{
imports::import_assets::LocatedImport,
RootDatabase, SnippetCap, SymbolKind,
};
use syntax::{AstNode, SmolStr, SyntaxKind, TextRange};
use syntax::{AstNode, SmolStr, SyntaxKind, SyntaxToken, TextRange};
use text_edit::TextEdit;

use crate::{
Expand Down Expand Up @@ -72,6 +72,10 @@ impl<'a> RenderContext<'a> {
self.completion.db
}

fn token(&self) -> &SyntaxToken {
&self.completion.token
}

fn source_range(&self) -> TextRange {
self.completion.source_range()
}
Expand Down Expand Up @@ -1176,6 +1180,7 @@ fn main() { let _: m::Spam = S$0 }
is_private_editable: false,
postfix_match: None,
is_definite: false,
bonus_score: 0,
},
trigger_call_info: true,
},
Expand All @@ -1202,6 +1207,7 @@ fn main() { let _: m::Spam = S$0 }
is_private_editable: false,
postfix_match: None,
is_definite: false,
bonus_score: 0,
},
trigger_call_info: true,
},
Expand Down Expand Up @@ -1280,6 +1286,7 @@ fn foo() { A { the$0 } }
is_private_editable: false,
postfix_match: None,
is_definite: false,
bonus_score: 0,
},
},
]
Expand Down Expand Up @@ -2002,6 +2009,60 @@ fn main() {
);
}

#[test]
fn colon_complete_preferred_order_relevances() {
check_relevance(
r#"
struct A;
struct ABuilder;
impl A {
fn foo(&self) {}
fn new_1(input: u32) -> A { A }
fn new_2() -> Self { A }
fn aaaabuilder() -> ABuilder { A }
}
fn test() {
let a = A::$0;
}
"#,
// preference:
// fn with no param that returns itself
// builder like fn
// fn with param that returns itself
expect![[r#"
fn new_2() [type_could_unify]
fn aaaabuilder() [type_could_unify]
fn new_1(…) [type_could_unify]
me foo(…) [type_could_unify]
"#]],
);

// Generic
check_relevance(
r#"
struct A<T>{item: T}
struct ABuilder;
impl A {
fn foo(&self) {}
fn new_1<T>(input: u32, l: T) -> A<T> { A }
fn new_2<T>() -> Self { A { item: <_>::default()} }
fn aaaabuilder<T>() -> ABuilder<T> { A }
}
fn test() {
let a = A::$0;
}
"#,
expect![[r#"
fn new_2() [type_could_unify]
fn aaaabuilder() [type_could_unify]
fn new_1(…) [type_could_unify]
me foo(…) [type_could_unify]
"#]],
);
}

#[test]
fn struct_field_method_ref() {
check_kinds(
Expand Down Expand Up @@ -2095,6 +2156,7 @@ fn foo() {
is_private_editable: false,
postfix_match: None,
is_definite: false,
bonus_score: 0,
},
},
]
Expand Down Expand Up @@ -2132,6 +2194,19 @@ fn main() {
),
lookup: "foo",
detail: "fn() -> S",
relevance: CompletionRelevance {
exact_name_match: false,
type_match: None,
is_local: false,
is_item_from_trait: false,
is_name_already_imported: false,
requires_import: false,
is_op_method: false,
is_private_editable: false,
postfix_match: None,
is_definite: false,
bonus_score: 30,
},
ref_match: "&@92",
},
]
Expand Down
29 changes: 29 additions & 0 deletions crates/ide-completion/src/render/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ fn render(
},
exact_name_match: compute_exact_name_match(completion, &call),
is_op_method,
bonus_score: calculate_bonus(&ctx, func, db),
..ctx.completion_relevance()
});

Expand Down Expand Up @@ -153,6 +154,34 @@ fn render(
item
}

/// When typing `::` of a type, the preferred order is:
/// * Constructors: new like functions to be able to create the type,
/// * Builder Methods,
/// * Constructors that take args: Any other function that creates Self
/// * Regular methods & Associated functions
///
fn calculate_bonus(ctx: &RenderContext<'_>, func: hir::Function, db: &dyn HirDatabase) -> u32 {
if ctx.token().kind() != syntax::SyntaxKind::COLON2 || func.self_param(db).is_some() {
return 0;
}

let mut bonus = 0;

let has_args = !func.assoc_fn_params(db).is_empty();
let ret_type = func.ret_type(db);
if !has_args && !ret_type.is_unit() {
// fn() -> A
bonus += 30;
} else if has_args && !ret_type.is_unit() {
// fn(..) -> A
bonus += 20;
} else if !has_args && ret_type.display(db).to_string().ends_with("Builder") {
// -> [..]Builder
bonus += 10;
}
bonus
}

pub(super) fn add_call_parens<'b>(
builder: &'b mut Builder,
ctx: &CompletionContext<'_>,
Expand Down

0 comments on commit 25f2c77

Please sign in to comment.