Skip to content

Commit 6f84bbf

Browse files
bors[bot]Veykril
andauthored
Merge #10943
10943: feat: Enable completions for attributes r=Veykril a=Veykril Co-authored-by: Lukas Wirth <[email protected]>
2 parents b519a17 + 1f254dd commit 6f84bbf

File tree

8 files changed

+217
-34
lines changed

8 files changed

+217
-34
lines changed

crates/hir/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1716,6 +1716,10 @@ impl MacroDef {
17161716
MacroKind::Attr | MacroKind::Derive => false,
17171717
}
17181718
}
1719+
1720+
pub fn is_attr(&self) -> bool {
1721+
matches!(self.kind(), MacroKind::Attr)
1722+
}
17191723
}
17201724

17211725
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]

crates/ide_completion/src/completions/flyimport.rs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
//! See [`import_on_the_fly`].
2+
use hir::ItemInNs;
23
use ide_db::helpers::{
3-
import_assets::{ImportAssets, ImportCandidate},
4+
import_assets::{ImportAssets, ImportCandidate, LocatedImport},
45
insert_use::ImportScope,
56
};
67
use itertools::Itertools;
78
use syntax::{AstNode, SyntaxNode, T};
89

910
use crate::{
10-
context::CompletionContext,
11+
context::{CompletionContext, PathKind},
1112
render::{render_resolution_with_import, RenderContext},
1213
ImportEdit,
1314
};
@@ -135,10 +136,35 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
135136
&ctx.sema,
136137
)?;
137138

139+
let ns_filter = |import: &LocatedImport| {
140+
let kind = match ctx.path_kind() {
141+
Some(kind) => kind,
142+
None => {
143+
return match import.original_item {
144+
ItemInNs::Macros(mac) => mac.is_fn_like(),
145+
_ => true,
146+
}
147+
}
148+
};
149+
match (kind, import.original_item) {
150+
(PathKind::Expr, ItemInNs::Types(_) | ItemInNs::Values(_)) => true,
151+
152+
(PathKind::Type, ItemInNs::Types(_)) => true,
153+
(PathKind::Type, ItemInNs::Values(_)) => false,
154+
155+
(PathKind::Expr | PathKind::Type, ItemInNs::Macros(mac)) => mac.is_fn_like(),
156+
157+
(PathKind::Attr, ItemInNs::Types(hir::ModuleDef::Module(_))) => true,
158+
(PathKind::Attr, ItemInNs::Macros(mac)) => mac.is_attr(),
159+
(PathKind::Attr, _) => false,
160+
}
161+
};
162+
138163
acc.add_all(
139164
import_assets
140165
.search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind)
141166
.into_iter()
167+
.filter(ns_filter)
142168
.filter(|import| {
143169
!ctx.is_item_hidden(&import.item_to_import)
144170
&& !ctx.is_item_hidden(&import.original_item)

crates/ide_completion/src/completions/qualified_path.rs

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
use std::iter;
44

5+
use hir::ScopeDef;
56
use rustc_hash::FxHashSet;
67
use syntax::{ast, AstNode};
78

@@ -31,35 +32,50 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
3132
Some(ImmediateLocation::ItemList | ImmediateLocation::Trait | ImmediateLocation::Impl) => {
3233
if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
3334
for (name, def) in module.scope(ctx.db, context_module) {
34-
if let hir::ScopeDef::MacroDef(macro_def) = def {
35+
if let ScopeDef::MacroDef(macro_def) = def {
3536
if macro_def.is_fn_like() {
3637
acc.add_macro(ctx, Some(name.clone()), macro_def);
3738
}
3839
}
39-
if let hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def {
40+
if let ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) = def {
4041
acc.add_resolution(ctx, name, &def);
4142
}
4243
}
4344
}
4445
return;
4546
}
4647
Some(ImmediateLocation::Visibility(_)) => {
47-
if let hir::PathResolution::Def(hir::ModuleDef::Module(resolved)) = resolution {
48+
if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
4849
if let Some(current_module) = ctx.scope.module() {
4950
if let Some(next) = current_module
5051
.path_to_root(ctx.db)
5152
.into_iter()
52-
.take_while(|&it| it != resolved)
53+
.take_while(|&it| it != module)
5354
.next()
5455
{
5556
if let Some(name) = next.name(ctx.db) {
56-
acc.add_resolution(ctx, name, &hir::ScopeDef::ModuleDef(next.into()));
57+
acc.add_resolution(ctx, name, &ScopeDef::ModuleDef(next.into()));
5758
}
5859
}
5960
}
6061
}
6162
return;
6263
}
64+
Some(ImmediateLocation::Attribute(_)) => {
65+
if let hir::PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
66+
for (name, def) in module.scope(ctx.db, context_module) {
67+
let add_resolution = match def {
68+
ScopeDef::MacroDef(mac) => mac.is_attr(),
69+
ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) => true,
70+
_ => false,
71+
};
72+
if add_resolution {
73+
acc.add_resolution(ctx, name, &def);
74+
}
75+
}
76+
}
77+
return;
78+
}
6379
_ => (),
6480
}
6581

@@ -91,7 +107,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
91107
let module_scope = module.scope(ctx.db, context_module);
92108
for (name, def) in module_scope {
93109
if ctx.in_use_tree() {
94-
if let hir::ScopeDef::Unknown = def {
110+
if let ScopeDef::Unknown = def {
95111
if let Some(ast::NameLike::NameRef(name_ref)) = ctx.name_syntax.as_ref() {
96112
if name_ref.syntax().text() == name.to_smol_str().as_str() {
97113
// for `use self::foo$0`, don't suggest `foo` as a completion
@@ -104,16 +120,16 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
104120

105121
let add_resolution = match def {
106122
// Don't suggest attribute macros and derives.
107-
hir::ScopeDef::MacroDef(mac) => mac.is_fn_like(),
123+
ScopeDef::MacroDef(mac) => mac.is_fn_like(),
108124
// no values in type places
109-
hir::ScopeDef::ModuleDef(
125+
ScopeDef::ModuleDef(
110126
hir::ModuleDef::Function(_)
111127
| hir::ModuleDef::Variant(_)
112128
| hir::ModuleDef::Static(_),
113129
)
114-
| hir::ScopeDef::Local(_) => !ctx.expects_type(),
130+
| ScopeDef::Local(_) => !ctx.expects_type(),
115131
// unless its a constant in a generic arg list position
116-
hir::ScopeDef::ModuleDef(hir::ModuleDef::Const(_)) => {
132+
ScopeDef::ModuleDef(hir::ModuleDef::Const(_)) => {
117133
!ctx.expects_type() || ctx.expects_generic_arg()
118134
}
119135
_ => true,

crates/ide_completion/src/completions/unqualified_path.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,19 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
5656
});
5757
return;
5858
}
59+
Some(ImmediateLocation::Attribute(_)) => {
60+
ctx.process_all_names(&mut |name, res| {
61+
let add_resolution = match res {
62+
ScopeDef::MacroDef(mac) => mac.is_attr(),
63+
ScopeDef::ModuleDef(hir::ModuleDef::Module(_)) => true,
64+
_ => false,
65+
};
66+
if add_resolution {
67+
acc.add_resolution(ctx, name, &res);
68+
}
69+
});
70+
return;
71+
}
5972
_ => (),
6073
}
6174

crates/ide_completion/src/context.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,11 @@ pub(crate) enum PatternRefutability {
3030
Irrefutable,
3131
}
3232

33-
#[derive(Debug)]
33+
#[derive(Copy, Clone, Debug)]
3434
pub(super) enum PathKind {
3535
Expr,
3636
Type,
37+
Attr,
3738
}
3839

3940
#[derive(Debug)]
@@ -232,17 +233,15 @@ impl<'a> CompletionContext<'a> {
232233
}
233234

234235
pub(crate) fn is_path_disallowed(&self) -> bool {
235-
self.attribute_under_caret.is_some()
236-
|| self.previous_token_is(T![unsafe])
236+
self.previous_token_is(T![unsafe])
237237
|| matches!(
238238
self.prev_sibling,
239239
Some(ImmediatePrevSibling::Attribute | ImmediatePrevSibling::Visibility)
240240
)
241241
|| matches!(
242242
self.completion_location,
243243
Some(
244-
ImmediateLocation::Attribute(_)
245-
| ImmediateLocation::ModDeclaration(_)
244+
ImmediateLocation::ModDeclaration(_)
246245
| ImmediateLocation::RecordPat(_)
247246
| ImmediateLocation::RecordExpr(_)
248247
| ImmediateLocation::Rename
@@ -274,6 +273,10 @@ impl<'a> CompletionContext<'a> {
274273
self.path_context.as_ref().and_then(|it| it.qualifier.as_ref())
275274
}
276275

276+
pub(crate) fn path_kind(&self) -> Option<PathKind> {
277+
self.path_context.as_ref().and_then(|it| it.kind)
278+
}
279+
277280
/// Checks if an item is visible and not `doc(hidden)` at the completion site.
278281
pub(crate) fn is_visible<I>(&self, item: &I) -> bool
279282
where
@@ -785,6 +788,7 @@ impl<'a> CompletionContext<'a> {
785788
match parent {
786789
ast::PathType(_it) => Some(PathKind::Type),
787790
ast::PathExpr(_it) => Some(PathKind::Expr),
791+
ast::Meta(_it) => Some(PathKind::Attr),
788792
_ => None,
789793
}
790794
};

crates/ide_completion/src/render/macro_.rs

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
//! Renderer for macro invocations.
22
3+
use either::Either;
34
use hir::HasSource;
45
use ide_db::SymbolKind;
5-
use syntax::{display::macro_label, SmolStr};
6+
use syntax::{
7+
display::{fn_as_proc_macro_label, macro_label},
8+
SmolStr,
9+
};
610

711
use crate::{
812
context::CallKind,
@@ -35,7 +39,8 @@ impl<'a> MacroRender<'a> {
3539
let name = name.to_smol_str();
3640
let docs = ctx.docs(macro_);
3741
let docs_str = docs.as_ref().map_or("", |s| s.as_str());
38-
let (bra, ket) = guess_macro_braces(&name, docs_str);
42+
let (bra, ket) =
43+
if macro_.is_fn_like() { guess_macro_braces(&name, docs_str) } else { ("", "") };
3944

4045
MacroRender { ctx, name, macro_, docs, bra, ket }
4146
}
@@ -47,15 +52,23 @@ impl<'a> MacroRender<'a> {
4752
} else {
4853
Some(self.ctx.source_range())
4954
}?;
50-
let mut item = CompletionItem::new(SymbolKind::Macro, source_range, self.label());
55+
let kind = match self.macro_.kind() {
56+
hir::MacroKind::Derive => SymbolKind::Derive,
57+
hir::MacroKind::Attr => SymbolKind::Attribute,
58+
hir::MacroKind::BuiltIn | hir::MacroKind::Declarative | hir::MacroKind::ProcMacro => {
59+
SymbolKind::Macro
60+
}
61+
};
62+
let mut item = CompletionItem::new(kind, source_range, self.label());
5163
item.set_deprecated(self.ctx.is_deprecated(self.macro_)).set_detail(self.detail());
5264

5365
if let Some(import_to_add) = import_to_add {
5466
item.add_import(import_to_add);
5567
}
5668

57-
let needs_bang = !(self.ctx.completion.in_use_tree()
58-
|| matches!(self.ctx.completion.path_call_kind(), Some(CallKind::Mac)));
69+
let needs_bang = self.macro_.is_fn_like()
70+
&& !(self.ctx.completion.in_use_tree()
71+
|| matches!(self.ctx.completion.path_call_kind(), Some(CallKind::Mac)));
5972
let has_parens = self.ctx.completion.path_call_kind().is_some();
6073

6174
match self.ctx.snippet_cap() {
@@ -84,10 +97,10 @@ impl<'a> MacroRender<'a> {
8497
}
8598

8699
fn label(&self) -> SmolStr {
87-
if self.needs_bang() && self.ctx.snippet_cap().is_some() {
88-
SmolStr::from_iter([&*self.name, "!", self.bra, "…", self.ket])
89-
} else if self.macro_.kind() == hir::MacroKind::Derive {
100+
if !self.macro_.is_fn_like() {
90101
self.name.clone()
102+
} else if self.needs_bang() && self.ctx.snippet_cap().is_some() {
103+
SmolStr::from_iter([&*self.name, "!", self.bra, "…", self.ket])
91104
} else {
92105
self.banged_name()
93106
}
@@ -98,8 +111,11 @@ impl<'a> MacroRender<'a> {
98111
}
99112

100113
fn detail(&self) -> Option<String> {
101-
let ast_node = self.macro_.source(self.ctx.db())?.value.left()?;
102-
Some(macro_label(&ast_node))
114+
let detail = match self.macro_.source(self.ctx.db())?.value {
115+
Either::Left(node) => macro_label(&node),
116+
Either::Right(node) => fn_as_proc_macro_label(&node),
117+
};
118+
Some(detail)
103119
}
104120
}
105121

0 commit comments

Comments
 (0)