Skip to content

Commit 55a083c

Browse files
Add #[rust_analyzer::macro_style()] attribute to control macro completion brace style
1 parent 2b4263b commit 55a083c

4 files changed

Lines changed: 138 additions & 13 deletions

File tree

crates/hir-def/src/attrs.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,20 @@ fn extract_ra_completions(attr_flags: &mut AttrFlags, tt: ast::TokenTree) {
9999
}
100100
}
101101

102+
fn extract_ra_macro_style(attr_flags: &mut AttrFlags, tt: ast::TokenTree) {
103+
let tt = TokenTreeChildren::new(&tt);
104+
if let Ok(NodeOrToken::Token(option)) = Itertools::exactly_one(tt)
105+
&& option.kind().is_any_identifier()
106+
{
107+
match option.text() {
108+
"braces" => attr_flags.insert(AttrFlags::MACRO_STYLE_BRACES),
109+
"brackets" => attr_flags.insert(AttrFlags::MACRO_STYLE_BRACKETS),
110+
"parentheses" => attr_flags.insert(AttrFlags::MACRO_STYLE_PARENTHESES),
111+
_ => {}
112+
}
113+
}
114+
}
115+
102116
fn extract_rustc_skip_during_method_dispatch(attr_flags: &mut AttrFlags, tt: ast::TokenTree) {
103117
let iter = TokenTreeChildren::new(&tt);
104118
for kind in iter {
@@ -163,6 +177,7 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: Meta) -> ControlFlow<Infal
163177
2 => match path.segments[0].text() {
164178
"rust_analyzer" => match path.segments[1].text() {
165179
"completions" => extract_ra_completions(attr_flags, tt),
180+
"macro_style" => extract_ra_macro_style(attr_flags, tt),
166181
_ => {}
167182
},
168183
_ => {}
@@ -291,6 +306,10 @@ bitflags::bitflags! {
291306
const RUSTC_COINDUCTIVE = 1 << 43;
292307
const RUSTC_FORCE_INLINE = 1 << 44;
293308
const IS_POINTEE = 1 << 45;
309+
310+
const MACRO_STYLE_BRACES = 1 << 46;
311+
const MACRO_STYLE_BRACKETS = 1 << 47;
312+
const MACRO_STYLE_PARENTHESES = 1 << 48;
294313
}
295314
}
296315

crates/hir/src/lib.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3429,6 +3429,50 @@ impl Macro {
34293429
pub fn is_derive(&self, db: &dyn HirDatabase) -> bool {
34303430
matches!(self.kind(db), MacroKind::Derive | MacroKind::DeriveBuiltIn)
34313431
}
3432+
3433+
pub fn preferred_brace_style(&self, db: &dyn HirDatabase) -> Option<MacroBraces> {
3434+
let attrs = self.attrs(db);
3435+
MacroBraces::extract(attrs.attrs)
3436+
}
3437+
}
3438+
3439+
// Feature: Macro Brace Style Attribute
3440+
// Crate authors can declare the preferred brace style for their macro. This will affect how completion
3441+
// insert calls to it.
3442+
//
3443+
// This is only supported on function-like macros.
3444+
//
3445+
// To do that, insert the `#[rust_analyzer::macro_style(style)]` attribute on the macro (for proc macros,
3446+
// insert it for the macro's function). `style` can be one of:
3447+
//
3448+
// - `braces` for `{...}` style.
3449+
// - `brackets` for `[...]` style.
3450+
// - `parentheses` for `(...)` style.
3451+
//
3452+
// Malformed attributes will be ignored without warnings.
3453+
//
3454+
// Note that users have no way to override this attribute, so be careful and only include things
3455+
// users definitely do not want to be completed!
3456+
3457+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
3458+
pub enum MacroBraces {
3459+
Braces,
3460+
Brackets,
3461+
Parentheses,
3462+
}
3463+
3464+
impl MacroBraces {
3465+
fn extract(attrs: AttrFlags) -> Option<Self> {
3466+
if attrs.contains(AttrFlags::MACRO_STYLE_BRACES) {
3467+
Some(Self::Braces)
3468+
} else if attrs.contains(AttrFlags::MACRO_STYLE_BRACKETS) {
3469+
Some(Self::Brackets)
3470+
} else if attrs.contains(AttrFlags::MACRO_STYLE_PARENTHESES) {
3471+
Some(Self::Parentheses)
3472+
} else {
3473+
None
3474+
}
3475+
}
34323476
}
34333477

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

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

Lines changed: 74 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Renderer for macro invocations.
22
3-
use hir::HirDisplay;
3+
use hir::{HirDisplay, db::HirDatabase};
44
use ide_db::{SymbolKind, documentation::Documentation};
55
use syntax::{SmolStr, ToSmolStr, format_smolstr};
66

@@ -46,17 +46,15 @@ fn render(
4646
ctx.source_range()
4747
};
4848

49-
let orig_name = macro_.name(ctx.db());
50-
let (name, orig_name, escaped_name) = (
51-
name.as_str(),
52-
orig_name.as_str(),
53-
name.display(ctx.db(), completion.edition).to_smolstr(),
54-
);
49+
let (name, escaped_name) =
50+
(name.as_str(), name.display(ctx.db(), completion.edition).to_smolstr());
5551
let docs = ctx.docs(macro_);
56-
let docs_str = docs.as_ref().map(Documentation::as_str).unwrap_or_default();
5752
let is_fn_like = macro_.is_fn_like(completion.db);
58-
let (bra, ket) =
59-
if is_fn_like { guess_macro_braces(name, orig_name, docs_str) } else { ("", "") };
53+
let (bra, ket) = if is_fn_like {
54+
guess_macro_braces(ctx.db(), macro_, name, docs.as_ref())
55+
} else {
56+
("", "")
57+
};
6058

6159
let needs_bang = is_fn_like && !is_use_path && !has_macro_bang;
6260

@@ -115,12 +113,24 @@ fn banged_name(name: &str) -> SmolStr {
115113
}
116114

117115
fn guess_macro_braces(
116+
db: &dyn HirDatabase,
117+
macro_: hir::Macro,
118118
macro_name: &str,
119-
orig_name: &str,
120-
docs: &str,
119+
docs: Option<&Documentation<'_>>,
121120
) -> (&'static str, &'static str) {
121+
if let Some(style) = macro_.preferred_brace_style(db) {
122+
return match style {
123+
hir::MacroBraces::Braces => (" {", "}"),
124+
hir::MacroBraces::Brackets => ("[", "]"),
125+
hir::MacroBraces::Parentheses => ("(", ")"),
126+
};
127+
}
128+
129+
let orig_name = macro_.name(db);
130+
let docs = docs.map(Documentation::as_str).unwrap_or_default();
131+
122132
let mut votes = [0, 0, 0];
123-
for (idx, s) in docs.match_indices(macro_name).chain(docs.match_indices(orig_name)) {
133+
for (idx, s) in docs.match_indices(macro_name).chain(docs.match_indices(orig_name.as_str())) {
124134
let (before, after) = (&docs[..idx], &docs[idx + s.len()..]);
125135
// Ensure to match the full word
126136
if after.starts_with('!')
@@ -199,6 +209,57 @@ fn main() {
199209
);
200210
}
201211

212+
#[test]
213+
fn preferred_macro_braces() {
214+
check_edit(
215+
"vec!",
216+
r#"
217+
#[rust_analyzer::macro_style(brackets)]
218+
macro_rules! vec { () => {} }
219+
220+
fn main() { v$0 }
221+
"#,
222+
r#"
223+
#[rust_analyzer::macro_style(brackets)]
224+
macro_rules! vec { () => {} }
225+
226+
fn main() { vec![$0] }
227+
"#,
228+
);
229+
230+
check_edit(
231+
"foo!",
232+
r#"
233+
#[rust_analyzer::macro_style(braces)]
234+
macro_rules! foo { () => {} }
235+
fn main() { $0 }
236+
"#,
237+
r#"
238+
#[rust_analyzer::macro_style(braces)]
239+
macro_rules! foo { () => {} }
240+
fn main() { foo! {$0} }
241+
"#,
242+
);
243+
244+
check_edit(
245+
"bar!",
246+
r#"
247+
#[macro_export]
248+
#[rust_analyzer::macro_style(brackets)]
249+
macro_rules! foo { () => {} }
250+
pub use crate::foo as bar;
251+
fn main() { $0 }
252+
"#,
253+
r#"
254+
#[macro_export]
255+
#[rust_analyzer::macro_style(brackets)]
256+
macro_rules! foo { () => {} }
257+
pub use crate::foo as bar;
258+
fn main() { bar![$0] }
259+
"#,
260+
);
261+
}
262+
202263
#[test]
203264
fn guesses_macro_braces() {
204265
check_edit(

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ fn macro_fuzzy_completion() {
7979
r#"
8080
//- /lib.rs crate:dep
8181
/// Please call me as macro_with_curlies! {}
82+
#[rust_analyzer::macro_style(braces)]
8283
#[macro_export]
8384
macro_rules! macro_with_curlies {
8485
() => {}

0 commit comments

Comments
 (0)