diff options
-rw-r--r-- | crates/ide_assists/src/handlers/add_missing_impl_members.rs | 3 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/generate_new.rs | 153 | ||||
-rw-r--r-- | crates/ide_assists/src/tests/generated.rs | 1 | ||||
-rw-r--r-- | crates/ide_assists/src/utils.rs | 26 | ||||
-rw-r--r-- | crates/syntax/src/ast/edit.rs | 151 | ||||
-rw-r--r-- | crates/syntax/src/ast/edit_in_place.rs | 72 | ||||
-rw-r--r-- | crates/syntax/src/ast/make.rs | 2 |
7 files changed, 187 insertions, 221 deletions
diff --git a/crates/ide_assists/src/handlers/add_missing_impl_members.rs b/crates/ide_assists/src/handlers/add_missing_impl_members.rs index 0148635f9..8225ae22c 100644 --- a/crates/ide_assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ide_assists/src/handlers/add_missing_impl_members.rs | |||
@@ -64,7 +64,6 @@ pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) - | |||
64 | // impl Trait for () { | 64 | // impl Trait for () { |
65 | // type X = (); | 65 | // type X = (); |
66 | // fn foo(&self) {}$0 | 66 | // fn foo(&self) {}$0 |
67 | // | ||
68 | // } | 67 | // } |
69 | // ``` | 68 | // ``` |
70 | // -> | 69 | // -> |
@@ -195,6 +194,7 @@ impl Foo for S { | |||
195 | fn baz(&self) { | 194 | fn baz(&self) { |
196 | todo!() | 195 | todo!() |
197 | } | 196 | } |
197 | |||
198 | }"#, | 198 | }"#, |
199 | ); | 199 | ); |
200 | } | 200 | } |
@@ -231,6 +231,7 @@ impl Foo for S { | |||
231 | fn foo(&self) { | 231 | fn foo(&self) { |
232 | ${0:todo!()} | 232 | ${0:todo!()} |
233 | } | 233 | } |
234 | |||
234 | }"#, | 235 | }"#, |
235 | ); | 236 | ); |
236 | } | 237 | } |
diff --git a/crates/ide_assists/src/handlers/generate_new.rs b/crates/ide_assists/src/handlers/generate_new.rs index 8ce5930b7..959a1f86c 100644 --- a/crates/ide_assists/src/handlers/generate_new.rs +++ b/crates/ide_assists/src/handlers/generate_new.rs | |||
@@ -1,4 +1,3 @@ | |||
1 | use ast::Adt; | ||
2 | use itertools::Itertools; | 1 | use itertools::Itertools; |
3 | use stdx::format_to; | 2 | use stdx::format_to; |
4 | use syntax::ast::{self, AstNode, NameOwner, StructKind, VisibilityOwner}; | 3 | use syntax::ast::{self, AstNode, NameOwner, StructKind, VisibilityOwner}; |
@@ -37,7 +36,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
37 | }; | 36 | }; |
38 | 37 | ||
39 | // Return early if we've found an existing new fn | 38 | // Return early if we've found an existing new fn |
40 | let impl_def = find_struct_impl(&ctx, &Adt::Struct(strukt.clone()), "new")?; | 39 | let impl_def = find_struct_impl(&ctx, &ast::Adt::Struct(strukt.clone()), "new")?; |
41 | 40 | ||
42 | let target = strukt.syntax().text_range(); | 41 | let target = strukt.syntax().text_range(); |
43 | acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| { | 42 | acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| { |
@@ -60,7 +59,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
60 | let start_offset = impl_def | 59 | let start_offset = impl_def |
61 | .and_then(|impl_def| find_impl_block_start(impl_def, &mut buf)) | 60 | .and_then(|impl_def| find_impl_block_start(impl_def, &mut buf)) |
62 | .unwrap_or_else(|| { | 61 | .unwrap_or_else(|| { |
63 | buf = generate_impl_text(&Adt::Struct(strukt.clone()), &buf); | 62 | buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf); |
64 | strukt.syntax().text_range().end() | 63 | strukt.syntax().text_range().end() |
65 | }); | 64 | }); |
66 | 65 | ||
@@ -81,101 +80,132 @@ mod tests { | |||
81 | use super::*; | 80 | use super::*; |
82 | 81 | ||
83 | #[test] | 82 | #[test] |
84 | #[rustfmt::skip] | ||
85 | fn test_generate_new() { | 83 | fn test_generate_new() { |
86 | // Check output of generation | ||
87 | check_assist( | 84 | check_assist( |
88 | generate_new, | 85 | generate_new, |
89 | "struct Foo {$0}", | 86 | r#" |
90 | "struct Foo {} | 87 | struct Foo {$0} |
88 | "#, | ||
89 | r#" | ||
90 | struct Foo {} | ||
91 | 91 | ||
92 | impl Foo { | 92 | impl Foo { |
93 | fn $0new() -> Self { Self { } } | 93 | fn $0new() -> Self { Self { } } |
94 | }", | 94 | } |
95 | "#, | ||
95 | ); | 96 | ); |
96 | check_assist( | 97 | check_assist( |
97 | generate_new, | 98 | generate_new, |
98 | "struct Foo<T: Clone> {$0}", | 99 | r#" |
99 | "struct Foo<T: Clone> {} | 100 | struct Foo<T: Clone> {$0} |
101 | "#, | ||
102 | r#" | ||
103 | struct Foo<T: Clone> {} | ||
100 | 104 | ||
101 | impl<T: Clone> Foo<T> { | 105 | impl<T: Clone> Foo<T> { |
102 | fn $0new() -> Self { Self { } } | 106 | fn $0new() -> Self { Self { } } |
103 | }", | 107 | } |
108 | "#, | ||
104 | ); | 109 | ); |
105 | check_assist( | 110 | check_assist( |
106 | generate_new, | 111 | generate_new, |
107 | "struct Foo<'a, T: Foo<'a>> {$0}", | 112 | r#" |
108 | "struct Foo<'a, T: Foo<'a>> {} | 113 | struct Foo<'a, T: Foo<'a>> {$0} |
114 | "#, | ||
115 | r#" | ||
116 | struct Foo<'a, T: Foo<'a>> {} | ||
109 | 117 | ||
110 | impl<'a, T: Foo<'a>> Foo<'a, T> { | 118 | impl<'a, T: Foo<'a>> Foo<'a, T> { |
111 | fn $0new() -> Self { Self { } } | 119 | fn $0new() -> Self { Self { } } |
112 | }", | 120 | } |
121 | "#, | ||
113 | ); | 122 | ); |
114 | check_assist( | 123 | check_assist( |
115 | generate_new, | 124 | generate_new, |
116 | "struct Foo { baz: String $0}", | 125 | r#" |
117 | "struct Foo { baz: String } | 126 | struct Foo { baz: String $0} |
127 | "#, | ||
128 | r#" | ||
129 | struct Foo { baz: String } | ||
118 | 130 | ||
119 | impl Foo { | 131 | impl Foo { |
120 | fn $0new(baz: String) -> Self { Self { baz } } | 132 | fn $0new(baz: String) -> Self { Self { baz } } |
121 | }", | 133 | } |
134 | "#, | ||
122 | ); | 135 | ); |
123 | check_assist( | 136 | check_assist( |
124 | generate_new, | 137 | generate_new, |
125 | "struct Foo { baz: String, qux: Vec<i32> $0}", | 138 | r#" |
126 | "struct Foo { baz: String, qux: Vec<i32> } | 139 | struct Foo { baz: String, qux: Vec<i32> $0} |
140 | "#, | ||
141 | r#" | ||
142 | struct Foo { baz: String, qux: Vec<i32> } | ||
127 | 143 | ||
128 | impl Foo { | 144 | impl Foo { |
129 | fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } } | 145 | fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } } |
130 | }", | 146 | } |
147 | "#, | ||
131 | ); | 148 | ); |
149 | } | ||
132 | 150 | ||
133 | // Check that visibility modifiers don't get brought in for fields | 151 | #[test] |
152 | fn check_that_visibility_modifiers_dont_get_brought_in() { | ||
134 | check_assist( | 153 | check_assist( |
135 | generate_new, | 154 | generate_new, |
136 | "struct Foo { pub baz: String, pub qux: Vec<i32> $0}", | 155 | r#" |
137 | "struct Foo { pub baz: String, pub qux: Vec<i32> } | 156 | struct Foo { pub baz: String, pub qux: Vec<i32> $0} |
157 | "#, | ||
158 | r#" | ||
159 | struct Foo { pub baz: String, pub qux: Vec<i32> } | ||
138 | 160 | ||
139 | impl Foo { | 161 | impl Foo { |
140 | fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } } | 162 | fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } } |
141 | }", | 163 | } |
164 | "#, | ||
142 | ); | 165 | ); |
166 | } | ||
143 | 167 | ||
144 | // Check that it reuses existing impls | 168 | #[test] |
169 | fn check_it_reuses_existing_impls() { | ||
145 | check_assist( | 170 | check_assist( |
146 | generate_new, | 171 | generate_new, |
147 | "struct Foo {$0} | 172 | r#" |
173 | struct Foo {$0} | ||
148 | 174 | ||
149 | impl Foo {} | 175 | impl Foo {} |
150 | ", | 176 | "#, |
151 | "struct Foo {} | 177 | r#" |
178 | struct Foo {} | ||
152 | 179 | ||
153 | impl Foo { | 180 | impl Foo { |
154 | fn $0new() -> Self { Self { } } | 181 | fn $0new() -> Self { Self { } } |
155 | } | 182 | } |
156 | ", | 183 | "#, |
157 | ); | 184 | ); |
158 | check_assist( | 185 | check_assist( |
159 | generate_new, | 186 | generate_new, |
160 | "struct Foo {$0} | 187 | r#" |
188 | struct Foo {$0} | ||
161 | 189 | ||
162 | impl Foo { | 190 | impl Foo { |
163 | fn qux(&self) {} | 191 | fn qux(&self) {} |
164 | } | 192 | } |
165 | ", | 193 | "#, |
166 | "struct Foo {} | 194 | r#" |
195 | struct Foo {} | ||
167 | 196 | ||
168 | impl Foo { | 197 | impl Foo { |
169 | fn $0new() -> Self { Self { } } | 198 | fn $0new() -> Self { Self { } } |
170 | 199 | ||
171 | fn qux(&self) {} | 200 | fn qux(&self) {} |
172 | } | 201 | } |
173 | ", | 202 | "#, |
174 | ); | 203 | ); |
175 | 204 | ||
176 | check_assist( | 205 | check_assist( |
177 | generate_new, | 206 | generate_new, |
178 | "struct Foo {$0} | 207 | r#" |
208 | struct Foo {$0} | ||
179 | 209 | ||
180 | impl Foo { | 210 | impl Foo { |
181 | fn qux(&self) {} | 211 | fn qux(&self) {} |
@@ -183,8 +213,9 @@ impl Foo { | |||
183 | 5 | 213 | 5 |
184 | } | 214 | } |
185 | } | 215 | } |
186 | ", | 216 | "#, |
187 | "struct Foo {} | 217 | r#" |
218 | struct Foo {} | ||
188 | 219 | ||
189 | impl Foo { | 220 | impl Foo { |
190 | fn $0new() -> Self { Self { } } | 221 | fn $0new() -> Self { Self { } } |
@@ -194,27 +225,37 @@ impl Foo { | |||
194 | 5 | 225 | 5 |
195 | } | 226 | } |
196 | } | 227 | } |
197 | ", | 228 | "#, |
198 | ); | 229 | ); |
230 | } | ||
199 | 231 | ||
200 | // Check visibility of new fn based on struct | 232 | #[test] |
233 | fn check_visibility_of_new_fn_based_on_struct() { | ||
201 | check_assist( | 234 | check_assist( |
202 | generate_new, | 235 | generate_new, |
203 | "pub struct Foo {$0}", | 236 | r#" |
204 | "pub struct Foo {} | 237 | pub struct Foo {$0} |
238 | "#, | ||
239 | r#" | ||
240 | pub struct Foo {} | ||
205 | 241 | ||
206 | impl Foo { | 242 | impl Foo { |
207 | pub fn $0new() -> Self { Self { } } | 243 | pub fn $0new() -> Self { Self { } } |
208 | }", | 244 | } |
245 | "#, | ||
209 | ); | 246 | ); |
210 | check_assist( | 247 | check_assist( |
211 | generate_new, | 248 | generate_new, |
212 | "pub(crate) struct Foo {$0}", | 249 | r#" |
213 | "pub(crate) struct Foo {} | 250 | pub(crate) struct Foo {$0} |
251 | "#, | ||
252 | r#" | ||
253 | pub(crate) struct Foo {} | ||
214 | 254 | ||
215 | impl Foo { | 255 | impl Foo { |
216 | pub(crate) fn $0new() -> Self { Self { } } | 256 | pub(crate) fn $0new() -> Self { Self { } } |
217 | }", | 257 | } |
258 | "#, | ||
218 | ); | 259 | ); |
219 | } | 260 | } |
220 | 261 | ||
@@ -222,26 +263,28 @@ impl Foo { | |||
222 | fn generate_new_not_applicable_if_fn_exists() { | 263 | fn generate_new_not_applicable_if_fn_exists() { |
223 | check_assist_not_applicable( | 264 | check_assist_not_applicable( |
224 | generate_new, | 265 | generate_new, |
225 | " | 266 | r#" |
226 | struct Foo {$0} | 267 | struct Foo {$0} |
227 | 268 | ||
228 | impl Foo { | 269 | impl Foo { |
229 | fn new() -> Self { | 270 | fn new() -> Self { |
230 | Self | 271 | Self |
231 | } | 272 | } |
232 | }", | 273 | } |
274 | "#, | ||
233 | ); | 275 | ); |
234 | 276 | ||
235 | check_assist_not_applicable( | 277 | check_assist_not_applicable( |
236 | generate_new, | 278 | generate_new, |
237 | " | 279 | r#" |
238 | struct Foo {$0} | 280 | struct Foo {$0} |
239 | 281 | ||
240 | impl Foo { | 282 | impl Foo { |
241 | fn New() -> Self { | 283 | fn New() -> Self { |
242 | Self | 284 | Self |
243 | } | 285 | } |
244 | }", | 286 | } |
287 | "#, | ||
245 | ); | 288 | ); |
246 | } | 289 | } |
247 | 290 | ||
@@ -249,12 +292,12 @@ impl Foo { | |||
249 | fn generate_new_target() { | 292 | fn generate_new_target() { |
250 | check_assist_target( | 293 | check_assist_target( |
251 | generate_new, | 294 | generate_new, |
252 | " | 295 | r#" |
253 | struct SomeThingIrrelevant; | 296 | struct SomeThingIrrelevant; |
254 | /// Has a lifetime parameter | 297 | /// Has a lifetime parameter |
255 | struct Foo<'a, T: Foo<'a>> {$0} | 298 | struct Foo<'a, T: Foo<'a>> {$0} |
256 | struct EvenMoreIrrelevant; | 299 | struct EvenMoreIrrelevant; |
257 | ", | 300 | "#, |
258 | "/// Has a lifetime parameter | 301 | "/// Has a lifetime parameter |
259 | struct Foo<'a, T: Foo<'a>> {}", | 302 | struct Foo<'a, T: Foo<'a>> {}", |
260 | ); | 303 | ); |
@@ -264,7 +307,7 @@ struct Foo<'a, T: Foo<'a>> {}", | |||
264 | fn test_unrelated_new() { | 307 | fn test_unrelated_new() { |
265 | check_assist( | 308 | check_assist( |
266 | generate_new, | 309 | generate_new, |
267 | r##" | 310 | r#" |
268 | pub struct AstId<N: AstNode> { | 311 | pub struct AstId<N: AstNode> { |
269 | file_id: HirFileId, | 312 | file_id: HirFileId, |
270 | file_ast_id: FileAstId<N>, | 313 | file_ast_id: FileAstId<N>, |
@@ -285,8 +328,9 @@ impl<T> Source<T> { | |||
285 | pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { | 328 | pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { |
286 | Source { file_id: self.file_id, ast: f(self.ast) } | 329 | Source { file_id: self.file_id, ast: f(self.ast) } |
287 | } | 330 | } |
288 | }"##, | 331 | } |
289 | r##" | 332 | "#, |
333 | r#" | ||
290 | pub struct AstId<N: AstNode> { | 334 | pub struct AstId<N: AstNode> { |
291 | file_id: HirFileId, | 335 | file_id: HirFileId, |
292 | file_ast_id: FileAstId<N>, | 336 | file_ast_id: FileAstId<N>, |
@@ -309,7 +353,8 @@ impl<T> Source<T> { | |||
309 | pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { | 353 | pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> { |
310 | Source { file_id: self.file_id, ast: f(self.ast) } | 354 | Source { file_id: self.file_id, ast: f(self.ast) } |
311 | } | 355 | } |
312 | }"##, | 356 | } |
357 | "#, | ||
313 | ); | 358 | ); |
314 | } | 359 | } |
315 | } | 360 | } |
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs index 49c1a9776..4406406a2 100644 --- a/crates/ide_assists/src/tests/generated.rs +++ b/crates/ide_assists/src/tests/generated.rs | |||
@@ -50,7 +50,6 @@ trait Trait { | |||
50 | impl Trait for () { | 50 | impl Trait for () { |
51 | type X = (); | 51 | type X = (); |
52 | fn foo(&self) {}$0 | 52 | fn foo(&self) {}$0 |
53 | |||
54 | } | 53 | } |
55 | "#####, | 54 | "#####, |
56 | r#####" | 55 | r#####" |
diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs index 0dcf20c61..7d562d1d4 100644 --- a/crates/ide_assists/src/utils.rs +++ b/crates/ide_assists/src/utils.rs | |||
@@ -128,15 +128,12 @@ pub fn add_trait_assoc_items_to_impl( | |||
128 | sema: &hir::Semantics<ide_db::RootDatabase>, | 128 | sema: &hir::Semantics<ide_db::RootDatabase>, |
129 | items: Vec<ast::AssocItem>, | 129 | items: Vec<ast::AssocItem>, |
130 | trait_: hir::Trait, | 130 | trait_: hir::Trait, |
131 | impl_def: ast::Impl, | 131 | impl_: ast::Impl, |
132 | target_scope: hir::SemanticsScope, | 132 | target_scope: hir::SemanticsScope, |
133 | ) -> (ast::Impl, ast::AssocItem) { | 133 | ) -> (ast::Impl, ast::AssocItem) { |
134 | let impl_item_list = impl_def.assoc_item_list().unwrap_or_else(make::assoc_item_list); | ||
135 | |||
136 | let n_existing_items = impl_item_list.assoc_items().count(); | ||
137 | let source_scope = sema.scope_for_def(trait_); | 134 | let source_scope = sema.scope_for_def(trait_); |
138 | let ast_transform = QualifyPaths::new(&target_scope, &source_scope) | 135 | let ast_transform = QualifyPaths::new(&target_scope, &source_scope) |
139 | .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def.clone())); | 136 | .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_.clone())); |
140 | 137 | ||
141 | let items = items | 138 | let items = items |
142 | .into_iter() | 139 | .into_iter() |
@@ -147,13 +144,18 @@ pub fn add_trait_assoc_items_to_impl( | |||
147 | ast::AssocItem::TypeAlias(def) => ast::AssocItem::TypeAlias(def.remove_bounds()), | 144 | ast::AssocItem::TypeAlias(def) => ast::AssocItem::TypeAlias(def.remove_bounds()), |
148 | _ => it, | 145 | _ => it, |
149 | }) | 146 | }) |
150 | .map(|it| edit::remove_attrs_and_docs(&it)); | 147 | .map(|it| edit::remove_attrs_and_docs(&it).clone_subtree().clone_for_update()); |
151 | 148 | ||
152 | let new_impl_item_list = impl_item_list.append_items(items); | 149 | let res = impl_.clone_for_update(); |
153 | let new_impl_def = impl_def.with_assoc_item_list(new_impl_item_list); | 150 | let assoc_item_list = res.get_or_create_assoc_item_list(); |
154 | let first_new_item = | 151 | let mut first_item = None; |
155 | new_impl_def.assoc_item_list().unwrap().assoc_items().nth(n_existing_items).unwrap(); | 152 | for item in items { |
156 | return (new_impl_def, first_new_item); | 153 | if first_item.is_none() { |
154 | first_item = Some(item.clone()) | ||
155 | } | ||
156 | assoc_item_list.add_item(item) | ||
157 | } | ||
158 | return (res, first_item.unwrap()); | ||
157 | 159 | ||
158 | fn add_body(fn_def: ast::Fn) -> ast::Fn { | 160 | fn add_body(fn_def: ast::Fn) -> ast::Fn { |
159 | match fn_def.body() { | 161 | match fn_def.body() { |
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs index 10ec94cd2..7e4b8252e 100644 --- a/crates/syntax/src/ast/edit.rs +++ b/crates/syntax/src/ast/edit.rs | |||
@@ -80,81 +80,6 @@ where | |||
80 | } | 80 | } |
81 | } | 81 | } |
82 | 82 | ||
83 | impl ast::Impl { | ||
84 | #[must_use] | ||
85 | pub fn with_assoc_item_list(&self, items: ast::AssocItemList) -> ast::Impl { | ||
86 | let mut to_insert: ArrayVec<SyntaxElement, 2> = ArrayVec::new(); | ||
87 | if let Some(old_items) = self.assoc_item_list() { | ||
88 | let to_replace: SyntaxElement = old_items.syntax().clone().into(); | ||
89 | to_insert.push(items.syntax().clone().into()); | ||
90 | self.replace_children(single_node(to_replace), to_insert) | ||
91 | } else { | ||
92 | to_insert.push(make::tokens::single_space().into()); | ||
93 | to_insert.push(items.syntax().clone().into()); | ||
94 | self.insert_children(InsertPosition::Last, to_insert) | ||
95 | } | ||
96 | } | ||
97 | } | ||
98 | |||
99 | impl ast::AssocItemList { | ||
100 | #[must_use] | ||
101 | pub fn append_items( | ||
102 | &self, | ||
103 | items: impl IntoIterator<Item = ast::AssocItem>, | ||
104 | ) -> ast::AssocItemList { | ||
105 | let mut res = self.clone(); | ||
106 | if !self.syntax().text().contains_char('\n') { | ||
107 | res = make_multiline(res); | ||
108 | } | ||
109 | items.into_iter().for_each(|it| res = res.append_item(it)); | ||
110 | res.fixup_trailing_whitespace().unwrap_or(res) | ||
111 | } | ||
112 | |||
113 | #[must_use] | ||
114 | pub fn append_item(&self, item: ast::AssocItem) -> ast::AssocItemList { | ||
115 | let (indent, position, whitespace) = match self.assoc_items().last() { | ||
116 | Some(it) => ( | ||
117 | leading_indent(it.syntax()).unwrap_or_default().to_string(), | ||
118 | InsertPosition::After(it.syntax().clone().into()), | ||
119 | "\n\n", | ||
120 | ), | ||
121 | None => match self.l_curly_token() { | ||
122 | Some(it) => ( | ||
123 | " ".to_string() + &leading_indent(self.syntax()).unwrap_or_default(), | ||
124 | InsertPosition::After(it.into()), | ||
125 | "\n", | ||
126 | ), | ||
127 | None => return self.clone(), | ||
128 | }, | ||
129 | }; | ||
130 | let ws = tokens::WsBuilder::new(&format!("{}{}", whitespace, indent)); | ||
131 | let to_insert: ArrayVec<SyntaxElement, 2> = | ||
132 | [ws.ws().into(), item.syntax().clone().into()].into(); | ||
133 | self.insert_children(position, to_insert) | ||
134 | } | ||
135 | |||
136 | /// Remove extra whitespace between last item and closing curly brace. | ||
137 | fn fixup_trailing_whitespace(&self) -> Option<ast::AssocItemList> { | ||
138 | let first_token_after_items = | ||
139 | self.assoc_items().last()?.syntax().next_sibling_or_token()?; | ||
140 | let last_token_before_curly = self.r_curly_token()?.prev_sibling_or_token()?; | ||
141 | if last_token_before_curly != first_token_after_items { | ||
142 | // there is something more between last item and | ||
143 | // right curly than just whitespace - bail out | ||
144 | return None; | ||
145 | } | ||
146 | let whitespace = | ||
147 | last_token_before_curly.clone().into_token().and_then(ast::Whitespace::cast)?; | ||
148 | let text = whitespace.syntax().text(); | ||
149 | let newline = text.rfind('\n')?; | ||
150 | let keep = tokens::WsBuilder::new(&text[newline..]); | ||
151 | Some(self.replace_children( | ||
152 | first_token_after_items..=last_token_before_curly, | ||
153 | std::iter::once(keep.ws().into()), | ||
154 | )) | ||
155 | } | ||
156 | } | ||
157 | |||
158 | impl ast::RecordExprFieldList { | 83 | impl ast::RecordExprFieldList { |
159 | #[must_use] | 84 | #[must_use] |
160 | pub fn append_field(&self, field: &ast::RecordExprField) -> ast::RecordExprFieldList { | 85 | pub fn append_field(&self, field: &ast::RecordExprField) -> ast::RecordExprFieldList { |
@@ -246,21 +171,6 @@ impl ast::TypeAlias { | |||
246 | } | 171 | } |
247 | } | 172 | } |
248 | 173 | ||
249 | impl ast::TypeParam { | ||
250 | #[must_use] | ||
251 | pub fn remove_bounds(&self) -> ast::TypeParam { | ||
252 | let colon = match self.colon_token() { | ||
253 | Some(it) => it, | ||
254 | None => return self.clone(), | ||
255 | }; | ||
256 | let end = match self.type_bound_list() { | ||
257 | Some(it) => it.syntax().clone().into(), | ||
258 | None => colon.clone().into(), | ||
259 | }; | ||
260 | self.replace_children(colon.into()..=end, iter::empty()) | ||
261 | } | ||
262 | } | ||
263 | |||
264 | impl ast::Path { | 174 | impl ast::Path { |
265 | #[must_use] | 175 | #[must_use] |
266 | pub fn with_segment(&self, segment: ast::PathSegment) -> ast::Path { | 176 | pub fn with_segment(&self, segment: ast::PathSegment) -> ast::Path { |
@@ -411,61 +321,6 @@ impl ast::MatchArmList { | |||
411 | } | 321 | } |
412 | } | 322 | } |
413 | 323 | ||
414 | impl ast::GenericParamList { | ||
415 | #[must_use] | ||
416 | pub fn append_params( | ||
417 | &self, | ||
418 | params: impl IntoIterator<Item = ast::GenericParam>, | ||
419 | ) -> ast::GenericParamList { | ||
420 | let mut res = self.clone(); | ||
421 | params.into_iter().for_each(|it| res = res.append_param(it)); | ||
422 | res | ||
423 | } | ||
424 | |||
425 | #[must_use] | ||
426 | pub fn append_param(&self, item: ast::GenericParam) -> ast::GenericParamList { | ||
427 | let space = tokens::single_space(); | ||
428 | |||
429 | let mut to_insert: ArrayVec<SyntaxElement, 4> = ArrayVec::new(); | ||
430 | if self.generic_params().next().is_some() { | ||
431 | to_insert.push(space.into()); | ||
432 | } | ||
433 | to_insert.push(item.syntax().clone().into()); | ||
434 | |||
435 | macro_rules! after_l_angle { | ||
436 | () => {{ | ||
437 | let anchor = match self.l_angle_token() { | ||
438 | Some(it) => it.into(), | ||
439 | None => return self.clone(), | ||
440 | }; | ||
441 | InsertPosition::After(anchor) | ||
442 | }}; | ||
443 | } | ||
444 | |||
445 | macro_rules! after_field { | ||
446 | ($anchor:expr) => { | ||
447 | if let Some(comma) = $anchor | ||
448 | .syntax() | ||
449 | .siblings_with_tokens(Direction::Next) | ||
450 | .find(|it| it.kind() == T![,]) | ||
451 | { | ||
452 | InsertPosition::After(comma) | ||
453 | } else { | ||
454 | to_insert.insert(0, make::token(T![,]).into()); | ||
455 | InsertPosition::After($anchor.syntax().clone().into()) | ||
456 | } | ||
457 | }; | ||
458 | } | ||
459 | |||
460 | let position = match self.generic_params().last() { | ||
461 | Some(it) => after_field!(it), | ||
462 | None => after_l_angle!(), | ||
463 | }; | ||
464 | |||
465 | self.insert_children(position, to_insert) | ||
466 | } | ||
467 | } | ||
468 | |||
469 | #[must_use] | 324 | #[must_use] |
470 | pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { | 325 | pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { |
471 | N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap() | 326 | N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap() |
@@ -516,6 +371,12 @@ impl ops::Add<u8> for IndentLevel { | |||
516 | } | 371 | } |
517 | 372 | ||
518 | impl IndentLevel { | 373 | impl IndentLevel { |
374 | pub fn single() -> IndentLevel { | ||
375 | IndentLevel(0) | ||
376 | } | ||
377 | pub fn is_zero(&self) -> bool { | ||
378 | self.0 == 0 | ||
379 | } | ||
519 | pub fn from_element(element: &SyntaxElement) -> IndentLevel { | 380 | pub fn from_element(element: &SyntaxElement) -> IndentLevel { |
520 | match element { | 381 | match element { |
521 | rowan::NodeOrToken::Node(it) => IndentLevel::from_node(it), | 382 | rowan::NodeOrToken::Node(it) => IndentLevel::from_node(it), |
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs index 168355555..9812e00c9 100644 --- a/crates/syntax/src/ast/edit_in_place.rs +++ b/crates/syntax/src/ast/edit_in_place.rs | |||
@@ -2,11 +2,16 @@ | |||
2 | 2 | ||
3 | use std::iter::empty; | 3 | use std::iter::empty; |
4 | 4 | ||
5 | use parser::T; | 5 | use parser::{SyntaxKind, T}; |
6 | use rowan::SyntaxElement; | ||
6 | 7 | ||
7 | use crate::{ | 8 | use crate::{ |
8 | algo::neighbor, | 9 | algo::neighbor, |
9 | ast::{self, edit::AstNodeEdit, make, GenericParamsOwner, WhereClause}, | 10 | ast::{ |
11 | self, | ||
12 | edit::{AstNodeEdit, IndentLevel}, | ||
13 | make, GenericParamsOwner, | ||
14 | }, | ||
10 | ted::{self, Position}, | 15 | ted::{self, Position}, |
11 | AstNode, AstToken, Direction, | 16 | AstNode, AstToken, Direction, |
12 | }; | 17 | }; |
@@ -37,7 +42,7 @@ impl GenericParamsOwnerEdit for ast::Fn { | |||
37 | } | 42 | } |
38 | } | 43 | } |
39 | 44 | ||
40 | fn get_or_create_where_clause(&self) -> WhereClause { | 45 | fn get_or_create_where_clause(&self) -> ast::WhereClause { |
41 | if self.where_clause().is_none() { | 46 | if self.where_clause().is_none() { |
42 | let position = if let Some(ty) = self.ret_type() { | 47 | let position = if let Some(ty) = self.ret_type() { |
43 | Position::after(ty.syntax()) | 48 | Position::after(ty.syntax()) |
@@ -67,7 +72,7 @@ impl GenericParamsOwnerEdit for ast::Impl { | |||
67 | } | 72 | } |
68 | } | 73 | } |
69 | 74 | ||
70 | fn get_or_create_where_clause(&self) -> WhereClause { | 75 | fn get_or_create_where_clause(&self) -> ast::WhereClause { |
71 | if self.where_clause().is_none() { | 76 | if self.where_clause().is_none() { |
72 | let position = if let Some(items) = self.assoc_item_list() { | 77 | let position = if let Some(items) = self.assoc_item_list() { |
73 | Position::before(items.syntax()) | 78 | Position::before(items.syntax()) |
@@ -97,7 +102,7 @@ impl GenericParamsOwnerEdit for ast::Trait { | |||
97 | } | 102 | } |
98 | } | 103 | } |
99 | 104 | ||
100 | fn get_or_create_where_clause(&self) -> WhereClause { | 105 | fn get_or_create_where_clause(&self) -> ast::WhereClause { |
101 | if self.where_clause().is_none() { | 106 | if self.where_clause().is_none() { |
102 | let position = if let Some(items) = self.assoc_item_list() { | 107 | let position = if let Some(items) = self.assoc_item_list() { |
103 | Position::before(items.syntax()) | 108 | Position::before(items.syntax()) |
@@ -127,7 +132,7 @@ impl GenericParamsOwnerEdit for ast::Struct { | |||
127 | } | 132 | } |
128 | } | 133 | } |
129 | 134 | ||
130 | fn get_or_create_where_clause(&self) -> WhereClause { | 135 | fn get_or_create_where_clause(&self) -> ast::WhereClause { |
131 | if self.where_clause().is_none() { | 136 | if self.where_clause().is_none() { |
132 | let tfl = self.field_list().and_then(|fl| match fl { | 137 | let tfl = self.field_list().and_then(|fl| match fl { |
133 | ast::FieldList::RecordFieldList(_) => None, | 138 | ast::FieldList::RecordFieldList(_) => None, |
@@ -165,7 +170,7 @@ impl GenericParamsOwnerEdit for ast::Enum { | |||
165 | } | 170 | } |
166 | } | 171 | } |
167 | 172 | ||
168 | fn get_or_create_where_clause(&self) -> WhereClause { | 173 | fn get_or_create_where_clause(&self) -> ast::WhereClause { |
169 | if self.where_clause().is_none() { | 174 | if self.where_clause().is_none() { |
170 | let position = if let Some(gpl) = self.generic_param_list() { | 175 | let position = if let Some(gpl) = self.generic_param_list() { |
171 | Position::after(gpl.syntax()) | 176 | Position::after(gpl.syntax()) |
@@ -272,6 +277,59 @@ impl ast::Use { | |||
272 | } | 277 | } |
273 | } | 278 | } |
274 | 279 | ||
280 | impl ast::Impl { | ||
281 | pub fn get_or_create_assoc_item_list(&self) -> ast::AssocItemList { | ||
282 | if self.assoc_item_list().is_none() { | ||
283 | let assoc_item_list = make::assoc_item_list().clone_for_update(); | ||
284 | ted::append_child(self.syntax(), assoc_item_list.syntax()); | ||
285 | } | ||
286 | self.assoc_item_list().unwrap() | ||
287 | } | ||
288 | } | ||
289 | |||
290 | impl ast::AssocItemList { | ||
291 | pub fn add_item(&self, item: ast::AssocItem) { | ||
292 | let (indent, position, whitespace) = match self.assoc_items().last() { | ||
293 | Some(last_item) => ( | ||
294 | IndentLevel::from_node(last_item.syntax()), | ||
295 | Position::after(last_item.syntax()), | ||
296 | "\n\n", | ||
297 | ), | ||
298 | None => match self.l_curly_token() { | ||
299 | Some(l_curly) => { | ||
300 | self.normalize_ws_between_braces(); | ||
301 | (IndentLevel::from_token(&l_curly) + 1, Position::after(&l_curly), "\n") | ||
302 | } | ||
303 | None => (IndentLevel::single(), Position::last_child_of(self.syntax()), "\n"), | ||
304 | }, | ||
305 | }; | ||
306 | let elements: Vec<SyntaxElement<_>> = vec![ | ||
307 | make::tokens::whitespace(&format!("{}{}", whitespace, indent)).into(), | ||
308 | item.syntax().clone().into(), | ||
309 | ]; | ||
310 | ted::insert_all(position, elements); | ||
311 | } | ||
312 | |||
313 | fn normalize_ws_between_braces(&self) -> Option<()> { | ||
314 | let l = self.l_curly_token()?; | ||
315 | let r = self.r_curly_token()?; | ||
316 | let indent = IndentLevel::from_node(self.syntax()); | ||
317 | |||
318 | match l.next_sibling_or_token() { | ||
319 | Some(ws) if ws.kind() == SyntaxKind::WHITESPACE => { | ||
320 | if ws.next_sibling_or_token()?.into_token()? == r { | ||
321 | ted::replace(ws, make::tokens::whitespace(&format!("\n{}", indent))); | ||
322 | } | ||
323 | } | ||
324 | Some(ws) if ws.kind() == T!['}'] => { | ||
325 | ted::insert(Position::after(l), make::tokens::whitespace(&format!("\n{}", indent))); | ||
326 | } | ||
327 | _ => (), | ||
328 | } | ||
329 | Some(()) | ||
330 | } | ||
331 | } | ||
332 | |||
275 | #[cfg(test)] | 333 | #[cfg(test)] |
276 | mod tests { | 334 | mod tests { |
277 | use std::fmt; | 335 | use std::fmt; |
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index de04c8620..d13926ded 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -99,7 +99,7 @@ fn ty_from_text(text: &str) -> ast::Type { | |||
99 | } | 99 | } |
100 | 100 | ||
101 | pub fn assoc_item_list() -> ast::AssocItemList { | 101 | pub fn assoc_item_list() -> ast::AssocItemList { |
102 | ast_from_text("impl C for D {};") | 102 | ast_from_text("impl C for D {}") |
103 | } | 103 | } |
104 | 104 | ||
105 | pub fn impl_trait(trait_: ast::Path, ty: ast::Path) -> ast::Impl { | 105 | pub fn impl_trait(trait_: ast::Path, ty: ast::Path) -> ast::Impl { |