Skip to content

Commit 13161ab

Browse files
bors[bot]matklad
andauthored
Merge #9489
9489: feat: always prefer postfix snippets if there's exact textual match r=matklad a=matklad bors r+ 🤖 Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
2 parents 030217d + 9b32925 commit 13161ab

File tree

4 files changed

+115
-28
lines changed

4 files changed

+115
-28
lines changed

crates/ide_completion/src/completions/postfix.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::{
1515
context::CompletionContext,
1616
item::{Builder, CompletionKind},
1717
patterns::ImmediateLocation,
18-
CompletionItem, CompletionItemKind, Completions,
18+
CompletionItem, CompletionItemKind, CompletionRelevance, Completions,
1919
};
2020

2121
pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
@@ -299,6 +299,12 @@ fn postfix_snippet(
299299
};
300300
let mut item = CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label);
301301
item.detail(detail).kind(CompletionItemKind::Snippet).snippet_edit(cap, edit);
302+
if ctx.original_token.text() == label {
303+
let mut relevance = CompletionRelevance::default();
304+
relevance.exact_postfix_snippet_match = true;
305+
item.set_relevance(relevance);
306+
}
307+
302308
item
303309
}
304310

crates/ide_completion/src/item.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,15 @@ pub struct CompletionRelevance {
144144
/// }
145145
/// ```
146146
pub is_local: bool,
147+
/// This is set in cases like these:
148+
///
149+
/// ```
150+
/// (a > b).not$0
151+
/// ```
152+
///
153+
/// Basically, we want to guarantee that postfix snippets always takes
154+
/// precedence over everything else.
155+
pub exact_postfix_snippet_match: bool,
147156
}
148157

149158
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
@@ -194,7 +203,9 @@ impl CompletionRelevance {
194203
if self.is_local {
195204
score += 1;
196205
}
197-
206+
if self.exact_postfix_snippet_match {
207+
score += 100;
208+
}
198209
score
199210
}
200211

@@ -598,6 +609,13 @@ mod tests {
598609
exact_name_match: true,
599610
type_match: Some(CompletionRelevanceTypeMatch::Exact),
600611
is_local: true,
612+
..CompletionRelevance::default()
613+
}],
614+
vec![CompletionRelevance {
615+
exact_name_match: false,
616+
type_match: None,
617+
is_local: false,
618+
exact_postfix_snippet_match: true,
601619
}],
602620
];
603621

crates/ide_completion/src/render.rs

Lines changed: 78 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,8 @@ fn compute_ref_match(
330330

331331
#[cfg(test)]
332332
mod tests {
333+
use std::cmp;
334+
333335
use expect_test::{expect, Expect};
334336
use itertools::Itertools;
335337

@@ -339,32 +341,25 @@ mod tests {
339341
CompletionKind, CompletionRelevance,
340342
};
341343

344+
#[track_caller]
342345
fn check(ra_fixture: &str, expect: Expect) {
343346
let actual = do_completion(ra_fixture, CompletionKind::Reference);
344347
expect.assert_debug_eq(&actual);
345348
}
346349

350+
#[track_caller]
347351
fn check_relevance(ra_fixture: &str, expect: Expect) {
348-
fn display_relevance(relevance: CompletionRelevance) -> String {
349-
let relevance_factors = vec![
350-
(relevance.type_match == Some(CompletionRelevanceTypeMatch::Exact), "type"),
351-
(
352-
relevance.type_match == Some(CompletionRelevanceTypeMatch::CouldUnify),
353-
"type_could_unify",
354-
),
355-
(relevance.exact_name_match, "name"),
356-
(relevance.is_local, "local"),
357-
]
358-
.into_iter()
359-
.filter_map(|(cond, desc)| if cond { Some(desc) } else { None })
360-
.join("+");
352+
check_relevance_for_kinds(&[CompletionKind::Reference], ra_fixture, expect)
353+
}
361354

362-
format!("[{}]", relevance_factors)
363-
}
355+
#[track_caller]
356+
fn check_relevance_for_kinds(kinds: &[CompletionKind], ra_fixture: &str, expect: Expect) {
357+
let mut actual = get_all_items(TEST_CONFIG, ra_fixture);
358+
actual.retain(|it| kinds.contains(&it.completion_kind));
359+
actual.sort_by_key(|it| cmp::Reverse(it.relevance().score()));
364360

365-
let actual = get_all_items(TEST_CONFIG, ra_fixture)
361+
let actual = actual
366362
.into_iter()
367-
.filter(|it| it.completion_kind == CompletionKind::Reference)
368363
.flat_map(|it| {
369364
let mut items = vec![];
370365

@@ -384,6 +379,24 @@ mod tests {
384379
.collect::<String>();
385380

386381
expect.assert_eq(&actual);
382+
383+
fn display_relevance(relevance: CompletionRelevance) -> String {
384+
let relevance_factors = vec![
385+
(relevance.type_match == Some(CompletionRelevanceTypeMatch::Exact), "type"),
386+
(
387+
relevance.type_match == Some(CompletionRelevanceTypeMatch::CouldUnify),
388+
"type_could_unify",
389+
),
390+
(relevance.exact_name_match, "name"),
391+
(relevance.is_local, "local"),
392+
(relevance.exact_postfix_snippet_match, "snippet"),
393+
]
394+
.into_iter()
395+
.filter_map(|(cond, desc)| if cond { Some(desc) } else { None })
396+
.join("+");
397+
398+
format!("[{}]", relevance_factors)
399+
}
387400
}
388401

389402
#[test]
@@ -528,6 +541,7 @@ fn main() { let _: m::Spam = S$0 }
528541
Exact,
529542
),
530543
is_local: false,
544+
exact_postfix_snippet_match: false,
531545
},
532546
trigger_call_info: true,
533547
},
@@ -556,6 +570,7 @@ fn main() { let _: m::Spam = S$0 }
556570
Exact,
557571
),
558572
is_local: false,
573+
exact_postfix_snippet_match: false,
559574
},
560575
},
561576
CompletionItem {
@@ -649,6 +664,7 @@ fn foo() { A { the$0 } }
649664
CouldUnify,
650665
),
651666
is_local: false,
667+
exact_postfix_snippet_match: false,
652668
},
653669
},
654670
]
@@ -910,9 +926,9 @@ fn test(bar: u32) { }
910926
fn foo(s: S) { test(s.$0) }
911927
"#,
912928
expect![[r#"
913-
fd foo []
914929
fd bar [type+name]
915930
fd baz [type]
931+
fd foo []
916932
"#]],
917933
);
918934
}
@@ -926,9 +942,9 @@ struct B { x: (), y: f32, bar: u32 }
926942
fn foo(a: A) { B { bar: a.$0 }; }
927943
"#,
928944
expect![[r#"
929-
fd foo []
930945
fd bar [type+name]
931946
fd baz [type]
947+
fd foo []
932948
"#]],
933949
)
934950
}
@@ -956,9 +972,9 @@ fn f(foo: i64) { }
956972
fn foo(a: A) { f(B { bar: a.$0 }); }
957973
"#,
958974
expect![[r#"
959-
fd foo []
960975
fd bar [type+name]
961976
fd baz [type]
977+
fd foo []
962978
"#]],
963979
);
964980
}
@@ -1004,9 +1020,9 @@ fn bar() -> u8 { 0 }
10041020
fn f() { A { bar: b$0 }; }
10051021
"#,
10061022
expect![[r#"
1023+
fn bar() [type+name]
10071024
fn baz() [type]
10081025
st A []
1009-
fn bar() [type+name]
10101026
fn f() []
10111027
"#]],
10121028
);
@@ -1329,14 +1345,54 @@ fn foo() {
13291345
}
13301346
"#,
13311347
expect![[r#"
1348+
lc foo [type+local]
13321349
ev Foo::A(…) [type_could_unify]
13331350
ev Foo::B [type_could_unify]
1334-
lc foo [type+local]
13351351
en Foo []
13361352
fn baz() []
13371353
fn bar() []
13381354
fn foo() []
13391355
"#]],
13401356
);
13411357
}
1358+
1359+
#[test]
1360+
fn postfix_completion_relevance() {
1361+
check_relevance_for_kinds(
1362+
&[CompletionKind::Postfix, CompletionKind::Magic],
1363+
r#"
1364+
mod ops {
1365+
pub trait Not {
1366+
type Output;
1367+
fn not(self) -> Self::Output;
1368+
}
1369+
1370+
impl Not for bool {
1371+
type Output = bool;
1372+
fn not(self) -> bool { if self { false } else { true }}
1373+
}
1374+
}
1375+
1376+
fn main() {
1377+
let _: bool = (9 > 2).not$0;
1378+
}
1379+
"#,
1380+
expect![[r#"
1381+
sn not [snippet]
1382+
me not() (ops::Not) [type_could_unify]
1383+
sn if []
1384+
sn while []
1385+
sn ref []
1386+
sn refm []
1387+
sn match []
1388+
sn box []
1389+
sn ok []
1390+
sn err []
1391+
sn some []
1392+
sn dbg []
1393+
sn dbgr []
1394+
sn call []
1395+
"#]],
1396+
);
1397+
}
13421398
}

crates/rust-analyzer/src/to_proto.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ pub(crate) fn completion_items(
195195
tdpp: lsp_types::TextDocumentPositionParams,
196196
items: Vec<CompletionItem>,
197197
) -> Vec<lsp_types::CompletionItem> {
198+
let max_relevance = items.iter().map(|it| it.relevance().score()).max().unwrap_or_default();
198199
let mut res = Vec::with_capacity(items.len());
199200
for item in items {
200201
completion_item(
@@ -203,6 +204,7 @@ pub(crate) fn completion_items(
203204
enable_imports_on_the_fly,
204205
line_index,
205206
&tdpp,
207+
max_relevance,
206208
item,
207209
)
208210
}
@@ -215,6 +217,7 @@ fn completion_item(
215217
enable_imports_on_the_fly: bool,
216218
line_index: &LineIndex,
217219
tdpp: &lsp_types::TextDocumentPositionParams,
220+
max_relevance: u32,
218221
item: CompletionItem,
219222
) {
220223
let mut additional_text_edits = Vec::new();
@@ -259,7 +262,7 @@ fn completion_item(
259262
..Default::default()
260263
};
261264

262-
set_score(&mut lsp_item, item.relevance());
265+
set_score(&mut lsp_item, max_relevance, item.relevance());
263266

264267
if item.deprecated() {
265268
lsp_item.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated])
@@ -288,7 +291,7 @@ fn completion_item(
288291

289292
if let Some((mutability, relevance)) = item.ref_match() {
290293
let mut lsp_item_with_ref = lsp_item.clone();
291-
set_score(&mut lsp_item_with_ref, relevance);
294+
set_score(&mut lsp_item_with_ref, max_relevance, relevance);
292295
lsp_item_with_ref.label =
293296
format!("&{}{}", mutability.as_keyword_for_ref(), lsp_item_with_ref.label);
294297
if let Some(it) = &mut lsp_item_with_ref.text_edit {
@@ -304,8 +307,12 @@ fn completion_item(
304307

305308
acc.push(lsp_item);
306309

307-
fn set_score(res: &mut lsp_types::CompletionItem, relevance: CompletionRelevance) {
308-
if relevance.is_relevant() {
310+
fn set_score(
311+
res: &mut lsp_types::CompletionItem,
312+
max_relevance: u32,
313+
relevance: CompletionRelevance,
314+
) {
315+
if relevance.is_relevant() && relevance.score() == max_relevance {
309316
res.preselect = Some(true);
310317
}
311318
// The relevance needs to be inverted to come up with a sort score

0 commit comments

Comments
 (0)