Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 06ee4d6

Browse files
committed
fix: Fix auto-ref completions inserting into wrong locations
1 parent 1f02840 commit 06ee4d6

File tree

10 files changed

+98
-94
lines changed

10 files changed

+98
-94
lines changed

crates/ide-completion/src/completions/flyimport.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ pub(crate) fn import_on_the_fly_pat(
169169
has_macro_bang: false,
170170
qualified: Qualified::No,
171171
parent: None,
172+
// FIXME
173+
path: syntax::ast::make::ext::ident_path("dummy__"),
172174
kind: crate::context::PathKind::Pat { pat_ctx: pat_ctx.clone() },
173175
has_type_args: false,
174176
use_tree_parent: false,

crates/ide-completion/src/completions/pattern.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,16 @@ pub(crate) fn complete_pattern(
8888
has_call_parens: false,
8989
has_macro_bang: false,
9090
qualified: Qualified::No,
91+
// FIXME
92+
path: syntax::ast::make::ext::ident_path("dummy__"),
9193
parent: None,
9294
kind: crate::context::PathKind::Pat { pat_ctx: pattern_ctx.clone() },
9395
has_type_args: false,
9496
use_tree_parent: false,
9597
},
9698
mac,
9799
name,
98-
)
100+
);
99101
}
100102
_ => false,
101103
},

crates/ide-completion/src/context.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ pub(crate) struct PathCompletionCtx {
6161
pub(super) qualified: Qualified,
6262
/// The parent of the path we are completing.
6363
pub(super) parent: Option<ast::Path>,
64+
/// The path of which we are completing the segment
65+
pub(super) path: ast::Path,
6466
pub(super) kind: PathKind,
6567
/// Whether the path segment has type args or not.
6668
pub(super) has_type_args: bool,

crates/ide-completion/src/context/analysis.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,7 @@ impl<'a> CompletionContext<'a> {
556556
has_macro_bang: false,
557557
qualified: Qualified::No,
558558
parent: path.parent_path(),
559+
path: path.clone(),
559560
kind: PathKind::Item { kind: ItemListKind::SourceFile },
560561
has_type_args: false,
561562
use_tree_parent: false,

crates/ide-completion/src/item.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use hir::{Documentation, Mutability};
66
use ide_db::{imports::import_assets::LocatedImport, SnippetCap, SymbolKind};
77
use smallvec::SmallVec;
88
use stdx::{impl_from, never};
9-
use syntax::{SmolStr, TextRange};
9+
use syntax::{SmolStr, TextRange, TextSize};
1010
use text_edit::TextEdit;
1111

1212
use crate::{
@@ -68,7 +68,7 @@ pub struct CompletionItem {
6868

6969
/// Indicates that a reference or mutable reference to this variable is a
7070
/// possible match.
71-
ref_match: Option<Mutability>,
71+
ref_match: Option<(Mutability, TextSize)>,
7272

7373
/// The import data to add to completion's edits.
7474
import_to_add: SmallVec<[LocatedImport; 1]>,
@@ -104,8 +104,8 @@ impl fmt::Debug for CompletionItem {
104104
s.field("relevance", &self.relevance);
105105
}
106106

107-
if let Some(mutability) = &self.ref_match {
108-
s.field("ref_match", &format!("&{}", mutability.as_keyword_for_ref()));
107+
if let Some((mutability, offset)) = &self.ref_match {
108+
s.field("ref_match", &format!("&{}@{offset:?}", mutability.as_keyword_for_ref()));
109109
}
110110
if self.trigger_call_info {
111111
s.field("trigger_call_info", &true);
@@ -395,14 +395,14 @@ impl CompletionItem {
395395
self.trigger_call_info
396396
}
397397

398-
pub fn ref_match(&self) -> Option<(Mutability, CompletionRelevance)> {
398+
pub fn ref_match(&self) -> Option<(Mutability, TextSize, CompletionRelevance)> {
399399
// Relevance of the ref match should be the same as the original
400400
// match, but with exact type match set because self.ref_match
401401
// is only set if there is an exact type match.
402402
let mut relevance = self.relevance;
403403
relevance.type_match = Some(CompletionRelevanceTypeMatch::Exact);
404404

405-
self.ref_match.map(|mutability| (mutability, relevance))
405+
self.ref_match.map(|(mutability, offset)| (mutability, offset, relevance))
406406
}
407407

408408
pub fn imports_to_add(&self) -> &[LocatedImport] {
@@ -428,7 +428,7 @@ pub(crate) struct Builder {
428428
deprecated: bool,
429429
trigger_call_info: bool,
430430
relevance: CompletionRelevance,
431-
ref_match: Option<Mutability>,
431+
ref_match: Option<(Mutability, TextSize)>,
432432
}
433433

434434
impl Builder {
@@ -548,8 +548,8 @@ impl Builder {
548548
self.imports_to_add.push(import_to_add);
549549
self
550550
}
551-
pub(crate) fn ref_match(&mut self, mutability: Mutability) -> &mut Builder {
552-
self.ref_match = Some(mutability);
551+
pub(crate) fn ref_match(&mut self, mutability: Mutability, offset: TextSize) -> &mut Builder {
552+
self.ref_match = Some((mutability, offset));
553553
self
554554
}
555555
}

crates/ide-completion/src/render.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use hir::{AsAssocItem, HasAttrs, HirDisplay, ScopeDef};
1414
use ide_db::{
1515
helpers::item_name, imports::import_assets::LocatedImport, RootDatabase, SnippetCap, SymbolKind,
1616
};
17-
use syntax::{SmolStr, SyntaxKind, TextRange};
17+
use syntax::{AstNode, SmolStr, SyntaxKind, TextRange};
1818

1919
use crate::{
2020
context::{PathCompletionCtx, PathKind},
@@ -167,7 +167,7 @@ pub(crate) fn render_resolution_simple(
167167
local_name: hir::Name,
168168
resolution: ScopeDef,
169169
) -> Builder {
170-
render_resolution_simple_(ctx, local_name, None, resolution)
170+
render_resolution_simple_(ctx, None, local_name, None, resolution)
171171
}
172172

173173
pub(crate) fn render_resolution_with_import(
@@ -235,7 +235,8 @@ fn render_resolution_simple_type(
235235
let db = ctx.completion.db;
236236
let config = ctx.completion.config;
237237
let name = local_name.to_smol_str();
238-
let mut item = render_resolution_simple_(ctx, local_name, import_to_add, resolution);
238+
let mut item =
239+
render_resolution_simple_(ctx, Some(path_ctx), local_name, import_to_add, resolution);
239240
// Add `<>` for generic types
240241
let type_path_no_ty_args = matches!(
241242
path_ctx,
@@ -264,6 +265,7 @@ fn render_resolution_simple_type(
264265

265266
fn render_resolution_simple_(
266267
ctx: RenderContext<'_>,
268+
path_ctx: Option<&PathCompletionCtx>,
267269
local_name: hir::Name,
268270
import_to_add: Option<LocatedImport>,
269271
resolution: ScopeDef,
@@ -317,8 +319,10 @@ fn render_resolution_simple_(
317319
..CompletionRelevance::default()
318320
});
319321

320-
if let Some(ref_match) = compute_ref_match(ctx.completion, &ty) {
321-
item.ref_match(ref_match);
322+
if let Some(path_ctx) = path_ctx {
323+
if let Some(ref_match) = compute_ref_match(ctx.completion, &ty) {
324+
item.ref_match(ref_match, path_ctx.path.syntax().text_range().start());
325+
}
322326
}
323327
};
324328

@@ -455,7 +459,7 @@ mod tests {
455459
let relevance = display_relevance(it.relevance());
456460
items.push(format!("{} {} {}\n", tag, it.label(), relevance));
457461

458-
if let Some((mutability, relevance)) = it.ref_match() {
462+
if let Some((mutability, _offset, relevance)) = it.ref_match() {
459463
let label = format!("&{}{}", mutability.as_keyword_for_ref(), it.label());
460464
let relevance = display_relevance(relevance);
461465

@@ -1495,7 +1499,7 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 }
14951499
"#,
14961500
&[CompletionItemKind::Method, CompletionItemKind::SymbolKind(SymbolKind::Field)],
14971501
// FIXME
1498-
// Ideally we'd also suggest &f.bar and &f.baz() as exact
1502+
// Ideally we'd also suggest &f.bar as exact
14991503
// type matches. See #8058.
15001504
expect![[r#"
15011505
[
@@ -1507,6 +1511,7 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 }
15071511
kind: Method,
15081512
lookup: "baz",
15091513
detail: "fn(&self) -> u32",
1514+
ref_match: "&@96",
15101515
},
15111516
CompletionItem {
15121517
label: "bar",
@@ -1525,7 +1530,6 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 }
15251530

15261531
#[test]
15271532
fn qualified_path_ref() {
1528-
// disabled right now because it doesn't render correctly, #8058
15291533
check_kinds(
15301534
r#"
15311535
struct S;
@@ -1554,6 +1558,7 @@ fn main() {
15541558
),
15551559
lookup: "foo",
15561560
detail: "fn() -> S",
1561+
ref_match: "&@92",
15571562
},
15581563
]
15591564
"#]],

crates/ide-completion/src/render/function.rs

Lines changed: 44 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,19 @@ use hir::{db::HirDatabase, AsAssocItem, HirDisplay};
44
use ide_db::{SnippetCap, SymbolKind};
55
use itertools::Itertools;
66
use stdx::{format_to, to_lower_snake_case};
7-
use syntax::SmolStr;
7+
use syntax::{AstNode, SmolStr};
88

99
use crate::{
10-
context::{
11-
CompletionContext, DotAccess, DotAccessKind, PathCompletionCtx, PathKind, Qualified,
12-
},
10+
context::{CompletionContext, DotAccess, DotAccessKind, PathCompletionCtx, PathKind},
1311
item::{Builder, CompletionItem, CompletionItemKind, CompletionRelevance},
1412
render::{compute_exact_name_match, compute_ref_match, compute_type_match, RenderContext},
1513
CallableSnippets,
1614
};
1715

18-
enum FuncKind {
19-
Function,
20-
Method(Option<hir::Name>),
16+
#[derive(Debug)]
17+
enum FuncKind<'ctx> {
18+
Function(&'ctx PathCompletionCtx),
19+
Method(&'ctx DotAccess, Option<hir::Name>),
2120
}
2221

2322
pub(crate) fn render_fn(
@@ -27,29 +26,7 @@ pub(crate) fn render_fn(
2726
func: hir::Function,
2827
) -> Builder {
2928
let _p = profile::span("render_fn");
30-
let func_kind = FuncKind::Function;
31-
let params = match ctx.completion.config.snippet_cap {
32-
Some(_) => {
33-
if !matches!(
34-
path_ctx,
35-
PathCompletionCtx { kind: PathKind::Expr { .. }, has_call_parens: true, .. }
36-
| PathCompletionCtx { kind: PathKind::Use | PathKind::Type { .. }, .. }
37-
) {
38-
params(ctx.completion, func, &func_kind, false)
39-
} else {
40-
None
41-
}
42-
}
43-
_ => None,
44-
};
45-
render(
46-
ctx,
47-
local_name,
48-
func,
49-
func_kind,
50-
params,
51-
matches!(path_ctx.qualified, Qualified::With { .. }),
52-
)
29+
render(ctx, local_name, func, FuncKind::Function(path_ctx))
5330
}
5431

5532
pub(crate) fn render_method(
@@ -60,32 +37,21 @@ pub(crate) fn render_method(
6037
func: hir::Function,
6138
) -> Builder {
6239
let _p = profile::span("render_method");
63-
let func_kind = FuncKind::Method(receiver);
64-
let params = match ctx.completion.config.snippet_cap {
65-
Some(_) => match dot_access {
66-
DotAccess { kind: DotAccessKind::Method { has_parens: true }, .. } => None,
67-
_ => params(ctx.completion, func, &func_kind, true),
68-
},
69-
_ => None,
70-
};
71-
72-
render(ctx, local_name, func, func_kind, params, false)
40+
render(ctx, local_name, func, FuncKind::Method(dot_access, receiver))
7341
}
7442

7543
fn render(
7644
ctx @ RenderContext { completion, .. }: RenderContext<'_>,
7745
local_name: Option<hir::Name>,
7846
func: hir::Function,
7947
func_kind: FuncKind,
80-
params: Option<(Option<hir::SelfParam>, Vec<hir::Param>)>,
81-
qualified_path: bool,
8248
) -> Builder {
8349
let db = completion.db;
8450

8551
let name = local_name.unwrap_or_else(|| func.name(db));
8652

8753
let call = match &func_kind {
88-
FuncKind::Method(Some(receiver)) => format!("{}.{}", receiver, &name).into(),
54+
FuncKind::Method(_, Some(receiver)) => format!("{}.{}", receiver, &name).into(),
8955
_ => name.to_smol_str(),
9056
};
9157
let mut item = CompletionItem::new(
@@ -111,11 +77,14 @@ fn render(
11177
});
11278

11379
if let Some(ref_match) = compute_ref_match(completion, &ret_type) {
114-
// FIXME For now we don't properly calculate the edits for ref match
115-
// completions on methods or qualified paths, so we've disabled them.
116-
// See #8058.
117-
if matches!(func_kind, FuncKind::Function) && !qualified_path {
118-
item.ref_match(ref_match);
80+
match func_kind {
81+
FuncKind::Function(path_ctx) => {
82+
item.ref_match(ref_match, path_ctx.path.syntax().text_range().start());
83+
}
84+
FuncKind::Method(DotAccess { receiver: Some(receiver), .. }, _) => {
85+
item.ref_match(ref_match, receiver.syntax().text_range().start());
86+
}
87+
_ => (),
11988
}
12089
}
12190

@@ -124,12 +93,34 @@ fn render(
12493
.detail(detail(db, func))
12594
.lookup_by(name.to_smol_str());
12695

127-
match completion.config.snippet_cap.zip(params) {
128-
Some((cap, (self_param, params))) => {
129-
add_call_parens(&mut item, completion, cap, call, self_param, params);
96+
match ctx.completion.config.snippet_cap {
97+
Some(cap) => {
98+
let complete_params = match func_kind {
99+
FuncKind::Function(PathCompletionCtx {
100+
kind: PathKind::Expr { .. },
101+
has_call_parens: false,
102+
..
103+
}) => Some(false),
104+
FuncKind::Method(
105+
DotAccess {
106+
kind:
107+
DotAccessKind::Method { has_parens: false } | DotAccessKind::Field { .. },
108+
..
109+
},
110+
_,
111+
) => Some(true),
112+
_ => None,
113+
};
114+
if let Some(has_dot_receiver) = complete_params {
115+
if let Some((self_param, params)) =
116+
params(ctx.completion, func, &func_kind, has_dot_receiver)
117+
{
118+
add_call_parens(&mut item, completion, cap, call, self_param, params);
119+
}
120+
}
130121
}
131122
_ => (),
132-
}
123+
};
133124

134125
match ctx.import_to_add {
135126
Some(import_to_add) => {
@@ -291,7 +282,7 @@ fn params(
291282
}
292283
}
293284

294-
let self_param = if has_dot_receiver || matches!(func_kind, FuncKind::Method(Some(_))) {
285+
let self_param = if has_dot_receiver || matches!(func_kind, FuncKind::Method(_, Some(_))) {
295286
None
296287
} else {
297288
func.self_param(ctx.db)

crates/ide-completion/src/render/literal.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use hir::{db::HirDatabase, Documentation, HasAttrs, StructKind};
44
use ide_db::SymbolKind;
5+
use syntax::AstNode;
56

67
use crate::{
78
context::{CompletionContext, PathCompletionCtx, PathKind},
@@ -117,7 +118,7 @@ fn render(
117118
..ctx.completion_relevance()
118119
});
119120
if let Some(ref_match) = compute_ref_match(completion, &ty) {
120-
item.ref_match(ref_match);
121+
item.ref_match(ref_match, path_ctx.path.syntax().text_range().start());
121122
}
122123

123124
if let Some(import_to_add) = ctx.import_to_add {

0 commit comments

Comments
 (0)