diff options
Diffstat (limited to 'crates/syntax/src/ast/edit_in_place.rs')
-rw-r--r-- | crates/syntax/src/ast/edit_in_place.rs | 182 |
1 files changed, 174 insertions, 8 deletions
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs index 168355555..2676ed8c9 100644 --- a/crates/syntax/src/ast/edit_in_place.rs +++ b/crates/syntax/src/ast/edit_in_place.rs | |||
@@ -2,13 +2,18 @@ | |||
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, SyntaxNode, |
12 | }; | 17 | }; |
13 | 18 | ||
14 | use super::NameOwner; | 19 | use super::NameOwner; |
@@ -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,167 @@ 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 | normalize_ws_between_braces(self.syntax()); | ||
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 | |||
314 | impl ast::Fn { | ||
315 | pub fn get_or_create_body(&self) -> ast::BlockExpr { | ||
316 | if self.body().is_none() { | ||
317 | let body = make::ext::empty_block_expr().clone_for_update(); | ||
318 | match self.semicolon_token() { | ||
319 | Some(semi) => { | ||
320 | ted::replace(semi, body.syntax()); | ||
321 | ted::insert(Position::before(body.syntax), make::tokens::single_space()); | ||
322 | } | ||
323 | None => ted::append_child(self.syntax(), body.syntax()), | ||
324 | } | ||
325 | } | ||
326 | self.body().unwrap() | ||
327 | } | ||
328 | } | ||
329 | |||
330 | impl ast::MatchArm { | ||
331 | pub fn remove(&self) { | ||
332 | if let Some(sibling) = self.syntax().prev_sibling_or_token() { | ||
333 | if sibling.kind() == SyntaxKind::WHITESPACE { | ||
334 | ted::remove(sibling); | ||
335 | } | ||
336 | } | ||
337 | if let Some(sibling) = self.syntax().next_sibling_or_token() { | ||
338 | if sibling.kind() == T![,] { | ||
339 | ted::remove(sibling); | ||
340 | } | ||
341 | } | ||
342 | ted::remove(self.syntax()); | ||
343 | } | ||
344 | } | ||
345 | |||
346 | impl ast::MatchArmList { | ||
347 | pub fn add_arm(&self, arm: ast::MatchArm) { | ||
348 | normalize_ws_between_braces(self.syntax()); | ||
349 | let position = match self.arms().last() { | ||
350 | Some(last_arm) => { | ||
351 | let curly = last_arm | ||
352 | .syntax() | ||
353 | .siblings_with_tokens(Direction::Next) | ||
354 | .find(|it| it.kind() == T![,]); | ||
355 | Position::after(curly.unwrap_or_else(|| last_arm.syntax().clone().into())) | ||
356 | } | ||
357 | None => match self.l_curly_token() { | ||
358 | Some(it) => Position::after(it), | ||
359 | None => Position::last_child_of(self.syntax()), | ||
360 | }, | ||
361 | }; | ||
362 | let indent = IndentLevel::from_node(self.syntax()) + 1; | ||
363 | let elements = vec![ | ||
364 | make::tokens::whitespace(&format!("\n{}", indent)).into(), | ||
365 | arm.syntax().clone().into(), | ||
366 | ]; | ||
367 | ted::insert_all(position, elements); | ||
368 | } | ||
369 | } | ||
370 | |||
371 | impl ast::RecordExprFieldList { | ||
372 | pub fn add_field(&self, field: ast::RecordExprField) { | ||
373 | let is_multiline = self.syntax().text().contains_char('\n'); | ||
374 | let whitespace = if is_multiline { | ||
375 | let indent = IndentLevel::from_node(self.syntax()) + 1; | ||
376 | make::tokens::whitespace(&format!("\n{}", indent)) | ||
377 | } else { | ||
378 | make::tokens::single_space() | ||
379 | }; | ||
380 | |||
381 | if is_multiline { | ||
382 | normalize_ws_between_braces(self.syntax()); | ||
383 | } | ||
384 | |||
385 | let position = match self.fields().last() { | ||
386 | Some(last_field) => { | ||
387 | let comma = match last_field | ||
388 | .syntax() | ||
389 | .siblings_with_tokens(Direction::Next) | ||
390 | .filter_map(|it| it.into_token()) | ||
391 | .find(|it| it.kind() == T![,]) | ||
392 | { | ||
393 | Some(it) => it, | ||
394 | None => { | ||
395 | let comma = ast::make::token(T![,]); | ||
396 | ted::insert(Position::after(last_field.syntax()), &comma); | ||
397 | comma | ||
398 | } | ||
399 | }; | ||
400 | Position::after(comma) | ||
401 | } | ||
402 | None => match self.l_curly_token() { | ||
403 | Some(it) => Position::after(it), | ||
404 | None => Position::last_child_of(self.syntax()), | ||
405 | }, | ||
406 | }; | ||
407 | |||
408 | ted::insert_all(position, vec![whitespace.into(), field.syntax().clone().into()]); | ||
409 | if is_multiline { | ||
410 | ted::insert(Position::after(field.syntax()), ast::make::token(T![,])); | ||
411 | } | ||
412 | } | ||
413 | } | ||
414 | |||
415 | fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> { | ||
416 | let l = node | ||
417 | .children_with_tokens() | ||
418 | .filter_map(|it| it.into_token()) | ||
419 | .find(|it| it.kind() == T!['{'])?; | ||
420 | let r = node | ||
421 | .children_with_tokens() | ||
422 | .filter_map(|it| it.into_token()) | ||
423 | .find(|it| it.kind() == T!['}'])?; | ||
424 | |||
425 | let indent = IndentLevel::from_node(node); | ||
426 | |||
427 | match l.next_sibling_or_token() { | ||
428 | Some(ws) if ws.kind() == SyntaxKind::WHITESPACE => { | ||
429 | if ws.next_sibling_or_token()?.into_token()? == r { | ||
430 | ted::replace(ws, make::tokens::whitespace(&format!("\n{}", indent))); | ||
431 | } | ||
432 | } | ||
433 | Some(ws) if ws.kind() == T!['}'] => { | ||
434 | ted::insert(Position::after(l), make::tokens::whitespace(&format!("\n{}", indent))); | ||
435 | } | ||
436 | _ => (), | ||
437 | } | ||
438 | Some(()) | ||
439 | } | ||
440 | |||
275 | #[cfg(test)] | 441 | #[cfg(test)] |
276 | mod tests { | 442 | mod tests { |
277 | use std::fmt; | 443 | use std::fmt; |