diff options
author | rdambrosio <[email protected]> | 2021-06-18 00:54:28 +0100 |
---|---|---|
committer | rdambrosio <[email protected]> | 2021-06-18 00:54:28 +0100 |
commit | 8e08b86304f8cf91e06f64855c8de306ad7efaa4 (patch) | |
tree | 134d42353a19d61fd7ebefd129fd7924c1d205f4 | |
parent | 0d863ccea96c6c3256fad12807a0eedbfccd8294 (diff) |
Feat: inline generics in const and func trait completions
-rw-r--r-- | crates/ide_assists/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/ide_assists/src/utils.rs | 6 | ||||
-rw-r--r-- | crates/ide_completion/src/completions/trait_impl.rs | 279 | ||||
-rw-r--r-- | crates/ide_db/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/ide_db/src/path_transform.rs (renamed from crates/ide_assists/src/path_transform.rs) | 12 |
5 files changed, 261 insertions, 38 deletions
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs index fa378a622..86a57ce5d 100644 --- a/crates/ide_assists/src/lib.rs +++ b/crates/ide_assists/src/lib.rs | |||
@@ -15,7 +15,6 @@ mod assist_context; | |||
15 | #[cfg(test)] | 15 | #[cfg(test)] |
16 | mod tests; | 16 | mod tests; |
17 | pub mod utils; | 17 | pub mod utils; |
18 | pub mod path_transform; | ||
19 | 18 | ||
20 | use hir::Semantics; | 19 | use hir::Semantics; |
21 | use ide_db::{base_db::FileRange, RootDatabase}; | 20 | use ide_db::{base_db::FileRange, RootDatabase}; |
diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs index 068df005b..0ec236aa0 100644 --- a/crates/ide_assists/src/utils.rs +++ b/crates/ide_assists/src/utils.rs | |||
@@ -8,6 +8,7 @@ use ast::TypeBoundsOwner; | |||
8 | use hir::{Adt, HasSource, Semantics}; | 8 | use hir::{Adt, HasSource, Semantics}; |
9 | use ide_db::{ | 9 | use ide_db::{ |
10 | helpers::{FamousDefs, SnippetCap}, | 10 | helpers::{FamousDefs, SnippetCap}, |
11 | path_transform::PathTransform, | ||
11 | RootDatabase, | 12 | RootDatabase, |
12 | }; | 13 | }; |
13 | use itertools::Itertools; | 14 | use itertools::Itertools; |
@@ -22,10 +23,7 @@ use syntax::{ | |||
22 | SyntaxNode, TextSize, T, | 23 | SyntaxNode, TextSize, T, |
23 | }; | 24 | }; |
24 | 25 | ||
25 | use crate::{ | 26 | use crate::assist_context::{AssistBuilder, AssistContext}; |
26 | assist_context::{AssistBuilder, AssistContext}, | ||
27 | path_transform::PathTransform, | ||
28 | }; | ||
29 | 27 | ||
30 | pub(crate) fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr { | 28 | pub(crate) fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr { |
31 | extract_trivial_expression(&block) | 29 | extract_trivial_expression(&block) |
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 | ||
34 | use hir::{self, HasAttrs, HasSource}; | 34 | use hir::{self, HasAttrs, HasSource}; |
35 | use ide_db::{traits::get_missing_assoc_items, SymbolKind}; | 35 | use ide_db::{path_transform::PathTransform, traits::get_missing_assoc_items, SymbolKind}; |
36 | use syntax::{ | 36 | use 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. | ||
182 | fn 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 | |||
167 | fn add_type_alias_impl( | 204 | fn 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#" | ||
834 | trait Foo<T> { | ||
835 | fn function() -> T; | ||
836 | } | ||
837 | struct Bar; | ||
838 | |||
839 | impl Foo<u32> for Bar { | ||
840 | fn f$0 | ||
841 | } | ||
842 | "#, | ||
843 | r#" | ||
844 | trait Foo<T> { | ||
845 | fn function() -> T; | ||
846 | } | ||
847 | struct Bar; | ||
848 | |||
849 | impl 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#" | ||
863 | trait Foo<T> { | ||
864 | fn function(bar: T); | ||
865 | } | ||
866 | struct Bar; | ||
867 | |||
868 | impl Foo<u32> for Bar { | ||
869 | fn f$0 | ||
870 | } | ||
871 | "#, | ||
872 | r#" | ||
873 | trait Foo<T> { | ||
874 | fn function(bar: T); | ||
875 | } | ||
876 | struct Bar; | ||
877 | |||
878 | impl 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#" | ||
892 | trait Foo<T> { | ||
893 | fn function(bar: Vec<T>); | ||
894 | } | ||
895 | struct Bar; | ||
896 | |||
897 | impl Foo<u32> for Bar { | ||
898 | fn f$0 | ||
899 | } | ||
900 | "#, | ||
901 | r#" | ||
902 | trait Foo<T> { | ||
903 | fn function(bar: Vec<T>); | ||
904 | } | ||
905 | struct Bar; | ||
906 | |||
907 | impl 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#" | ||
921 | trait Foo<T, U, V> { | ||
922 | fn function(bar: Vec<T>, baz: U) -> Arc<Vec<V>>; | ||
923 | } | ||
924 | struct Bar; | ||
925 | |||
926 | impl Foo<u32, Vec<usize>, u8> for Bar { | ||
927 | fn f$0 | ||
928 | } | ||
929 | "#, | ||
930 | r#" | ||
931 | trait Foo<T, U, V> { | ||
932 | fn function(bar: Vec<T>, baz: U) -> Arc<Vec<V>>; | ||
933 | } | ||
934 | struct Bar; | ||
935 | |||
936 | impl 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#" | ||
950 | trait Foo<T> { | ||
951 | const BAR: T; | ||
952 | } | ||
953 | struct Bar; | ||
954 | |||
955 | impl Foo<u32> for Bar { | ||
956 | const B$0 | ||
957 | } | ||
958 | "#, | ||
959 | r#" | ||
960 | trait Foo<T> { | ||
961 | const BAR: T; | ||
962 | } | ||
963 | struct Bar; | ||
964 | |||
965 | impl 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#" | ||
977 | trait SomeTrait<T> {} | ||
978 | |||
979 | trait Foo<T> { | ||
980 | fn function() | ||
981 | where Self: SomeTrait<T>; | ||
982 | } | ||
983 | struct Bar; | ||
984 | |||
985 | impl Foo<u32> for Bar { | ||
986 | fn f$0 | ||
987 | } | ||
988 | "#, | ||
989 | r#" | ||
990 | trait SomeTrait<T> {} | ||
991 | |||
992 | trait Foo<T> { | ||
993 | fn function() | ||
994 | where Self: SomeTrait<T>; | ||
995 | } | ||
996 | struct Bar; | ||
997 | |||
998 | impl Foo<u32> for Bar { | ||
999 | fn function() | ||
1000 | where Self: SomeTrait<u32> { | ||
1001 | $0 | ||
1002 | } | ||
1003 | } | ||
1004 | "#, | ||
1005 | ) | ||
1006 | } | ||
782 | } | 1007 | } |
diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs index 7bbd08d6f..bde8767dd 100644 --- a/crates/ide_db/src/lib.rs +++ b/crates/ide_db/src/lib.rs | |||
@@ -14,6 +14,7 @@ pub mod ty_filter; | |||
14 | pub mod traits; | 14 | pub mod traits; |
15 | pub mod call_info; | 15 | pub mod call_info; |
16 | pub mod helpers; | 16 | pub mod helpers; |
17 | pub mod path_transform; | ||
17 | 18 | ||
18 | pub mod search; | 19 | pub mod search; |
19 | pub mod rename; | 20 | pub mod rename; |
diff --git a/crates/ide_assists/src/path_transform.rs b/crates/ide_db/src/path_transform.rs index 48a7fa06a..f3d7aa920 100644 --- a/crates/ide_assists/src/path_transform.rs +++ b/crates/ide_db/src/path_transform.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! See [`PathTransform`]. | 1 | //! See [`PathTransform`]. |
2 | 2 | ||
3 | use crate::helpers::mod_path_to_ast; | ||
3 | use hir::{HirDisplay, SemanticsScope}; | 4 | use hir::{HirDisplay, SemanticsScope}; |
4 | use ide_db::helpers::mod_path_to_ast; | ||
5 | use rustc_hash::FxHashMap; | 5 | use rustc_hash::FxHashMap; |
6 | use syntax::{ | 6 | use syntax::{ |
7 | ast::{self, AstNode}, | 7 | ast::{self, AstNode}, |
@@ -31,14 +31,14 @@ use syntax::{ | |||
31 | /// } | 31 | /// } |
32 | /// } | 32 | /// } |
33 | /// ``` | 33 | /// ``` |
34 | pub(crate) struct PathTransform<'a> { | 34 | pub struct PathTransform<'a> { |
35 | pub(crate) subst: (hir::Trait, ast::Impl), | 35 | pub subst: (hir::Trait, ast::Impl), |
36 | pub(crate) target_scope: &'a SemanticsScope<'a>, | 36 | pub target_scope: &'a SemanticsScope<'a>, |
37 | pub(crate) source_scope: &'a SemanticsScope<'a>, | 37 | pub source_scope: &'a SemanticsScope<'a>, |
38 | } | 38 | } |
39 | 39 | ||
40 | impl<'a> PathTransform<'a> { | 40 | impl<'a> PathTransform<'a> { |
41 | pub(crate) fn apply(&self, item: ast::AssocItem) { | 41 | pub fn apply(&self, item: ast::AssocItem) { |
42 | if let Some(ctx) = self.build_ctx() { | 42 | if let Some(ctx) = self.build_ctx() { |
43 | ctx.apply(item) | 43 | ctx.apply(item) |
44 | } | 44 | } |