From f26d5484d89bd41e4fe89367fc4d13463d498d8b Mon Sep 17 00:00:00 2001 From: yue4u Date: Sat, 12 Nov 2022 22:33:40 +0900 Subject: [PATCH 1/5] fix: filter unnecessary completions after colon --- crates/ide-completion/src/context.rs | 42 ++++++++- crates/ide-completion/src/lib.rs | 1 - crates/ide-completion/src/tests/attribute.rs | 24 +++++ crates/ide-completion/src/tests/special.rs | 97 +++++++++++++++++++- crates/rust-analyzer/src/handlers.rs | 14 +-- 5 files changed, 162 insertions(+), 16 deletions(-) diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 9850813a0c..27f6745d24 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -19,7 +19,7 @@ use syntax::{ ast::{self, AttrKind, NameOrNameRef}, AstNode, SyntaxKind::{self, *}, - SyntaxToken, TextRange, TextSize, + SyntaxToken, TextRange, TextSize, T, }; use text_edit::Indel; @@ -569,6 +569,28 @@ impl<'a> CompletionContext<'a> { // completing on let original_token = original_file.syntax().token_at_offset(offset).left_biased()?; + // try to skip completions on path with qinvalid colons + // this approach works in normal path and inside token tree + match original_token.kind() { + T![:] => { + // return if no prev token before colon + let prev_token = original_token.prev_token()?; + + // only has a single colon + if prev_token.kind() != T![:] { + return None; + } + + if !is_prev_token_valid_path_start_or_segment(&prev_token) { + return None; + } + } + T![::] if !is_prev_token_valid_path_start_or_segment(&original_token) => { + return None; + } + _ => {} + } + let AnalysisResult { analysis, expected: (expected_type, expected_name), @@ -618,6 +640,24 @@ impl<'a> CompletionContext<'a> { } } +fn is_prev_token_valid_path_start_or_segment(token: &SyntaxToken) -> bool { + if let Some(prev_token) = token.prev_token() { + // token before coloncolon is invalid + if !matches!( + prev_token.kind(), + // trival + WHITESPACE | COMMENT + // PathIdentSegment + | IDENT | T![super] | T![self] | T![Self] | T![crate] + // QualifiedPath + | T![>] + ) { + return false; + } + } + true +} + const OP_TRAIT_LANG_NAMES: &[&str] = &[ "add_assign", "add", diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs index 9d0044e55f..4b48ec6bc3 100644 --- a/crates/ide-completion/src/lib.rs +++ b/crates/ide-completion/src/lib.rs @@ -164,7 +164,6 @@ pub fn completions( completions::vis::complete_vis_path(&mut completions, ctx, path_ctx, has_in_token); } } - // prevent `(` from triggering unwanted completion noise return Some(completions.into()); } diff --git a/crates/ide-completion/src/tests/attribute.rs b/crates/ide-completion/src/tests/attribute.rs index 1578ba2c37..4e60820dd6 100644 --- a/crates/ide-completion/src/tests/attribute.rs +++ b/crates/ide-completion/src/tests/attribute.rs @@ -607,6 +607,30 @@ fn attr_in_source_file_end() { ); } +#[test] +fn invalid_path() { + check( + r#" +//- proc_macros: identity +#[proc_macros:::$0] +struct Foo; +"#, + expect![[r#""#]], + ); + + check( + r#" +//- minicore: derive, copy +mod foo { + pub use Copy as Bar; +} +#[derive(foo:::::$0)] +struct Foo; +"#, + expect![""], + ); +} + mod cfg { use super::*; diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs index 033dc99c26..1aea5d89b4 100644 --- a/crates/ide-completion/src/tests/special.rs +++ b/crates/ide-completion/src/tests/special.rs @@ -2,13 +2,22 @@ use expect_test::{expect, Expect}; -use crate::tests::{check_edit, completion_list_no_kw}; +use crate::tests::{check_edit, completion_list_no_kw, completion_list_with_trigger_character}; fn check(ra_fixture: &str, expect: Expect) { let actual = completion_list_no_kw(ra_fixture); expect.assert_eq(&actual) } +pub(crate) fn check_with_trigger_character( + ra_fixture: &str, + trigger_character: Option, + expect: Expect, +) { + let actual = completion_list_with_trigger_character(ra_fixture, trigger_character); + expect.assert_eq(&actual) +} + #[test] fn completes_if_prefix_is_keyword() { check_edit( @@ -893,3 +902,89 @@ fn f() { "#]], ); } + +#[test] +fn completes_after_colon_with_trigger() { + check_with_trigger_character( + r#" +//- minicore: option +fn foo { ::$0 } +"#, + Some(':'), + expect![[r#" + md core + "#]], + ); + check_with_trigger_character( + r#" +//- minicore: option +fn foo { /* test */::$0 } +"#, + Some(':'), + expect![[r#" + md core + "#]], + ); + + check_with_trigger_character( + r#" +fn foo { crate::$0 } +"#, + Some(':'), + expect![[r#" + fn foo() fn() + "#]], + ); + + check_with_trigger_character( + r#" +fn foo { crate:$0 } +"#, + Some(':'), + expect![""], + ); +} + +#[test] +fn completes_after_colon_without_trigger() { + check_with_trigger_character( + r#" +fn foo { crate::$0 } +"#, + None, + expect![[r#" + fn foo() fn() + "#]], + ); + + check_with_trigger_character( + r#" +fn foo { crate:$0 } +"#, + None, + expect![""], + ); +} + +#[test] +fn no_completions_in_invalid_path() { + check( + r#" +fn foo { crate:::$0 } +"#, + expect![""], + ); + check( + r#" +fn foo { crate::::$0 } +"#, + expect![""], + ); + + check( + r#" +fn foo { crate:::::$0 } +"#, + expect![""], + ); +} diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index d190a9f4e2..4f318f39de 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -28,7 +28,7 @@ use lsp_types::{ use project_model::{ManifestPath, ProjectWorkspace, TargetKind}; use serde_json::json; use stdx::{format_to, never}; -use syntax::{algo, ast, AstNode, TextRange, TextSize, T}; +use syntax::{algo, ast, AstNode, TextRange, TextSize}; use vfs::AbsPathBuf; use crate::{ @@ -812,18 +812,6 @@ pub(crate) fn handle_completion( let completion_trigger_character = params.context.and_then(|ctx| ctx.trigger_character).and_then(|s| s.chars().next()); - if Some(':') == completion_trigger_character { - let source_file = snap.analysis.parse(position.file_id)?; - let left_token = source_file.syntax().token_at_offset(position.offset).left_biased(); - let completion_triggered_after_single_colon = match left_token { - Some(left_token) => left_token.kind() == T![:], - None => true, - }; - if completion_triggered_after_single_colon { - return Ok(None); - } - } - let completion_config = &snap.config.completion(); let items = match snap.analysis.completions( completion_config, From a6d0e342a3eff8494f49966ca58f858a0ff5cc85 Mon Sep 17 00:00:00 2001 From: yue Date: Sun, 20 Nov 2022 00:58:59 +0900 Subject: [PATCH 2/5] Update crates/ide-completion/src/context.rs Co-authored-by: Lukas Wirth --- crates/ide-completion/src/context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 27f6745d24..1201854ff4 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -569,7 +569,7 @@ impl<'a> CompletionContext<'a> { // completing on let original_token = original_file.syntax().token_at_offset(offset).left_biased()?; - // try to skip completions on path with qinvalid colons + // try to skip completions on path with invalid colons // this approach works in normal path and inside token tree match original_token.kind() { T![:] => { From 7a568f7f9571c2c93fd8e60712878736d942c787 Mon Sep 17 00:00:00 2001 From: yue4u Date: Sun, 20 Nov 2022 01:29:02 +0900 Subject: [PATCH 3/5] fix: remove insufficient check for coloncolon --- crates/ide-completion/src/context.rs | 3 --- crates/ide-completion/src/tests/special.rs | 7 ------- 2 files changed, 10 deletions(-) diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 1201854ff4..a0e1153f08 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -585,9 +585,6 @@ impl<'a> CompletionContext<'a> { return None; } } - T![::] if !is_prev_token_valid_path_start_or_segment(&original_token) => { - return None; - } _ => {} } diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs index 1aea5d89b4..6cfa72d653 100644 --- a/crates/ide-completion/src/tests/special.rs +++ b/crates/ide-completion/src/tests/special.rs @@ -977,13 +977,6 @@ fn foo { crate:::$0 } check( r#" fn foo { crate::::$0 } -"#, - expect![""], - ); - - check( - r#" -fn foo { crate:::::$0 } "#, expect![""], ); From e1de04d60ceff530295f57ae47a65e9c05716bbf Mon Sep 17 00:00:00 2001 From: yue4u Date: Sun, 27 Nov 2022 01:53:45 +0900 Subject: [PATCH 4/5] fix: only special casing 3 colon in a row --- crates/ide-completion/src/context.rs | 22 +++------------------- crates/ide-completion/src/tests/special.rs | 8 +------- 2 files changed, 4 insertions(+), 26 deletions(-) diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index a0e1153f08..0e3b677f2d 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -581,7 +581,9 @@ impl<'a> CompletionContext<'a> { return None; } - if !is_prev_token_valid_path_start_or_segment(&prev_token) { + // has 3 colon in a row + // special casing this as per discussion in https://github.com/rust-lang/rust-analyzer/pull/13611#discussion_r1031845205 + if prev_token.prev_token().map(|t| t.kind() == T![:]).unwrap_or(false) { return None; } } @@ -637,24 +639,6 @@ impl<'a> CompletionContext<'a> { } } -fn is_prev_token_valid_path_start_or_segment(token: &SyntaxToken) -> bool { - if let Some(prev_token) = token.prev_token() { - // token before coloncolon is invalid - if !matches!( - prev_token.kind(), - // trival - WHITESPACE | COMMENT - // PathIdentSegment - | IDENT | T![super] | T![self] | T![Self] | T![crate] - // QualifiedPath - | T![>] - ) { - return false; - } - } - true -} - const OP_TRAIT_LANG_NAMES: &[&str] = &[ "add_assign", "add", diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs index 6cfa72d653..0e59f4ec54 100644 --- a/crates/ide-completion/src/tests/special.rs +++ b/crates/ide-completion/src/tests/special.rs @@ -967,16 +967,10 @@ fn foo { crate:$0 } } #[test] -fn no_completions_in_invalid_path() { +fn no_completions_in_after_tripple_colon() { check( r#" fn foo { crate:::$0 } -"#, - expect![""], - ); - check( - r#" -fn foo { crate::::$0 } "#, expect![""], ); From 1ca5cb7ed9928fad4222cb26727da41ff9ac148f Mon Sep 17 00:00:00 2001 From: yue4u Date: Sun, 27 Nov 2022 02:39:38 +0900 Subject: [PATCH 5/5] fix: also exclude 2 coloncolon in a row --- crates/ide-completion/src/context.rs | 9 +++++++-- crates/ide-completion/src/tests/special.rs | 8 +++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 0e3b677f2d..aa77f44953 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -581,9 +581,14 @@ impl<'a> CompletionContext<'a> { return None; } - // has 3 colon in a row + // has 3 colon or 2 coloncolon in a row // special casing this as per discussion in https://github.com/rust-lang/rust-analyzer/pull/13611#discussion_r1031845205 - if prev_token.prev_token().map(|t| t.kind() == T![:]).unwrap_or(false) { + // and https://github.com/rust-lang/rust-analyzer/pull/13611#discussion_r1032812751 + if prev_token + .prev_token() + .map(|t| t.kind() == T![:] || t.kind() == T![::]) + .unwrap_or(false) + { return None; } } diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs index 0e59f4ec54..cad4af4937 100644 --- a/crates/ide-completion/src/tests/special.rs +++ b/crates/ide-completion/src/tests/special.rs @@ -967,11 +967,17 @@ fn foo { crate:$0 } } #[test] -fn no_completions_in_after_tripple_colon() { +fn no_completions_in_invalid_path() { check( r#" fn foo { crate:::$0 } "#, expect![""], ); + check( + r#" +fn foo { crate::::$0 } +"#, + expect![""], + ) }