Skip to content

Commit 8b6c3ea

Browse files
bors[bot]Veykril
andauthored
Merge #9164
9164: internal: Reduce the number of direct fields in `CompletionContext` some more r=Veykril a=Veykril Doesn't make the code much simpler yet. bors r+ Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
2 parents 5f7fc5a + 4eabcb2 commit 8b6c3ea

File tree

12 files changed

+108
-75
lines changed

12 files changed

+108
-75
lines changed

crates/ide_completion/src/completions/dot.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use either::Either;
44
use hir::{HasVisibility, ScopeDef};
55
use rustc_hash::FxHashSet;
66

7-
use crate::{context::CompletionContext, Completions};
7+
use crate::{context::CompletionContext, patterns::ImmediateLocation, Completions};
88

99
/// Complete dot accesses, i.e. fields or methods.
1010
pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
@@ -18,7 +18,7 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
1818
_ => return,
1919
};
2020

21-
if ctx.is_call {
21+
if matches!(ctx.completion_location, Some(ImmediateLocation::MethodCall { .. })) {
2222
cov_mark::hit!(test_no_struct_field_completion_for_method_call);
2323
} else {
2424
complete_fields(ctx, &receiver_ty, |field, ty| match field {
@@ -33,7 +33,7 @@ fn complete_undotted_self(acc: &mut Completions, ctx: &CompletionContext) {
3333
if !ctx.config.enable_self_on_the_fly {
3434
return;
3535
}
36-
if !ctx.is_trivial_path || ctx.is_path_disallowed() {
36+
if !ctx.is_trivial_path() || ctx.is_path_disallowed() {
3737
return;
3838
}
3939
ctx.scope.process_all_names(&mut |name, def| {

crates/ide_completion/src/completions/flyimport.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,13 +161,13 @@ pub(crate) fn position_for_import<'a>(
161161
) -> Option<&'a SyntaxNode> {
162162
Some(match import_candidate {
163163
Some(ImportCandidate::Path(_)) => ctx.name_ref_syntax.as_ref()?.syntax(),
164-
Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual.as_ref()?.syntax(),
164+
Some(ImportCandidate::TraitAssocItem(_)) => ctx.path_qual()?.syntax(),
165165
Some(ImportCandidate::TraitMethod(_)) => ctx.dot_receiver()?.syntax(),
166166
None => ctx
167167
.name_ref_syntax
168168
.as_ref()
169169
.map(|name_ref| name_ref.syntax())
170-
.or_else(|| ctx.path_qual.as_ref().map(|path| path.syntax()))
170+
.or_else(|| ctx.path_qual().map(|path| path.syntax()))
171171
.or_else(|| ctx.dot_receiver().map(|expr| expr.syntax()))?,
172172
})
173173
}
@@ -190,7 +190,7 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs
190190
};
191191
let assets_for_path = ImportAssets::for_fuzzy_path(
192192
current_module,
193-
ctx.path_qual.clone(),
193+
ctx.path_qual().cloned(),
194194
fuzzy_name,
195195
&ctx.sema,
196196
approximate_node,

crates/ide_completion/src/completions/keyword.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
1919
};
2020

2121
if ctx.use_item_syntax.is_some() {
22-
if ctx.path_qual.is_none() {
22+
let qual = ctx.path_qual();
23+
if qual.is_none() {
2324
kw_completion("crate::").add_to(acc);
2425
}
2526
kw_completion("self").add_to(acc);
26-
if iter::successors(ctx.path_qual.clone(), |p| p.qualifier())
27+
if iter::successors(qual.cloned(), |p| p.qualifier())
2728
.all(|p| p.segment().and_then(|s| s.super_token()).is_some())
2829
{
2930
kw_completion("super::").add_to(acc);
@@ -128,7 +129,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
128129
}
129130

130131
if ctx.in_loop_body {
131-
if ctx.can_be_stmt {
132+
if ctx.can_be_stmt() {
132133
add_keyword("continue", "continue;");
133134
add_keyword("break", "break;");
134135
} else {
@@ -137,7 +138,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
137138
}
138139
}
139140

140-
if !ctx.is_trivial_path {
141+
if !ctx.is_trivial_path() {
141142
return;
142143
}
143144
let fn_def = match &ctx.function_def {
@@ -147,7 +148,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
147148

148149
add_keyword(
149150
"return",
150-
match (ctx.can_be_stmt, fn_def.ret_type().is_some()) {
151+
match (ctx.can_be_stmt(), fn_def.ret_type().is_some()) {
151152
(true, true) => "return $0;",
152153
(true, false) => "return;",
153154
(false, true) => "return $0",

crates/ide_completion/src/completions/postfix.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
2424
}
2525

2626
let (dot_receiver, receiver_is_ambiguous_float_literal) = match &ctx.completion_location {
27-
Some(ImmediateLocation::MethodCall { receiver: Some(it) }) => (it, false),
27+
Some(ImmediateLocation::MethodCall { receiver: Some(it), .. }) => (it, false),
2828
Some(ImmediateLocation::FieldAccess {
2929
receiver: Some(it),
3030
receiver_is_ambiguous_float_literal,

crates/ide_completion/src/completions/qualified_path.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
1010
if ctx.is_path_disallowed() || ctx.expects_item() {
1111
return;
1212
}
13-
let path = match &ctx.path_qual {
14-
Some(path) => path.clone(),
13+
let path = match ctx.path_qual() {
14+
Some(path) => path,
1515
None => return,
1616
};
1717

crates/ide_completion/src/completions/snippet.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str)
1414
}
1515

1616
pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) {
17-
if !(ctx.is_trivial_path && ctx.function_def.is_some()) {
17+
if !(ctx.is_trivial_path() && ctx.function_def.is_some()) {
1818
return;
1919
}
2020
let cap = match ctx.config.snippet_cap {
2121
Some(it) => it,
2222
None => return,
2323
};
2424

25-
if ctx.can_be_stmt {
25+
if ctx.can_be_stmt() {
2626
snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc);
2727
snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc);
2828
}

crates/ide_completion/src/completions/unqualified_path.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use hir::ScopeDef;
55
use crate::{CompletionContext, Completions};
66

77
pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
8-
if !ctx.is_trivial_path {
8+
if !ctx.is_trivial_path() {
99
return;
1010
}
1111
if ctx.is_path_disallowed() || ctx.expects_item() {

crates/ide_completion/src/context.rs

Lines changed: 71 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,28 @@ pub(crate) enum PatternRefutability {
2929
Irrefutable,
3030
}
3131

32+
#[derive(Debug)]
33+
pub(crate) struct PathCompletionContext {
34+
/// If this is a call with () already there
35+
call_kind: Option<CallKind>,
36+
/// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
37+
pub(super) is_trivial_path: bool,
38+
/// If not a trivial path, the prefix (qualifier).
39+
pub(super) path_qual: Option<ast::Path>,
40+
pub(super) is_path_type: bool,
41+
pub(super) has_type_args: bool,
42+
/// `true` if we are a statement or a last expr in the block.
43+
pub(super) can_be_stmt: bool,
44+
/// `true` if we expect an expression at the cursor position.
45+
pub(super) is_expr: bool,
46+
}
47+
48+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
49+
pub(crate) enum CallKind {
50+
Pat,
51+
Mac,
52+
Expr,
53+
}
3254
/// `CompletionContext` is created early during completion to figure out, where
3355
/// exactly is the cursor, syntax-wise.
3456
#[derive(Debug)]
@@ -68,24 +90,9 @@ pub(crate) struct CompletionContext<'a> {
6890
pub(super) prev_sibling: Option<ImmediatePrevSibling>,
6991
pub(super) attribute_under_caret: Option<ast::Attr>,
7092

93+
pub(super) path_context: Option<PathCompletionContext>,
7194
/// FIXME: `ActiveParameter` is string-based, which is very very wrong
7295
pub(super) active_parameter: Option<ActiveParameter>,
73-
/// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
74-
pub(super) is_trivial_path: bool,
75-
/// If not a trivial path, the prefix (qualifier).
76-
pub(super) path_qual: Option<ast::Path>,
77-
/// `true` if we are a statement or a last expr in the block.
78-
pub(super) can_be_stmt: bool,
79-
/// `true` if we expect an expression at the cursor position.
80-
pub(super) is_expr: bool,
81-
/// If this is a call (method or function) in particular, i.e. the () are already there.
82-
pub(super) is_call: bool,
83-
/// Like `is_call`, but for tuple patterns.
84-
pub(super) is_pattern_call: bool,
85-
/// If this is a macro call, i.e. the () are already there.
86-
pub(super) is_macro_call: bool,
87-
pub(super) is_path_type: bool,
88-
pub(super) has_type_args: bool,
8996
pub(super) locals: Vec<(String, Local)>,
9097

9198
pub(super) previous_token: Option<SyntaxToken>,
@@ -149,15 +156,7 @@ impl<'a> CompletionContext<'a> {
149156
is_label_ref: false,
150157
is_param: false,
151158
is_pat_or_const: None,
152-
is_trivial_path: false,
153-
path_qual: None,
154-
can_be_stmt: false,
155-
is_expr: false,
156-
is_call: false,
157-
is_pattern_call: false,
158-
is_macro_call: false,
159-
is_path_type: false,
160-
has_type_args: false,
159+
path_context: None,
161160
previous_token: None,
162161
in_loop_body: false,
163162
completion_location: None,
@@ -250,14 +249,14 @@ impl<'a> CompletionContext<'a> {
250249
pub(crate) fn has_dot_receiver(&self) -> bool {
251250
matches!(
252251
&self.completion_location,
253-
Some(ImmediateLocation::FieldAccess { receiver, .. }) | Some(ImmediateLocation::MethodCall { receiver })
252+
Some(ImmediateLocation::FieldAccess { receiver, .. }) | Some(ImmediateLocation::MethodCall { receiver,.. })
254253
if receiver.is_some()
255254
)
256255
}
257256

258257
pub(crate) fn dot_receiver(&self) -> Option<&ast::Expr> {
259258
match &self.completion_location {
260-
Some(ImmediateLocation::MethodCall { receiver })
259+
Some(ImmediateLocation::MethodCall { receiver, .. })
261260
| Some(ImmediateLocation::FieldAccess { receiver, .. }) => receiver.as_ref(),
262261
_ => None,
263262
}
@@ -275,11 +274,6 @@ impl<'a> CompletionContext<'a> {
275274
matches!(self.completion_location, Some(ImmediateLocation::ItemList))
276275
}
277276

278-
// fn expects_value(&self) -> bool {
279-
pub(crate) fn expects_expression(&self) -> bool {
280-
self.is_expr
281-
}
282-
283277
pub(crate) fn has_block_expr_parent(&self) -> bool {
284278
matches!(self.completion_location, Some(ImmediateLocation::BlockExpr))
285279
}
@@ -316,6 +310,26 @@ impl<'a> CompletionContext<'a> {
316310
) || self.attribute_under_caret.is_some()
317311
}
318312

313+
pub(crate) fn expects_expression(&self) -> bool {
314+
self.path_context.as_ref().map_or(false, |it| it.is_expr)
315+
}
316+
317+
pub(crate) fn path_call_kind(&self) -> Option<CallKind> {
318+
self.path_context.as_ref().and_then(|it| it.call_kind)
319+
}
320+
321+
pub(crate) fn is_trivial_path(&self) -> bool {
322+
self.path_context.as_ref().map_or(false, |it| it.is_trivial_path)
323+
}
324+
325+
pub(crate) fn path_qual(&self) -> Option<&ast::Path> {
326+
self.path_context.as_ref().and_then(|it| it.path_qual.as_ref())
327+
}
328+
329+
pub(crate) fn can_be_stmt(&self) -> bool {
330+
self.path_context.as_ref().map_or(false, |it| it.can_be_stmt)
331+
}
332+
319333
fn fill_impl_def(&mut self) {
320334
self.impl_def = self
321335
.sema
@@ -568,22 +582,32 @@ impl<'a> CompletionContext<'a> {
568582
};
569583

570584
if let Some(segment) = ast::PathSegment::cast(parent) {
585+
let path_ctx = self.path_context.get_or_insert(PathCompletionContext {
586+
call_kind: None,
587+
is_trivial_path: false,
588+
path_qual: None,
589+
has_type_args: false,
590+
is_path_type: false,
591+
can_be_stmt: false,
592+
is_expr: false,
593+
});
571594
let path = segment.parent_path();
572-
self.is_call = path
573-
.syntax()
574-
.parent()
575-
.and_then(ast::PathExpr::cast)
576-
.and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast))
577-
.is_some();
578-
self.is_macro_call = path.syntax().parent().and_then(ast::MacroCall::cast).is_some();
579-
self.is_pattern_call =
580-
path.syntax().parent().and_then(ast::TupleStructPat::cast).is_some();
581595

582-
self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some();
583-
self.has_type_args = segment.generic_arg_list().is_some();
596+
if let Some(p) = path.syntax().parent() {
597+
path_ctx.call_kind = match_ast! {
598+
match p {
599+
ast::PathExpr(it) => it.syntax().parent().and_then(ast::CallExpr::cast).map(|_| CallKind::Expr),
600+
ast::MacroCall(_it) => Some(CallKind::Mac),
601+
ast::TupleStructPat(_it) => Some(CallKind::Pat),
602+
_ => None
603+
}
604+
};
605+
}
606+
path_ctx.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some();
607+
path_ctx.has_type_args = segment.generic_arg_list().is_some();
584608

585609
if let Some(path) = path_or_use_tree_qualifier(&path) {
586-
self.path_qual = path
610+
path_ctx.path_qual = path
587611
.segment()
588612
.and_then(|it| {
589613
find_node_with_range::<ast::PathSegment>(
@@ -601,11 +625,11 @@ impl<'a> CompletionContext<'a> {
601625
}
602626
}
603627

604-
self.is_trivial_path = true;
628+
path_ctx.is_trivial_path = true;
605629

606630
// Find either enclosing expr statement (thing with `;`) or a
607631
// block. If block, check that we are the last expr.
608-
self.can_be_stmt = name_ref
632+
path_ctx.can_be_stmt = name_ref
609633
.syntax()
610634
.ancestors()
611635
.find_map(|node| {
@@ -621,10 +645,8 @@ impl<'a> CompletionContext<'a> {
621645
None
622646
})
623647
.unwrap_or(false);
624-
self.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some();
648+
path_ctx.is_expr = path.syntax().parent().and_then(ast::PathExpr::cast).is_some();
625649
}
626-
self.is_call |=
627-
matches!(self.completion_location, Some(ImmediateLocation::MethodCall { .. }));
628650
}
629651
}
630652

crates/ide_completion/src/patterns.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use hir::Semantics;
44
use ide_db::RootDatabase;
55
use syntax::{
66
algo::non_trivia_sibling,
7-
ast::{self, LoopBodyOwner},
7+
ast::{self, ArgListOwner, LoopBodyOwner},
88
match_ast, AstNode, Direction, SyntaxElement,
99
SyntaxKind::*,
1010
SyntaxNode, SyntaxToken, TextRange, TextSize, T,
@@ -39,6 +39,7 @@ pub(crate) enum ImmediateLocation {
3939
// Original file ast node
4040
MethodCall {
4141
receiver: Option<ast::Expr>,
42+
has_parens: bool,
4243
},
4344
// Original file ast node
4445
FieldAccess {
@@ -204,6 +205,7 @@ pub(crate) fn determine_location(
204205
.receiver()
205206
.map(|e| e.syntax().text_range())
206207
.and_then(|r| find_node_with_range(original_file, r)),
208+
has_parens: it.arg_list().map_or(false, |it| it.l_paren_token().is_some())
207209
},
208210
_ => return None,
209211
}

crates/ide_completion/src/render.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,8 +275,12 @@ impl<'a> Render<'a> {
275275
};
276276

277277
// Add `<>` for generic types
278-
if self.ctx.completion.is_path_type
279-
&& !self.ctx.completion.has_type_args
278+
if self
279+
.ctx
280+
.completion
281+
.path_context
282+
.as_ref()
283+
.map_or(false, |it| it.is_path_type && !it.has_type_args)
280284
&& self.ctx.completion.config.add_call_parenthesis
281285
{
282286
if let Some(cap) = self.ctx.snippet_cap() {

0 commit comments

Comments
 (0)