From 1e458efe628215dfc07943f8dd39f66ac059d3de Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Tue, 10 Nov 2020 12:00:42 +0200 Subject: Add braces to functions and macros --- .../completion/src/completions/complete_magic.rs | 66 +++++++++++++++++--- crates/completion/src/render/macro_.rs | 72 ++++++++++++---------- 2 files changed, 99 insertions(+), 39 deletions(-) (limited to 'crates/completion') diff --git a/crates/completion/src/completions/complete_magic.rs b/crates/completion/src/completions/complete_magic.rs index 15af2190d..4cf21e19d 100644 --- a/crates/completion/src/completions/complete_magic.rs +++ b/crates/completion/src/completions/complete_magic.rs @@ -1,7 +1,8 @@ //! TODO kb move this into the complete_unqualified_path when starts to work properly use assists::utils::{insert_use, mod_path_to_ast, ImportScope, MergeBehaviour}; -use hir::Query; +use either::Either; +use hir::{db::HirDatabase, MacroDef, ModuleDef, Query}; use itertools::Itertools; use syntax::{algo, AstNode}; use text_edit::TextEdit; @@ -28,14 +29,23 @@ pub(crate) fn complete_magic(acc: &mut Completions, ctx: &CompletionContext) -> // TODO kb use imports_locator instead? .query_external_importables(ctx.db, Query::new(&potential_import_name).limit(40)) .unique() - .filter_map(|import_candidate| match import_candidate { - either::Either::Left(module_def) => current_module.find_use_path(ctx.db, module_def), - either::Either::Right(macro_def) => current_module.find_use_path(ctx.db, macro_def), + .filter_map(|import_candidate| { + let use_path = match import_candidate { + Either::Left(module_def) => current_module.find_use_path(ctx.db, module_def), + Either::Right(macro_def) => current_module.find_use_path(ctx.db, macro_def), + }?; + // TODO kb need to omit braces when there are some already. + // maybe remove braces completely? + Some((use_path, additional_completion(ctx.db, import_candidate))) }) - .filter_map(|mod_path| { + .filter_map(|(mod_path, additional_completion)| { let mut builder = TextEdit::builder(); - let correct_qualifier = mod_path.segments.last()?.to_string(); + let correct_qualifier = format!( + "{}{}", + mod_path.segments.last()?, + additional_completion.unwrap_or_default() + ); builder.replace(anchor.syntax().text_range(), correct_qualifier); // 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) -> Some(()) } +fn additional_completion( + db: &dyn HirDatabase, + import_candidate: Either, +) -> Option { + match import_candidate { + Either::Left(ModuleDef::Function(_)) => Some("()".to_string()), + Either::Right(macro_def) => { + let (left_brace, right_brace) = + crate::render::macro_::guess_macro_braces(db, macro_def); + Some(format!("!{}{}", left_brace, right_brace)) + } + _ => None, + } +} + #[cfg(test)] mod tests { use crate::test_utils::check_edit; @@ -83,7 +108,34 @@ fn main() { use dep::io::stdin; fn main() { - stdin + stdin() +} +"#, + ); + } + + #[test] + fn macro_magic_completion() { + check_edit( + "dep::macro_with_curlies", + r#" +//- /lib.rs crate:dep +/// Please call me as macro_with_curlies! {} +#[macro_export] +macro_rules! macro_with_curlies { + () => {} +} + +//- /main.rs crate:main deps:dep +fn main() { + curli<|> +} +"#, + r#" +use dep::macro_with_curlies; + +fn main() { + macro_with_curlies! {} } "#, ); diff --git a/crates/completion/src/render/macro_.rs b/crates/completion/src/render/macro_.rs index 96be59cc3..b41c00b98 100644 --- a/crates/completion/src/render/macro_.rs +++ b/crates/completion/src/render/macro_.rs @@ -1,6 +1,6 @@ //! Renderer for macro invocations. -use hir::{Documentation, HasSource}; +use hir::{db::HirDatabase, Documentation, HasAttrs, HasSource}; use syntax::display::macro_label; use test_utils::mark; @@ -27,12 +27,48 @@ struct MacroRender<'a> { ket: &'static str, } +pub fn guess_macro_braces( + db: &dyn HirDatabase, + macro_: hir::MacroDef, +) -> (&'static str, &'static str) { + let macro_name = match macro_.name(db) { + Some(name) => name.to_string(), + None => return ("(", ")"), + }; + let macro_docs = macro_.docs(db); + let macro_docs = macro_docs.as_ref().map(Documentation::as_str).unwrap_or(""); + + let mut votes = [0, 0, 0]; + for (idx, s) in macro_docs.match_indices(¯o_name) { + let (before, after) = (¯o_docs[..idx], ¯o_docs[idx + s.len()..]); + // Ensure to match the full word + if after.starts_with('!') + && !before.ends_with(|c: char| c == '_' || c.is_ascii_alphanumeric()) + { + // It may have spaces before the braces like `foo! {}` + match after[1..].chars().find(|&c| !c.is_whitespace()) { + Some('{') => votes[0] += 1, + Some('[') => votes[1] += 1, + Some('(') => votes[2] += 1, + _ => {} + } + } + } + + // Insert a space before `{}`. + // We prefer the last one when some votes equal. + let (_vote, (bra, ket)) = votes + .iter() + .zip(&[(" {", "}"), ("[", "]"), ("(", ")")]) + .max_by_key(|&(&vote, _)| vote) + .unwrap(); + (*bra, *ket) +} + impl<'a> MacroRender<'a> { fn new(ctx: RenderContext<'a>, name: String, macro_: hir::MacroDef) -> MacroRender<'a> { let docs = ctx.docs(macro_); - let docs_str = docs.as_ref().map_or("", |s| s.as_str()); - let (bra, ket) = guess_macro_braces(&name, docs_str); - + let (bra, ket) = guess_macro_braces(ctx.db(), macro_); MacroRender { ctx, name, macro_, docs, bra, ket } } @@ -97,34 +133,6 @@ impl<'a> MacroRender<'a> { } } -fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static str) { - let mut votes = [0, 0, 0]; - for (idx, s) in docs.match_indices(¯o_name) { - let (before, after) = (&docs[..idx], &docs[idx + s.len()..]); - // Ensure to match the full word - if after.starts_with('!') - && !before.ends_with(|c: char| c == '_' || c.is_ascii_alphanumeric()) - { - // It may have spaces before the braces like `foo! {}` - match after[1..].chars().find(|&c| !c.is_whitespace()) { - Some('{') => votes[0] += 1, - Some('[') => votes[1] += 1, - Some('(') => votes[2] += 1, - _ => {} - } - } - } - - // Insert a space before `{}`. - // We prefer the last one when some votes equal. - let (_vote, (bra, ket)) = votes - .iter() - .zip(&[(" {", "}"), ("[", "]"), ("(", ")")]) - .max_by_key(|&(&vote, _)| vote) - .unwrap(); - (*bra, *ket) -} - #[cfg(test)] mod tests { use test_utils::mark; -- cgit v1.2.3