Merge pull request #18713 from Veykril/push-zmmkzspnwxsn

internal: Cleanup label structure of `CompletionItem`
This commit is contained in:
Lukas Wirth 2024-12-18 12:53:07 +00:00 committed by GitHub
commit d7fa33e2d0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 1201 additions and 1070 deletions

View file

@ -10,7 +10,7 @@ use ide_db::{
}; };
use itertools::Itertools; use itertools::Itertools;
use smallvec::SmallVec; use smallvec::SmallVec;
use stdx::{impl_from, never}; use stdx::{format_to, impl_from, never};
use syntax::{format_smolstr, Edition, SmolStr, TextRange, TextSize}; use syntax::{format_smolstr, Edition, SmolStr, TextRange, TextSize};
use crate::{ use crate::{
@ -27,10 +27,7 @@ use crate::{
#[non_exhaustive] #[non_exhaustive]
pub struct CompletionItem { pub struct CompletionItem {
/// Label in the completion pop up which identifies completion. /// Label in the completion pop up which identifies completion.
pub label: SmolStr, pub label: CompletionItemLabel,
/// Additional label details in the completion pop up that are
/// displayed and aligned on the right side after the label.
pub label_detail: Option<SmolStr>,
/// Range of identifier that is being completed. /// Range of identifier that is being completed.
/// ///
@ -89,11 +86,23 @@ pub struct CompletionItem {
pub import_to_add: SmallVec<[(String, String); 1]>, pub import_to_add: SmallVec<[(String, String); 1]>,
} }
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct CompletionItemLabel {
/// The primary label for the completion item.
pub primary: SmolStr,
/// The left detail for the completion item, usually rendered right next to the primary label.
pub detail_left: Option<String>,
/// The right detail for the completion item, usually rendered right aligned at the end of the completion item.
pub detail_right: Option<String>,
}
// We use custom debug for CompletionItem to make snapshot tests more readable. // We use custom debug for CompletionItem to make snapshot tests more readable.
impl fmt::Debug for CompletionItem { impl fmt::Debug for CompletionItem {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut s = f.debug_struct("CompletionItem"); let mut s = f.debug_struct("CompletionItem");
s.field("label", &self.label).field("source_range", &self.source_range); s.field("label", &self.label.primary)
.field("detail_left", &self.label.detail_left)
.field("detail_right", &self.label.detail_right)
.field("source_range", &self.source_range);
if self.text_edit.len() == 1 { if self.text_edit.len() == 1 {
let atom = self.text_edit.iter().next().unwrap(); let atom = self.text_edit.iter().next().unwrap();
s.field("delete", &atom.delete); s.field("delete", &atom.delete);
@ -102,7 +111,7 @@ impl fmt::Debug for CompletionItem {
s.field("text_edit", &self.text_edit); s.field("text_edit", &self.text_edit);
} }
s.field("kind", &self.kind); s.field("kind", &self.kind);
if self.lookup() != self.label { if self.lookup() != self.label.primary {
s.field("lookup", &self.lookup()); s.field("lookup", &self.lookup());
} }
if let Some(detail) = &self.detail { if let Some(detail) = &self.detail {
@ -434,7 +443,7 @@ impl CompletionItem {
self.ref_match.map(|(mutability, offset)| { self.ref_match.map(|(mutability, offset)| {
( (
format!("&{}{}", mutability.as_keyword_for_ref(), self.label), format!("&{}{}", mutability.as_keyword_for_ref(), self.label.primary),
ide_db::text_edit::Indel::insert( ide_db::text_edit::Indel::insert(
offset, offset,
format!("&{}", mutability.as_keyword_for_ref()), format!("&{}", mutability.as_keyword_for_ref()),
@ -488,13 +497,13 @@ impl Builder {
let _p = tracing::info_span!("item::Builder::build").entered(); let _p = tracing::info_span!("item::Builder::build").entered();
let label = self.label; let label = self.label;
let mut label_detail = None;
let mut lookup = self.lookup.unwrap_or_else(|| label.clone()); let mut lookup = self.lookup.unwrap_or_else(|| label.clone());
let insert_text = self.insert_text.unwrap_or_else(|| label.to_string()); let insert_text = self.insert_text.unwrap_or_else(|| label.to_string());
let mut detail_left = None;
if !self.doc_aliases.is_empty() { if !self.doc_aliases.is_empty() {
let doc_aliases = self.doc_aliases.iter().join(", "); let doc_aliases = self.doc_aliases.iter().join(", ");
label_detail.replace(format_smolstr!(" (alias {doc_aliases})")); detail_left = Some(format!("(alias {doc_aliases})"));
let lookup_doc_aliases = self let lookup_doc_aliases = self
.doc_aliases .doc_aliases
.iter() .iter()
@ -516,16 +525,20 @@ impl Builder {
} }
if let [import_edit] = &*self.imports_to_add { if let [import_edit] = &*self.imports_to_add {
// snippets can have multiple imports, but normal completions only have up to one // snippets can have multiple imports, but normal completions only have up to one
label_detail.replace(format_smolstr!( let detail_left = detail_left.get_or_insert_with(String::new);
"{} (use {})", format_to!(
label_detail.as_deref().unwrap_or_default(), detail_left,
"{}(use {})",
if detail_left.is_empty() { "" } else { " " },
import_edit.import_path.display(db, self.edition) import_edit.import_path.display(db, self.edition)
)); );
} else if let Some(trait_name) = self.trait_name { } else if let Some(trait_name) = self.trait_name {
label_detail.replace(format_smolstr!( let detail_left = detail_left.get_or_insert_with(String::new);
"{} (as {trait_name})", format_to!(
label_detail.as_deref().unwrap_or_default(), detail_left,
)); "{}(as {trait_name})",
if detail_left.is_empty() { "" } else { " " },
);
} }
let text_edit = match self.text_edit { let text_edit = match self.text_edit {
@ -546,8 +559,11 @@ impl Builder {
CompletionItem { CompletionItem {
source_range: self.source_range, source_range: self.source_range,
label, label: CompletionItemLabel {
label_detail, primary: label,
detail_left,
detail_right: self.detail.clone(),
},
text_edit, text_edit,
is_snippet: self.is_snippet, is_snippet: self.is_snippet,
detail: self.detail, detail: self.detail,

View file

@ -748,9 +748,9 @@ mod tests {
let tag = it.kind.tag(); let tag = it.kind.tag();
let relevance = display_relevance(it.relevance); let relevance = display_relevance(it.relevance);
items.push(format!( items.push(format!(
"{tag} {}{} {relevance}\n", "{tag} {} {} {relevance}\n",
it.label, it.label.primary,
it.label_detail.clone().unwrap_or_default(), it.label.detail_right.clone().unwrap_or_default(),
)); ));
if let Some((label, _indel, relevance)) = it.ref_match() { if let Some((label, _indel, relevance)) = it.ref_match() {
@ -812,13 +812,13 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
st dep::test_mod_b::Struct {} [type_could_unify] st dep::test_mod_b::Struct {} dep::test_mod_b::Struct { } [type_could_unify]
ex dep::test_mod_b::Struct { } [type_could_unify] ex dep::test_mod_b::Struct { } [type_could_unify]
st Struct (use dep::test_mod_b::Struct) [type_could_unify+requires_import] st Struct Struct [type_could_unify+requires_import]
fn main() [] fn main() fn() []
fn test() [] fn test() fn(Struct) []
md dep [] md dep []
st Struct (use dep::test_mod_a::Struct) [requires_import] st Struct Struct [requires_import]
"#]], "#]],
); );
} }
@ -852,11 +852,11 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
un Union (use dep::test_mod_b::Union) [type_could_unify+requires_import] un Union Union [type_could_unify+requires_import]
fn main() [] fn main() fn() []
fn test() [] fn test() fn(Union) []
md dep [] md dep []
en Union (use dep::test_mod_a::Union) [requires_import] en Union Union [requires_import]
"#]], "#]],
); );
} }
@ -888,13 +888,13 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
ev dep::test_mod_b::Enum::variant [type_could_unify] ev dep::test_mod_b::Enum::variant dep::test_mod_b::Enum::variant [type_could_unify]
ex dep::test_mod_b::Enum::variant [type_could_unify] ex dep::test_mod_b::Enum::variant [type_could_unify]
en Enum (use dep::test_mod_b::Enum) [type_could_unify+requires_import] en Enum Enum [type_could_unify+requires_import]
fn main() [] fn main() fn() []
fn test() [] fn test() fn(Enum) []
md dep [] md dep []
en Enum (use dep::test_mod_a::Enum) [requires_import] en Enum Enum [requires_import]
"#]], "#]],
); );
} }
@ -926,10 +926,10 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
ev dep::test_mod_b::Enum::Variant [type_could_unify] ev dep::test_mod_b::Enum::Variant dep::test_mod_b::Enum::Variant [type_could_unify]
ex dep::test_mod_b::Enum::Variant [type_could_unify] ex dep::test_mod_b::Enum::Variant [type_could_unify]
fn main() [] fn main() fn() []
fn test() [] fn test() fn(Enum) []
md dep [] md dep []
"#]], "#]],
); );
@ -958,11 +958,11 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
fn main() [] fn main() fn() []
fn test() [] fn test() fn(fn(usize) -> i32) []
md dep [] md dep []
fn function (use dep::test_mod_a::function) [requires_import] fn function fn(usize) -> i32 [requires_import]
fn function() (use dep::test_mod_b::function) [requires_import] fn function() fn(isize) -> i32 [requires_import]
"#]], "#]],
); );
} }
@ -990,11 +990,11 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
ct CONST (use dep::test_mod_b::CONST) [type_could_unify+requires_import] ct CONST i32 [type_could_unify+requires_import]
fn main() [] fn main() fn() []
fn test() [] fn test() fn(i32) []
md dep [] md dep []
ct CONST (use dep::test_mod_a::CONST) [requires_import] ct CONST i64 [requires_import]
"#]], "#]],
); );
} }
@ -1022,11 +1022,11 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
sc STATIC (use dep::test_mod_b::STATIC) [type_could_unify+requires_import] sc STATIC i32 [type_could_unify+requires_import]
fn main() [] fn main() fn() []
fn test() [] fn test() fn(i32) []
md dep [] md dep []
sc STATIC (use dep::test_mod_a::STATIC) [requires_import] sc STATIC i64 [requires_import]
"#]], "#]],
); );
} }
@ -1058,7 +1058,7 @@ fn main() {
"#, "#,
expect![[r#" expect![[r#"
me Function [] me Function fn(&self, i32) -> bool []
"#]], "#]],
); );
} }
@ -1081,14 +1081,14 @@ fn func(input: Struct) { }
"#, "#,
expect![[r#" expect![[r#"
st Struct [type] st Struct Struct [type]
st Self [type] st Self Self [type]
sp Self [type] sp Self Struct [type]
st Struct [type] st Struct Struct [type]
ex Struct [type] ex Struct [type]
lc self [local] lc self &Struct [local]
fn func() [] fn func() fn(Struct) []
me self.test() [] me self.test() fn(&self) []
"#]], "#]],
); );
} }
@ -1109,13 +1109,13 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
lc input [type+name+local] lc input bool [type+name+local]
ex input [type] ex input [type]
ex true [type] ex true [type]
ex false [type] ex false [type]
lc inputbad [local] lc inputbad i32 [local]
fn main() [] fn main() fn() []
fn test() [] fn test() fn(bool) []
"#]], "#]],
); );
} }
@ -1133,6 +1133,10 @@ fn main() { Foo::Fo$0 }
[ [
CompletionItem { CompletionItem {
label: "Foo {…}", label: "Foo {…}",
detail_left: None,
detail_right: Some(
"Foo { x: i32, y: i32 }",
),
source_range: 54..56, source_range: 54..56,
delete: 54..56, delete: 54..56,
insert: "Foo { x: ${1:()}, y: ${2:()} }$0", insert: "Foo { x: ${1:()}, y: ${2:()} }$0",
@ -1161,6 +1165,10 @@ fn main() { Foo::Fo$0 }
[ [
CompletionItem { CompletionItem {
label: "Foo(…)", label: "Foo(…)",
detail_left: None,
detail_right: Some(
"Foo(i32, i32)",
),
source_range: 46..48, source_range: 46..48,
delete: 46..48, delete: 46..48,
insert: "Foo(${1:()}, ${2:()})$0", insert: "Foo(${1:()}, ${2:()})$0",
@ -1189,6 +1197,10 @@ fn main() { fo$0 }
[ [
CompletionItem { CompletionItem {
label: "foo(…)", label: "foo(…)",
detail_left: None,
detail_right: Some(
"fn(u32, u32, T) -> (u32, T)",
),
source_range: 68..70, source_range: 68..70,
delete: 68..70, delete: 68..70,
insert: "foo(${1:a}, ${2:b}, ${3:t})$0", insert: "foo(${1:a}, ${2:b}, ${3:t})$0",
@ -1201,6 +1213,10 @@ fn main() { fo$0 }
}, },
CompletionItem { CompletionItem {
label: "main()", label: "main()",
detail_left: None,
detail_right: Some(
"fn()",
),
source_range: 68..70, source_range: 68..70,
delete: 68..70, delete: 68..70,
insert: "main();$0", insert: "main();$0",
@ -1228,6 +1244,10 @@ fn main() { Foo::Fo$0 }
[ [
CompletionItem { CompletionItem {
label: "Foo", label: "Foo",
detail_left: None,
detail_right: Some(
"Foo",
),
source_range: 35..37, source_range: 35..37,
delete: 35..37, delete: 35..37,
insert: "Foo$0", insert: "Foo$0",
@ -1260,6 +1280,10 @@ fn main() { let _: m::Spam = S$0 }
[ [
CompletionItem { CompletionItem {
label: "main()", label: "main()",
detail_left: None,
detail_right: Some(
"fn()",
),
source_range: 75..76, source_range: 75..76,
delete: 75..76, delete: 75..76,
insert: "main();$0", insert: "main();$0",
@ -1271,6 +1295,8 @@ fn main() { let _: m::Spam = S$0 }
}, },
CompletionItem { CompletionItem {
label: "m", label: "m",
detail_left: None,
detail_right: None,
source_range: 75..76, source_range: 75..76,
delete: 75..76, delete: 75..76,
insert: "m", insert: "m",
@ -1280,6 +1306,10 @@ fn main() { let _: m::Spam = S$0 }
}, },
CompletionItem { CompletionItem {
label: "m::Spam::Bar(…)", label: "m::Spam::Bar(…)",
detail_left: None,
detail_right: Some(
"m::Spam::Bar(i32)",
),
source_range: 75..76, source_range: 75..76,
delete: 75..76, delete: 75..76,
insert: "m::Spam::Bar(${1:()})$0", insert: "m::Spam::Bar(${1:()})$0",
@ -1305,6 +1335,10 @@ fn main() { let _: m::Spam = S$0 }
}, },
CompletionItem { CompletionItem {
label: "m::Spam::Foo", label: "m::Spam::Foo",
detail_left: None,
detail_right: Some(
"m::Spam::Foo",
),
source_range: 75..76, source_range: 75..76,
delete: 75..76, delete: 75..76,
insert: "m::Spam::Foo$0", insert: "m::Spam::Foo$0",
@ -1347,6 +1381,10 @@ fn main() { som$0 }
[ [
CompletionItem { CompletionItem {
label: "main()", label: "main()",
detail_left: None,
detail_right: Some(
"fn()",
),
source_range: 56..59, source_range: 56..59,
delete: 56..59, delete: 56..59,
insert: "main();$0", insert: "main();$0",
@ -1358,6 +1396,10 @@ fn main() { som$0 }
}, },
CompletionItem { CompletionItem {
label: "something_deprecated()", label: "something_deprecated()",
detail_left: None,
detail_right: Some(
"fn()",
),
source_range: 56..59, source_range: 56..59,
delete: 56..59, delete: 56..59,
insert: "something_deprecated();$0", insert: "something_deprecated();$0",
@ -1382,6 +1424,10 @@ fn foo() { A { the$0 } }
[ [
CompletionItem { CompletionItem {
label: "the_field", label: "the_field",
detail_left: None,
detail_right: Some(
"u32",
),
source_range: 57..60, source_range: 57..60,
delete: 57..60, delete: 57..60,
insert: "the_field", insert: "the_field",
@ -1429,6 +1475,10 @@ impl S {
[ [
CompletionItem { CompletionItem {
label: "bar()", label: "bar()",
detail_left: None,
detail_right: Some(
"fn(self)",
),
source_range: 94..94, source_range: 94..94,
delete: 94..94, delete: 94..94,
insert: "bar();$0", insert: "bar();$0",
@ -1460,6 +1510,10 @@ impl S {
}, },
CompletionItem { CompletionItem {
label: "foo", label: "foo",
detail_left: None,
detail_right: Some(
"{unknown}",
),
source_range: 94..94, source_range: 94..94,
delete: 94..94, delete: 94..94,
insert: "foo", insert: "foo",
@ -1498,6 +1552,8 @@ use self::E::*;
[ [
CompletionItem { CompletionItem {
label: "my", label: "my",
detail_left: None,
detail_right: None,
source_range: 10..12, source_range: 10..12,
delete: 10..12, delete: 10..12,
insert: "my", insert: "my",
@ -1510,6 +1566,10 @@ use self::E::*;
}, },
CompletionItem { CompletionItem {
label: "V", label: "V",
detail_left: None,
detail_right: Some(
"V",
),
source_range: 10..12, source_range: 10..12,
delete: 10..12, delete: 10..12,
insert: "V$0", insert: "V$0",
@ -1524,6 +1584,10 @@ use self::E::*;
}, },
CompletionItem { CompletionItem {
label: "E", label: "E",
detail_left: None,
detail_right: Some(
"E",
),
source_range: 10..12, source_range: 10..12,
delete: 10..12, delete: 10..12,
insert: "E", insert: "E",
@ -1556,6 +1620,10 @@ fn foo(s: S) { s.$0 }
[ [
CompletionItem { CompletionItem {
label: "the_method()", label: "the_method()",
detail_left: None,
detail_right: Some(
"fn(&self)",
),
source_range: 81..81, source_range: 81..81,
delete: 81..81, delete: 81..81,
insert: "the_method();$0", insert: "the_method();$0",
@ -1729,9 +1797,9 @@ fn test(bar: u32) { }
fn foo(s: S) { test(s.$0) } fn foo(s: S) { test(s.$0) }
"#, "#,
expect![[r#" expect![[r#"
fd bar [type+name] fd bar u32 [type+name]
fd baz [type] fd baz u32 [type]
fd foo [] fd foo i64 []
"#]], "#]],
); );
} }
@ -1745,9 +1813,9 @@ struct B { x: (), y: f32, bar: u32 }
fn foo(a: A) { B { bar: a.$0 }; } fn foo(a: A) { B { bar: a.$0 }; }
"#, "#,
expect![[r#" expect![[r#"
fd bar [type+name] fd bar u32 [type+name]
fd baz [type] fd baz u32 [type]
fd foo [] fd foo i64 []
"#]], "#]],
) )
} }
@ -1768,6 +1836,10 @@ fn f() -> i32 {
[ [
CompletionItem { CompletionItem {
label: "0", label: "0",
detail_left: None,
detail_right: Some(
"i32",
),
source_range: 56..57, source_range: 56..57,
delete: 56..57, delete: 56..57,
insert: "0", insert: "0",
@ -1804,9 +1876,9 @@ fn f(foo: i64) { }
fn foo(a: A) { B { bar: f(a.$0) }; } fn foo(a: A) { B { bar: f(a.$0) }; }
"#, "#,
expect![[r#" expect![[r#"
fd foo [type+name] fd foo i64 [type+name]
fd bar [] fd bar u32 []
fd baz [] fd baz u32 []
"#]], "#]],
); );
check_relevance( check_relevance(
@ -1817,9 +1889,9 @@ fn f(foo: i64) { }
fn foo(a: A) { f(B { bar: a.$0 }); } fn foo(a: A) { f(B { bar: a.$0 }); }
"#, "#,
expect![[r#" expect![[r#"
fd bar [type+name] fd bar u32 [type+name]
fd baz [type] fd baz u32 [type]
fd foo [] fd foo i64 []
"#]], "#]],
); );
} }
@ -1832,13 +1904,13 @@ struct WorldSnapshot { _f: () };
fn go(world: &WorldSnapshot) { go(w$0) } fn go(world: &WorldSnapshot) { go(w$0) }
"#, "#,
expect![[r#" expect![[r#"
lc world [type+name+local] lc world &WorldSnapshot [type+name+local]
ex world [type] ex world [type]
st WorldSnapshot {} [] st WorldSnapshot {} WorldSnapshot { _f: () } []
st &WorldSnapshot {} [type] st &WorldSnapshot {} [type]
st WorldSnapshot [] st WorldSnapshot WorldSnapshot []
st &WorldSnapshot [type] st &WorldSnapshot [type]
fn go() [] fn go() fn(&WorldSnapshot) []
"#]], "#]],
); );
} }
@ -1852,9 +1924,9 @@ struct Foo;
fn f(foo: &Foo) { f(foo, w$0) } fn f(foo: &Foo) { f(foo, w$0) }
"#, "#,
expect![[r#" expect![[r#"
lc foo [local] lc foo &Foo [local]
st Foo [] st Foo Foo []
fn f() [] fn f() fn(&Foo) []
"#]], "#]],
); );
} }
@ -1869,12 +1941,12 @@ fn bar() -> u8 { 0 }
fn f() { A { bar: b$0 }; } fn f() { A { bar: b$0 }; }
"#, "#,
expect![[r#" expect![[r#"
fn bar() [type+name] fn bar() fn() -> u8 [type+name]
fn baz() [type] fn baz() fn() -> u8 [type]
ex bar() [type] ex bar() [type]
ex baz() [type] ex baz() [type]
st A [] st A A []
fn f() [] fn f() fn() []
"#]], "#]],
); );
} }
@ -1895,9 +1967,9 @@ fn f() {
} }
"#, "#,
expect![[r#" expect![[r#"
me aaa() [type+name] me aaa() fn(&self) -> u32 [type+name]
me bbb() [type] me bbb() fn(&self) -> u32 [type]
me ccc() [] me ccc() fn(&self) -> u64 []
"#]], "#]],
); );
} }
@ -1916,7 +1988,7 @@ fn f() {
} }
"#, "#,
expect![[r#" expect![[r#"
me aaa() [name] me aaa() fn(&self) -> u64 [name]
"#]], "#]],
); );
} }
@ -1934,14 +2006,14 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
lc s [name+local] lc s S [name+local]
lc &mut s [type+name+local] lc &mut s [type+name+local]
st S [] st S S []
st &mut S [type] st &mut S [type]
st S [] st S S []
st &mut S [type] st &mut S [type]
fn foo() [] fn foo() fn(&mut S) []
fn main() [] fn main() fn() []
"#]], "#]],
); );
check_relevance( check_relevance(
@ -1954,13 +2026,13 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
lc s [type+name+local] lc s S [type+name+local]
st S [type] st S S [type]
st S [type] st S S [type]
ex s [type] ex s [type]
ex S [type] ex S [type]
fn foo() [] fn foo() fn(&mut S) []
fn main() [] fn main() fn() []
"#]], "#]],
); );
check_relevance( check_relevance(
@ -1973,13 +2045,13 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
lc ssss [type+local] lc ssss S [type+local]
st S [type] st S S [type]
st S [type] st S S [type]
ex ssss [type] ex ssss [type]
ex S [type] ex S [type]
fn foo() [] fn foo() fn(&mut S) []
fn main() [] fn main() fn() []
"#]], "#]],
); );
} }
@ -2010,18 +2082,18 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
ex core::ops::Deref::deref(&t) (use core::ops::Deref) [type_could_unify] ex core::ops::Deref::deref(&t) [type_could_unify]
lc m [local] lc m i32 [local]
lc t [local] lc t T [local]
lc &t [type+local] lc &t [type+local]
st S [] st S S []
st &S [type] st &S [type]
st S [] st S S []
st &S [type] st &S [type]
st T [] st T T []
st &T [type] st &T [type]
fn foo() [] fn foo() fn(&S) []
fn main() [] fn main() fn() []
md core [] md core []
"#]], "#]],
) )
@ -2059,18 +2131,18 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
ex core::ops::DerefMut::deref_mut(&mut t) (use core::ops::DerefMut) [type_could_unify] ex core::ops::DerefMut::deref_mut(&mut t) [type_could_unify]
lc m [local] lc m i32 [local]
lc t [local] lc t T [local]
lc &mut t [type+local] lc &mut t [type+local]
st S [] st S S []
st &mut S [type] st &mut S [type]
st S [] st S S []
st &mut S [type] st &mut S [type]
st T [] st T T []
st &mut T [type] st &mut T [type]
fn foo() [] fn foo() fn(&mut S) []
fn main() [] fn main() fn() []
md core [] md core []
"#]], "#]],
) )
@ -2087,9 +2159,9 @@ fn foo(bar: u32) {
} }
"#, "#,
expect![[r#" expect![[r#"
lc baz [local] lc baz i32 [local]
lc bar [local] lc bar u32 [local]
fn foo() [] fn foo() fn(u32) []
"#]], "#]],
); );
} }
@ -2105,13 +2177,13 @@ fn foo() {
fn bar(t: Foo) {} fn bar(t: Foo) {}
"#, "#,
expect![[r#" expect![[r#"
ev Foo::A [type] ev Foo::A Foo::A [type]
ev Foo::B [type] ev Foo::B Foo::B [type]
en Foo [type] en Foo Foo [type]
ex Foo::A [type] ex Foo::A [type]
ex Foo::B [type] ex Foo::B [type]
fn bar() [] fn bar() fn(Foo) []
fn foo() [] fn foo() fn() []
"#]], "#]],
); );
} }
@ -2127,14 +2199,14 @@ fn foo() {
fn bar(t: &Foo) {} fn bar(t: &Foo) {}
"#, "#,
expect![[r#" expect![[r#"
ev Foo::A [] ev Foo::A Foo::A []
ev &Foo::A [type] ev &Foo::A [type]
ev Foo::B [] ev Foo::B Foo::B []
ev &Foo::B [type] ev &Foo::B [type]
en Foo [] en Foo Foo []
en &Foo [type] en &Foo [type]
fn bar() [] fn bar() fn(&Foo) []
fn foo() [] fn foo() fn() []
"#]], "#]],
); );
} }
@ -2163,17 +2235,17 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
ex core::ops::Deref::deref(&bar()) (use core::ops::Deref) [type_could_unify] ex core::ops::Deref::deref(&bar()) [type_could_unify]
st S [] st S S []
st &S [type] st &S [type]
st S [] st S S []
st &S [type] st &S [type]
st T [] st T T []
st &T [type] st &T [type]
fn bar() [] fn bar() fn() -> T []
fn &bar() [type] fn &bar() [type]
fn foo() [] fn foo() fn(&S) []
fn main() [] fn main() fn() []
md core [] md core []
"#]], "#]],
) )
@ -2191,7 +2263,7 @@ impl Sub for u32 {}
fn foo(a: u32) { a.$0 } fn foo(a: u32) { a.$0 }
"#, "#,
expect![[r#" expect![[r#"
me sub() (as Sub) [op_method] me sub() fn(self, Self) -> Self [op_method]
"#]], "#]],
); );
check_relevance( check_relevance(
@ -2212,9 +2284,9 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
fn new() [] fn new() fn() -> Foo []
me eq() (as PartialEq) [op_method] me eq() fn(&self, &Rhs) -> bool [op_method]
me ne() (as PartialEq) [op_method] me ne() fn(&self, &Rhs) -> bool [op_method]
"#]], "#]],
); );
} }
@ -2238,9 +2310,9 @@ fn test() {
} }
"#, "#,
expect![[r#" expect![[r#"
fn fn_ctr() [type_could_unify] fn fn_ctr() fn() -> Foo [type_could_unify]
fn fn_ctr_self() [type_could_unify] fn fn_ctr_self() fn() -> Option<Foo> [type_could_unify]
fn fn_another() [type_could_unify] fn fn_another() fn(u32) -> Other [type_could_unify]
"#]], "#]],
); );
} }
@ -2384,12 +2456,12 @@ fn test() {
// Constructor // Constructor
// Others // Others
expect![[r#" expect![[r#"
fn fn_direct_ctr() [type_could_unify] fn fn_direct_ctr() fn() -> Foo [type_could_unify]
fn fn_ctr_with_args() [type_could_unify] fn fn_ctr_with_args() fn(u32) -> Foo [type_could_unify]
fn fn_builder() [type_could_unify] fn fn_builder() fn() -> FooBuilder [type_could_unify]
fn fn_ctr() [type_could_unify] fn fn_ctr() fn() -> Result<Foo> [type_could_unify]
me fn_no_ret() [type_could_unify] me fn_no_ret() fn(&self) [type_could_unify]
fn fn_other() [type_could_unify] fn fn_other() fn() -> Result<u32> [type_could_unify]
"#]], "#]],
); );
@ -2420,13 +2492,13 @@ fn test() {
} }
"#, "#,
expect![[r#" expect![[r#"
fn fn_direct_ctr() [type_could_unify] fn fn_direct_ctr() fn() -> Foo<T> [type_could_unify]
fn fn_ctr_with_args() [type_could_unify] fn fn_ctr_with_args() fn(T) -> Foo<T> [type_could_unify]
fn fn_builder() [type_could_unify] fn fn_builder() fn() -> FooBuilder [type_could_unify]
fn fn_ctr_wrapped() [type_could_unify] fn fn_ctr_wrapped() fn() -> Option<Foo<T>> [type_could_unify]
fn fn_ctr_wrapped_2() [type_could_unify] fn fn_ctr_wrapped_2() fn() -> Result<Foo<T>, u32> [type_could_unify]
me fn_returns_unit() [type_could_unify] me fn_returns_unit() fn(&self) [type_could_unify]
fn fn_other() [type_could_unify] fn fn_other() fn() -> Option<u32> [type_could_unify]
"#]], "#]],
); );
} }
@ -2456,13 +2528,13 @@ fn test() {
} }
"#, "#,
expect![[r#" expect![[r#"
fn fn_direct_ctr() [type_could_unify] fn fn_direct_ctr() fn() -> Foo<T> [type_could_unify]
fn fn_ctr_with_args() [type_could_unify] fn fn_ctr_with_args() fn(T) -> Foo<T> [type_could_unify]
fn fn_builder() [type_could_unify] fn fn_builder() fn() -> FooBuilder [type_could_unify]
fn fn_ctr() [type_could_unify] fn fn_ctr() fn() -> Option<Foo<T>> [type_could_unify]
fn fn_ctr2() [type_could_unify] fn fn_ctr2() fn() -> Result<Foo<T>, u32> [type_could_unify]
me fn_no_ret() [type_could_unify] me fn_no_ret() fn(&self) [type_could_unify]
fn fn_other() [type_could_unify] fn fn_other() fn() -> Option<u32> [type_could_unify]
"#]], "#]],
); );
} }
@ -2484,6 +2556,10 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 }
[ [
CompletionItem { CompletionItem {
label: "baz()", label: "baz()",
detail_left: None,
detail_right: Some(
"fn(&self) -> u32",
),
source_range: 109..110, source_range: 109..110,
delete: 109..110, delete: 109..110,
insert: "baz()$0", insert: "baz()$0",
@ -2513,6 +2589,10 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 }
}, },
CompletionItem { CompletionItem {
label: "bar", label: "bar",
detail_left: None,
detail_right: Some(
"u32",
),
source_range: 109..110, source_range: 109..110,
delete: 109..110, delete: 109..110,
insert: "bar", insert: "bar",
@ -2524,6 +2604,10 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 }
}, },
CompletionItem { CompletionItem {
label: "qux", label: "qux",
detail_left: None,
detail_right: Some(
"fn()",
),
source_range: 109..110, source_range: 109..110,
text_edit: TextEdit { text_edit: TextEdit {
indels: [ indels: [
@ -2562,6 +2646,10 @@ fn foo() {
[ [
CompletionItem { CompletionItem {
label: "field", label: "field",
detail_left: None,
detail_right: Some(
"fn()",
),
source_range: 76..78, source_range: 76..78,
delete: 76..78, delete: 76..78,
insert: "field", insert: "field",
@ -2610,6 +2698,10 @@ fn main() {
[ [
CompletionItem { CompletionItem {
label: "foo()", label: "foo()",
detail_left: None,
detail_right: Some(
"fn() -> S",
),
source_range: 95..95, source_range: 95..95,
delete: 95..95, delete: 95..95,
insert: "foo()$0", insert: "foo()$0",
@ -2661,15 +2753,15 @@ fn foo() {
} }
"#, "#,
expect![[r#" expect![[r#"
lc foo [type+local] lc foo Foo<u32> [type+local]
ex foo [type] ex foo [type]
ex Foo::B [type] ex Foo::B [type]
ev Foo::A() [type_could_unify] ev Foo::A() Foo::A(T) [type_could_unify]
ev Foo::B [type_could_unify] ev Foo::B Foo::B [type_could_unify]
en Foo [type_could_unify] en Foo Foo<{unknown}> [type_could_unify]
fn foo() [] fn foo() fn() []
fn bar() [] fn bar() fn() -> Foo<u8> []
fn baz() [] fn baz() fn() -> Foo<T> []
"#]], "#]],
); );
} }
@ -2697,20 +2789,20 @@ fn main() {
"#, "#,
&[CompletionItemKind::Snippet, CompletionItemKind::SymbolKind(SymbolKind::Method)], &[CompletionItemKind::Snippet, CompletionItemKind::SymbolKind(SymbolKind::Method)],
expect![[r#" expect![[r#"
sn not [snippet] sn not !expr [snippet]
me not() (use ops::Not) [type_could_unify+requires_import] me not() fn(self) -> <Self as Not>::Output [type_could_unify+requires_import]
sn if [] sn if if expr {} []
sn while [] sn while while expr {} []
sn ref [] sn ref &expr []
sn refm [] sn refm &mut expr []
sn deref [] sn deref *expr []
sn unsafe [] sn unsafe unsafe {} []
sn match [] sn match match expr {} []
sn box [] sn box Box::new(expr) []
sn dbg [] sn dbg dbg!(expr) []
sn dbgr [] sn dbgr dbg!(&expr) []
sn call [] sn call function(expr) []
sn return [] sn return return expr []
"#]], "#]],
); );
} }
@ -2730,19 +2822,19 @@ fn main() {
"#, "#,
&[CompletionItemKind::Snippet, CompletionItemKind::SymbolKind(SymbolKind::Method)], &[CompletionItemKind::Snippet, CompletionItemKind::SymbolKind(SymbolKind::Method)],
expect![[r#" expect![[r#"
me f() [] me f() fn(&self) []
sn ref [] sn ref &expr []
sn refm [] sn refm &mut expr []
sn deref [] sn deref *expr []
sn unsafe [] sn unsafe unsafe {} []
sn match [] sn match match expr {} []
sn box [] sn box Box::new(expr) []
sn dbg [] sn dbg dbg!(expr) []
sn dbgr [] sn dbgr dbg!(&expr) []
sn call [] sn call function(expr) []
sn let [] sn let let []
sn letm [] sn letm let mut []
sn return [] sn return return expr []
"#]], "#]],
); );
} }
@ -2765,12 +2857,12 @@ fn f() {
} }
"#, "#,
expect![[r#" expect![[r#"
st Buffer [] st Buffer Buffer []
fn f() [] fn f() fn() []
md std [] md std []
tt BufRead (use std::io::BufRead) [requires_import] tt BufRead [requires_import]
st BufReader (use std::io::BufReader) [requires_import] st BufReader BufReader [requires_import]
st BufWriter (use std::io::BufWriter) [requires_import] st BufWriter BufWriter [requires_import]
"#]], "#]],
); );
} }
@ -2979,6 +3071,12 @@ fn main() {
[ [
CompletionItem { CompletionItem {
label: "flush()", label: "flush()",
detail_left: Some(
"(as Write)",
),
detail_right: Some(
"fn(&self)",
),
source_range: 193..193, source_range: 193..193,
delete: 193..193, delete: 193..193,
insert: "flush();$0", insert: "flush();$0",
@ -3006,6 +3104,12 @@ fn main() {
}, },
CompletionItem { CompletionItem {
label: "write()", label: "write()",
detail_left: Some(
"(as Write)",
),
detail_right: Some(
"fn(&self)",
),
source_range: 193..193, source_range: 193..193,
delete: 193..193, delete: 193..193,
insert: "write();$0", insert: "write();$0",

View file

@ -118,10 +118,16 @@ fn completion_list_with_config_raw(
let items = get_all_items(config, ra_fixture, trigger_character); let items = get_all_items(config, ra_fixture, trigger_character);
items items
.into_iter() .into_iter()
.filter(|it| it.kind != CompletionItemKind::BuiltinType || it.label == "u32") .filter(|it| it.kind != CompletionItemKind::BuiltinType || it.label.primary == "u32")
.filter(|it| include_keywords || it.kind != CompletionItemKind::Keyword) .filter(|it| include_keywords || it.kind != CompletionItemKind::Keyword)
.filter(|it| include_keywords || it.kind != CompletionItemKind::Snippet) .filter(|it| include_keywords || it.kind != CompletionItemKind::Snippet)
.sorted_by_key(|it| (it.kind, it.label.clone(), it.detail.as_ref().map(ToOwned::to_owned))) .sorted_by_key(|it| {
(
it.kind,
it.label.primary.clone(),
it.label.detail_left.as_ref().map(ToOwned::to_owned),
)
})
.collect() .collect()
} }
@ -173,27 +179,30 @@ fn render_completion_list(completions: Vec<CompletionItem>) -> String {
let label_width = completions let label_width = completions
.iter() .iter()
.map(|it| { .map(|it| {
monospace_width(&it.label) monospace_width(&it.label.primary)
+ monospace_width(it.label_detail.as_deref().unwrap_or_default()) + monospace_width(it.label.detail_left.as_deref().unwrap_or_default())
+ monospace_width(it.label.detail_right.as_deref().unwrap_or_default())
+ it.label.detail_left.is_some() as usize
+ it.label.detail_right.is_some() as usize
}) })
.max() .max()
.unwrap_or_default() .unwrap_or_default();
.min(22);
completions completions
.into_iter() .into_iter()
.map(|it| { .map(|it| {
let tag = it.kind.tag(); let tag = it.kind.tag();
let var_name = format!("{tag} {}", it.label); let mut buf = format!("{tag} {}", it.label.primary);
let mut buf = var_name; if let Some(label_detail) = &it.label.detail_left {
if let Some(ref label_detail) = it.label_detail { format_to!(buf, " {label_detail}");
format_to!(buf, "{label_detail}");
} }
if let Some(detail) = it.detail { if let Some(detail_right) = it.label.detail_right {
let width = label_width.saturating_sub( let pad_with = label_width.saturating_sub(
monospace_width(&it.label) monospace_width(&it.label.primary)
+ monospace_width(&it.label_detail.unwrap_or_default()), + monospace_width(it.label.detail_left.as_deref().unwrap_or_default())
+ monospace_width(&detail_right)
+ it.label.detail_left.is_some() as usize,
); );
format_to!(buf, "{:width$} {}", "", detail, width = width); format_to!(buf, "{:pad_with$}{detail_right}", "",);
} }
if it.deprecated { if it.deprecated {
format_to!(buf, " DEPRECATED"); format_to!(buf, " DEPRECATED");

View file

@ -692,9 +692,9 @@ fn main() {
Foo::$0 Foo::$0
} }
", ",
expect![[r" expect![[r#"
fn bar() fn(impl Trait<U>) fn bar() fn(impl Trait<U>)
"]], "#]],
); );
} }

View file

@ -1315,10 +1315,9 @@ use krate::e;
fn main() { fn main() {
e::$0 e::$0
}"#, }"#,
expect![ expect![[r#"
"fn i_am_public() fn() fn i_am_public() fn()
" "#]],
],
) )
} }

View file

@ -1144,7 +1144,7 @@ pub(crate) fn handle_completion_resolve(
let Some(corresponding_completion) = completions.into_iter().find(|completion_item| { let Some(corresponding_completion) = completions.into_iter().find(|completion_item| {
// Avoid computing hashes for items that obviously do not match // Avoid computing hashes for items that obviously do not match
// r-a might append a detail-based suffix to the label, so we cannot check for equality // r-a might append a detail-based suffix to the label, so we cannot check for equality
original_completion.label.starts_with(completion_item.label.as_str()) original_completion.label.starts_with(completion_item.label.primary.as_str())
&& resolve_data_hash == completion_item_hash(completion_item, resolve_data.for_ref) && resolve_data_hash == completion_item_hash(completion_item, resolve_data.for_ref)
}) else { }) else {
return Ok(original_completion); return Ok(original_completion);

View file

@ -114,8 +114,11 @@ fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8;
u8::from(item.deprecated), u8::from(item.deprecated),
u8::from(item.trigger_call_info), u8::from(item.trigger_call_info),
]); ]);
hasher.update(&item.label); hasher.update(&item.label.primary);
if let Some(label_detail) = &item.label_detail { if let Some(label_detail) = &item.label.detail_left {
hasher.update(label_detail);
}
if let Some(label_detail) = &item.label.detail_right {
hasher.update(label_detail); hasher.update(label_detail);
} }
// NB: do not hash edits or source range, as those may change between the time the client sends the resolve request // NB: do not hash edits or source range, as those may change between the time the client sends the resolve request

View file

@ -354,7 +354,7 @@ fn completion_item(
}; };
let mut lsp_item = lsp_types::CompletionItem { let mut lsp_item = lsp_types::CompletionItem {
label: item.label.to_string(), label: item.label.primary.to_string(),
detail, detail,
filter_text, filter_text,
kind: Some(completion_item_kind(item.kind)), kind: Some(completion_item_kind(item.kind)),
@ -374,13 +374,13 @@ fn completion_item(
if config.completion_label_details_support() { if config.completion_label_details_support() {
if fields_to_resolve.resolve_label_details { if fields_to_resolve.resolve_label_details {
something_to_resolve |= true; something_to_resolve |= true;
} else if item.label_detail.is_some() || item.detail.is_some() { } else if item.label.detail_left.is_some() || item.label.detail_left.is_some() {
lsp_item.label_details = Some(lsp_types::CompletionItemLabelDetails { lsp_item.label_details = Some(lsp_types::CompletionItemLabelDetails {
detail: item.label_detail.as_ref().map(ToString::to_string), detail: item.label.detail_left.clone(),
description: item.detail.clone(), description: item.label.detail_right.clone(),
}); });
} }
} else if let Some(label_detail) = &item.label_detail { } else if let Some(label_detail) = &item.label.detail_left {
lsp_item.label.push_str(label_detail.as_str()); lsp_item.label.push_str(label_detail.as_str());
} }