mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-29 06:23:25 +00:00
fix: filter unnecessary completions after colon
This commit is contained in:
parent
45ec315e01
commit
f26d5484d8
5 changed files with 162 additions and 16 deletions
|
@ -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",
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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::*;
|
||||
|
||||
|
|
|
@ -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<char>,
|
||||
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![""],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue