Repository: rust-analyzer/rust-analyzer
Pull request #10527 (2021-10-12 10:16:03)

internal: Remove a few snippet completions, replace them with user snippets definitions in VSCode
Closes https://github.com/rust-analyzer/rust-analyzer/issues/9636
cc https://github.com/rust-analyzer/rust-analyzer/issues/7033#issuecomment-939959905

Veykril: bors r-
Veykril: No it wouldn't, any other client would have to ship this default setting as well.
Veykril: I am going to go ahead merging this now in spirit of the issue it closes, other clients will have to follow up with the same default set of snippets if they wish to retain the current ones.
bors r+
bors[bot]: Build failed:
  * [Rust (ubuntu-latest)](https://github.com/rust-analyzer/rust-analyzer/runs/4562082105?check_suite_focus=true)
Veykril: or not

Patch:
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 5e260880ff..68d77084a3 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -216,7 +216,7 @@ impl ItemTree {
         self.attrs.get(&of).unwrap_or(&RawAttrs::EMPTY)
     }
 
-    pub(crate) fn attrs(&self, db: &dyn DefDatabase, krate: CrateId, of: AttrOwner) -> Attrs {
+    pub fn attrs(&self, db: &dyn DefDatabase, krate: CrateId, of: AttrOwner) -> Attrs {
         self.raw_attrs(of).clone().filter(db, krate)
     }
 
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 9d19d7c1c0..7d5cfaa937 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -94,11 +94,10 @@ pub(crate) fn hover(
     let sema = &hir::Semantics::new(db);
     let file = sema.parse(file_id).syntax().clone();
 
-    let offset = if !range.is_empty() {
+    if !range.is_empty() {
         return hover_ranged(&file, range, sema, config);
-    } else {
-        range.start()
-    };
+    }
+    let offset = range.start();
 
     let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
         IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] => 3,
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs
index d642c8bc4d..d92e311915 100644
--- a/crates/ide_completion/src/completions/attribute.rs
+++ b/crates/ide_completion/src/completions/attribute.rs
@@ -2,8 +2,8 @@
 //!
 //! This module uses a bit of static metadata to provide completions
 //! for built-in attributes.
-//! Non-builtin attribute(excluding derives attributes) completions are done in [`super::unqualified_path`].
 
+use hir::HasAttrs;
 use ide_db::{
     helpers::{
         generated_lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES, RUSTDOC_LINTS},
@@ -93,6 +93,23 @@ fn complete_new_attribute(acc: &mut Completions, ctx: &CompletionContext, attrib
         None if is_inner => ATTRIBUTES.iter().for_each(add_completion),
         None => ATTRIBUTES.iter().filter(|compl| !compl.prefer_inner).for_each(add_completion),
     }
+
+    // FIXME: write a test for this when we can
+    ctx.scope.process_all_names(&mut |name, scope_def| {
+        if let hir::ScopeDef::MacroDef(mac) = scope_def {
+            if mac.kind() == hir::MacroKind::Attr {
+                let mut item = CompletionItem::new(
+                    SymbolKind::Attribute,
+                    ctx.source_range(),
+                    name.to_smol_str(),
+                );
+                if let Some(docs) = mac.docs(ctx.sema.db) {
+                    item.documentation(docs);
+                }
+                item.add_to(acc);
+            }
+        }
+    });
 }
 
 struct AttrCompletion {
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs
index 1c686dc0a6..90e06221db 100644
--- a/crates/ide_completion/src/completions/keyword.rs
+++ b/crates/ide_completion/src/completions/keyword.rs
@@ -229,9 +229,6 @@ fn foo(a: A) { a.$0 }
                 sn refm  &mut expr
                 sn match match expr {}
                 sn box   Box::new(expr)
-                sn ok    Ok(expr)
-                sn err   Err(expr)
-                sn some  Some(expr)
                 sn dbg   dbg!(expr)
                 sn dbgr  dbg!(&expr)
                 sn call  function(expr)
@@ -255,9 +252,6 @@ fn foo() {
                 sn refm  &mut expr
                 sn match match expr {}
                 sn box   Box::new(expr)
-                sn ok    Ok(expr)
-                sn err   Err(expr)
-                sn some  Some(expr)
                 sn dbg   dbg!(expr)
                 sn dbgr  dbg!(&expr)
                 sn call  function(expr)
diff --git a/crates/ide_completion/src/completions/postfix.rs b/crates/ide_completion/src/completions/postfix.rs
index c239401e48..8a25da7ddb 100644
--- a/crates/ide_completion/src/completions/postfix.rs
+++ b/crates/ide_completion/src/completions/postfix.rs
@@ -163,9 +163,6 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
     }
 
     postfix_snippet("box", "Box::new(expr)", &format!("Box::new({})", receiver_text)).add_to(acc);
-    postfix_snippet("ok", "Ok(expr)", &format!("Ok({})", receiver_text)).add_to(acc);
-    postfix_snippet("err", "Err(expr)", &format!("Err({})", receiver_text)).add_to(acc);
-    postfix_snippet("some", "Some(expr)", &format!("Some({})", receiver_text)).add_to(acc);
     postfix_snippet("dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text)).add_to(acc);
     postfix_snippet("dbgr", "dbg!(&expr)", &format!("dbg!(&{})", receiver_text)).add_to(acc);
     postfix_snippet("call", "function(expr)", &format!("${{1}}({})", receiver_text)).add_to(acc);
@@ -295,9 +292,6 @@ fn main() {
                 sn refm  &mut expr
                 sn match match expr {}
                 sn box   Box::new(expr)
-                sn ok    Ok(expr)
-                sn err   Err(expr)
-                sn some  Some(expr)
                 sn dbg   dbg!(expr)
                 sn dbgr  dbg!(&expr)
                 sn call  function(expr)
@@ -328,9 +322,6 @@ fn main() {
                 sn refm  &mut expr
                 sn match match expr {}
                 sn box   Box::new(expr)
-                sn ok    Ok(expr)
-                sn err   Err(expr)
-                sn some  Some(expr)
                 sn dbg   dbg!(expr)
                 sn dbgr  dbg!(&expr)
                 sn call  function(expr)
@@ -352,9 +343,6 @@ fn main() {
                 sn refm  &mut expr
                 sn match match expr {}
                 sn box   Box::new(expr)
-                sn ok    Ok(expr)
-                sn err   Err(expr)
-                sn some  Some(expr)
                 sn dbg   dbg!(expr)
                 sn dbgr  dbg!(&expr)
                 sn call  function(expr)
@@ -381,9 +369,6 @@ fn main() {
                 sn refm  &mut expr
                 sn match match expr {}
                 sn box   Box::new(expr)
-                sn ok    Ok(expr)
-                sn err   Err(expr)
-                sn some  Some(expr)
                 sn dbg   dbg!(expr)
                 sn dbgr  dbg!(&expr)
                 sn call  function(expr)
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index d4f35b5dcd..4b4fbb3ed9 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -1437,9 +1437,6 @@ fn main() {
                 sn refm []
                 sn match []
                 sn box []
-                sn ok []
-                sn err []
-                sn some []
                 sn dbg []
                 sn dbgr []
                 sn call []
diff --git a/crates/ide_completion/src/snippet.rs b/crates/ide_completion/src/snippet.rs
index 1a3f42b839..c5e2b009c7 100644
--- a/crates/ide_completion/src/snippet.rs
+++ b/crates/ide_completion/src/snippet.rs
@@ -52,8 +52,56 @@ use std::ops::Deref;
 // These placeholders take the form of `$number` or `${number:placeholder_text}` which can be traversed as tabstop in ascending order starting from 1,
 // with `$0` being a special case that always comes last.
 //
-// There is also a special placeholder, `${receiver}`, which will be replaced by the receiver expression for postfix snippets, or nothing in case of normal snippets.
-// It does not act as a tabstop.
+// There is also a special placeholder, `${receiver}`, which will be replaced by the receiver expression for postfix snippets, or a `$0` tabstop in case of normal snippets.
+// This replacement for normal snippets allows you to reuse a snippet for both post- and prefix in a single definition.
+//
+// For the VSCode editor, rust-analyzer also ships with a small set of defaults which can be removed
+// by overwriting the settings object mentioned above, the defaults are:
+// [source,json]
+// ----
+// {
+//     "Arc::new": {
+//         "postfix": "arc",
+//         "body": "Arc::new(${receiver})",
+//         "requires": "std::sync::Arc",
+//         "description": "Put the expression into an `Arc`",
+//         "scope": "expr"
+//     },
+//     "Rc::new": {
+//         "postfix": "rc",
+//         "body": "Rc::new(${receiver})",
+//         "requires": "std::rc::Rc",
+//         "description": "Put the expression into an `Rc`",
+//         "scope": "expr"
+//     },
+//     "Box::pin": {
+//         "postfix": "pinbox",
+//         "body": "Box::pin(${receiver})",
+//         "requires": "std::boxed::Box",
+//         "description": "Put the expression into a pinned `Box`",
+//         "scope": "expr"
+//     },
+//     "Ok": {
+//         "postfix": "ok",
+//         "body": "Ok(${receiver})",
+//         "description": "Wrap the expression in a `Result::Ok`",
+//         "scope": "expr"
+//     },
+//     "Err": {
+//         "postfix": "err",
+//         "body": "Err(${receiver})",
+//         "description": "Wrap the expression in a `Result::Err`",
+//         "scope": "expr"
+//     },
+//     "Some": {
+//         "postfix": "some",
+//         "body": "Some(${receiver})",
+//         "description": "Wrap the expression in an `Option::Some`",
+//         "scope": "expr"
+//     }
+// }
+// ----
+
 use ide_db::helpers::{import_assets::LocatedImport, insert_use::ImportScope};
 use itertools::Itertools;
 use syntax::{ast, AstNode, GreenNode, SyntaxNode};
@@ -117,7 +165,7 @@ impl Snippet {
     }
 
     pub fn snippet(&self) -> String {
-        self.snippet.replace("${receiver}", "")
+        self.snippet.replace("${receiver}", "$0")
     }
 
     pub fn postfix_snippet(&self, receiver: &str) -> String {
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 5826b6b114..867f2bd1f0 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -116,7 +116,48 @@ config_data! {
         /// Whether to add parenthesis when completing functions.
         completion_addCallParenthesis: bool      = "true",
         /// Custom completion snippets.
-        completion_snippets: FxHashMap<String, SnippetDef> = "{}",
+        // NOTE: Keep this list in sync with the feature docs of user snippets.
+        completion_snippets: FxHashMap<String, SnippetDef> = r#"{
+            "Arc::new": {
+                "postfix": "arc",
+                "body": "Arc::new(${receiver})",
+                "requires": "std::sync::Arc",
+                "description": "Put the expression into an `Arc`",
+                "scope": "expr"
+            },
+            "Rc::new": {
+                "postfix": "rc",
+                "body": "Rc::new(${receiver})",
+                "requires": "std::rc::Rc",
+                "description": "Put the expression into an `Rc`",
+                "scope": "expr"
+            },
+            "Box::pin": {
+                "postfix": "pinbox",
+                "body": "Box::pin(${receiver})",
+                "requires": "std::boxed::Box",
+                "description": "Put the expression into a pinned `Box`",
+                "scope": "expr"
+            },
+            "Ok": {
+                "postfix": "ok",
+                "body": "Ok(${receiver})",
+                "description": "Wrap the expression in a `Result::Ok`",
+                "scope": "expr"
+            },
+            "Err": {
+                "postfix": "err",
+                "body": "Err(${receiver})",
+                "description": "Wrap the expression in a `Result::Err`",
+                "scope": "expr"
+            },
+            "Some": {
+                "postfix": "some",
+                "body": "Some(${receiver})",
+                "description": "Wrap the expression in an `Option::Some`",
+                "scope": "expr"
+            }
+        }"#,
         /// Whether to show postfix snippets like `dbg`, `if`, `not`, etc.
         completion_postfix_enable: bool          = "true",
         /// Toggles the additional completions that automatically add imports when completed.
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index 9f6f046014..494051b306 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -141,7 +141,47 @@ Only applies when `#rust-analyzer.completion.addCallParenthesis#` is set.
 --
 Whether to add parenthesis when completing functions.
 --
-[[rust-analyzer.completion.snippets]]rust-analyzer.completion.snippets (default: `{}`)::
+[[rust-analyzer.completion.snippets]]rust-analyzer.completion.snippets (default: `{
+            "Arc::new": {
+                "postfix": "arc",
+                "body": "Arc::new(${receiver})",
+                "requires": "std::sync::Arc",
+                "description": "Put the expression into an `Arc`",
+                "scope": "expr"
+            },
+            "Rc::new": {
+                "postfix": "rc",
+                "body": "Rc::new(${receiver})",
+                "requires": "std::rc::Rc",
+                "description": "Put the expression into an `Rc`",
+                "scope": "expr"
+            },
+            "Box::pin": {
+                "postfix": "pinbox",
+                "body": "Box::pin(${receiver})",
+                "requires": "std::boxed::Box",
+                "description": "Put the expression into a pinned `Box`",
+                "scope": "expr"
+            },
+            "Ok": {
+                "postfix": "ok",
+                "body": "Ok(${receiver})",
+                "description": "Wrap the expression in a `Result::Ok`",
+                "scope": "expr"
+            },
+            "Err": {
+                "postfix": "err",
+                "body": "Err(${receiver})",
+                "description": "Wrap the expression in a `Result::Err`",
+                "scope": "expr"
+            },
+            "Some": {
+                "postfix": "some",
+                "body": "Some(${receiver})",
+                "description": "Wrap the expression in an `Option::Some`",
+                "scope": "expr"
+            }
+        }`)::
 +
 --
 Custom completion snippets.
diff --git a/editors/code/package.json b/editors/code/package.json
index 2460308820..d70ac9aacb 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -592,7 +592,47 @@
                 },
                 "rust-analyzer.completion.snippets": {
                     "markdownDescription": "Custom completion snippets.",
-                    "default": {},
+                    "default": {
+                        "Arc::new": {
+                            "postfix": "arc",
+                            "body": "Arc::new(${receiver})",
+                            "requires": "std::sync::Arc",
+                            "description": "Put the expression into an `Arc`",
+                            "scope": "expr"
+                        },
+                        "Rc::new": {
+                            "postfix": "rc",
+                            "body": "Rc::new(${receiver})",
+                            "requires": "std::rc::Rc",
+                            "description": "Put the expression into an `Rc`",
+                            "scope": "expr"
+                        },
+                        "Box::pin": {
+                            "postfix": "pinbox",
+                            "body": "Box::pin(${receiver})",
+                            "requires": "std::boxed::Box",
+                            "description": "Put the expression into a pinned `Box`",
+                            "scope": "expr"
+                        },
+                        "Ok": {
+                            "postfix": "ok",
+                            "body": "Ok(${receiver})",
+                            "description": "Wrap the expression in a `Result::Ok`",
+                            "scope": "expr"
+                        },
+                        "Err": {
+                            "postfix": "err",
+                            "body": "Err(${receiver})",
+                            "description": "Wrap the expression in a `Result::Err`",
+                            "scope": "expr"
+                        },
+                        "Some": {
+                            "postfix": "some",
+                            "body": "Some(${receiver})",
+                            "description": "Wrap the expression in an `Option::Some`",
+                            "scope": "expr"
+                        }
+                    },
                     "type": "object"
                 },
                 "rust-analyzer.completion.postfix.enable": {


Test patch:
diff --git a/crates/ide_completion/src/tests/attribute.rs b/crates/ide_completion/src/tests/attribute.rs
index c2bd26e2d8..c90d4966f3 100644
--- a/crates/ide_completion/src/tests/attribute.rs
+++ b/crates/ide_completion/src/tests/attribute.rs
@@ -283,11 +283,7 @@ fn attr_on_type_alias() {
 #[test]
 fn attr_on_struct() {
     check(
-        r#"
-//- minicore:derive
-#[$0]
-struct Foo;
-"#,
+        r#"#[$0] struct Foo;"#,
         expect![[r#"
             at allow(…)
             at cfg(…)
@@ -307,8 +303,6 @@ struct Foo;
             kw self
             kw super
             kw crate
-            md core
-            at derive           pub macro derive
         "#]],
     );
 }
diff --git a/crates/ide_completion/src/tests/proc_macros.rs b/crates/ide_completion/src/tests/proc_macros.rs
index 73fc293526..f54286fe59 100644
--- a/crates/ide_completion/src/tests/proc_macros.rs
+++ b/crates/ide_completion/src/tests/proc_macros.rs
@@ -29,9 +29,6 @@ fn main() {
             sn refm  &mut expr
             sn match match expr {}
             sn box   Box::new(expr)
-            sn ok    Ok(expr)
-            sn err   Err(expr)
-            sn some  Some(expr)
             sn dbg   dbg!(expr)
             sn dbgr  dbg!(&expr)
             sn call  function(expr)
@@ -62,9 +59,6 @@ fn main() {
             sn refm  &mut expr
             sn match match expr {}
             sn box   Box::new(expr)
-            sn ok    Ok(expr)
-            sn err   Err(expr)
-            sn some  Some(expr)
             sn dbg   dbg!(expr)
             sn dbgr  dbg!(&expr)
             sn call  function(expr)
@@ -97,9 +91,6 @@ fn main() {}
             sn refm  &mut expr
             sn match match expr {}
             sn box   Box::new(expr)
-            sn ok    Ok(expr)
-            sn err   Err(expr)
-            sn some  Some(expr)
             sn dbg   dbg!(expr)
             sn dbgr  dbg!(&expr)
             sn call  function(expr)
@@ -132,9 +123,6 @@ fn main() {}
             sn refm  &mut expr
             sn match match expr {}
             sn box   Box::new(expr)
-            sn ok    Ok(expr)
-            sn err   Err(expr)
-            sn some  Some(expr)
             sn dbg   dbg!(expr)
             sn dbgr  dbg!(&expr)
             sn call  function(expr)