aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion
diff options
context:
space:
mode:
authorrdambrosio <[email protected]>2021-06-18 00:54:28 +0100
committerrdambrosio <[email protected]>2021-06-18 00:54:28 +0100
commit8e08b86304f8cf91e06f64855c8de306ad7efaa4 (patch)
tree134d42353a19d61fd7ebefd129fd7924c1d205f4 /crates/ide_completion
parent0d863ccea96c6c3256fad12807a0eedbfccd8294 (diff)
Feat: inline generics in const and func trait completions
Diffstat (limited to 'crates/ide_completion')
-rw-r--r--crates/ide_completion/src/completions/trait_impl.rs279
1 files changed, 252 insertions, 27 deletions
diff --git a/crates/ide_completion/src/completions/trait_impl.rs b/crates/ide_completion/src/completions/trait_impl.rs
index dc1d198cc..1f6b959af 100644
--- a/crates/ide_completion/src/completions/trait_impl.rs
+++ b/crates/ide_completion/src/completions/trait_impl.rs
@@ -32,7 +32,7 @@
32//! ``` 32//! ```
33 33
34use hir::{self, HasAttrs, HasSource}; 34use hir::{self, HasAttrs, HasSource};
35use ide_db::{traits::get_missing_assoc_items, SymbolKind}; 35use ide_db::{path_transform::PathTransform, traits::get_missing_assoc_items, SymbolKind};
36use syntax::{ 36use syntax::{
37 ast::{self, edit}, 37 ast::{self, edit},
38 display::function_declaration, 38 display::function_declaration,
@@ -56,7 +56,9 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext
56 hir::AssocItem::Function(fn_item) 56 hir::AssocItem::Function(fn_item)
57 if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Fn => 57 if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Fn =>
58 { 58 {
59 add_function_impl(&trigger, acc, ctx, fn_item) 59 if let Some(impl_def) = ctx.sema.to_def(&impl_def) {
60 add_function_impl(&trigger, acc, ctx, fn_item, impl_def)
61 }
60 } 62 }
61 hir::AssocItem::TypeAlias(type_item) 63 hir::AssocItem::TypeAlias(type_item)
62 if kind == ImplCompletionKind::All || kind == ImplCompletionKind::TypeAlias => 64 if kind == ImplCompletionKind::All || kind == ImplCompletionKind::TypeAlias =>
@@ -66,7 +68,9 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext
66 hir::AssocItem::Const(const_item) 68 hir::AssocItem::Const(const_item)
67 if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Const => 69 if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Const =>
68 { 70 {
69 add_const_impl(&trigger, acc, ctx, const_item) 71 if let Some(impl_def) = ctx.sema.to_def(&impl_def) {
72 add_const_impl(&trigger, acc, ctx, const_item, impl_def)
73 }
70 } 74 }
71 _ => {} 75 _ => {}
72 }); 76 });
@@ -129,6 +133,7 @@ fn add_function_impl(
129 acc: &mut Completions, 133 acc: &mut Completions,
130 ctx: &CompletionContext, 134 ctx: &CompletionContext,
131 func: hir::Function, 135 func: hir::Function,
136 impl_def: hir::Impl,
132) { 137) {
133 let fn_name = func.name(ctx.db).to_string(); 138 let fn_name = func.name(ctx.db).to_string();
134 139
@@ -147,23 +152,55 @@ fn add_function_impl(
147 CompletionItemKind::SymbolKind(SymbolKind::Function) 152 CompletionItemKind::SymbolKind(SymbolKind::Function)
148 }; 153 };
149 let range = replacement_range(ctx, fn_def_node); 154 let range = replacement_range(ctx, fn_def_node);
150 if let Some(src) = func.source(ctx.db) { 155
151 let function_decl = function_declaration(&src.value); 156 if let Some(source) = func.source(ctx.db) {
152 match ctx.config.snippet_cap { 157 let assoc_item = ast::AssocItem::Fn(source.value);
153 Some(cap) => { 158 if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) {
154 let snippet = format!("{} {{\n $0\n}}", function_decl); 159 let transformed_fn = match transformed_item {
155 item.snippet_edit(cap, TextEdit::replace(range, snippet)); 160 ast::AssocItem::Fn(func) => func,
156 } 161 _ => unreachable!(),
157 None => { 162 };
158 let header = format!("{} {{", function_decl); 163
159 item.text_edit(TextEdit::replace(range, header)); 164 let function_decl = function_declaration(&transformed_fn);
160 } 165 match ctx.config.snippet_cap {
161 }; 166 Some(cap) => {
162 item.kind(completion_kind); 167 let snippet = format!("{} {{\n $0\n}}", function_decl);
163 item.add_to(acc); 168 item.snippet_edit(cap, TextEdit::replace(range, snippet));
169 }
170 None => {
171 let header = format!("{} {{", function_decl);
172 item.text_edit(TextEdit::replace(range, header));
173 }
174 };
175 item.kind(completion_kind);
176 item.add_to(acc);
177 }
164 } 178 }
165} 179}
166 180
181/// Transform a relevant associated item to inline generics from the impl, remove attrs and docs, etc.
182fn get_transformed_assoc_item(
183 ctx: &CompletionContext,
184 assoc_item: ast::AssocItem,
185 impl_def: hir::Impl,
186) -> Option<ast::AssocItem> {
187 let assoc_item = assoc_item.clone_for_update();
188 let trait_ = impl_def.trait_(ctx.db)?;
189 let source_scope = &ctx.sema.scope_for_def(trait_);
190 let target_scope = &ctx.sema.scope(impl_def.source(ctx.db)?.syntax().value);
191 let transform = PathTransform {
192 subst: (trait_, impl_def.source(ctx.db)?.value),
193 source_scope,
194 target_scope,
195 };
196
197 transform.apply(assoc_item.clone());
198 Some(match assoc_item {
199 ast::AssocItem::Fn(func) => ast::AssocItem::Fn(edit::remove_attrs_and_docs(&func)),
200 _ => assoc_item,
201 })
202}
203
167fn add_type_alias_impl( 204fn add_type_alias_impl(
168 type_def_node: &SyntaxNode, 205 type_def_node: &SyntaxNode,
169 acc: &mut Completions, 206 acc: &mut Completions,
@@ -188,21 +225,30 @@ fn add_const_impl(
188 acc: &mut Completions, 225 acc: &mut Completions,
189 ctx: &CompletionContext, 226 ctx: &CompletionContext,
190 const_: hir::Const, 227 const_: hir::Const,
228 impl_def: hir::Impl,
191) { 229) {
192 let const_name = const_.name(ctx.db).map(|n| n.to_string()); 230 let const_name = const_.name(ctx.db).map(|n| n.to_string());
193 231
194 if let Some(const_name) = const_name { 232 if let Some(const_name) = const_name {
195 if let Some(source) = const_.source(ctx.db) { 233 if let Some(source) = const_.source(ctx.db) {
196 let snippet = make_const_compl_syntax(&source.value); 234 let assoc_item = ast::AssocItem::Const(source.value);
197 235 if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) {
198 let range = replacement_range(ctx, const_def_node); 236 let transformed_const = match transformed_item {
199 let mut item = 237 ast::AssocItem::Const(const_) => const_,
200 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()); 238 _ => unreachable!(),
201 item.text_edit(TextEdit::replace(range, snippet)) 239 };
202 .lookup_by(const_name) 240
203 .kind(SymbolKind::Const) 241 let snippet = make_const_compl_syntax(&transformed_const);
204 .set_documentation(const_.docs(ctx.db)); 242
205 item.add_to(acc); 243 let range = replacement_range(ctx, const_def_node);
244 let mut item =
245 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone());
246 item.text_edit(TextEdit::replace(range, snippet))
247 .lookup_by(const_name)
248 .kind(SymbolKind::Const)
249 .set_documentation(const_.docs(ctx.db));
250 item.add_to(acc);
251 }
206 } 252 }
207 } 253 }
208} 254}
@@ -779,4 +825,183 @@ impl Foo for T {{
779 test("Type", "type T$0", "type Type = "); 825 test("Type", "type T$0", "type Type = ");
780 test("CONST", "const C$0", "const CONST: i32 = "); 826 test("CONST", "const C$0", "const CONST: i32 = ");
781 } 827 }
828
829 #[test]
830 fn generics_are_inlined_in_return_type() {
831 check_edit(
832 "function",
833 r#"
834trait Foo<T> {
835 fn function() -> T;
836}
837struct Bar;
838
839impl Foo<u32> for Bar {
840 fn f$0
841}
842"#,
843 r#"
844trait Foo<T> {
845 fn function() -> T;
846}
847struct Bar;
848
849impl Foo<u32> for Bar {
850 fn function() -> u32 {
851 $0
852}
853}
854"#,
855 )
856 }
857
858 #[test]
859 fn generics_are_inlined_in_parameter() {
860 check_edit(
861 "function",
862 r#"
863trait Foo<T> {
864 fn function(bar: T);
865}
866struct Bar;
867
868impl Foo<u32> for Bar {
869 fn f$0
870}
871"#,
872 r#"
873trait Foo<T> {
874 fn function(bar: T);
875}
876struct Bar;
877
878impl Foo<u32> for Bar {
879 fn function(bar: u32) {
880 $0
881}
882}
883"#,
884 )
885 }
886
887 #[test]
888 fn generics_are_inlined_when_part_of_other_types() {
889 check_edit(
890 "function",
891 r#"
892trait Foo<T> {
893 fn function(bar: Vec<T>);
894}
895struct Bar;
896
897impl Foo<u32> for Bar {
898 fn f$0
899}
900"#,
901 r#"
902trait Foo<T> {
903 fn function(bar: Vec<T>);
904}
905struct Bar;
906
907impl Foo<u32> for Bar {
908 fn function(bar: Vec<u32>) {
909 $0
910}
911}
912"#,
913 )
914 }
915
916 #[test]
917 fn generics_are_inlined_complex() {
918 check_edit(
919 "function",
920 r#"
921trait Foo<T, U, V> {
922 fn function(bar: Vec<T>, baz: U) -> Arc<Vec<V>>;
923}
924struct Bar;
925
926impl Foo<u32, Vec<usize>, u8> for Bar {
927 fn f$0
928}
929"#,
930 r#"
931trait Foo<T, U, V> {
932 fn function(bar: Vec<T>, baz: U) -> Arc<Vec<V>>;
933}
934struct Bar;
935
936impl Foo<u32, Vec<usize>, u8> for Bar {
937 fn function(bar: Vec<u32>, baz: Vec<usize>) -> Arc<Vec<u8>> {
938 $0
939}
940}
941"#,
942 )
943 }
944
945 #[test]
946 fn generics_are_inlined_in_associated_const() {
947 check_edit(
948 "BAR",
949 r#"
950trait Foo<T> {
951 const BAR: T;
952}
953struct Bar;
954
955impl Foo<u32> for Bar {
956 const B$0
957}
958"#,
959 r#"
960trait Foo<T> {
961 const BAR: T;
962}
963struct Bar;
964
965impl Foo<u32> for Bar {
966 const BAR: u32 =
967}
968"#,
969 )
970 }
971
972 #[test]
973 fn generics_are_inlined_in_where_clause() {
974 check_edit(
975 "function",
976 r#"
977trait SomeTrait<T> {}
978
979trait Foo<T> {
980 fn function()
981 where Self: SomeTrait<T>;
982}
983struct Bar;
984
985impl Foo<u32> for Bar {
986 fn f$0
987}
988"#,
989 r#"
990trait SomeTrait<T> {}
991
992trait Foo<T> {
993 fn function()
994 where Self: SomeTrait<T>;
995}
996struct Bar;
997
998impl Foo<u32> for Bar {
999 fn function()
1000where Self: SomeTrait<u32> {
1001 $0
1002}
1003}
1004"#,
1005 )
1006 }
782} 1007}