diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-11-07 18:21:11 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2020-11-07 18:21:11 +0000 |
commit | dac70603821c3f4785a285e837b220addb16d13b (patch) | |
tree | a0adc6df6a84b57bcba97fd1835a2da8020f5258 /crates/assists/src/handlers/add_custom_impl.rs | |
parent | f3fe6561c00a07a7d6131db42983cbc3eb658957 (diff) | |
parent | 19443c1fa30e5a360c84f82d0b7aac733ea2e240 (diff) |
Merge #6476
6476: Add missing AssocItems in add_custom_impl assist r=matklad a=Veykril
```rust
use std::fmt;
#[derive(Debu<|>g)]
struct Foo {
bar: String,
}
```
->
```rust
use std::fmt;
struct Foo {
bar: String,
}
impl fmt::Debug for Foo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
${0:todo!()}
}
}
```
Co-authored-by: Lukas Wirth <[email protected]>
Diffstat (limited to 'crates/assists/src/handlers/add_custom_impl.rs')
-rw-r--r-- | crates/assists/src/handlers/add_custom_impl.rs | 161 |
1 files changed, 131 insertions, 30 deletions
diff --git a/crates/assists/src/handlers/add_custom_impl.rs b/crates/assists/src/handlers/add_custom_impl.rs index 669dd9b21..c13493fd8 100644 --- a/crates/assists/src/handlers/add_custom_impl.rs +++ b/crates/assists/src/handlers/add_custom_impl.rs | |||
@@ -4,13 +4,15 @@ use syntax::{ | |||
4 | ast::{self, make, AstNode}, | 4 | ast::{self, make, AstNode}, |
5 | Direction, SmolStr, | 5 | Direction, SmolStr, |
6 | SyntaxKind::{IDENT, WHITESPACE}, | 6 | SyntaxKind::{IDENT, WHITESPACE}, |
7 | TextRange, TextSize, | 7 | TextSize, |
8 | }; | 8 | }; |
9 | 9 | ||
10 | use crate::{ | 10 | use crate::{ |
11 | assist_config::SnippetCap, | ||
12 | assist_context::{AssistBuilder, AssistContext, Assists}, | 11 | assist_context::{AssistBuilder, AssistContext, Assists}, |
13 | utils::mod_path_to_ast, | 12 | utils::{ |
13 | add_trait_assoc_items_to_impl, filter_assoc_items, mod_path_to_ast, render_snippet, Cursor, | ||
14 | DefaultMethods, | ||
15 | }, | ||
14 | AssistId, AssistKind, | 16 | AssistId, AssistKind, |
15 | }; | 17 | }; |
16 | 18 | ||
@@ -47,11 +49,10 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
47 | ctx.token_at_offset().find(|t| t.kind() == IDENT && *t.text() != attr_name)?; | 49 | ctx.token_at_offset().find(|t| t.kind() == IDENT && *t.text() != attr_name)?; |
48 | let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_token.text()))); | 50 | let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_token.text()))); |
49 | 51 | ||
50 | let annotated = attr.syntax().siblings(Direction::Next).find_map(ast::Name::cast)?; | 52 | let annotated_name = attr.syntax().siblings(Direction::Next).find_map(ast::Name::cast)?; |
51 | let annotated_name = annotated.syntax().text().to_string(); | 53 | let insert_pos = annotated_name.syntax().parent()?.text_range().end(); |
52 | let insert_pos = annotated.syntax().parent()?.text_range().end(); | ||
53 | 54 | ||
54 | let current_module = ctx.sema.scope(annotated.syntax()).module()?; | 55 | let current_module = ctx.sema.scope(annotated_name.syntax()).module()?; |
55 | let current_crate = current_module.krate(); | 56 | let current_crate = current_module.krate(); |
56 | 57 | ||
57 | let found_traits = imports_locator::find_imports(&ctx.sema, current_crate, trait_token.text()) | 58 | let found_traits = imports_locator::find_imports(&ctx.sema, current_crate, trait_token.text()) |
@@ -69,21 +70,22 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
69 | }); | 70 | }); |
70 | 71 | ||
71 | let mut no_traits_found = true; | 72 | let mut no_traits_found = true; |
72 | for (trait_path, _trait) in found_traits.inspect(|_| no_traits_found = false) { | 73 | for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { |
73 | add_assist(acc, ctx.config.snippet_cap, &attr, &trait_path, &annotated_name, insert_pos)?; | 74 | add_assist(acc, ctx, &attr, &trait_path, Some(trait_), &annotated_name, insert_pos)?; |
74 | } | 75 | } |
75 | if no_traits_found { | 76 | if no_traits_found { |
76 | add_assist(acc, ctx.config.snippet_cap, &attr, &trait_path, &annotated_name, insert_pos)?; | 77 | add_assist(acc, ctx, &attr, &trait_path, None, &annotated_name, insert_pos)?; |
77 | } | 78 | } |
78 | Some(()) | 79 | Some(()) |
79 | } | 80 | } |
80 | 81 | ||
81 | fn add_assist( | 82 | fn add_assist( |
82 | acc: &mut Assists, | 83 | acc: &mut Assists, |
83 | snippet_cap: Option<SnippetCap>, | 84 | ctx: &AssistContext, |
84 | attr: &ast::Attr, | 85 | attr: &ast::Attr, |
85 | trait_path: &ast::Path, | 86 | trait_path: &ast::Path, |
86 | annotated_name: &str, | 87 | trait_: Option<hir::Trait>, |
88 | annotated_name: &ast::Name, | ||
87 | insert_pos: TextSize, | 89 | insert_pos: TextSize, |
88 | ) -> Option<()> { | 90 | ) -> Option<()> { |
89 | let target = attr.syntax().text_range(); | 91 | let target = attr.syntax().text_range(); |
@@ -92,25 +94,62 @@ fn add_assist( | |||
92 | let trait_name = trait_path.segment().and_then(|seg| seg.name_ref())?; | 94 | let trait_name = trait_path.segment().and_then(|seg| seg.name_ref())?; |
93 | 95 | ||
94 | acc.add(AssistId("add_custom_impl", AssistKind::Refactor), label, target, |builder| { | 96 | acc.add(AssistId("add_custom_impl", AssistKind::Refactor), label, target, |builder| { |
97 | let impl_def_with_items = | ||
98 | impl_def_from_trait(&ctx.sema, annotated_name, trait_, trait_path); | ||
95 | update_attribute(builder, &input, &trait_name, &attr); | 99 | update_attribute(builder, &input, &trait_name, &attr); |
96 | match snippet_cap { | 100 | match (ctx.config.snippet_cap, impl_def_with_items) { |
97 | Some(cap) => { | 101 | (None, _) => builder.insert( |
102 | insert_pos, | ||
103 | format!("\n\nimpl {} for {} {{\n\n}}", trait_path, annotated_name), | ||
104 | ), | ||
105 | (Some(cap), None) => builder.insert_snippet( | ||
106 | cap, | ||
107 | insert_pos, | ||
108 | format!("\n\nimpl {} for {} {{\n $0\n}}", trait_path, annotated_name), | ||
109 | ), | ||
110 | (Some(cap), Some((impl_def, first_assoc_item))) => { | ||
111 | let mut cursor = Cursor::Before(first_assoc_item.syntax()); | ||
112 | let placeholder; | ||
113 | if let ast::AssocItem::Fn(ref func) = first_assoc_item { | ||
114 | if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) { | ||
115 | if m.syntax().text() == "todo!()" { | ||
116 | placeholder = m; | ||
117 | cursor = Cursor::Replace(placeholder.syntax()); | ||
118 | } | ||
119 | } | ||
120 | } | ||
121 | |||
98 | builder.insert_snippet( | 122 | builder.insert_snippet( |
99 | cap, | 123 | cap, |
100 | insert_pos, | 124 | insert_pos, |
101 | format!("\n\nimpl {} for {} {{\n $0\n}}", trait_path, annotated_name), | 125 | format!("\n\n{}", render_snippet(cap, impl_def.syntax(), cursor)), |
102 | ); | 126 | ) |
103 | } | ||
104 | None => { | ||
105 | builder.insert( | ||
106 | insert_pos, | ||
107 | format!("\n\nimpl {} for {} {{\n\n}}", trait_path, annotated_name), | ||
108 | ); | ||
109 | } | 127 | } |
110 | } | 128 | }; |
111 | }) | 129 | }) |
112 | } | 130 | } |
113 | 131 | ||
132 | fn impl_def_from_trait( | ||
133 | sema: &hir::Semantics<ide_db::RootDatabase>, | ||
134 | annotated_name: &ast::Name, | ||
135 | trait_: Option<hir::Trait>, | ||
136 | trait_path: &ast::Path, | ||
137 | ) -> Option<(ast::Impl, ast::AssocItem)> { | ||
138 | let trait_ = trait_?; | ||
139 | let target_scope = sema.scope(annotated_name.syntax()); | ||
140 | let trait_items = filter_assoc_items(sema.db, &trait_.items(sema.db), DefaultMethods::No); | ||
141 | if trait_items.is_empty() { | ||
142 | return None; | ||
143 | } | ||
144 | let impl_def = make::impl_trait( | ||
145 | trait_path.clone(), | ||
146 | make::path_unqualified(make::path_segment(make::name_ref(annotated_name.text()))), | ||
147 | ); | ||
148 | let (impl_def, first_assoc_item) = | ||
149 | add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope); | ||
150 | Some((impl_def, first_assoc_item)) | ||
151 | } | ||
152 | |||
114 | fn update_attribute( | 153 | fn update_attribute( |
115 | builder: &mut AssistBuilder, | 154 | builder: &mut AssistBuilder, |
116 | input: &ast::TokenTree, | 155 | input: &ast::TokenTree, |
@@ -133,13 +172,14 @@ fn update_attribute( | |||
133 | let attr_range = attr.syntax().text_range(); | 172 | let attr_range = attr.syntax().text_range(); |
134 | builder.delete(attr_range); | 173 | builder.delete(attr_range); |
135 | 174 | ||
136 | let line_break_range = attr | 175 | if let Some(line_break_range) = attr |
137 | .syntax() | 176 | .syntax() |
138 | .next_sibling_or_token() | 177 | .next_sibling_or_token() |
139 | .filter(|t| t.kind() == WHITESPACE) | 178 | .filter(|t| t.kind() == WHITESPACE) |
140 | .map(|t| t.text_range()) | 179 | .map(|t| t.text_range()) |
141 | .unwrap_or_else(|| TextRange::new(TextSize::from(0), TextSize::from(0))); | 180 | { |
142 | builder.delete(line_break_range); | 181 | builder.delete(line_break_range); |
182 | } | ||
143 | } | 183 | } |
144 | } | 184 | } |
145 | 185 | ||
@@ -150,12 +190,17 @@ mod tests { | |||
150 | use super::*; | 190 | use super::*; |
151 | 191 | ||
152 | #[test] | 192 | #[test] |
153 | fn add_custom_impl_qualified() { | 193 | fn add_custom_impl_debug() { |
154 | check_assist( | 194 | check_assist( |
155 | add_custom_impl, | 195 | add_custom_impl, |
156 | " | 196 | " |
157 | mod fmt { | 197 | mod fmt { |
158 | pub trait Debug {} | 198 | pub struct Error; |
199 | pub type Result = Result<(), Error>; | ||
200 | pub struct Formatter<'a>; | ||
201 | pub trait Debug { | ||
202 | fn fmt(&self, f: &mut Formatter<'_>) -> Result; | ||
203 | } | ||
159 | } | 204 | } |
160 | 205 | ||
161 | #[derive(Debu<|>g)] | 206 | #[derive(Debu<|>g)] |
@@ -165,7 +210,12 @@ struct Foo { | |||
165 | ", | 210 | ", |
166 | " | 211 | " |
167 | mod fmt { | 212 | mod fmt { |
168 | pub trait Debug {} | 213 | pub struct Error; |
214 | pub type Result = Result<(), Error>; | ||
215 | pub struct Formatter<'a>; | ||
216 | pub trait Debug { | ||
217 | fn fmt(&self, f: &mut Formatter<'_>) -> Result; | ||
218 | } | ||
169 | } | 219 | } |
170 | 220 | ||
171 | struct Foo { | 221 | struct Foo { |
@@ -173,7 +223,58 @@ struct Foo { | |||
173 | } | 223 | } |
174 | 224 | ||
175 | impl fmt::Debug for Foo { | 225 | impl fmt::Debug for Foo { |
176 | $0 | 226 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
227 | ${0:todo!()} | ||
228 | } | ||
229 | } | ||
230 | ", | ||
231 | ) | ||
232 | } | ||
233 | #[test] | ||
234 | fn add_custom_impl_all() { | ||
235 | check_assist( | ||
236 | add_custom_impl, | ||
237 | " | ||
238 | mod foo { | ||
239 | pub trait Bar { | ||
240 | type Qux; | ||
241 | const Baz: usize = 42; | ||
242 | const Fez: usize; | ||
243 | fn foo(); | ||
244 | fn bar() {} | ||
245 | } | ||
246 | } | ||
247 | |||
248 | #[derive(<|>Bar)] | ||
249 | struct Foo { | ||
250 | bar: String, | ||
251 | } | ||
252 | ", | ||
253 | " | ||
254 | mod foo { | ||
255 | pub trait Bar { | ||
256 | type Qux; | ||
257 | const Baz: usize = 42; | ||
258 | const Fez: usize; | ||
259 | fn foo(); | ||
260 | fn bar() {} | ||
261 | } | ||
262 | } | ||
263 | |||
264 | struct Foo { | ||
265 | bar: String, | ||
266 | } | ||
267 | |||
268 | impl foo::Bar for Foo { | ||
269 | $0type Qux; | ||
270 | |||
271 | const Baz: usize = 42; | ||
272 | |||
273 | const Fez: usize; | ||
274 | |||
275 | fn foo() { | ||
276 | todo!() | ||
277 | } | ||
177 | } | 278 | } |
178 | ", | 279 | ", |
179 | ) | 280 | ) |