Skip to content

Commit 34e2bc6

Browse files
committed
Auto merge of rust-lang#13611 - yue4u:fix/completion-after-colon, r=yue4u
fix: filter unnecessary completions after colon close rust-lang#13597 related: rust-lang#10173 This PR also happens to fix two extra issues: 1. The test case in https://github.com/rust-lang/rust-analyzer/blob/master/crates/ide-completion/src/tests/attribute.rs#L778-L801 was never triggered in previous behavior. after: https://user-images.githubusercontent.com/26110087/201476995-56adf955-0fa7-4f75-ab32-28a8e6cb9504.mp4 <del> 2. completions were triggered even in invalid paths, like ```rust fn main() { core:::::$0 } ``` ```rust #[:::::$0] struct X; ``` </del> only `:::` is excluded as discussed in rust-lang/rust-analyzer#13611 (comment)
2 parents d2281f0 + 1ca5cb7 commit 34e2bc6

File tree

5 files changed

+141
-16
lines changed

5 files changed

+141
-16
lines changed

crates/ide-completion/src/context.rs

+27-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use syntax::{
1919
ast::{self, AttrKind, NameOrNameRef},
2020
AstNode,
2121
SyntaxKind::{self, *},
22-
SyntaxToken, TextRange, TextSize,
22+
SyntaxToken, TextRange, TextSize, T,
2323
};
2424
use text_edit::Indel;
2525

@@ -569,6 +569,32 @@ impl<'a> CompletionContext<'a> {
569569
// completing on
570570
let original_token = original_file.syntax().token_at_offset(offset).left_biased()?;
571571

572+
// try to skip completions on path with invalid colons
573+
// this approach works in normal path and inside token tree
574+
match original_token.kind() {
575+
T![:] => {
576+
// return if no prev token before colon
577+
let prev_token = original_token.prev_token()?;
578+
579+
// only has a single colon
580+
if prev_token.kind() != T![:] {
581+
return None;
582+
}
583+
584+
// has 3 colon or 2 coloncolon in a row
585+
// special casing this as per discussion in https://github.com/rust-lang/rust-analyzer/pull/13611#discussion_r1031845205
586+
// and https://github.com/rust-lang/rust-analyzer/pull/13611#discussion_r1032812751
587+
if prev_token
588+
.prev_token()
589+
.map(|t| t.kind() == T![:] || t.kind() == T![::])
590+
.unwrap_or(false)
591+
{
592+
return None;
593+
}
594+
}
595+
_ => {}
596+
}
597+
572598
let AnalysisResult {
573599
analysis,
574600
expected: (expected_type, expected_name),

crates/ide-completion/src/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,6 @@ pub fn completions(
164164
completions::vis::complete_vis_path(&mut completions, ctx, path_ctx, has_in_token);
165165
}
166166
}
167-
// prevent `(` from triggering unwanted completion noise
168167
return Some(completions.into());
169168
}
170169

crates/ide-completion/src/tests/attribute.rs

+24
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,30 @@ fn attr_in_source_file_end() {
607607
);
608608
}
609609

610+
#[test]
611+
fn invalid_path() {
612+
check(
613+
r#"
614+
//- proc_macros: identity
615+
#[proc_macros:::$0]
616+
struct Foo;
617+
"#,
618+
expect![[r#""#]],
619+
);
620+
621+
check(
622+
r#"
623+
//- minicore: derive, copy
624+
mod foo {
625+
pub use Copy as Bar;
626+
}
627+
#[derive(foo:::::$0)]
628+
struct Foo;
629+
"#,
630+
expect![""],
631+
);
632+
}
633+
610634
mod cfg {
611635
use super::*;
612636

crates/ide-completion/src/tests/special.rs

+89-1
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,22 @@
22
33
use expect_test::{expect, Expect};
44

5-
use crate::tests::{check_edit, completion_list_no_kw};
5+
use crate::tests::{check_edit, completion_list_no_kw, completion_list_with_trigger_character};
66

77
fn check(ra_fixture: &str, expect: Expect) {
88
let actual = completion_list_no_kw(ra_fixture);
99
expect.assert_eq(&actual)
1010
}
1111

12+
pub(crate) fn check_with_trigger_character(
13+
ra_fixture: &str,
14+
trigger_character: Option<char>,
15+
expect: Expect,
16+
) {
17+
let actual = completion_list_with_trigger_character(ra_fixture, trigger_character);
18+
expect.assert_eq(&actual)
19+
}
20+
1221
#[test]
1322
fn completes_if_prefix_is_keyword() {
1423
check_edit(
@@ -893,3 +902,82 @@ fn f() {
893902
"#]],
894903
);
895904
}
905+
906+
#[test]
907+
fn completes_after_colon_with_trigger() {
908+
check_with_trigger_character(
909+
r#"
910+
//- minicore: option
911+
fn foo { ::$0 }
912+
"#,
913+
Some(':'),
914+
expect![[r#"
915+
md core
916+
"#]],
917+
);
918+
check_with_trigger_character(
919+
r#"
920+
//- minicore: option
921+
fn foo { /* test */::$0 }
922+
"#,
923+
Some(':'),
924+
expect![[r#"
925+
md core
926+
"#]],
927+
);
928+
929+
check_with_trigger_character(
930+
r#"
931+
fn foo { crate::$0 }
932+
"#,
933+
Some(':'),
934+
expect![[r#"
935+
fn foo() fn()
936+
"#]],
937+
);
938+
939+
check_with_trigger_character(
940+
r#"
941+
fn foo { crate:$0 }
942+
"#,
943+
Some(':'),
944+
expect![""],
945+
);
946+
}
947+
948+
#[test]
949+
fn completes_after_colon_without_trigger() {
950+
check_with_trigger_character(
951+
r#"
952+
fn foo { crate::$0 }
953+
"#,
954+
None,
955+
expect![[r#"
956+
fn foo() fn()
957+
"#]],
958+
);
959+
960+
check_with_trigger_character(
961+
r#"
962+
fn foo { crate:$0 }
963+
"#,
964+
None,
965+
expect![""],
966+
);
967+
}
968+
969+
#[test]
970+
fn no_completions_in_invalid_path() {
971+
check(
972+
r#"
973+
fn foo { crate:::$0 }
974+
"#,
975+
expect![""],
976+
);
977+
check(
978+
r#"
979+
fn foo { crate::::$0 }
980+
"#,
981+
expect![""],
982+
)
983+
}

crates/rust-analyzer/src/handlers.rs

+1-13
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use lsp_types::{
2828
use project_model::{ManifestPath, ProjectWorkspace, TargetKind};
2929
use serde_json::json;
3030
use stdx::{format_to, never};
31-
use syntax::{algo, ast, AstNode, TextRange, TextSize, T};
31+
use syntax::{algo, ast, AstNode, TextRange, TextSize};
3232
use vfs::AbsPathBuf;
3333

3434
use crate::{
@@ -812,18 +812,6 @@ pub(crate) fn handle_completion(
812812
let completion_trigger_character =
813813
params.context.and_then(|ctx| ctx.trigger_character).and_then(|s| s.chars().next());
814814

815-
if Some(':') == completion_trigger_character {
816-
let source_file = snap.analysis.parse(position.file_id)?;
817-
let left_token = source_file.syntax().token_at_offset(position.offset).left_biased();
818-
let completion_triggered_after_single_colon = match left_token {
819-
Some(left_token) => left_token.kind() == T![:],
820-
None => true,
821-
};
822-
if completion_triggered_after_single_colon {
823-
return Ok(None);
824-
}
825-
}
826-
827815
let completion_config = &snap.config.completion();
828816
let items = match snap.analysis.completions(
829817
completion_config,

0 commit comments

Comments
 (0)