Skip to content

Commit 1e458ef

Browse files
Add braces to functions and macros
1 parent 6ab9724 commit 1e458ef

File tree

2 files changed

+99
-39
lines changed

2 files changed

+99
-39
lines changed

crates/completion/src/completions/complete_magic.rs

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
//! TODO kb move this into the complete_unqualified_path when starts to work properly
22
33
use assists::utils::{insert_use, mod_path_to_ast, ImportScope, MergeBehaviour};
4-
use hir::Query;
4+
use either::Either;
5+
use hir::{db::HirDatabase, MacroDef, ModuleDef, Query};
56
use itertools::Itertools;
67
use syntax::{algo, AstNode};
78
use text_edit::TextEdit;
@@ -28,14 +29,23 @@ pub(crate) fn complete_magic(acc: &mut Completions, ctx: &CompletionContext) ->
2829
// TODO kb use imports_locator instead?
2930
.query_external_importables(ctx.db, Query::new(&potential_import_name).limit(40))
3031
.unique()
31-
.filter_map(|import_candidate| match import_candidate {
32-
either::Either::Left(module_def) => current_module.find_use_path(ctx.db, module_def),
33-
either::Either::Right(macro_def) => current_module.find_use_path(ctx.db, macro_def),
32+
.filter_map(|import_candidate| {
33+
let use_path = match import_candidate {
34+
Either::Left(module_def) => current_module.find_use_path(ctx.db, module_def),
35+
Either::Right(macro_def) => current_module.find_use_path(ctx.db, macro_def),
36+
}?;
37+
// TODO kb need to omit braces when there are some already.
38+
// maybe remove braces completely?
39+
Some((use_path, additional_completion(ctx.db, import_candidate)))
3440
})
35-
.filter_map(|mod_path| {
41+
.filter_map(|(mod_path, additional_completion)| {
3642
let mut builder = TextEdit::builder();
3743

38-
let correct_qualifier = mod_path.segments.last()?.to_string();
44+
let correct_qualifier = format!(
45+
"{}{}",
46+
mod_path.segments.last()?,
47+
additional_completion.unwrap_or_default()
48+
);
3949
builder.replace(anchor.syntax().text_range(), correct_qualifier);
4050

4151
// TODO kb: assists already have the merge behaviour setting, need to unite both
@@ -60,6 +70,21 @@ pub(crate) fn complete_magic(acc: &mut Completions, ctx: &CompletionContext) ->
6070
Some(())
6171
}
6272

73+
fn additional_completion(
74+
db: &dyn HirDatabase,
75+
import_candidate: Either<ModuleDef, MacroDef>,
76+
) -> Option<String> {
77+
match import_candidate {
78+
Either::Left(ModuleDef::Function(_)) => Some("()".to_string()),
79+
Either::Right(macro_def) => {
80+
let (left_brace, right_brace) =
81+
crate::render::macro_::guess_macro_braces(db, macro_def);
82+
Some(format!("!{}{}", left_brace, right_brace))
83+
}
84+
_ => None,
85+
}
86+
}
87+
6388
#[cfg(test)]
6489
mod tests {
6590
use crate::test_utils::check_edit;
@@ -83,7 +108,34 @@ fn main() {
83108
use dep::io::stdin;
84109
85110
fn main() {
86-
stdin
111+
stdin()
112+
}
113+
"#,
114+
);
115+
}
116+
117+
#[test]
118+
fn macro_magic_completion() {
119+
check_edit(
120+
"dep::macro_with_curlies",
121+
r#"
122+
//- /lib.rs crate:dep
123+
/// Please call me as macro_with_curlies! {}
124+
#[macro_export]
125+
macro_rules! macro_with_curlies {
126+
() => {}
127+
}
128+
129+
//- /main.rs crate:main deps:dep
130+
fn main() {
131+
curli<|>
132+
}
133+
"#,
134+
r#"
135+
use dep::macro_with_curlies;
136+
137+
fn main() {
138+
macro_with_curlies! {}
87139
}
88140
"#,
89141
);

crates/completion/src/render/macro_.rs

Lines changed: 40 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Renderer for macro invocations.
22
3-
use hir::{Documentation, HasSource};
3+
use hir::{db::HirDatabase, Documentation, HasAttrs, HasSource};
44
use syntax::display::macro_label;
55
use test_utils::mark;
66

@@ -27,12 +27,48 @@ struct MacroRender<'a> {
2727
ket: &'static str,
2828
}
2929

30+
pub fn guess_macro_braces(
31+
db: &dyn HirDatabase,
32+
macro_: hir::MacroDef,
33+
) -> (&'static str, &'static str) {
34+
let macro_name = match macro_.name(db) {
35+
Some(name) => name.to_string(),
36+
None => return ("(", ")"),
37+
};
38+
let macro_docs = macro_.docs(db);
39+
let macro_docs = macro_docs.as_ref().map(Documentation::as_str).unwrap_or("");
40+
41+
let mut votes = [0, 0, 0];
42+
for (idx, s) in macro_docs.match_indices(&macro_name) {
43+
let (before, after) = (&macro_docs[..idx], &macro_docs[idx + s.len()..]);
44+
// Ensure to match the full word
45+
if after.starts_with('!')
46+
&& !before.ends_with(|c: char| c == '_' || c.is_ascii_alphanumeric())
47+
{
48+
// It may have spaces before the braces like `foo! {}`
49+
match after[1..].chars().find(|&c| !c.is_whitespace()) {
50+
Some('{') => votes[0] += 1,
51+
Some('[') => votes[1] += 1,
52+
Some('(') => votes[2] += 1,
53+
_ => {}
54+
}
55+
}
56+
}
57+
58+
// Insert a space before `{}`.
59+
// We prefer the last one when some votes equal.
60+
let (_vote, (bra, ket)) = votes
61+
.iter()
62+
.zip(&[(" {", "}"), ("[", "]"), ("(", ")")])
63+
.max_by_key(|&(&vote, _)| vote)
64+
.unwrap();
65+
(*bra, *ket)
66+
}
67+
3068
impl<'a> MacroRender<'a> {
3169
fn new(ctx: RenderContext<'a>, name: String, macro_: hir::MacroDef) -> MacroRender<'a> {
3270
let docs = ctx.docs(macro_);
33-
let docs_str = docs.as_ref().map_or("", |s| s.as_str());
34-
let (bra, ket) = guess_macro_braces(&name, docs_str);
35-
71+
let (bra, ket) = guess_macro_braces(ctx.db(), macro_);
3672
MacroRender { ctx, name, macro_, docs, bra, ket }
3773
}
3874

@@ -97,34 +133,6 @@ impl<'a> MacroRender<'a> {
97133
}
98134
}
99135

100-
fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static str) {
101-
let mut votes = [0, 0, 0];
102-
for (idx, s) in docs.match_indices(&macro_name) {
103-
let (before, after) = (&docs[..idx], &docs[idx + s.len()..]);
104-
// Ensure to match the full word
105-
if after.starts_with('!')
106-
&& !before.ends_with(|c: char| c == '_' || c.is_ascii_alphanumeric())
107-
{
108-
// It may have spaces before the braces like `foo! {}`
109-
match after[1..].chars().find(|&c| !c.is_whitespace()) {
110-
Some('{') => votes[0] += 1,
111-
Some('[') => votes[1] += 1,
112-
Some('(') => votes[2] += 1,
113-
_ => {}
114-
}
115-
}
116-
}
117-
118-
// Insert a space before `{}`.
119-
// We prefer the last one when some votes equal.
120-
let (_vote, (bra, ket)) = votes
121-
.iter()
122-
.zip(&[(" {", "}"), ("[", "]"), ("(", ")")])
123-
.max_by_key(|&(&vote, _)| vote)
124-
.unwrap();
125-
(*bra, *ket)
126-
}
127-
128136
#[cfg(test)]
129137
mod tests {
130138
use test_utils::mark;

0 commit comments

Comments
 (0)