Repository: rust-analyzer/rust-analyzer
Pull request #10853 (2021-11-24 15:02:10)

fix: Remove possible multiline details in completions
Fixes https://github.com/rust-analyzer/rust-analyzer/issues/10316
Fixes https://github.com/rust-analyzer/rust-analyzer/issues/7531(this has already been fixed prior, from what I've seen)
bors r+

bors[bot]: Build failed:
  * [Rust (ubuntu-latest)](https://github.com/rust-analyzer/rust-analyzer/runs/4313652822?check_suite_focus=true)

Patch:
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index b5bf6b5514..a2662d2932 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -260,8 +260,8 @@ fn foo() { let _ = lib::S::$0 }
 "#,
             expect![[r#"
                 fn public_method() fn()
-                ct PUBLIC_CONST    pub const PUBLIC_CONST: u32 = 1;
-                ta PublicType      pub type PublicType = u32;
+                ct PUBLIC_CONST    pub const PUBLIC_CONST: u32;
+                ta PublicType      pub type PublicType;
             "#]],
         );
     }
@@ -392,10 +392,10 @@ impl<T> Sub for Wrap<T> {
             expect![[r#"
                 ta SubTy (as Sub)        type SubTy;
                 ta Ty (as Super)         type Ty;
-                ct CONST (as Super)      const CONST: u8 = 0;
+                ct CONST (as Super)      const CONST: u8;
                 fn func() (as Super)     fn()
                 me method(…) (as Super)  fn(&self)
-                ct C2 (as Sub)           const C2: () = ();
+                ct C2 (as Sub)           const C2: ();
                 fn subfunc() (as Sub)    fn()
                 me submethod(…) (as Sub) fn(&self)
             "#]],
@@ -626,7 +626,7 @@ impl u8 {
 }
 "#,
             expect![[r#"
-                ct MAX     pub const MAX: Self = 255;
+                ct MAX     pub const MAX: Self;
                 me func(…) fn(self)
             "#]],
         );
diff --git a/crates/ide_completion/src/completions/snippet.rs b/crates/ide_completion/src/completions/snippet.rs
index 12bccfae11..02f711f51d 100644
--- a/crates/ide_completion/src/completions/snippet.rs
+++ b/crates/ide_completion/src/completions/snippet.rs
@@ -115,7 +115,7 @@ fn add_custom_completions(
             for import in imports.into_iter() {
                 builder.add_import(import);
             }
-            builder.detail(snip.description.as_deref().unwrap_or_default());
+            builder.set_detail(snip.description.clone());
             builder.add_to(acc);
         },
     );
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index a1d4eb998f..d4f35b5dcd 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -433,7 +433,7 @@ fn main() { Foo::Fo$0 }
                         kind: SymbolKind(
                             Variant,
                         ),
-                        detail: "{ x: i32, y: i32 }",
+                        detail: "{x: i32, y: i32}",
                     },
                 ]
             "#]],
diff --git a/crates/ide_completion/src/render/enum_variant.rs b/crates/ide_completion/src/render/enum_variant.rs
index 728e0e2a17..26cb73d09c 100644
--- a/crates/ide_completion/src/render/enum_variant.rs
+++ b/crates/ide_completion/src/render/enum_variant.rs
@@ -1,10 +1,10 @@
 //! Renderer for `enum` variants.
 
-use std::iter;
+use std::{iter, mem};
 
 use hir::{HasAttrs, HirDisplay};
 use ide_db::SymbolKind;
-use itertools::Itertools;
+use stdx::format_to;
 
 use crate::{
     item::{CompletionItem, ImportEdit},
@@ -105,18 +105,31 @@ impl<'a> EnumRender<'a> {
             .into_iter()
             .map(|field| (field.name(self.ctx.db()), field.ty(self.ctx.db())));
 
+        let mut b = String::new();
+        let mut first_run = true;
         match self.variant_kind {
-            hir::StructKind::Tuple | hir::StructKind::Unit => format!(
-                "({})",
-                detail_types.map(|(_, t)| t.display(self.ctx.db()).to_string()).format(", ")
-            ),
-            hir::StructKind::Record => format!(
-                "{{ {} }}",
-                detail_types
-                    .map(|(n, t)| format!("{}: {}", n, t.display(self.ctx.db()).to_string()))
-                    .format(", ")
-            ),
+            hir::StructKind::Tuple | hir::StructKind::Unit => {
+                format_to!(b, "(");
+                for (_, t) in detail_types {
+                    if !mem::take(&mut first_run) {
+                        format_to!(b, ", ");
+                    }
+                    format_to!(b, "{}", t.display(self.ctx.db()));
+                }
+                format_to!(b, ")");
+            }
+            hir::StructKind::Record => {
+                format_to!(b, "{{");
+                for (n, t) in detail_types {
+                    if !mem::take(&mut first_run) {
+                        format_to!(b, ", ");
+                    }
+                    format_to!(b, "{}: {}", n, t.display(self.ctx.db()));
+                }
+                format_to!(b, "}}");
+            }
         }
+        b
     }
 }
 
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs
index f598b414a7..86dfbf7fc8 100644
--- a/crates/ide_completion/src/render/function.rs
+++ b/crates/ide_completion/src/render/function.rs
@@ -4,6 +4,7 @@ use either::Either;
 use hir::{AsAssocItem, HasSource, HirDisplay};
 use ide_db::SymbolKind;
 use itertools::Itertools;
+use stdx::format_to;
 use syntax::ast;
 
 use crate::{
@@ -122,14 +123,11 @@ impl<'a> FunctionRender<'a> {
 
     fn detail(&self) -> String {
         let ret_ty = self.func.ret_type(self.ctx.db());
-        let ret = if ret_ty.is_unit() {
-            // Omit the return type if it is the unit type
-            String::new()
-        } else {
-            format!(" {}", self.ty_display())
-        };
-
-        format!("fn({}){}", self.params_display(), ret)
+        let mut detail = format!("fn({})", self.params_display());
+        if !ret_ty.is_unit() {
+            format_to!(detail, " -> {}", ret_ty.display(self.ctx.db()));
+        }
+        detail
     }
 
     fn params_display(&self) -> String {
@@ -153,12 +151,6 @@ impl<'a> FunctionRender<'a> {
         }
     }
 
-    fn ty_display(&self) -> String {
-        let ret_ty = self.func.ret_type(self.ctx.db());
-
-        format!("-> {}", ret_ty.display(self.ctx.db()))
-    }
-
     fn params(&self) -> Params {
         let ast_params = match self.ast_node.param_list() {
             Some(it) => it,
diff --git a/crates/ide_completion/src/snippet.rs b/crates/ide_completion/src/snippet.rs
index bcaa1ded8f..1a3f42b839 100644
--- a/crates/ide_completion/src/snippet.rs
+++ b/crates/ide_completion/src/snippet.rs
@@ -176,6 +176,9 @@ fn validate_snippet(
         imports.push(green);
     }
     let snippet = snippet.iter().join("\n");
-    let description = if description.is_empty() { None } else { Some(description.into()) };
+    let description = (!description.is_empty())
+        .then(|| description.split_once('\n').map_or(description, |(it, _)| it))
+        .map(ToOwned::to_owned)
+        .map(Into::into);
     Some((imports.into_boxed_slice(), snippet, description))
 }
diff --git a/crates/syntax/src/display.rs b/crates/syntax/src/display.rs
index 95e37944cc..e2115cbd2b 100644
--- a/crates/syntax/src/display.rs
+++ b/crates/syntax/src/display.rs
@@ -1,10 +1,6 @@
-//! This module contains utilities for turning SyntaxNodes and HIR types
-//! into types that may be used to render in a UI.
+//! This module contains utilities for rendering syntax nodes into a string representing their signature.
 
-use crate::{
-    ast::{self, AstNode, HasAttrs, HasGenericParams, HasName},
-    SyntaxKind::{ATTR, COMMENT},
-};
+use crate::ast::{self, HasAttrs, HasGenericParams, HasName};
 
 use ast::HasVisibility;
 use stdx::format_to;
@@ -55,25 +51,39 @@ pub fn function_declaration(node: &ast::Fn) -> String {
 }
 
 pub fn const_label(node: &ast::Const) -> String {
-    let label: String = node
-        .syntax()
-        .children_with_tokens()
-        .filter(|child| !(child.kind() == COMMENT || child.kind() == ATTR))
-        .map(|node| node.to_string())
-        .collect();
-
-    label.trim().to_owned()
+    let mut s = String::new();
+    if let Some(vis) = node.visibility() {
+        format_to!(s, "{} ", vis);
+    }
+    format_to!(s, "const ");
+    if let Some(name) = node.name() {
+        format_to!(s, "{}", name);
+    } else {
+        format_to!(s, "?");
+    }
+    format_to!(s, ": ");
+    if let Some(ty) = node.ty() {
+        format_to!(s, "{}", ty);
+    } else {
+        format_to!(s, "?");
+    }
+    format_to!(s, ";");
+    s
 }
 
 pub fn type_label(node: &ast::TypeAlias) -> String {
-    let label: String = node
-        .syntax()
-        .children_with_tokens()
-        .filter(|child| !(child.kind() == COMMENT || child.kind() == ATTR))
-        .map(|node| node.to_string())
-        .collect();
-
-    label.trim().to_owned()
+    let mut s = String::new();
+    if let Some(vis) = node.visibility() {
+        format_to!(s, "{} ", vis);
+    }
+    format_to!(s, "type ");
+    if let Some(name) = node.name() {
+        format_to!(s, "{}", name);
+    } else {
+        format_to!(s, "?");
+    }
+    format_to!(s, ";");
+    s
 }
 
 pub fn macro_label(node: &ast::Macro) -> String {


Test patch:
diff --git a/crates/ide_completion/src/tests/expression.rs b/crates/ide_completion/src/tests/expression.rs
index a634808734..eba2a5e1ef 100644
--- a/crates/ide_completion/src/tests/expression.rs
+++ b/crates/ide_completion/src/tests/expression.rs
@@ -544,11 +544,11 @@ fn func() {
 "#,
         expect![[r#"
             ev TupleV(…)   (u32)
-            ev RecordV     { field: u32 }
+            ev RecordV     {field: u32}
             ev UnitV       ()
-            ct ASSOC_CONST const ASSOC_CONST: () = ();
+            ct ASSOC_CONST const ASSOC_CONST: ();
             fn assoc_fn()  fn()
-            ta AssocType   type AssocType = ();
+            ta AssocType   type AssocType;
         "#]],
     );
 }
diff --git a/crates/ide_completion/src/tests/pattern.rs b/crates/ide_completion/src/tests/pattern.rs
index 6dd1b66998..81c45c64cc 100644
--- a/crates/ide_completion/src/tests/pattern.rs
+++ b/crates/ide_completion/src/tests/pattern.rs
@@ -286,11 +286,11 @@ fn func() {
 "#,
         expect![[r#"
             ev TupleV(…)   (u32)
-            ev RecordV     { field: u32 }
+            ev RecordV     {field: u32}
             ev UnitV       ()
-            ct ASSOC_CONST const ASSOC_CONST: () = ();
+            ct ASSOC_CONST const ASSOC_CONST: ();
             fn assoc_fn()  fn()
-            ta AssocType   type AssocType = ();
+            ta AssocType   type AssocType;
         "#]],
     );
 }
diff --git a/crates/ide_completion/src/tests/type_pos.rs b/crates/ide_completion/src/tests/type_pos.rs
index a03f1e7ff8..a76f97f3da 100644
--- a/crates/ide_completion/src/tests/type_pos.rs
+++ b/crates/ide_completion/src/tests/type_pos.rs
@@ -199,7 +199,7 @@ impl Enum {
 fn func(_: Enum::$0) {}
 "#,
         expect![[r#"
-            ta AssocType type AssocType = ();
+            ta AssocType type AssocType;
         "#]],
     );
 }