]> git.lizzy.rs Git - rust.git/commitdiff
Add braces to functions and macros
authorKirill Bulatov <mail4score@gmail.com>
Tue, 10 Nov 2020 10:00:42 +0000 (12:00 +0200)
committerKirill Bulatov <mail4score@gmail.com>
Mon, 16 Nov 2020 19:19:06 +0000 (21:19 +0200)
crates/completion/src/completions/complete_magic.rs
crates/completion/src/render/macro_.rs

index 15af2190dc3ee0d6bebb1669b922d87c6b0cb382..4cf21e19df0fff7dd1f01ebf80258b4cd58ff578 100644 (file)
@@ -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<ModuleDef, MacroDef>,
+) -> Option<String> {
+    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! {}
 }
 "#,
         );
index 96be59cc336aeda40e1b3865b6ce8a0c60c691a9..b41c00b987d6c3cb101176cef2c9f4a189c54d11 100644 (file)
@@ -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(&macro_name) {
+        let (before, after) = (&macro_docs[..idx], &macro_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 @@ fn detail(&self) -> String {
     }
 }
 
-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(&macro_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;