diff options
-rw-r--r-- | crates/ra_assists/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_assists/src/utils.rs | 77 | ||||
-rw-r--r-- | crates/ra_ide/src/completion.rs | 2 | ||||
-rw-r--r-- | crates/ra_ide/src/completion/complete_trait_impl.rs | 132 |
4 files changed, 95 insertions, 118 deletions
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 828a8e9e8..cb124eaf0 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -9,7 +9,7 @@ mod assist_ctx; | |||
9 | mod marks; | 9 | mod marks; |
10 | #[cfg(test)] | 10 | #[cfg(test)] |
11 | mod doc_tests; | 11 | mod doc_tests; |
12 | mod utils; | 12 | pub mod utils; |
13 | pub mod ast_transform; | 13 | pub mod ast_transform; |
14 | 14 | ||
15 | use ra_db::FileRange; | 15 | use ra_db::FileRange; |
diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs index 0d5722295..7bc21c6e4 100644 --- a/crates/ra_assists/src/utils.rs +++ b/crates/ra_assists/src/utils.rs | |||
@@ -1,10 +1,83 @@ | |||
1 | //! Assorted functions shared by several assists. | 1 | //! Assorted functions shared by several assists. |
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | ast::{self, make}, | 4 | ast::{self, make, NameOwner}, |
5 | T, | 5 | AstNode, T, |
6 | }; | 6 | }; |
7 | 7 | ||
8 | use hir::db::HirDatabase; | ||
9 | |||
10 | use rustc_hash::FxHashSet; | ||
11 | |||
12 | pub fn get_missing_impl_items( | ||
13 | db: &impl HirDatabase, | ||
14 | analyzer: &hir::SourceAnalyzer, | ||
15 | impl_block: &ast::ImplBlock, | ||
16 | ) -> Vec<hir::AssocItem> { | ||
17 | // since the names are unique only to each associated type (fn/type/const), | ||
18 | // create buckets of each already implemented type that we'll use in the | ||
19 | // lookup later. | ||
20 | let mut impl_fns = FxHashSet::default(); | ||
21 | let mut impl_type = FxHashSet::default(); | ||
22 | let mut impl_const = FxHashSet::default(); | ||
23 | |||
24 | if let Some(item_list) = impl_block.item_list() { | ||
25 | for item in item_list.impl_items() { | ||
26 | match item { | ||
27 | ast::ImplItem::FnDef(f) => { | ||
28 | if let Some(n) = f.name() { | ||
29 | impl_fns.insert(n.syntax().to_string()); | ||
30 | } | ||
31 | } | ||
32 | |||
33 | ast::ImplItem::TypeAliasDef(t) => { | ||
34 | if let Some(n) = t.name() { | ||
35 | impl_type.insert(n.syntax().to_string()); | ||
36 | } | ||
37 | } | ||
38 | |||
39 | ast::ImplItem::ConstDef(c) => { | ||
40 | if let Some(n) = c.name() { | ||
41 | impl_const.insert(n.syntax().to_string()); | ||
42 | } | ||
43 | } | ||
44 | } | ||
45 | } | ||
46 | } | ||
47 | |||
48 | resolve_target_trait(db, analyzer, impl_block).map_or(vec![], |target_trait| { | ||
49 | target_trait | ||
50 | .items(db) | ||
51 | .iter() | ||
52 | .filter(|i| match i { | ||
53 | hir::AssocItem::Function(f) => !impl_fns.contains(&f.name(db).to_string()), | ||
54 | hir::AssocItem::TypeAlias(t) => !impl_type.contains(&t.name(db).to_string()), | ||
55 | hir::AssocItem::Const(c) => { | ||
56 | c.name(db).map(|n| !impl_const.contains(&n.to_string())).unwrap_or_default() | ||
57 | } | ||
58 | }) | ||
59 | .map(|i| i.clone()) | ||
60 | .collect() | ||
61 | }) | ||
62 | } | ||
63 | |||
64 | fn resolve_target_trait( | ||
65 | db: &impl HirDatabase, | ||
66 | analyzer: &hir::SourceAnalyzer, | ||
67 | impl_block: &ast::ImplBlock, | ||
68 | ) -> Option<hir::Trait> { | ||
69 | let ast_path = impl_block | ||
70 | .target_trait() | ||
71 | .map(|it| it.syntax().clone()) | ||
72 | .and_then(ast::PathType::cast)? | ||
73 | .path()?; | ||
74 | |||
75 | match analyzer.resolve_path(db, &ast_path) { | ||
76 | Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => Some(def), | ||
77 | _ => None, | ||
78 | } | ||
79 | } | ||
80 | |||
8 | pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr { | 81 | pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr { |
9 | if let Some(expr) = invert_special_case(&expr) { | 82 | if let Some(expr) = invert_special_case(&expr) { |
10 | return expr; | 83 | return expr; |
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs index 4f24cd1f9..4bdc6ba23 100644 --- a/crates/ra_ide/src/completion.rs +++ b/crates/ra_ide/src/completion.rs | |||
@@ -76,6 +76,6 @@ pub(crate) fn completions(db: &RootDatabase, position: FilePosition) -> Option<C | |||
76 | complete_postfix::complete_postfix(&mut acc, &ctx); | 76 | complete_postfix::complete_postfix(&mut acc, &ctx); |
77 | complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx); | 77 | complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx); |
78 | complete_trait_impl::complete_trait_impl(&mut acc, &ctx); | 78 | complete_trait_impl::complete_trait_impl(&mut acc, &ctx); |
79 | 79 | ||
80 | Some(acc) | 80 | Some(acc) |
81 | } | 81 | } |
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs index 0175f5e55..e2854ee97 100644 --- a/crates/ra_ide/src/completion/complete_trait_impl.rs +++ b/crates/ra_ide/src/completion/complete_trait_impl.rs | |||
@@ -1,124 +1,27 @@ | |||
1 | use crate::completion::{CompletionContext, Completions, CompletionItem, CompletionKind, CompletionItemKind}; | 1 | use crate::completion::{ |
2 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, | ||
3 | }; | ||
2 | 4 | ||
3 | use ra_syntax::ast::{self, NameOwner, AstNode}; | 5 | use hir::{self, Docs}; |
4 | |||
5 | use hir::{self, db::HirDatabase, Docs}; | ||
6 | 6 | ||
7 | use ra_assists::utils::get_missing_impl_items; | ||
7 | 8 | ||
8 | pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { | 9 | pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { |
9 | let impl_block = ctx.impl_block.as_ref(); | 10 | let impl_block = ctx.impl_block.as_ref(); |
10 | let item_list = impl_block.and_then(|i| i.item_list()); | 11 | let item_list = impl_block.and_then(|i| i.item_list()); |
11 | 12 | ||
12 | if item_list.is_none() | 13 | if item_list.is_none() || impl_block.is_none() || ctx.function_syntax.is_some() { |
13 | || impl_block.is_none() | ||
14 | || ctx.function_syntax.is_some() { | ||
15 | return; | 14 | return; |
16 | } | 15 | } |
17 | 16 | ||
18 | let item_list = item_list.unwrap(); | ||
19 | let impl_block = impl_block.unwrap(); | 17 | let impl_block = impl_block.unwrap(); |
20 | 18 | ||
21 | let target_trait = resolve_target_trait(ctx.db, &ctx.analyzer, &impl_block); | 19 | for item in get_missing_impl_items(ctx.db, &ctx.analyzer, impl_block) { |
22 | if target_trait.is_none() { | ||
23 | return; | ||
24 | } | ||
25 | |||
26 | let target_trait = target_trait.unwrap(); | ||
27 | |||
28 | let trait_items = target_trait.items(ctx.db); | ||
29 | let missing_items = trait_items | ||
30 | .iter() | ||
31 | .filter(|i| { | ||
32 | match i { | ||
33 | hir::AssocItem::Function(f) => { | ||
34 | let f_name = f.name(ctx.db).to_string(); | ||
35 | |||
36 | item_list | ||
37 | .impl_items() | ||
38 | .find(|impl_item| { | ||
39 | match impl_item { | ||
40 | ast::ImplItem::FnDef(impl_f) => { | ||
41 | if let Some(n) = impl_f.name() { | ||
42 | f_name == n.syntax().to_string() | ||
43 | } else { | ||
44 | false | ||
45 | } | ||
46 | }, | ||
47 | _ => false | ||
48 | } | ||
49 | }).is_none() | ||
50 | }, | ||
51 | hir::AssocItem::Const(c) => { | ||
52 | let c_name = c.name(ctx.db) | ||
53 | .map(|f| f.to_string()); | ||
54 | |||
55 | if c_name.is_none() { | ||
56 | return false; | ||
57 | } | ||
58 | |||
59 | let c_name = c_name.unwrap(); | ||
60 | |||
61 | item_list | ||
62 | .impl_items() | ||
63 | .find(|impl_item| { | ||
64 | match impl_item { | ||
65 | ast::ImplItem::ConstDef(c) => { | ||
66 | if let Some(n) = c.name() { | ||
67 | c_name == n.syntax().to_string() | ||
68 | } else { | ||
69 | false | ||
70 | } | ||
71 | }, | ||
72 | _ => false | ||
73 | } | ||
74 | }).is_none() | ||
75 | }, | ||
76 | hir::AssocItem::TypeAlias(t) => { | ||
77 | let t_name = t.name(ctx.db).to_string(); | ||
78 | |||
79 | item_list | ||
80 | .impl_items() | ||
81 | .find(|impl_item| { | ||
82 | match impl_item { | ||
83 | ast::ImplItem::TypeAliasDef(t) => { | ||
84 | if let Some(n) = t.name() { | ||
85 | t_name == n.syntax().to_string() | ||
86 | } else { | ||
87 | false | ||
88 | } | ||
89 | }, | ||
90 | _ => false | ||
91 | } | ||
92 | }).is_none() | ||
93 | } | ||
94 | } | ||
95 | }); | ||
96 | |||
97 | for item in missing_items { | ||
98 | match item { | 20 | match item { |
99 | hir::AssocItem::Function(f) => add_function_impl(acc, ctx, f), | 21 | hir::AssocItem::Function(f) => add_function_impl(acc, ctx, &f), |
100 | hir::AssocItem::TypeAlias(t) => add_type_alias_impl(acc, ctx, t), | 22 | hir::AssocItem::TypeAlias(t) => add_type_alias_impl(acc, ctx, &t), |
101 | _ => {}, | 23 | _ => {} |
102 | } | ||
103 | } | ||
104 | } | ||
105 | |||
106 | fn resolve_target_trait( | ||
107 | db: &impl HirDatabase, | ||
108 | analyzer: &hir::SourceAnalyzer, | ||
109 | impl_block: &ast::ImplBlock | ||
110 | ) -> Option<hir::Trait> { | ||
111 | let ast_path = impl_block | ||
112 | .target_trait() | ||
113 | .map(|it| it.syntax().clone()) | ||
114 | .and_then(ast::PathType::cast)? | ||
115 | .path()?; | ||
116 | |||
117 | match analyzer.resolve_path(db, &ast_path) { | ||
118 | Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => { | ||
119 | Some(def) | ||
120 | } | 24 | } |
121 | _ => None, | ||
122 | } | 25 | } |
123 | } | 26 | } |
124 | 27 | ||
@@ -144,20 +47,21 @@ fn add_function_impl(acc: &mut Completions, ctx: &CompletionContext, func: &hir: | |||
144 | } else { | 47 | } else { |
145 | CompletionItemKind::Function | 48 | CompletionItemKind::Function |
146 | }; | 49 | }; |
147 | 50 | ||
148 | let snippet = { | 51 | let snippet = { |
149 | let mut s = format!("{}", display); | 52 | let mut s = format!("{}", display); |
150 | s.push_str(" {}"); | 53 | s.push_str(" {}"); |
151 | s | 54 | s |
152 | }; | 55 | }; |
153 | 56 | ||
154 | builder | 57 | builder.insert_text(snippet).kind(completion_kind).add_to(acc); |
155 | .insert_text(snippet) | ||
156 | .kind(completion_kind) | ||
157 | .add_to(acc); | ||
158 | } | 58 | } |
159 | 59 | ||
160 | fn add_type_alias_impl(acc: &mut Completions, ctx: &CompletionContext, type_alias: &hir::TypeAlias) { | 60 | fn add_type_alias_impl( |
61 | acc: &mut Completions, | ||
62 | ctx: &CompletionContext, | ||
63 | type_alias: &hir::TypeAlias, | ||
64 | ) { | ||
161 | let snippet = format!("type {} = ", type_alias.name(ctx.db).to_string()); | 65 | let snippet = format!("type {} = ", type_alias.name(ctx.db).to_string()); |
162 | 66 | ||
163 | CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()) | 67 | CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()) |
@@ -290,4 +194,4 @@ mod tests { | |||
290 | ] | 194 | ] |
291 | "###); | 195 | "###); |
292 | } | 196 | } |
293 | } \ No newline at end of file | 197 | } |