mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-15 01:17:27 +00:00
Collapse more CompletionContext booleans into enums
This commit is contained in:
parent
d6ed315806
commit
7de925b8ab
6 changed files with 133 additions and 98 deletions
|
@ -114,8 +114,7 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
|
|||
|| ctx.attribute_under_caret.is_some()
|
||||
|| ctx.mod_declaration_under_caret.is_some()
|
||||
|| ctx.record_lit_syntax.is_some()
|
||||
|| ctx.has_trait_parent
|
||||
|| ctx.has_impl_parent
|
||||
|| ctx.has_impl_or_trait_parent()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
|
|
@ -49,34 +49,35 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
|
|||
return;
|
||||
}
|
||||
|
||||
let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent;
|
||||
if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling {
|
||||
let has_trait_or_impl_parent = ctx.has_impl_or_trait_parent();
|
||||
let has_block_expr_parent = ctx.has_block_expr_parent();
|
||||
let has_item_list_parent = ctx.has_item_list_parent();
|
||||
if ctx.has_impl_or_trait_prev_sibling() {
|
||||
add_keyword(ctx, acc, "where", "where ");
|
||||
return;
|
||||
}
|
||||
if ctx.previous_token_is(T![unsafe]) {
|
||||
if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent {
|
||||
if has_item_list_parent || has_block_expr_parent {
|
||||
add_keyword(ctx, acc, "fn", "fn $1($2) {\n $0\n}")
|
||||
}
|
||||
|
||||
if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
|
||||
if has_item_list_parent || has_block_expr_parent {
|
||||
add_keyword(ctx, acc, "trait", "trait $1 {\n $0\n}");
|
||||
add_keyword(ctx, acc, "impl", "impl $1 {\n $0\n}");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
if ctx.has_item_list_or_source_file_parent || has_trait_or_impl_parent || ctx.block_expr_parent
|
||||
{
|
||||
if has_item_list_parent || has_trait_or_impl_parent || has_block_expr_parent {
|
||||
add_keyword(ctx, acc, "fn", "fn $1($2) {\n $0\n}");
|
||||
}
|
||||
if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
|
||||
if has_item_list_parent || has_block_expr_parent {
|
||||
add_keyword(ctx, acc, "use", "use ");
|
||||
add_keyword(ctx, acc, "impl", "impl $1 {\n $0\n}");
|
||||
add_keyword(ctx, acc, "trait", "trait $1 {\n $0\n}");
|
||||
}
|
||||
|
||||
if ctx.has_item_list_or_source_file_parent {
|
||||
if has_item_list_parent {
|
||||
add_keyword(ctx, acc, "enum", "enum $1 {\n $0\n}");
|
||||
add_keyword(ctx, acc, "struct", "struct $0");
|
||||
add_keyword(ctx, acc, "union", "union $1 {\n $0\n}");
|
||||
|
@ -92,7 +93,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
|
|||
add_keyword(ctx, acc, "for", "for $1 in $2 {\n $0\n}");
|
||||
}
|
||||
|
||||
if ctx.previous_token_is(T![if]) || ctx.previous_token_is(T![while]) || ctx.block_expr_parent {
|
||||
if ctx.previous_token_is(T![if]) || ctx.previous_token_is(T![while]) || has_block_expr_parent {
|
||||
add_keyword(ctx, acc, "let", "let ");
|
||||
}
|
||||
|
||||
|
@ -100,27 +101,23 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
|
|||
add_keyword(ctx, acc, "else", "else {\n $0\n}");
|
||||
add_keyword(ctx, acc, "else if", "else if $1 {\n $0\n}");
|
||||
}
|
||||
if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
|
||||
if has_item_list_parent || has_block_expr_parent {
|
||||
add_keyword(ctx, acc, "mod", "mod $0");
|
||||
}
|
||||
if ctx.bind_pat_parent || ctx.ref_pat_parent {
|
||||
if ctx.has_ident_or_ref_pat_parent() {
|
||||
add_keyword(ctx, acc, "mut", "mut ");
|
||||
}
|
||||
if ctx.has_item_list_or_source_file_parent || has_trait_or_impl_parent || ctx.block_expr_parent
|
||||
{
|
||||
if has_item_list_parent || has_trait_or_impl_parent || has_block_expr_parent {
|
||||
add_keyword(ctx, acc, "const", "const ");
|
||||
add_keyword(ctx, acc, "type", "type ");
|
||||
}
|
||||
if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
|
||||
if has_item_list_parent || has_block_expr_parent {
|
||||
add_keyword(ctx, acc, "static", "static ");
|
||||
};
|
||||
if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
|
||||
if has_item_list_parent || has_block_expr_parent {
|
||||
add_keyword(ctx, acc, "extern", "extern ");
|
||||
}
|
||||
if ctx.has_item_list_or_source_file_parent
|
||||
|| has_trait_or_impl_parent
|
||||
|| ctx.block_expr_parent
|
||||
|| ctx.is_match_arm
|
||||
if has_item_list_parent || has_trait_or_impl_parent || has_block_expr_parent || ctx.is_match_arm
|
||||
{
|
||||
add_keyword(ctx, acc, "unsafe", "unsafe ");
|
||||
}
|
||||
|
@ -133,7 +130,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
|
|||
add_keyword(ctx, acc, "break", "break");
|
||||
}
|
||||
}
|
||||
if ctx.has_item_list_or_source_file_parent || ctx.has_impl_parent | ctx.has_field_list_parent {
|
||||
if has_item_list_parent || ctx.has_impl_parent() || ctx.has_field_list_parent() {
|
||||
add_keyword(ctx, acc, "pub(crate)", "pub(crate) ");
|
||||
add_keyword(ctx, acc, "pub", "pub ");
|
||||
}
|
||||
|
@ -141,7 +138,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
|
|||
if !ctx.is_trivial_path {
|
||||
return;
|
||||
}
|
||||
let fn_def = match &ctx.function_syntax {
|
||||
let fn_def = match &ctx.function_def {
|
||||
Some(it) => it,
|
||||
None => return,
|
||||
};
|
||||
|
|
|
@ -14,7 +14,7 @@ fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str)
|
|||
}
|
||||
|
||||
pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) {
|
||||
if !(ctx.is_trivial_path && ctx.function_syntax.is_some()) {
|
||||
if !(ctx.is_trivial_path && ctx.function_def.is_some()) {
|
||||
return;
|
||||
}
|
||||
let cap = match ctx.config.snippet_cap {
|
||||
|
|
|
@ -13,8 +13,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
|
|||
|| ctx.record_pat_syntax.is_some()
|
||||
|| ctx.attribute_under_caret.is_some()
|
||||
|| ctx.mod_declaration_under_caret.is_some()
|
||||
|| ctx.has_impl_parent
|
||||
|| ctx.has_trait_parent
|
||||
|| ctx.has_impl_or_trait_parent()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -31,6 +31,24 @@ pub(crate) enum PatternRefutability {
|
|||
Irrefutable,
|
||||
}
|
||||
|
||||
/// Direct parent container of the cursor position
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub(crate) enum ImmediateLocation {
|
||||
Impl,
|
||||
Trait,
|
||||
RecordFieldList,
|
||||
RefPatOrExpr,
|
||||
IdentPat,
|
||||
BlockExpr,
|
||||
ItemList,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum PrevSibling {
|
||||
Trait,
|
||||
Impl,
|
||||
}
|
||||
|
||||
/// `CompletionContext` is created early during completion to figure out, where
|
||||
/// exactly is the cursor, syntax-wise.
|
||||
#[derive(Debug)]
|
||||
|
@ -48,14 +66,19 @@ pub(crate) struct CompletionContext<'a> {
|
|||
pub(super) expected_name: Option<NameOrNameRef>,
|
||||
pub(super) expected_type: Option<Type>,
|
||||
pub(super) name_ref_syntax: Option<ast::NameRef>,
|
||||
pub(super) function_syntax: Option<ast::Fn>,
|
||||
|
||||
pub(super) use_item_syntax: Option<ast::Use>,
|
||||
pub(super) record_lit_syntax: Option<ast::RecordExpr>,
|
||||
pub(super) record_pat_syntax: Option<ast::RecordPat>,
|
||||
pub(super) record_field_syntax: Option<ast::RecordExprField>,
|
||||
|
||||
/// The parent function of the cursor position if it exists.
|
||||
pub(super) function_def: Option<ast::Fn>,
|
||||
/// The parent impl of the cursor position if it exists.
|
||||
pub(super) impl_def: Option<ast::Impl>,
|
||||
|
||||
/// RecordExpr the token is a field of
|
||||
pub(super) record_lit_syntax: Option<ast::RecordExpr>,
|
||||
/// RecordPat the token is a field of
|
||||
pub(super) record_pat_syntax: Option<ast::RecordPat>,
|
||||
|
||||
// potentially set if we are completing a lifetime
|
||||
pub(super) lifetime_syntax: Option<ast::Lifetime>,
|
||||
pub(super) lifetime_param_syntax: Option<ast::LifetimeParam>,
|
||||
|
@ -66,6 +89,8 @@ pub(crate) struct CompletionContext<'a> {
|
|||
pub(super) is_pat_or_const: Option<PatternRefutability>,
|
||||
pub(super) is_param: bool,
|
||||
|
||||
pub(super) completion_location: Option<ImmediateLocation>,
|
||||
|
||||
/// FIXME: `ActiveParameter` is string-based, which is very very wrong
|
||||
pub(super) active_parameter: Option<ActiveParameter>,
|
||||
/// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
|
||||
|
@ -94,20 +119,12 @@ pub(crate) struct CompletionContext<'a> {
|
|||
pub(super) locals: Vec<(String, Local)>,
|
||||
|
||||
pub(super) mod_declaration_under_caret: Option<ast::Module>,
|
||||
pub(super) has_trait_parent: bool,
|
||||
pub(super) has_impl_parent: bool,
|
||||
|
||||
// keyword patterns
|
||||
pub(super) previous_token: Option<SyntaxToken>,
|
||||
pub(super) block_expr_parent: bool,
|
||||
pub(super) bind_pat_parent: bool,
|
||||
pub(super) ref_pat_parent: bool,
|
||||
pub(super) in_loop_body: bool,
|
||||
pub(super) has_field_list_parent: bool,
|
||||
pub(super) trait_as_prev_sibling: bool,
|
||||
pub(super) impl_as_prev_sibling: bool,
|
||||
pub(super) prev_sibling: Option<PrevSibling>,
|
||||
pub(super) is_match_arm: bool,
|
||||
pub(super) has_item_list_or_source_file_parent: bool,
|
||||
pub(super) incomplete_let: bool,
|
||||
|
||||
no_completion_required: bool,
|
||||
|
@ -159,11 +176,10 @@ impl<'a> CompletionContext<'a> {
|
|||
name_ref_syntax: None,
|
||||
lifetime_syntax: None,
|
||||
lifetime_param_syntax: None,
|
||||
function_syntax: None,
|
||||
function_def: None,
|
||||
use_item_syntax: None,
|
||||
record_lit_syntax: None,
|
||||
record_pat_syntax: None,
|
||||
record_field_syntax: None,
|
||||
impl_def: None,
|
||||
active_parameter: ActiveParameter::at(db, position),
|
||||
is_label_ref: false,
|
||||
|
@ -185,17 +201,10 @@ impl<'a> CompletionContext<'a> {
|
|||
attribute_under_caret: None,
|
||||
mod_declaration_under_caret: None,
|
||||
previous_token: None,
|
||||
block_expr_parent: false,
|
||||
bind_pat_parent: false,
|
||||
ref_pat_parent: false,
|
||||
in_loop_body: false,
|
||||
has_trait_parent: false,
|
||||
has_impl_parent: false,
|
||||
has_field_list_parent: false,
|
||||
trait_as_prev_sibling: false,
|
||||
impl_as_prev_sibling: false,
|
||||
completion_location: None,
|
||||
prev_sibling: None,
|
||||
is_match_arm: false,
|
||||
has_item_list_or_source_file_parent: false,
|
||||
no_completion_required: false,
|
||||
incomplete_let: false,
|
||||
locals,
|
||||
|
@ -274,23 +283,68 @@ impl<'a> CompletionContext<'a> {
|
|||
self.previous_token.as_ref().map_or(false, |tok| tok.kind() == kind)
|
||||
}
|
||||
|
||||
pub(crate) fn has_impl_or_trait_parent(&self) -> bool {
|
||||
matches!(
|
||||
self.completion_location,
|
||||
Some(ImmediateLocation::Trait) | Some(ImmediateLocation::Impl)
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn has_block_expr_parent(&self) -> bool {
|
||||
matches!(self.completion_location, Some(ImmediateLocation::BlockExpr))
|
||||
}
|
||||
|
||||
pub(crate) fn has_item_list_parent(&self) -> bool {
|
||||
matches!(self.completion_location, Some(ImmediateLocation::ItemList))
|
||||
}
|
||||
|
||||
pub(crate) fn has_ident_or_ref_pat_parent(&self) -> bool {
|
||||
matches!(
|
||||
self.completion_location,
|
||||
Some(ImmediateLocation::IdentPat) | Some(ImmediateLocation::RefPatOrExpr)
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn has_impl_parent(&self) -> bool {
|
||||
matches!(self.completion_location, Some(ImmediateLocation::Impl))
|
||||
}
|
||||
|
||||
pub(crate) fn has_field_list_parent(&self) -> bool {
|
||||
matches!(self.completion_location, Some(ImmediateLocation::RecordFieldList))
|
||||
}
|
||||
|
||||
pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool {
|
||||
self.prev_sibling.is_some()
|
||||
}
|
||||
|
||||
fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) {
|
||||
let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
|
||||
let syntax_element = NodeOrToken::Token(fake_ident_token);
|
||||
self.previous_token = previous_token(syntax_element.clone());
|
||||
self.block_expr_parent = has_block_expr_parent(syntax_element.clone());
|
||||
self.bind_pat_parent = has_bind_pat_parent(syntax_element.clone());
|
||||
self.ref_pat_parent = has_ref_parent(syntax_element.clone());
|
||||
self.in_loop_body = is_in_loop_body(syntax_element.clone());
|
||||
self.has_trait_parent = has_trait_parent(syntax_element.clone());
|
||||
self.has_impl_parent = has_impl_parent(syntax_element.clone());
|
||||
self.has_field_list_parent = has_field_list_parent(syntax_element.clone());
|
||||
self.impl_as_prev_sibling = has_impl_as_prev_sibling(syntax_element.clone());
|
||||
self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone());
|
||||
self.is_match_arm = is_match_arm(syntax_element.clone());
|
||||
if has_impl_as_prev_sibling(syntax_element.clone()) {
|
||||
self.prev_sibling = Some(PrevSibling::Impl)
|
||||
} else if has_trait_as_prev_sibling(syntax_element.clone()) {
|
||||
self.prev_sibling = Some(PrevSibling::Trait)
|
||||
}
|
||||
|
||||
if has_block_expr_parent(syntax_element.clone()) {
|
||||
self.completion_location = Some(ImmediateLocation::BlockExpr);
|
||||
} else if has_bind_pat_parent(syntax_element.clone()) {
|
||||
self.completion_location = Some(ImmediateLocation::IdentPat);
|
||||
} else if has_ref_parent(syntax_element.clone()) {
|
||||
self.completion_location = Some(ImmediateLocation::RefPatOrExpr);
|
||||
} else if has_impl_parent(syntax_element.clone()) {
|
||||
self.completion_location = Some(ImmediateLocation::Impl);
|
||||
} else if has_field_list_parent(syntax_element.clone()) {
|
||||
self.completion_location = Some(ImmediateLocation::RecordFieldList);
|
||||
} else if has_trait_parent(syntax_element.clone()) {
|
||||
self.completion_location = Some(ImmediateLocation::Trait);
|
||||
} else if has_item_list_or_source_file_parent(syntax_element.clone()) {
|
||||
self.completion_location = Some(ImmediateLocation::ItemList);
|
||||
}
|
||||
|
||||
self.has_item_list_or_source_file_parent =
|
||||
has_item_list_or_source_file_parent(syntax_element.clone());
|
||||
self.mod_declaration_under_caret =
|
||||
find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset)
|
||||
.filter(|module| module.item_list().is_none());
|
||||
|
@ -542,31 +596,20 @@ impl<'a> CompletionContext<'a> {
|
|||
.last()
|
||||
.unwrap();
|
||||
|
||||
match top_node.parent().map(|it| it.kind()) {
|
||||
Some(SOURCE_FILE) | Some(ITEM_LIST) => {
|
||||
self.is_new_item = true;
|
||||
return;
|
||||
}
|
||||
_ => (),
|
||||
if matches!(top_node.parent().map(|it| it.kind()), Some(SOURCE_FILE) | Some(ITEM_LIST)) {
|
||||
self.is_new_item = true;
|
||||
return;
|
||||
}
|
||||
|
||||
self.use_item_syntax =
|
||||
self.sema.token_ancestors_with_macros(self.token.clone()).find_map(ast::Use::cast);
|
||||
|
||||
self.function_syntax = self
|
||||
self.function_def = self
|
||||
.sema
|
||||
.token_ancestors_with_macros(self.token.clone())
|
||||
.take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
|
||||
.find_map(ast::Fn::cast);
|
||||
|
||||
self.record_field_syntax = self
|
||||
.sema
|
||||
.token_ancestors_with_macros(self.token.clone())
|
||||
.take_while(|it| {
|
||||
it.kind() != SOURCE_FILE && it.kind() != MODULE && it.kind() != CALL_EXPR
|
||||
})
|
||||
.find_map(ast::RecordExprField::cast);
|
||||
|
||||
let parent = match name_ref.syntax().parent() {
|
||||
Some(it) => it,
|
||||
None => return,
|
||||
|
@ -639,6 +682,7 @@ impl<'a> CompletionContext<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
|
||||
// The receiver comes before the point of insertion of the fake
|
||||
// ident, so it should have the same range in the non-modified file
|
||||
|
@ -656,6 +700,7 @@ impl<'a> CompletionContext<'a> {
|
|||
false
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) {
|
||||
// As above
|
||||
self.dot_receiver = method_call_expr
|
||||
|
|
|
@ -91,11 +91,10 @@ fn test_has_ref_parent() {
|
|||
}
|
||||
|
||||
pub(crate) fn has_item_list_or_source_file_parent(element: SyntaxElement) -> bool {
|
||||
let ancestor = not_same_range_ancestor(element);
|
||||
if !ancestor.is_some() {
|
||||
return true;
|
||||
match not_same_range_ancestor(element) {
|
||||
Some(it) => it.kind() == SOURCE_FILE || it.kind() == ITEM_LIST,
|
||||
None => true,
|
||||
}
|
||||
ancestor.filter(|it| it.kind() == SOURCE_FILE || it.kind() == ITEM_LIST).is_some()
|
||||
}
|
||||
#[test]
|
||||
fn test_has_item_list_or_source_file_parent() {
|
||||
|
@ -151,25 +150,21 @@ fn test_has_impl_as_prev_sibling() {
|
|||
}
|
||||
|
||||
pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool {
|
||||
for node in element.ancestors() {
|
||||
if node.kind() == FN || node.kind() == CLOSURE_EXPR {
|
||||
break;
|
||||
}
|
||||
let loop_body = match_ast! {
|
||||
match node {
|
||||
ast::ForExpr(it) => it.loop_body(),
|
||||
ast::WhileExpr(it) => it.loop_body(),
|
||||
ast::LoopExpr(it) => it.loop_body(),
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
if let Some(body) = loop_body {
|
||||
if body.syntax().text_range().contains_range(element.text_range()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
element
|
||||
.ancestors()
|
||||
.take_while(|it| it.kind() != FN && it.kind() != CLOSURE_EXPR)
|
||||
.find_map(|it| {
|
||||
let loop_body = match_ast! {
|
||||
match it {
|
||||
ast::ForExpr(it) => it.loop_body(),
|
||||
ast::WhileExpr(it) => it.loop_body(),
|
||||
ast::LoopExpr(it) => it.loop_body(),
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
loop_body.filter(|it| it.syntax().text_range().contains_range(element.text_range()))
|
||||
})
|
||||
.is_some()
|
||||
}
|
||||
|
||||
fn not_same_range_ancestor(element: SyntaxElement) -> Option<SyntaxNode> {
|
||||
|
|
Loading…
Reference in a new issue