Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-completion/src/render.rs')
-rw-r--r--crates/ide-completion/src/render.rs443
1 files changed, 442 insertions, 1 deletions
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index 2ed080a834..3f374b307f 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -17,7 +17,7 @@ use ide_db::{
imports::import_assets::LocatedImport,
RootDatabase, SnippetCap, SymbolKind,
};
-use syntax::{format_smolstr, AstNode, SmolStr, SyntaxKind, TextRange};
+use syntax::{ast, format_smolstr, AstNode, SmolStr, SyntaxKind, TextRange};
use text_edit::TextEdit;
use crate::{
@@ -272,6 +272,82 @@ pub(crate) fn render_resolution_with_import_pat(
Some(render_resolution_pat(ctx, pattern_ctx, local_name, Some(import_edit), resolution))
}
+pub(crate) fn render_expr(
+ ctx: &CompletionContext<'_>,
+ expr: &hir::term_search::Expr,
+) -> Option<Builder> {
+ let mut i = 1;
+ let mut snippet_formatter = |ty: &hir::Type| {
+ let arg_name = ty
+ .as_adt()
+ .and_then(|adt| adt.name(ctx.db).as_text())
+ .map(|s| stdx::to_lower_snake_case(s.as_str()))
+ .unwrap_or_else(|| String::from("_"));
+ let res = format!("${{{i}:{arg_name}}}");
+ i += 1;
+ res
+ };
+
+ let mut label_formatter = |ty: &hir::Type| {
+ ty.as_adt()
+ .and_then(|adt| adt.name(ctx.db).as_text())
+ .map(|s| stdx::to_lower_snake_case(s.as_str()))
+ .unwrap_or_else(|| String::from("..."))
+ };
+
+ let label = expr
+ .gen_source_code(
+ &ctx.scope,
+ &mut label_formatter,
+ ctx.config.prefer_no_std,
+ ctx.config.prefer_prelude,
+ )
+ .ok()?;
+
+ let source_range = match ctx.original_token.parent() {
+ Some(node) => match node.ancestors().find_map(ast::Path::cast) {
+ Some(path) => path.syntax().text_range(),
+ None => node.text_range(),
+ },
+ None => ctx.source_range(),
+ };
+
+ let mut item = CompletionItem::new(CompletionItemKind::Expression, source_range, label.clone());
+
+ let snippet = format!(
+ "{}$0",
+ expr.gen_source_code(
+ &ctx.scope,
+ &mut snippet_formatter,
+ ctx.config.prefer_no_std,
+ ctx.config.prefer_prelude
+ )
+ .ok()?
+ );
+ let edit = TextEdit::replace(source_range, snippet);
+ item.snippet_edit(ctx.config.snippet_cap?, edit);
+ item.documentation(Documentation::new(String::from("Autogenerated expression by term search")));
+ item.set_relevance(crate::CompletionRelevance {
+ type_match: compute_type_match(ctx, &expr.ty(ctx.db)),
+ ..Default::default()
+ });
+ for trait_ in expr.traits_used(ctx.db) {
+ let trait_item = hir::ItemInNs::from(hir::ModuleDef::from(trait_));
+ let Some(path) = ctx.module.find_use_path(
+ ctx.db,
+ trait_item,
+ ctx.config.prefer_no_std,
+ ctx.config.prefer_prelude,
+ ) else {
+ continue;
+ };
+
+ item.add_import(LocatedImport::new(path, trait_item, trait_item));
+ }
+
+ Some(item)
+}
+
fn scope_def_to_name(
resolution: ScopeDef,
ctx: &RenderContext<'_>,
@@ -600,6 +676,16 @@ mod tests {
}
#[track_caller]
+ fn check_function_relevance(ra_fixture: &str, expect: Expect) {
+ let actual: Vec<_> = do_completion(ra_fixture, CompletionItemKind::Method)
+ .into_iter()
+ .map(|item| (item.detail.unwrap_or_default(), item.relevance.function))
+ .collect();
+
+ expect.assert_debug_eq(&actual);
+ }
+
+ #[track_caller]
fn check_relevance_for_kinds(ra_fixture: &str, kinds: &[CompletionItemKind], expect: Expect) {
let mut actual = get_all_items(TEST_CONFIG, ra_fixture, None);
actual.retain(|it| kinds.contains(&it.kind));
@@ -961,6 +1047,7 @@ fn func(input: Struct) { }
st Self [type]
sp Self [type]
st Struct [type]
+ ex Struct [type]
lc self [local]
fn func(…) []
me self.test() []
@@ -985,6 +1072,9 @@ fn main() {
"#,
expect![[r#"
lc input [type+name+local]
+ ex input [type]
+ ex true [type]
+ ex false [type]
lc inputbad [local]
fn main() []
fn test(…) []
@@ -1174,6 +1264,7 @@ fn main() { let _: m::Spam = S$0 }
is_private_editable: false,
postfix_match: None,
is_definite: false,
+ function: None,
},
trigger_call_info: true,
},
@@ -1201,6 +1292,7 @@ fn main() { let _: m::Spam = S$0 }
is_private_editable: false,
postfix_match: None,
is_definite: false,
+ function: None,
},
trigger_call_info: true,
},
@@ -1280,6 +1372,7 @@ fn foo() { A { the$0 } }
is_private_editable: false,
postfix_match: None,
is_definite: false,
+ function: None,
},
},
]
@@ -1313,6 +1406,26 @@ impl S {
documentation: Documentation(
"Method docs",
),
+ relevance: CompletionRelevance {
+ exact_name_match: false,
+ type_match: None,
+ is_local: false,
+ is_item_from_trait: false,
+ is_item_from_notable_trait: false,
+ is_name_already_imported: false,
+ requires_import: false,
+ is_op_method: false,
+ is_private_editable: false,
+ postfix_match: None,
+ is_definite: false,
+ function: Some(
+ CompletionRelevanceFn {
+ has_params: true,
+ has_self_param: true,
+ return_type: Other,
+ },
+ ),
+ },
},
CompletionItem {
label: "foo",
@@ -1418,6 +1531,26 @@ fn foo(s: S) { s.$0 }
kind: Method,
lookup: "the_method",
detail: "fn(&self)",
+ relevance: CompletionRelevance {
+ exact_name_match: false,
+ type_match: None,
+ is_local: false,
+ is_item_from_trait: false,
+ is_item_from_notable_trait: false,
+ is_name_already_imported: false,
+ requires_import: false,
+ is_op_method: false,
+ is_private_editable: false,
+ postfix_match: None,
+ is_definite: false,
+ function: Some(
+ CompletionRelevanceFn {
+ has_params: true,
+ has_self_param: true,
+ return_type: Other,
+ },
+ ),
+ },
},
]
"#]],
@@ -1665,6 +1798,10 @@ fn f() { A { bar: b$0 }; }
expect![[r#"
fn bar() [type+name]
fn baz() [type]
+ ex baz() [type]
+ ex bar() [type]
+ ex A { bar: baz() }.bar [type]
+ ex A { bar: bar() }.bar [type]
st A []
fn f() []
"#]],
@@ -1749,6 +1886,8 @@ fn main() {
lc s [type+name+local]
st S [type]
st S [type]
+ ex s [type]
+ ex S [type]
fn foo(…) []
fn main() []
"#]],
@@ -1766,6 +1905,8 @@ fn main() {
lc ssss [type+local]
st S [type]
st S [type]
+ ex ssss [type]
+ ex S [type]
fn foo(…) []
fn main() []
"#]],
@@ -1798,6 +1939,8 @@ fn main() {
}
"#,
expect![[r#"
+ ex core::ops::Deref::deref(&T(S)) (use core::ops::Deref) [type_could_unify]
+ ex core::ops::Deref::deref(&t) (use core::ops::Deref) [type_could_unify]
lc m [local]
lc t [local]
lc &t [type+local]
@@ -1846,6 +1989,8 @@ fn main() {
}
"#,
expect![[r#"
+ ex core::ops::DerefMut::deref_mut(&mut T(S)) (use core::ops::DerefMut) [type_could_unify]
+ ex core::ops::DerefMut::deref_mut(&mut t) (use core::ops::DerefMut) [type_could_unify]
lc m [local]
lc t [local]
lc &mut t [type+local]
@@ -1894,6 +2039,8 @@ fn bar(t: Foo) {}
ev Foo::A [type]
ev Foo::B [type]
en Foo [type]
+ ex Foo::A [type]
+ ex Foo::B [type]
fn bar(…) []
fn foo() []
"#]],
@@ -1947,6 +2094,8 @@ fn main() {
}
"#,
expect![[r#"
+ ex core::ops::Deref::deref(&T(S)) (use core::ops::Deref) [type_could_unify]
+ ex core::ops::Deref::deref(&bar()) (use core::ops::Deref) [type_could_unify]
st S []
st &S [type]
st S []
@@ -2003,6 +2152,254 @@ fn main() {
}
#[test]
+ fn constructor_order_simple() {
+ check_relevance(
+ r#"
+struct Foo;
+struct Other;
+struct Option<T>(T);
+
+impl Foo {
+ fn fn_ctr() -> Foo { unimplemented!() }
+ fn fn_another(n: u32) -> Other { unimplemented!() }
+ fn fn_ctr_self() -> Option<Self> { unimplemented!() }
+}
+
+fn test() {
+ let a = Foo::$0;
+}
+"#,
+ expect![[r#"
+ fn fn_ctr() [type_could_unify]
+ fn fn_ctr_self() [type_could_unify]
+ fn fn_another(…) [type_could_unify]
+ "#]],
+ );
+ }
+
+ #[test]
+ fn constructor_order_kind() {
+ check_function_relevance(
+ r#"
+struct Foo;
+struct Bar;
+struct Option<T>(T);
+enum Result<T, E> { Ok(T), Err(E) };
+
+impl Foo {
+ fn fn_ctr(&self) -> Foo { unimplemented!() }
+ fn fn_ctr_with_args(&self, n: u32) -> Foo { unimplemented!() }
+ fn fn_another(&self, n: u32) -> Bar { unimplemented!() }
+ fn fn_ctr_wrapped(&self, ) -> Option<Self> { unimplemented!() }
+ fn fn_ctr_wrapped_2(&self, ) -> Result<Self, Bar> { unimplemented!() }
+ fn fn_ctr_wrapped_3(&self, ) -> Result<Bar, Self> { unimplemented!() } // Self is not the first type
+ fn fn_ctr_wrapped_with_args(&self, m: u32) -> Option<Self> { unimplemented!() }
+ fn fn_another_unit(&self) { unimplemented!() }
+}
+
+fn test() {
+ let a = self::Foo::$0;
+}
+"#,
+ expect![[r#"
+ [
+ (
+ "fn(&self, u32) -> Bar",
+ Some(
+ CompletionRelevanceFn {
+ has_params: true,
+ has_self_param: true,
+ return_type: Other,
+ },
+ ),
+ ),
+ (
+ "fn(&self)",
+ Some(
+ CompletionRelevanceFn {
+ has_params: true,
+ has_self_param: true,
+ return_type: Other,
+ },
+ ),
+ ),
+ (
+ "fn(&self) -> Foo",
+ Some(
+ CompletionRelevanceFn {
+ has_params: true,
+ has_self_param: true,
+ return_type: DirectConstructor,
+ },
+ ),
+ ),
+ (
+ "fn(&self, u32) -> Foo",
+ Some(
+ CompletionRelevanceFn {
+ has_params: true,
+ has_self_param: true,
+ return_type: DirectConstructor,
+ },
+ ),
+ ),
+ (
+ "fn(&self) -> Option<Foo>",
+ Some(
+ CompletionRelevanceFn {
+ has_params: true,
+ has_self_param: true,
+ return_type: Constructor,
+ },
+ ),
+ ),
+ (
+ "fn(&self) -> Result<Foo, Bar>",
+ Some(
+ CompletionRelevanceFn {
+ has_params: true,
+ has_self_param: true,
+ return_type: Constructor,
+ },
+ ),
+ ),
+ (
+ "fn(&self) -> Result<Bar, Foo>",
+ Some(
+ CompletionRelevanceFn {
+ has_params: true,
+ has_self_param: true,
+ return_type: Constructor,
+ },
+ ),
+ ),
+ (
+ "fn(&self, u32) -> Option<Foo>",
+ Some(
+ CompletionRelevanceFn {
+ has_params: true,
+ has_self_param: true,
+ return_type: Constructor,
+ },
+ ),
+ ),
+ ]
+ "#]],
+ );
+ }
+
+ #[test]
+ fn constructor_order_relevance() {
+ check_relevance(
+ r#"
+struct Foo;
+struct FooBuilder;
+struct Result<T>(T);
+
+impl Foo {
+ fn fn_no_ret(&self) {}
+ fn fn_ctr_with_args(input: u32) -> Foo { unimplemented!() }
+ fn fn_direct_ctr() -> Self { unimplemented!() }
+ fn fn_ctr() -> Result<Self> { unimplemented!() }
+ fn fn_other() -> Result<u32> { unimplemented!() }
+ fn fn_builder() -> FooBuilder { unimplemented!() }
+}
+
+fn test() {
+ let a = self::Foo::$0;
+}
+"#,
+ // preference:
+ // Direct Constructor
+ // Direct Constructor with args
+ // Builder
+ // Constructor
+ // Others
+ expect![[r#"
+ fn fn_direct_ctr() [type_could_unify]
+ fn fn_ctr_with_args(…) [type_could_unify]
+ fn fn_builder() [type_could_unify]
+ fn fn_ctr() [type_could_unify]
+ me fn_no_ret(…) [type_could_unify]
+ fn fn_other() [type_could_unify]
+ "#]],
+ );
+
+ //
+ }
+
+ #[test]
+ fn function_relevance_generic_1() {
+ check_relevance(
+ r#"
+struct Foo<T: Default>(T);
+struct FooBuilder;
+struct Option<T>(T);
+enum Result<T, E>{Ok(T), Err(E)};
+
+impl<T: Default> Foo<T> {
+ fn fn_returns_unit(&self) {}
+ fn fn_ctr_with_args(input: T) -> Foo<T> { unimplemented!() }
+ fn fn_direct_ctr() -> Self { unimplemented!() }
+ fn fn_ctr_wrapped() -> Option<Self> { unimplemented!() }
+ fn fn_ctr_wrapped_2() -> Result<Self, u32> { unimplemented!() }
+ fn fn_other() -> Option<u32> { unimplemented!() }
+ fn fn_builder() -> FooBuilder { unimplemented!() }
+}
+
+fn test() {
+ let a = self::Foo::<u32>::$0;
+}
+ "#,
+ expect![[r#"
+ fn fn_direct_ctr() [type_could_unify]
+ fn fn_ctr_with_args(…) [type_could_unify]
+ fn fn_builder() [type_could_unify]
+ fn fn_ctr_wrapped() [type_could_unify]
+ fn fn_ctr_wrapped_2() [type_could_unify]
+ me fn_returns_unit(…) [type_could_unify]
+ fn fn_other() [type_could_unify]
+ "#]],
+ );
+ }
+
+ #[test]
+ fn function_relevance_generic_2() {
+ // Generic 2
+ check_relevance(
+ r#"
+struct Foo<T: Default>(T);
+struct FooBuilder;
+struct Option<T>(T);
+enum Result<T, E>{Ok(T), Err(E)};
+
+impl<T: Default> Foo<T> {
+ fn fn_no_ret(&self) {}
+ fn fn_ctr_with_args(input: T) -> Foo<T> { unimplemented!() }
+ fn fn_direct_ctr() -> Self { unimplemented!() }
+ fn fn_ctr() -> Option<Self> { unimplemented!() }
+ fn fn_ctr2() -> Result<Self, u32> { unimplemented!() }
+ fn fn_other() -> Option<u32> { unimplemented!() }
+ fn fn_builder() -> FooBuilder { unimplemented!() }
+}
+
+fn test() {
+ let a : Res<Foo<u32>> = Foo::$0;
+}
+ "#,
+ expect![[r#"
+ fn fn_direct_ctr() [type_could_unify]
+ fn fn_ctr_with_args(…) [type_could_unify]
+ fn fn_builder() [type_could_unify]
+ fn fn_ctr() [type_could_unify]
+ fn fn_ctr2() [type_could_unify]
+ me fn_no_ret(…) [type_could_unify]
+ fn fn_other() [type_could_unify]
+ "#]],
+ );
+ }
+
+ #[test]
fn struct_field_method_ref() {
check_kinds(
r#"
@@ -2022,6 +2419,26 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 }
kind: Method,
lookup: "baz",
detail: "fn(&self) -> u32",
+ relevance: CompletionRelevance {
+ exact_name_match: false,
+ type_match: None,
+ is_local: false,
+ is_item_from_trait: false,
+ is_item_from_notable_trait: false,
+ is_name_already_imported: false,
+ requires_import: false,
+ is_op_method: false,
+ is_private_editable: false,
+ postfix_match: None,
+ is_definite: false,
+ function: Some(
+ CompletionRelevanceFn {
+ has_params: true,
+ has_self_param: true,
+ return_type: Other,
+ },
+ ),
+ },
ref_match: "&@107",
},
CompletionItem {
@@ -2096,6 +2513,7 @@ fn foo() {
is_private_editable: false,
postfix_match: None,
is_definite: false,
+ function: None,
},
},
]
@@ -2133,6 +2551,26 @@ fn main() {
),
lookup: "foo",
detail: "fn() -> S",
+ relevance: CompletionRelevance {
+ exact_name_match: false,
+ type_match: None,
+ is_local: false,
+ is_item_from_trait: false,
+ is_item_from_notable_trait: false,
+ is_name_already_imported: false,
+ requires_import: false,
+ is_op_method: false,
+ is_private_editable: false,
+ postfix_match: None,
+ is_definite: false,
+ function: Some(
+ CompletionRelevanceFn {
+ has_params: false,
+ has_self_param: false,
+ return_type: Other,
+ },
+ ),
+ },
ref_match: "&@92",
},
]
@@ -2160,6 +2598,7 @@ fn foo() {
"#,
expect![[r#"
lc foo [type+local]
+ ex foo [type]
ev Foo::A(…) [type_could_unify]
ev Foo::B [type_could_unify]
en Foo [type_could_unify]
@@ -2493,6 +2932,7 @@ fn main() {
is_private_editable: false,
postfix_match: None,
is_definite: false,
+ function: None,
},
},
CompletionItem {
@@ -2515,6 +2955,7 @@ fn main() {
is_private_editable: false,
postfix_match: None,
is_definite: false,
+ function: None,
},
},
]