fix: visibility completion

This commit is contained in:
yue4u 2022-05-30 00:06:48 +09:00
parent 6c9fc4fec2
commit 1b5f0462ed
9 changed files with 103 additions and 57 deletions

View file

@ -143,36 +143,40 @@ pub fn completions(
db: &RootDatabase, db: &RootDatabase,
config: &CompletionConfig, config: &CompletionConfig,
position: FilePosition, position: FilePosition,
trigger_character: Option<&str>,
) -> Option<Completions> { ) -> Option<Completions> {
let ctx = &CompletionContext::new(db, position, config)?; let ctx = &CompletionContext::new(db, position, config)?;
let mut acc = Completions::default(); let mut acc = Completions::default();
{ {
let acc = &mut acc; let acc = &mut acc;
completions::attribute::complete_attribute(acc, ctx); // prevent `(` from triggering unwanted completion noise
completions::attribute::complete_derive(acc, ctx); if trigger_character != Some("(") {
completions::attribute::complete_known_attribute_input(acc, ctx); completions::attribute::complete_attribute(acc, ctx);
completions::dot::complete_dot(acc, ctx); completions::attribute::complete_derive(acc, ctx);
completions::expr::complete_expr_path(acc, ctx); completions::attribute::complete_known_attribute_input(acc, ctx);
completions::extern_abi::complete_extern_abi(acc, ctx); completions::dot::complete_dot(acc, ctx);
completions::flyimport::import_on_the_fly(acc, ctx); completions::expr::complete_expr_path(acc, ctx);
completions::fn_param::complete_fn_param(acc, ctx); completions::extern_abi::complete_extern_abi(acc, ctx);
completions::format_string::format_string(acc, ctx); completions::flyimport::import_on_the_fly(acc, ctx);
completions::item_list::complete_item_list(acc, ctx); completions::fn_param::complete_fn_param(acc, ctx);
completions::keyword::complete_expr_keyword(acc, ctx); completions::format_string::format_string(acc, ctx);
completions::lifetime::complete_label(acc, ctx); completions::item_list::complete_item_list(acc, ctx);
completions::lifetime::complete_lifetime(acc, ctx); completions::keyword::complete_expr_keyword(acc, ctx);
completions::mod_::complete_mod(acc, ctx); completions::lifetime::complete_label(acc, ctx);
completions::pattern::complete_pattern(acc, ctx); completions::lifetime::complete_lifetime(acc, ctx);
completions::postfix::complete_postfix(acc, ctx); completions::mod_::complete_mod(acc, ctx);
completions::record::complete_record_literal(acc, ctx); completions::pattern::complete_pattern(acc, ctx);
completions::record::complete_record(acc, ctx); completions::postfix::complete_postfix(acc, ctx);
completions::snippet::complete_expr_snippet(acc, ctx); completions::record::complete_record_literal(acc, ctx);
completions::snippet::complete_item_snippet(acc, ctx); completions::record::complete_record(acc, ctx);
completions::trait_impl::complete_trait_impl(acc, ctx); completions::snippet::complete_expr_snippet(acc, ctx);
completions::r#type::complete_type_path(acc, ctx); completions::snippet::complete_item_snippet(acc, ctx);
completions::r#type::complete_inferred_type(acc, ctx); completions::trait_impl::complete_trait_impl(acc, ctx);
completions::use_::complete_use_tree(acc, ctx); completions::r#type::complete_type_path(acc, ctx);
completions::r#type::complete_inferred_type(acc, ctx);
completions::use_::complete_use_tree(acc, ctx);
}
completions::vis::complete_vis_path(acc, ctx); completions::vis::complete_vis_path(acc, ctx);
} }

View file

@ -410,7 +410,7 @@ mod tests {
#[track_caller] #[track_caller]
fn check_relevance_for_kinds(ra_fixture: &str, kinds: &[CompletionItemKind], expect: Expect) { fn check_relevance_for_kinds(ra_fixture: &str, kinds: &[CompletionItemKind], expect: Expect) {
let mut actual = get_all_items(TEST_CONFIG, ra_fixture); let mut actual = get_all_items(TEST_CONFIG, ra_fixture, None);
actual.retain(|it| kinds.contains(&it.kind())); actual.retain(|it| kinds.contains(&it.kind()));
actual.sort_by_key(|it| cmp::Reverse(it.relevance().score())); actual.sort_by_key(|it| cmp::Reverse(it.relevance().score()));
check_relevance_(actual, expect); check_relevance_(actual, expect);
@ -418,7 +418,7 @@ mod tests {
#[track_caller] #[track_caller]
fn check_relevance(ra_fixture: &str, expect: Expect) { fn check_relevance(ra_fixture: &str, expect: Expect) {
let mut actual = get_all_items(TEST_CONFIG, ra_fixture); let mut actual = get_all_items(TEST_CONFIG, ra_fixture, None);
actual.retain(|it| it.kind() != CompletionItemKind::Snippet); actual.retain(|it| it.kind() != CompletionItemKind::Snippet);
actual.retain(|it| it.kind() != CompletionItemKind::Keyword); actual.retain(|it| it.kind() != CompletionItemKind::Keyword);
actual.retain(|it| it.kind() != CompletionItemKind::BuiltinType); actual.retain(|it| it.kind() != CompletionItemKind::BuiltinType);

View file

@ -79,20 +79,28 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig {
}; };
pub(crate) fn completion_list(ra_fixture: &str) -> String { pub(crate) fn completion_list(ra_fixture: &str) -> String {
completion_list_with_config(TEST_CONFIG, ra_fixture, true) completion_list_with_config(TEST_CONFIG, ra_fixture, true, None)
} }
pub(crate) fn completion_list_no_kw(ra_fixture: &str) -> String { pub(crate) fn completion_list_no_kw(ra_fixture: &str) -> String {
completion_list_with_config(TEST_CONFIG, ra_fixture, false) completion_list_with_config(TEST_CONFIG, ra_fixture, false, None)
}
pub(crate) fn completion_list_with_trigger_character(
ra_fixture: &str,
trigger_character: Option<&str>,
) -> String {
completion_list_with_config(TEST_CONFIG, ra_fixture, true, trigger_character)
} }
fn completion_list_with_config( fn completion_list_with_config(
config: CompletionConfig, config: CompletionConfig,
ra_fixture: &str, ra_fixture: &str,
include_keywords: bool, include_keywords: bool,
trigger_character: Option<&str>,
) -> String { ) -> String {
// filter out all but one builtintype completion for smaller test outputs // filter out all but one builtintype completion for smaller test outputs
let items = get_all_items(config, ra_fixture); let items = get_all_items(config, ra_fixture, trigger_character);
let mut bt_seen = false; let mut bt_seen = false;
let items = items let items = items
.into_iter() .into_iter()
@ -126,7 +134,7 @@ pub(crate) fn do_completion_with_config(
code: &str, code: &str,
kind: CompletionItemKind, kind: CompletionItemKind,
) -> Vec<CompletionItem> { ) -> Vec<CompletionItem> {
get_all_items(config, code) get_all_items(config, code, None)
.into_iter() .into_iter()
.filter(|c| c.kind() == kind) .filter(|c| c.kind() == kind)
.sorted_by(|l, r| l.label().cmp(r.label())) .sorted_by(|l, r| l.label().cmp(r.label()))
@ -173,7 +181,7 @@ pub(crate) fn check_edit_with_config(
let ra_fixture_after = trim_indent(ra_fixture_after); let ra_fixture_after = trim_indent(ra_fixture_after);
let (db, position) = position(ra_fixture_before); let (db, position) = position(ra_fixture_before);
let completions: Vec<CompletionItem> = let completions: Vec<CompletionItem> =
crate::completions(&db, &config, position).unwrap().into(); crate::completions(&db, &config, position, None).unwrap().into();
let (completion,) = completions let (completion,) = completions
.iter() .iter()
.filter(|it| it.lookup() == what) .filter(|it| it.lookup() == what)
@ -214,9 +222,14 @@ pub(crate) fn check_pattern_is_applicable(code: &str, check: impl FnOnce(SyntaxE
assert!(check(NodeOrToken::Token(token))); assert!(check(NodeOrToken::Token(token)));
} }
pub(crate) fn get_all_items(config: CompletionConfig, code: &str) -> Vec<CompletionItem> { pub(crate) fn get_all_items(
config: CompletionConfig,
code: &str,
trigger_character: Option<&str>,
) -> Vec<CompletionItem> {
let (db, position) = position(code); let (db, position) = position(code);
let res = crate::completions(&db, &config, position).map_or_else(Vec::default, Into::into); let res = crate::completions(&db, &config, position, trigger_character)
.map_or_else(Vec::default, Into::into);
// validate // validate
res.iter().for_each(|it| { res.iter().for_each(|it| {
let sr = it.source_range(); let sr = it.source_range();

View file

@ -1,12 +1,17 @@
use expect_test::{expect, Expect}; use expect_test::{expect, Expect};
use crate::tests::completion_list; use crate::tests::{completion_list, completion_list_with_trigger_character};
fn check(ra_fixture: &str, expect: Expect) { fn check(ra_fixture: &str, expect: Expect) {
let actual = completion_list(ra_fixture); let actual = completion_list(ra_fixture);
expect.assert_eq(&actual); expect.assert_eq(&actual);
} }
fn check_with_trigger_character(ra_fixture: &str, trigger_character: Option<&str>, expect: Expect) {
let actual = completion_list_with_trigger_character(ra_fixture, trigger_character);
expect.assert_eq(&actual)
}
#[test] #[test]
fn only_param() { fn only_param() {
check( check(
@ -113,6 +118,17 @@ fn outer(text: &str) {
) )
} }
#[test]
fn trigger_by_l_paren() {
check_with_trigger_character(
r#"
fn foo($0)
"#,
Some("("),
expect![[]],
)
}
#[test] #[test]
fn shows_non_ident_pat_param() { fn shows_non_ident_pat_param() {
check( check(

View file

@ -1,20 +1,26 @@
//! Completion tests for visibility modifiers. //! Completion tests for visibility modifiers.
use expect_test::{expect, Expect}; use expect_test::{expect, Expect};
use crate::tests::completion_list; use crate::tests::{completion_list, completion_list_with_trigger_character};
fn check(ra_fixture: &str, expect: Expect) { fn check(ra_fixture: &str, expect: Expect) {
let actual = completion_list(ra_fixture); let actual = completion_list(ra_fixture);
expect.assert_eq(&actual) expect.assert_eq(&actual)
} }
fn check_with_trigger_character(ra_fixture: &str, trigger_character: Option<&str>, expect: Expect) {
let actual = completion_list_with_trigger_character(ra_fixture, trigger_character);
expect.assert_eq(&actual)
}
#[test] #[test]
fn empty_pub() { fn empty_pub() {
cov_mark::check!(kw_completion_in); cov_mark::check!(kw_completion_in);
check( check_with_trigger_character(
r#" r#"
pub($0) pub($0)
"#, "#,
Some("("),
expect![[r#" expect![[r#"
kw crate kw crate
kw in kw in

View file

@ -547,8 +547,11 @@ impl Analysis {
&self, &self,
config: &CompletionConfig, config: &CompletionConfig,
position: FilePosition, position: FilePosition,
trigger_character: Option<&str>,
) -> Cancellable<Option<Vec<CompletionItem>>> { ) -> Cancellable<Option<Vec<CompletionItem>>> {
self.with_db(|db| ide_completion::completions(db, config, position).map(Into::into)) self.with_db(|db| {
ide_completion::completions(db, config, position, trigger_character).map(Into::into)
})
} }
/// Resolves additional completion data at the position given. /// Resolves additional completion data at the position given.

View file

@ -29,7 +29,12 @@ pub fn server_capabilities(config: &Config) -> ServerCapabilities {
hover_provider: Some(HoverProviderCapability::Simple(true)), hover_provider: Some(HoverProviderCapability::Simple(true)),
completion_provider: Some(CompletionOptions { completion_provider: Some(CompletionOptions {
resolve_provider: completions_resolve_provider(config.caps()), resolve_provider: completions_resolve_provider(config.caps()),
trigger_characters: Some(vec![":".to_string(), ".".to_string(), "'".to_string()]), trigger_characters: Some(vec![
":".to_string(),
".".to_string(),
"'".to_string(),
"(".to_string(),
]),
all_commit_characters: None, all_commit_characters: None,
completion_item: None, completion_item: None,
work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None }, work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },

View file

@ -796,27 +796,26 @@ pub(crate) fn handle_completion(
let _p = profile::span("handle_completion"); let _p = profile::span("handle_completion");
let text_document_position = params.text_document_position.clone(); let text_document_position = params.text_document_position.clone();
let position = from_proto::file_position(&snap, params.text_document_position)?; let position = from_proto::file_position(&snap, params.text_document_position)?;
let completion_triggered_after_single_colon = { let completion_trigger_character = params.context.and_then(|ctx| ctx.trigger_character);
let mut res = false;
if let Some(ctx) = params.context { if Some(":") == completion_trigger_character.as_deref() {
if ctx.trigger_character.as_deref() == Some(":") { let source_file = snap.analysis.parse(position.file_id)?;
let source_file = snap.analysis.parse(position.file_id)?; let left_token = source_file.syntax().token_at_offset(position.offset).left_biased();
let left_token = let completion_triggered_after_single_colon = match left_token {
source_file.syntax().token_at_offset(position.offset).left_biased(); Some(left_token) => left_token.kind() == T![:],
match left_token { None => true,
Some(left_token) => res = left_token.kind() == T![:], };
None => res = true, if completion_triggered_after_single_colon {
} return Ok(None);
}
} }
res
};
if completion_triggered_after_single_colon {
return Ok(None);
} }
let completion_config = &snap.config.completion(); let completion_config = &snap.config.completion();
let items = match snap.analysis.completions(completion_config, position)? { let items = match snap.analysis.completions(
completion_config,
position,
completion_trigger_character.as_deref(),
)? {
None => return Ok(None), None => return Ok(None),
Some(items) => items, Some(items) => items,
}; };

View file

@ -148,7 +148,7 @@ fn integrated_completion_benchmark() {
}; };
let position = let position =
FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
analysis.completions(&config, position).unwrap(); analysis.completions(&config, position, None).unwrap();
} }
let completion_offset = { let completion_offset = {
@ -185,7 +185,7 @@ fn integrated_completion_benchmark() {
}; };
let position = let position =
FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
analysis.completions(&config, position).unwrap(); analysis.completions(&config, position, None).unwrap();
} }
} }