1use std::iter::{empty, once, successors};
4
5use parser::{SyntaxKind, T};
6
7use crate::{
8 AstNode, AstToken, Direction, SyntaxElement,
9 SyntaxKind::{ATTR, COMMENT, WHITESPACE},
10 SyntaxNode, SyntaxToken,
11 algo::{self, neighbor},
12 ast::{self, HasGenericParams, edit::IndentLevel, make},
13 ted::{self, Position},
14};
15
16use super::{GenericParam, HasName};
17
18pub trait GenericParamsOwnerEdit: ast::HasGenericParams {
19 fn get_or_create_generic_param_list(&self) -> ast::GenericParamList;
20 fn get_or_create_where_clause(&self) -> ast::WhereClause;
21}
22
23impl GenericParamsOwnerEdit for ast::Fn {
24 fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
25 match self.generic_param_list() {
26 Some(it) => it,
27 None => {
28 let position = if let Some(name) = self.name() {
29 Position::after(name.syntax)
30 } else if let Some(fn_token) = self.fn_token() {
31 Position::after(fn_token)
32 } else if let Some(param_list) = self.param_list() {
33 Position::before(param_list.syntax)
34 } else {
35 Position::last_child_of(self.syntax())
36 };
37 create_generic_param_list(position)
38 }
39 }
40 }
41
42 fn get_or_create_where_clause(&self) -> ast::WhereClause {
43 if self.where_clause().is_none() {
44 let position = if let Some(ty) = self.ret_type() {
45 Position::after(ty.syntax())
46 } else if let Some(param_list) = self.param_list() {
47 Position::after(param_list.syntax())
48 } else {
49 Position::last_child_of(self.syntax())
50 };
51 create_where_clause(position);
52 }
53 self.where_clause().unwrap()
54 }
55}
56
57impl GenericParamsOwnerEdit for ast::Impl {
58 fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
59 match self.generic_param_list() {
60 Some(it) => it,
61 None => {
62 let position = match self.impl_token() {
63 Some(imp_token) => Position::after(imp_token),
64 None => Position::last_child_of(self.syntax()),
65 };
66 create_generic_param_list(position)
67 }
68 }
69 }
70
71 fn get_or_create_where_clause(&self) -> ast::WhereClause {
72 if self.where_clause().is_none() {
73 let position = match self.assoc_item_list() {
74 Some(items) => Position::before(items.syntax()),
75 None => Position::last_child_of(self.syntax()),
76 };
77 create_where_clause(position);
78 }
79 self.where_clause().unwrap()
80 }
81}
82
83impl GenericParamsOwnerEdit for ast::Trait {
84 fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
85 match self.generic_param_list() {
86 Some(it) => it,
87 None => {
88 let position = if let Some(name) = self.name() {
89 Position::after(name.syntax)
90 } else if let Some(trait_token) = self.trait_token() {
91 Position::after(trait_token)
92 } else {
93 Position::last_child_of(self.syntax())
94 };
95 create_generic_param_list(position)
96 }
97 }
98 }
99
100 fn get_or_create_where_clause(&self) -> ast::WhereClause {
101 if self.where_clause().is_none() {
102 let position = match self.assoc_item_list() {
103 Some(items) => Position::before(items.syntax()),
104 None => Position::last_child_of(self.syntax()),
105 };
106 create_where_clause(position);
107 }
108 self.where_clause().unwrap()
109 }
110}
111
112impl GenericParamsOwnerEdit for ast::TraitAlias {
113 fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
114 match self.generic_param_list() {
115 Some(it) => it,
116 None => {
117 let position = if let Some(name) = self.name() {
118 Position::after(name.syntax)
119 } else if let Some(trait_token) = self.trait_token() {
120 Position::after(trait_token)
121 } else {
122 Position::last_child_of(self.syntax())
123 };
124 create_generic_param_list(position)
125 }
126 }
127 }
128
129 fn get_or_create_where_clause(&self) -> ast::WhereClause {
130 if self.where_clause().is_none() {
131 let position = match self.semicolon_token() {
132 Some(tok) => Position::before(tok),
133 None => Position::last_child_of(self.syntax()),
134 };
135 create_where_clause(position);
136 }
137 self.where_clause().unwrap()
138 }
139}
140
141impl GenericParamsOwnerEdit for ast::TypeAlias {
142 fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
143 match self.generic_param_list() {
144 Some(it) => it,
145 None => {
146 let position = if let Some(name) = self.name() {
147 Position::after(name.syntax)
148 } else if let Some(trait_token) = self.type_token() {
149 Position::after(trait_token)
150 } else {
151 Position::last_child_of(self.syntax())
152 };
153 create_generic_param_list(position)
154 }
155 }
156 }
157
158 fn get_or_create_where_clause(&self) -> ast::WhereClause {
159 if self.where_clause().is_none() {
160 let position = match self.eq_token() {
161 Some(tok) => Position::before(tok),
162 None => match self.semicolon_token() {
163 Some(tok) => Position::before(tok),
164 None => Position::last_child_of(self.syntax()),
165 },
166 };
167 create_where_clause(position);
168 }
169 self.where_clause().unwrap()
170 }
171}
172
173impl GenericParamsOwnerEdit for ast::Struct {
174 fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
175 match self.generic_param_list() {
176 Some(it) => it,
177 None => {
178 let position = if let Some(name) = self.name() {
179 Position::after(name.syntax)
180 } else if let Some(struct_token) = self.struct_token() {
181 Position::after(struct_token)
182 } else {
183 Position::last_child_of(self.syntax())
184 };
185 create_generic_param_list(position)
186 }
187 }
188 }
189
190 fn get_or_create_where_clause(&self) -> ast::WhereClause {
191 if self.where_clause().is_none() {
192 let tfl = self.field_list().and_then(|fl| match fl {
193 ast::FieldList::RecordFieldList(_) => None,
194 ast::FieldList::TupleFieldList(it) => Some(it),
195 });
196 let position = if let Some(tfl) = tfl {
197 Position::after(tfl.syntax())
198 } else if let Some(gpl) = self.generic_param_list() {
199 Position::after(gpl.syntax())
200 } else if let Some(name) = self.name() {
201 Position::after(name.syntax())
202 } else {
203 Position::last_child_of(self.syntax())
204 };
205 create_where_clause(position);
206 }
207 self.where_clause().unwrap()
208 }
209}
210
211impl GenericParamsOwnerEdit for ast::Enum {
212 fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
213 match self.generic_param_list() {
214 Some(it) => it,
215 None => {
216 let position = if let Some(name) = self.name() {
217 Position::after(name.syntax)
218 } else if let Some(enum_token) = self.enum_token() {
219 Position::after(enum_token)
220 } else {
221 Position::last_child_of(self.syntax())
222 };
223 create_generic_param_list(position)
224 }
225 }
226 }
227
228 fn get_or_create_where_clause(&self) -> ast::WhereClause {
229 if self.where_clause().is_none() {
230 let position = if let Some(gpl) = self.generic_param_list() {
231 Position::after(gpl.syntax())
232 } else if let Some(name) = self.name() {
233 Position::after(name.syntax())
234 } else {
235 Position::last_child_of(self.syntax())
236 };
237 create_where_clause(position);
238 }
239 self.where_clause().unwrap()
240 }
241}
242
243fn create_where_clause(position: Position) {
244 let where_clause = make::where_clause(empty()).clone_for_update();
245 ted::insert(position, where_clause.syntax());
246}
247
248fn create_generic_param_list(position: Position) -> ast::GenericParamList {
249 let gpl = make::generic_param_list(empty()).clone_for_update();
250 ted::insert_raw(position, gpl.syntax());
251 gpl
252}
253
254pub trait AttrsOwnerEdit: ast::HasAttrs {
255 fn remove_attrs_and_docs(&self) {
256 remove_attrs_and_docs(self.syntax());
257
258 fn remove_attrs_and_docs(node: &SyntaxNode) {
259 let mut remove_next_ws = false;
260 for child in node.children_with_tokens() {
261 match child.kind() {
262 ATTR | COMMENT => {
263 remove_next_ws = true;
264 child.detach();
265 continue;
266 }
267 WHITESPACE if remove_next_ws => {
268 child.detach();
269 }
270 _ => (),
271 }
272 remove_next_ws = false;
273 }
274 }
275 }
276
277 fn add_attr(&self, attr: ast::Attr) {
278 add_attr(self.syntax(), attr);
279
280 fn add_attr(node: &SyntaxNode, attr: ast::Attr) {
281 let indent = IndentLevel::from_node(node);
282 attr.reindent_to(indent);
283
284 let after_attrs_and_comments = node
285 .children_with_tokens()
286 .find(|it| !matches!(it.kind(), WHITESPACE | COMMENT | ATTR))
287 .map_or(Position::first_child_of(node), Position::before);
288
289 ted::insert_all(
290 after_attrs_and_comments,
291 vec![
292 attr.syntax().clone().into(),
293 make::tokens::whitespace(&format!("\n{indent}")).into(),
294 ],
295 )
296 }
297 }
298}
299
300impl<T: ast::HasAttrs> AttrsOwnerEdit for T {}
301
302impl ast::GenericParamList {
303 pub fn add_generic_param(&self, generic_param: ast::GenericParam) {
304 match self.generic_params().last() {
305 Some(last_param) => {
306 let position = Position::after(last_param.syntax());
307 let elements = vec![
308 make::token(T![,]).into(),
309 make::tokens::single_space().into(),
310 generic_param.syntax().clone().into(),
311 ];
312 ted::insert_all(position, elements);
313 }
314 None => {
315 let after_l_angle = Position::after(self.l_angle_token().unwrap());
316 ted::insert(after_l_angle, generic_param.syntax());
317 }
318 }
319 }
320
321 pub fn remove_generic_param(&self, generic_param: ast::GenericParam) {
323 if let Some(previous) = generic_param.syntax().prev_sibling() {
324 if let Some(next_token) = previous.next_sibling_or_token() {
325 ted::remove_all(next_token..=generic_param.syntax().clone().into());
326 }
327 } else if let Some(next) = generic_param.syntax().next_sibling() {
328 if let Some(next_token) = next.prev_sibling_or_token() {
329 ted::remove_all(generic_param.syntax().clone().into()..=next_token);
330 }
331 } else {
332 ted::remove(generic_param.syntax());
333 }
334 }
335
336 pub fn find_generic_arg(&self, generic_arg: &ast::GenericArg) -> Option<GenericParam> {
338 self.generic_params().find_map(move |param| match (¶m, &generic_arg) {
339 (ast::GenericParam::LifetimeParam(a), ast::GenericArg::LifetimeArg(b)) => {
340 (a.lifetime()?.lifetime_ident_token()?.text()
341 == b.lifetime()?.lifetime_ident_token()?.text())
342 .then_some(param)
343 }
344 (ast::GenericParam::TypeParam(a), ast::GenericArg::TypeArg(b)) => {
345 debug_assert_eq!(b.syntax().first_token(), b.syntax().last_token());
346 (a.name()?.text() == b.syntax().first_token()?.text()).then_some(param)
347 }
348 (ast::GenericParam::ConstParam(a), ast::GenericArg::TypeArg(b)) => {
349 debug_assert_eq!(b.syntax().first_token(), b.syntax().last_token());
350 (a.name()?.text() == b.syntax().first_token()?.text()).then_some(param)
351 }
352 _ => None,
353 })
354 }
355
356 pub fn remove_generic_arg(&self, generic_arg: &ast::GenericArg) {
358 let param_to_remove = self.find_generic_arg(generic_arg);
359
360 if let Some(param) = ¶m_to_remove {
361 self.remove_generic_param(param.clone());
362 }
363 }
364
365 pub fn to_generic_args(&self) -> ast::GenericArgList {
367 let args = self.generic_params().filter_map(|param| match param {
368 ast::GenericParam::LifetimeParam(it) => {
369 Some(ast::GenericArg::LifetimeArg(make::lifetime_arg(it.lifetime()?)))
370 }
371 ast::GenericParam::TypeParam(it) => {
372 Some(ast::GenericArg::TypeArg(make::type_arg(make::ext::ty_name(it.name()?))))
373 }
374 ast::GenericParam::ConstParam(it) => {
375 Some(ast::GenericArg::TypeArg(make::type_arg(make::ext::ty_name(it.name()?))))
377 }
378 });
379
380 make::generic_arg_list(args)
381 }
382}
383
384impl ast::WhereClause {
385 pub fn add_predicate(&self, predicate: ast::WherePred) {
386 if let Some(pred) = self.predicates().last()
387 && !pred.syntax().siblings_with_tokens(Direction::Next).any(|it| it.kind() == T![,])
388 {
389 ted::append_child_raw(self.syntax(), make::token(T![,]));
390 }
391 ted::append_child(self.syntax(), predicate.syntax());
392 }
393
394 pub fn remove_predicate(&self, predicate: ast::WherePred) {
395 if let Some(previous) = predicate.syntax().prev_sibling() {
396 if let Some(next_token) = previous.next_sibling_or_token() {
397 ted::remove_all(next_token..=predicate.syntax().clone().into());
398 }
399 } else if let Some(next) = predicate.syntax().next_sibling() {
400 if let Some(next_token) = next.prev_sibling_or_token() {
401 ted::remove_all(predicate.syntax().clone().into()..=next_token);
402 }
403 } else {
404 ted::remove(predicate.syntax());
405 }
406 }
407}
408
409pub trait Removable: AstNode {
410 fn remove(&self);
411}
412
413impl Removable for ast::TypeBoundList {
414 fn remove(&self) {
415 match self.syntax().siblings_with_tokens(Direction::Prev).find(|it| it.kind() == T![:]) {
416 Some(colon) => ted::remove_all(colon..=self.syntax().clone().into()),
417 None => ted::remove(self.syntax()),
418 }
419 }
420}
421
422impl Removable for ast::UseTree {
423 fn remove(&self) {
424 for dir in [Direction::Next, Direction::Prev] {
425 if let Some(next_use_tree) = neighbor(self, dir) {
426 let separators = self
427 .syntax()
428 .siblings_with_tokens(dir)
429 .skip(1)
430 .take_while(|it| it.as_node() != Some(next_use_tree.syntax()));
431 ted::remove_all_iter(separators);
432 break;
433 }
434 }
435 ted::remove(self.syntax());
436 }
437}
438
439impl ast::UseTree {
440 pub fn remove_recursive(self) {
442 let parent = self.syntax().parent();
443
444 self.remove();
445
446 if let Some(u) = parent.clone().and_then(ast::Use::cast) {
447 if u.use_tree().is_none() {
448 u.remove();
449 }
450 } else if let Some(u) = parent.and_then(ast::UseTreeList::cast) {
451 if u.use_trees().next().is_none() {
452 let parent = u.syntax().parent().and_then(ast::UseTree::cast);
453 if let Some(u) = parent {
454 u.remove_recursive();
455 }
456 }
457 u.remove_unnecessary_braces();
458 }
459 }
460
461 pub fn get_or_create_use_tree_list(&self) -> ast::UseTreeList {
462 match self.use_tree_list() {
463 Some(it) => it,
464 None => {
465 let position = Position::last_child_of(self.syntax());
466 let use_tree_list = make::use_tree_list(empty()).clone_for_update();
467 let mut elements = Vec::with_capacity(2);
468 if self.coloncolon_token().is_none() {
469 elements.push(make::token(T![::]).into());
470 }
471 elements.push(use_tree_list.syntax().clone().into());
472 ted::insert_all_raw(position, elements);
473 use_tree_list
474 }
475 }
476 }
477
478 pub fn split_prefix(&self, prefix: &ast::Path) {
489 debug_assert_eq!(self.path(), Some(prefix.top_path()));
490 let path = self.path().unwrap();
491 if &path == prefix && self.use_tree_list().is_none() {
492 if self.star_token().is_some() {
493 if let Some(a) = self.coloncolon_token() {
495 ted::remove(a)
496 }
497 ted::remove(prefix.syntax());
498 } else {
499 let self_suffix =
501 make::path_unqualified(make::path_segment_self()).clone_for_update();
502 ted::replace(path.syntax(), self_suffix.syntax());
503 }
504 } else if split_path_prefix(prefix).is_none() {
505 return;
506 }
507 let subtree = self.clone_subtree().clone_for_update();
510 ted::remove_all_iter(self.syntax().children_with_tokens());
511 ted::insert(Position::first_child_of(self.syntax()), prefix.syntax());
512 self.get_or_create_use_tree_list().add_use_tree(subtree);
513
514 fn split_path_prefix(prefix: &ast::Path) -> Option<()> {
515 let parent = prefix.parent_path()?;
516 let segment = parent.segment()?;
517 if algo::has_errors(segment.syntax()) {
518 return None;
519 }
520 for p in successors(parent.parent_path(), |it| it.parent_path()) {
521 p.segment()?;
522 }
523 if let Some(a) = prefix.parent_path().and_then(|p| p.coloncolon_token()) {
524 ted::remove(a)
525 }
526 ted::remove(prefix.syntax());
527 Some(())
528 }
529 }
530
531 pub fn wrap_in_tree_list(&self) -> Option<()> {
539 if self.use_tree_list().is_some()
540 && self.path().is_none()
541 && self.star_token().is_none()
542 && self.rename().is_none()
543 {
544 return None;
545 }
546 let subtree = self.clone_subtree().clone_for_update();
547 ted::remove_all_iter(self.syntax().children_with_tokens());
548 ted::append_child(
549 self.syntax(),
550 make::use_tree_list(once(subtree)).clone_for_update().syntax(),
551 );
552 Some(())
553 }
554}
555
556impl ast::UseTreeList {
557 pub fn add_use_tree(&self, use_tree: ast::UseTree) {
558 let (position, elements) = match self.use_trees().last() {
559 Some(last_tree) => (
560 Position::after(last_tree.syntax()),
561 vec![
562 make::token(T![,]).into(),
563 make::tokens::single_space().into(),
564 use_tree.syntax.into(),
565 ],
566 ),
567 None => {
568 let position = match self.l_curly_token() {
569 Some(l_curly) => Position::after(l_curly),
570 None => Position::last_child_of(self.syntax()),
571 };
572 (position, vec![use_tree.syntax.into()])
573 }
574 };
575 ted::insert_all_raw(position, elements);
576 }
577}
578
579impl Removable for ast::Use {
580 fn remove(&self) {
581 let next_ws = self
582 .syntax()
583 .next_sibling_or_token()
584 .and_then(|it| it.into_token())
585 .and_then(ast::Whitespace::cast);
586 if let Some(next_ws) = next_ws {
587 let ws_text = next_ws.syntax().text();
588 if let Some(rest) = ws_text.strip_prefix('\n') {
589 if rest.is_empty() {
590 ted::remove(next_ws.syntax());
591 } else {
592 ted::replace(next_ws.syntax(), make::tokens::whitespace(rest));
593 }
594 }
595 }
596 let prev_ws = self
597 .syntax()
598 .prev_sibling_or_token()
599 .and_then(|it| it.into_token())
600 .and_then(ast::Whitespace::cast);
601 if let Some(prev_ws) = prev_ws {
602 let ws_text = prev_ws.syntax().text();
603 let prev_newline = ws_text.rfind('\n').map(|x| x + 1).unwrap_or(0);
604 let rest = &ws_text[0..prev_newline];
605 if rest.is_empty() {
606 ted::remove(prev_ws.syntax());
607 } else {
608 ted::replace(prev_ws.syntax(), make::tokens::whitespace(rest));
609 }
610 }
611
612 ted::remove(self.syntax());
613 }
614}
615
616impl ast::Impl {
617 pub fn get_or_create_assoc_item_list(&self) -> ast::AssocItemList {
618 if self.assoc_item_list().is_none() {
619 let assoc_item_list = make::assoc_item_list(None).clone_for_update();
620 ted::append_child(self.syntax(), assoc_item_list.syntax());
621 }
622 self.assoc_item_list().unwrap()
623 }
624}
625
626impl ast::AssocItemList {
627 pub fn add_item(&self, item: ast::AssocItem) {
632 let (indent, position, whitespace) = match self.assoc_items().last() {
633 Some(last_item) => (
634 IndentLevel::from_node(last_item.syntax()),
635 Position::after(last_item.syntax()),
636 "\n\n",
637 ),
638 None => match self.l_curly_token() {
639 Some(l_curly) => {
640 normalize_ws_between_braces(self.syntax());
641 (IndentLevel::from_token(&l_curly) + 1, Position::after(&l_curly), "\n")
642 }
643 None => (IndentLevel::single(), Position::last_child_of(self.syntax()), "\n"),
644 },
645 };
646 let elements: Vec<SyntaxElement> = vec![
647 make::tokens::whitespace(&format!("{whitespace}{indent}")).into(),
648 item.syntax().clone().into(),
649 ];
650 ted::insert_all(position, elements);
651 }
652}
653
654impl ast::RecordExprFieldList {
655 pub fn add_field(&self, field: ast::RecordExprField) {
656 let is_multiline = self.syntax().text().contains_char('\n');
657 let whitespace = if is_multiline {
658 let indent = IndentLevel::from_node(self.syntax()) + 1;
659 make::tokens::whitespace(&format!("\n{indent}"))
660 } else {
661 make::tokens::single_space()
662 };
663
664 if is_multiline {
665 normalize_ws_between_braces(self.syntax());
666 }
667
668 let position = match self.fields().last() {
669 Some(last_field) => {
670 let comma = get_or_insert_comma_after(last_field.syntax());
671 Position::after(comma)
672 }
673 None => match self.l_curly_token() {
674 Some(it) => Position::after(it),
675 None => Position::last_child_of(self.syntax()),
676 },
677 };
678
679 ted::insert_all(position, vec![whitespace.into(), field.syntax().clone().into()]);
680 if is_multiline {
681 ted::insert(Position::after(field.syntax()), ast::make::token(T![,]));
682 }
683 }
684}
685
686impl ast::RecordExprField {
687 pub fn replace_expr(&self, expr: ast::Expr) {
690 if self.name_ref().is_some() {
691 match self.expr() {
692 Some(prev) => ted::replace(prev.syntax(), expr.syntax()),
693 None => ted::append_child(self.syntax(), expr.syntax()),
694 }
695 return;
696 }
697 if let Some(ast::Expr::PathExpr(path_expr)) = self.expr()
699 && let Some(path) = path_expr.path()
700 && let Some(name_ref) = path.as_single_name_ref()
701 {
702 path_expr.syntax().detach();
703 let children = vec![
704 name_ref.syntax().clone().into(),
705 ast::make::token(T![:]).into(),
706 ast::make::tokens::single_space().into(),
707 expr.syntax().clone().into(),
708 ];
709 ted::insert_all_raw(Position::last_child_of(self.syntax()), children);
710 }
711 }
712}
713
714impl ast::RecordPatFieldList {
715 pub fn add_field(&self, field: ast::RecordPatField) {
716 let is_multiline = self.syntax().text().contains_char('\n');
717 let whitespace = if is_multiline {
718 let indent = IndentLevel::from_node(self.syntax()) + 1;
719 make::tokens::whitespace(&format!("\n{indent}"))
720 } else {
721 make::tokens::single_space()
722 };
723
724 if is_multiline {
725 normalize_ws_between_braces(self.syntax());
726 }
727
728 let position = match self.fields().last() {
729 Some(last_field) => {
730 let syntax = last_field.syntax();
731 let comma = get_or_insert_comma_after(syntax);
732 Position::after(comma)
733 }
734 None => match self.l_curly_token() {
735 Some(it) => Position::after(it),
736 None => Position::last_child_of(self.syntax()),
737 },
738 };
739
740 ted::insert_all(position, vec![whitespace.into(), field.syntax().clone().into()]);
741 if is_multiline {
742 ted::insert(Position::after(field.syntax()), ast::make::token(T![,]));
743 }
744 }
745}
746
747fn get_or_insert_comma_after(syntax: &SyntaxNode) -> SyntaxToken {
748 match syntax
749 .siblings_with_tokens(Direction::Next)
750 .filter_map(|it| it.into_token())
751 .find(|it| it.kind() == T![,])
752 {
753 Some(it) => it,
754 None => {
755 let comma = ast::make::token(T![,]);
756 ted::insert(Position::after(syntax), &comma);
757 comma
758 }
759 }
760}
761
762fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> {
763 let l = node
764 .children_with_tokens()
765 .filter_map(|it| it.into_token())
766 .find(|it| it.kind() == T!['{'])?;
767 let r = node
768 .children_with_tokens()
769 .filter_map(|it| it.into_token())
770 .find(|it| it.kind() == T!['}'])?;
771
772 let indent = IndentLevel::from_node(node);
773
774 match l.next_sibling_or_token() {
775 Some(ws) if ws.kind() == SyntaxKind::WHITESPACE => {
776 if ws.next_sibling_or_token()?.into_token()? == r {
777 ted::replace(ws, make::tokens::whitespace(&format!("\n{indent}")));
778 }
779 }
780 Some(ws) if ws.kind() == T!['}'] => {
781 ted::insert(Position::after(l), make::tokens::whitespace(&format!("\n{indent}")));
782 }
783 _ => (),
784 }
785 Some(())
786}
787
788impl ast::IdentPat {
789 pub fn set_pat(&self, pat: Option<ast::Pat>) {
790 match pat {
791 None => {
792 if let Some(at_token) = self.at_token() {
793 let start = at_token.clone().into();
795 let end = self
796 .pat()
797 .map(|it| it.syntax().clone().into())
798 .unwrap_or_else(|| at_token.into());
799
800 ted::remove_all(start..=end);
801
802 if let Some(last) =
804 self.syntax().last_token().filter(|it| it.kind() == WHITESPACE)
805 {
806 last.detach();
807 }
808 }
809 }
810 Some(pat) => {
811 if let Some(old_pat) = self.pat() {
812 ted::replace(old_pat.syntax(), pat.syntax())
814 } else if let Some(at_token) = self.at_token() {
815 ted::insert(ted::Position::after(at_token), pat.syntax());
817 } else {
818 let name = self.name().unwrap();
820
821 ted::insert_all(
822 ted::Position::after(name.syntax()),
823 vec![
824 make::token(T![@]).into(),
825 make::tokens::single_space().into(),
826 pat.syntax().clone().into(),
827 ],
828 )
829 }
830 }
831 }
832 }
833}
834
835pub trait HasVisibilityEdit: ast::HasVisibility {
836 fn set_visibility(&self, visibility: Option<ast::Visibility>) {
837 if let Some(visibility) = visibility {
838 match self.visibility() {
839 Some(current_visibility) => {
840 ted::replace(current_visibility.syntax(), visibility.syntax())
841 }
842 None => {
843 let vis_before = self
844 .syntax()
845 .children_with_tokens()
846 .find(|it| !matches!(it.kind(), WHITESPACE | COMMENT | ATTR))
847 .unwrap_or_else(|| self.syntax().first_child_or_token().unwrap());
848
849 ted::insert(ted::Position::before(vis_before), visibility.syntax());
850 }
851 }
852 } else if let Some(visibility) = self.visibility() {
853 ted::remove(visibility.syntax());
854 }
855 }
856}
857
858impl<T: ast::HasVisibility> HasVisibilityEdit for T {}
859
860pub trait Indent: AstNode + Clone + Sized {
861 fn indent_level(&self) -> IndentLevel {
862 IndentLevel::from_node(self.syntax())
863 }
864 fn indent(&self, by: IndentLevel) {
865 by.increase_indent(self.syntax());
866 }
867 fn dedent(&self, by: IndentLevel) {
868 by.decrease_indent(self.syntax());
869 }
870 fn reindent_to(&self, target_level: IndentLevel) {
871 let current_level = IndentLevel::from_node(self.syntax());
872 self.dedent(current_level);
873 self.indent(target_level);
874 }
875}
876
877impl<N: AstNode + Clone> Indent for N {}
878
879#[cfg(test)]
880mod tests {
881 use std::fmt;
882
883 use parser::Edition;
884
885 use crate::SourceFile;
886
887 use super::*;
888
889 fn ast_mut_from_text<N: AstNode>(text: &str) -> N {
890 let parse = SourceFile::parse(text, Edition::CURRENT);
891 parse.tree().syntax().descendants().find_map(N::cast).unwrap().clone_for_update()
892 }
893
894 #[test]
895 fn test_create_generic_param_list() {
896 fn check_create_gpl<N: GenericParamsOwnerEdit + fmt::Display>(before: &str, after: &str) {
897 let gpl_owner = ast_mut_from_text::<N>(before);
898 gpl_owner.get_or_create_generic_param_list();
899 assert_eq!(gpl_owner.to_string(), after);
900 }
901
902 check_create_gpl::<ast::Fn>("fn foo", "fn foo<>");
903 check_create_gpl::<ast::Fn>("fn foo() {}", "fn foo<>() {}");
904
905 check_create_gpl::<ast::Impl>("impl", "impl<>");
906 check_create_gpl::<ast::Impl>("impl Struct {}", "impl<> Struct {}");
907 check_create_gpl::<ast::Impl>("impl Trait for Struct {}", "impl<> Trait for Struct {}");
908
909 check_create_gpl::<ast::Trait>("trait Trait<>", "trait Trait<>");
910 check_create_gpl::<ast::Trait>("trait Trait<> {}", "trait Trait<> {}");
911
912 check_create_gpl::<ast::Struct>("struct A", "struct A<>");
913 check_create_gpl::<ast::Struct>("struct A;", "struct A<>;");
914 check_create_gpl::<ast::Struct>("struct A();", "struct A<>();");
915 check_create_gpl::<ast::Struct>("struct A {}", "struct A<> {}");
916
917 check_create_gpl::<ast::Enum>("enum E", "enum E<>");
918 check_create_gpl::<ast::Enum>("enum E {", "enum E<> {");
919 }
920
921 #[test]
922 fn test_increase_indent() {
923 let arm_list = ast_mut_from_text::<ast::Fn>(
924 "fn foo() {
925 ;
926 ;
927}",
928 );
929 arm_list.indent(IndentLevel(2));
930 assert_eq!(
931 arm_list.to_string(),
932 "fn foo() {
933 ;
934 ;
935 }",
936 );
937 }
938
939 #[test]
940 fn test_ident_pat_set_pat() {
941 #[track_caller]
942 fn check(before: &str, expected: &str, pat: Option<ast::Pat>) {
943 let pat = pat.map(|it| it.clone_for_update());
944
945 let ident_pat = ast_mut_from_text::<ast::IdentPat>(&format!("fn f() {{ {before} }}"));
946 ident_pat.set_pat(pat);
947
948 let after = ast_mut_from_text::<ast::IdentPat>(&format!("fn f() {{ {expected} }}"));
949 assert_eq!(ident_pat.to_string(), after.to_string());
950 }
951
952 check("let a @ _;", "let a @ ();", Some(make::tuple_pat([]).into()));
954
955 check("let a ", "let a @ ()", Some(make::tuple_pat([]).into()));
960 check("let a @ ", "let a @ ()", Some(make::tuple_pat([]).into()));
961
962 check("let a @ ()", "let a", None);
964 check("let a @ ", "let a", None);
965 }
966}