1 //! Structural editing for ast.
3 use std
::iter
::{empty, successors}
;
5 use parser
::{SyntaxKind, T}
;
6 use rowan
::SyntaxElement
;
9 algo
::{self, neighbor}
,
10 ast
::{self, edit::IndentLevel, make, HasGenericParams}
,
11 ted
::{self, Position}
,
12 AstNode
, AstToken
, Direction
,
13 SyntaxKind
::{ATTR, COMMENT, WHITESPACE}
,
14 SyntaxNode
, SyntaxToken
,
19 pub trait GenericParamsOwnerEdit
: ast
::HasGenericParams
{
20 fn get_or_create_generic_param_list(&self) -> ast
::GenericParamList
;
21 fn get_or_create_where_clause(&self) -> ast
::WhereClause
;
24 impl GenericParamsOwnerEdit
for ast
::Fn
{
25 fn get_or_create_generic_param_list(&self) -> ast
::GenericParamList
{
26 match self.generic_param_list() {
29 let position
= if let Some(name
) = self.name() {
30 Position
::after(name
.syntax
)
31 } else if let Some(fn_token
) = self.fn_token() {
32 Position
::after(fn_token
)
33 } else if let Some(param_list
) = self.param_list() {
34 Position
::before(param_list
.syntax
)
36 Position
::last_child_of(self.syntax())
38 create_generic_param_list(position
)
43 fn get_or_create_where_clause(&self) -> ast
::WhereClause
{
44 if self.where_clause().is_none() {
45 let position
= if let Some(ty
) = self.ret_type() {
46 Position
::after(ty
.syntax())
47 } else if let Some(param_list
) = self.param_list() {
48 Position
::after(param_list
.syntax())
50 Position
::last_child_of(self.syntax())
52 create_where_clause(position
);
54 self.where_clause().unwrap()
58 impl GenericParamsOwnerEdit
for ast
::Impl
{
59 fn get_or_create_generic_param_list(&self) -> ast
::GenericParamList
{
60 match self.generic_param_list() {
63 let position
= match self.impl_token() {
64 Some(imp_token
) => Position
::after(imp_token
),
65 None
=> Position
::last_child_of(self.syntax()),
67 create_generic_param_list(position
)
72 fn get_or_create_where_clause(&self) -> ast
::WhereClause
{
73 if self.where_clause().is_none() {
74 let position
= match self.assoc_item_list() {
75 Some(items
) => Position
::before(items
.syntax()),
76 None
=> Position
::last_child_of(self.syntax()),
78 create_where_clause(position
);
80 self.where_clause().unwrap()
84 impl GenericParamsOwnerEdit
for ast
::Trait
{
85 fn get_or_create_generic_param_list(&self) -> ast
::GenericParamList
{
86 match self.generic_param_list() {
89 let position
= if let Some(name
) = self.name() {
90 Position
::after(name
.syntax
)
91 } else if let Some(trait_token
) = self.trait_token() {
92 Position
::after(trait_token
)
94 Position
::last_child_of(self.syntax())
96 create_generic_param_list(position
)
101 fn get_or_create_where_clause(&self) -> ast
::WhereClause
{
102 if self.where_clause().is_none() {
103 let position
= match self.assoc_item_list() {
104 Some(items
) => Position
::before(items
.syntax()),
105 None
=> Position
::last_child_of(self.syntax()),
107 create_where_clause(position
);
109 self.where_clause().unwrap()
113 impl GenericParamsOwnerEdit
for ast
::Struct
{
114 fn get_or_create_generic_param_list(&self) -> ast
::GenericParamList
{
115 match self.generic_param_list() {
118 let position
= if let Some(name
) = self.name() {
119 Position
::after(name
.syntax
)
120 } else if let Some(struct_token
) = self.struct_token() {
121 Position
::after(struct_token
)
123 Position
::last_child_of(self.syntax())
125 create_generic_param_list(position
)
130 fn get_or_create_where_clause(&self) -> ast
::WhereClause
{
131 if self.where_clause().is_none() {
132 let tfl
= self.field_list().and_then(|fl
| match fl
{
133 ast
::FieldList
::RecordFieldList(_
) => None
,
134 ast
::FieldList
::TupleFieldList(it
) => Some(it
),
136 let position
= if let Some(tfl
) = tfl
{
137 Position
::after(tfl
.syntax())
138 } else if let Some(gpl
) = self.generic_param_list() {
139 Position
::after(gpl
.syntax())
140 } else if let Some(name
) = self.name() {
141 Position
::after(name
.syntax())
143 Position
::last_child_of(self.syntax())
145 create_where_clause(position
);
147 self.where_clause().unwrap()
151 impl GenericParamsOwnerEdit
for ast
::Enum
{
152 fn get_or_create_generic_param_list(&self) -> ast
::GenericParamList
{
153 match self.generic_param_list() {
156 let position
= if let Some(name
) = self.name() {
157 Position
::after(name
.syntax
)
158 } else if let Some(enum_token
) = self.enum_token() {
159 Position
::after(enum_token
)
161 Position
::last_child_of(self.syntax())
163 create_generic_param_list(position
)
168 fn get_or_create_where_clause(&self) -> ast
::WhereClause
{
169 if self.where_clause().is_none() {
170 let position
= if let Some(gpl
) = self.generic_param_list() {
171 Position
::after(gpl
.syntax())
172 } else if let Some(name
) = self.name() {
173 Position
::after(name
.syntax())
175 Position
::last_child_of(self.syntax())
177 create_where_clause(position
);
179 self.where_clause().unwrap()
183 fn create_where_clause(position
: Position
) {
184 let where_clause
= make
::where_clause(empty()).clone_for_update();
185 ted
::insert(position
, where_clause
.syntax());
188 fn create_generic_param_list(position
: Position
) -> ast
::GenericParamList
{
189 let gpl
= make
::generic_param_list(empty()).clone_for_update();
190 ted
::insert_raw(position
, gpl
.syntax());
194 pub trait AttrsOwnerEdit
: ast
::HasAttrs
{
195 fn remove_attrs_and_docs(&self) {
196 remove_attrs_and_docs(self.syntax());
198 fn remove_attrs_and_docs(node
: &SyntaxNode
) {
199 let mut remove_next_ws
= false;
200 for child
in node
.children_with_tokens() {
203 remove_next_ws
= true;
207 WHITESPACE
if remove_next_ws
=> {
212 remove_next_ws
= false;
218 impl<T
: ast
::HasAttrs
> AttrsOwnerEdit
for T {}
220 impl ast
::GenericParamList
{
221 pub fn add_generic_param(&self, generic_param
: ast
::GenericParam
) {
222 match self.generic_params().last() {
223 Some(last_param
) => {
224 let position
= Position
::after(last_param
.syntax());
226 make
::token(T
![,]).into(),
227 make
::tokens
::single_space().into(),
228 generic_param
.syntax().clone().into(),
230 ted
::insert_all(position
, elements
);
233 let after_l_angle
= Position
::after(self.l_angle_token().unwrap());
234 ted
::insert(after_l_angle
, generic_param
.syntax());
239 /// Constructs a matching [`ast::GenericArgList`]
240 pub fn to_generic_args(&self) -> ast
::GenericArgList
{
241 let args
= self.generic_params().filter_map(|param
| match param
{
242 ast
::GenericParam
::LifetimeParam(it
) => {
243 Some(ast
::GenericArg
::LifetimeArg(make
::lifetime_arg(it
.lifetime()?
)))
245 ast
::GenericParam
::TypeParam(it
) => {
246 Some(ast
::GenericArg
::TypeArg(make
::type_arg(make
::ext
::ty_name(it
.name()?
))))
248 ast
::GenericParam
::ConstParam(it
) => {
249 // Name-only const params get parsed as `TypeArg`s
250 Some(ast
::GenericArg
::TypeArg(make
::type_arg(make
::ext
::ty_name(it
.name()?
))))
254 make
::generic_arg_list(args
)
258 impl ast
::WhereClause
{
259 pub fn add_predicate(&self, predicate
: ast
::WherePred
) {
260 if let Some(pred
) = self.predicates().last() {
261 if !pred
.syntax().siblings_with_tokens(Direction
::Next
).any(|it
| it
.kind() == T
![,]) {
262 ted
::append_child_raw(self.syntax(), make
::token(T
![,]));
265 ted
::append_child(self.syntax(), predicate
.syntax());
269 impl ast
::TypeParam
{
270 pub fn remove_default(&self) {
271 if let Some((eq
, last
)) = self
273 .children_with_tokens()
274 .find(|it
| it
.kind() == T
![=])
275 .zip(self.syntax().last_child_or_token())
277 ted
::remove_all(eq
..=last
);
279 // remove any trailing ws
280 if let Some(last
) = self.syntax().last_token().filter(|it
| it
.kind() == WHITESPACE
) {
287 impl ast
::ConstParam
{
288 pub fn remove_default(&self) {
289 if let Some((eq
, last
)) = self
291 .children_with_tokens()
292 .find(|it
| it
.kind() == T
![=])
293 .zip(self.syntax().last_child_or_token())
295 ted
::remove_all(eq
..=last
);
297 // remove any trailing ws
298 if let Some(last
) = self.syntax().last_token().filter(|it
| it
.kind() == WHITESPACE
) {
305 pub trait Removable
: AstNode
{
309 impl Removable
for ast
::TypeBoundList
{
311 match self.syntax().siblings_with_tokens(Direction
::Prev
).find(|it
| it
.kind() == T
![:]) {
312 Some(colon
) => ted
::remove_all(colon
..=self.syntax().clone().into()),
313 None
=> ted
::remove(self.syntax()),
318 impl ast
::PathSegment
{
319 pub fn get_or_create_generic_arg_list(&self) -> ast
::GenericArgList
{
320 if self.generic_arg_list().is_none() {
321 let arg_list
= make
::generic_arg_list(empty()).clone_for_update();
322 ted
::append_child(self.syntax(), arg_list
.syntax());
324 self.generic_arg_list().unwrap()
328 impl Removable
for ast
::UseTree
{
330 for dir
in [Direction
::Next
, Direction
::Prev
] {
331 if let Some(next_use_tree
) = neighbor(self, dir
) {
332 let separators
= self
334 .siblings_with_tokens(dir
)
336 .take_while(|it
| it
.as_node() != Some(next_use_tree
.syntax()));
337 ted
::remove_all_iter(separators
);
341 ted
::remove(self.syntax());
346 pub fn get_or_create_use_tree_list(&self) -> ast
::UseTreeList
{
347 match self.use_tree_list() {
350 let position
= Position
::last_child_of(self.syntax());
351 let use_tree_list
= make
::use_tree_list(empty()).clone_for_update();
352 let mut elements
= Vec
::with_capacity(2);
353 if self.coloncolon_token().is_none() {
354 elements
.push(make
::token(T
![::]).into());
356 elements
.push(use_tree_list
.syntax().clone().into());
357 ted
::insert_all_raw(position
, elements
);
363 /// Splits off the given prefix, making it the path component of the use tree,
364 /// appending the rest of the path to all UseTreeList items.
368 /// `prefix$0::suffix` -> `prefix::{suffix}`
370 /// `prefix$0` -> `prefix::{self}`
372 /// `prefix$0::*` -> `prefix::{*}`
373 pub fn split_prefix(&self, prefix
: &ast
::Path
) {
374 debug_assert_eq
!(self.path(), Some(prefix
.top_path()));
375 let path
= self.path().unwrap();
376 if &path
== prefix
&& self.use_tree_list().is_none() {
377 if self.star_token().is_some() {
379 self.coloncolon_token().map(ted
::remove
);
380 ted
::remove(prefix
.syntax());
384 make
::path_unqualified(make
::path_segment_self()).clone_for_update();
385 ted
::replace(path
.syntax(), self_suffix
.syntax());
387 } else if split_path_prefix(prefix
).is_none() {
390 // At this point, prefix path is detached; _self_ use tree has suffix path.
391 // Next, transform 'suffix' use tree into 'prefix::{suffix}'
392 let subtree
= self.clone_subtree().clone_for_update();
393 ted
::remove_all_iter(self.syntax().children_with_tokens());
394 ted
::insert(Position
::first_child_of(self.syntax()), prefix
.syntax());
395 self.get_or_create_use_tree_list().add_use_tree(subtree
);
397 fn split_path_prefix(prefix
: &ast
::Path
) -> Option
<()> {
398 let parent
= prefix
.parent_path()?
;
399 let segment
= parent
.segment()?
;
400 if algo
::has_errors(segment
.syntax()) {
403 for p
in successors(parent
.parent_path(), |it
| it
.parent_path()) {
406 prefix
.parent_path().and_then(|p
| p
.coloncolon_token()).map(ted
::remove
);
407 ted
::remove(prefix
.syntax());
413 impl ast
::UseTreeList
{
414 pub fn add_use_tree(&self, use_tree
: ast
::UseTree
) {
415 let (position
, elements
) = match self.use_trees().last() {
417 Position
::after(last_tree
.syntax()),
419 make
::token(T
![,]).into(),
420 make
::tokens
::single_space().into(),
421 use_tree
.syntax
.into(),
425 let position
= match self.l_curly_token() {
426 Some(l_curly
) => Position
::after(l_curly
),
427 None
=> Position
::last_child_of(self.syntax()),
429 (position
, vec
![use_tree
.syntax
.into()])
432 ted
::insert_all_raw(position
, elements
);
436 impl Removable
for ast
::Use
{
440 .next_sibling_or_token()
441 .and_then(|it
| it
.into_token())
442 .and_then(ast
::Whitespace
::cast
);
443 if let Some(next_ws
) = next_ws
{
444 let ws_text
= next_ws
.syntax().text();
445 if let Some(rest
) = ws_text
.strip_prefix('
\n'
) {
447 ted
::remove(next_ws
.syntax());
449 ted
::replace(next_ws
.syntax(), make
::tokens
::whitespace(rest
));
453 ted
::remove(self.syntax());
458 pub fn get_or_create_assoc_item_list(&self) -> ast
::AssocItemList
{
459 if self.assoc_item_list().is_none() {
460 let assoc_item_list
= make
::assoc_item_list().clone_for_update();
461 ted
::append_child(self.syntax(), assoc_item_list
.syntax());
463 self.assoc_item_list().unwrap()
467 impl ast
::AssocItemList
{
468 pub fn add_item(&self, item
: ast
::AssocItem
) {
469 let (indent
, position
, whitespace
) = match self.assoc_items().last() {
471 IndentLevel
::from_node(last_item
.syntax()),
472 Position
::after(last_item
.syntax()),
475 None
=> match self.l_curly_token() {
477 normalize_ws_between_braces(self.syntax());
478 (IndentLevel
::from_token(&l_curly
) + 1, Position
::after(&l_curly
), "\n")
480 None
=> (IndentLevel
::single(), Position
::last_child_of(self.syntax()), "\n"),
483 let elements
: Vec
<SyntaxElement
<_
>> = vec
![
484 make
::tokens
::whitespace(&format
!("{whitespace}{indent}")).into(),
485 item
.syntax().clone().into(),
487 ted
::insert_all(position
, elements
);
492 pub fn get_or_create_body(&self) -> ast
::BlockExpr
{
493 if self.body().is_none() {
494 let body
= make
::ext
::empty_block_expr().clone_for_update();
495 match self.semicolon_token() {
497 ted
::replace(semi
, body
.syntax());
498 ted
::insert(Position
::before(body
.syntax
), make
::tokens
::single_space());
500 None
=> ted
::append_child(self.syntax(), body
.syntax()),
507 impl Removable
for ast
::MatchArm
{
509 if let Some(sibling
) = self.syntax().prev_sibling_or_token() {
510 if sibling
.kind() == SyntaxKind
::WHITESPACE
{
511 ted
::remove(sibling
);
514 if let Some(sibling
) = self.syntax().next_sibling_or_token() {
515 if sibling
.kind() == T
![,] {
516 ted
::remove(sibling
);
519 ted
::remove(self.syntax());
523 impl ast
::MatchArmList
{
524 pub fn add_arm(&self, arm
: ast
::MatchArm
) {
525 normalize_ws_between_braces(self.syntax());
526 let mut elements
= Vec
::new();
527 let position
= match self.arms().last() {
529 if needs_comma(&last_arm
) {
530 ted
::append_child(last_arm
.syntax(), make
::token(SyntaxKind
::COMMA
));
532 Position
::after(last_arm
.syntax().clone())
534 None
=> match self.l_curly_token() {
535 Some(it
) => Position
::after(it
),
536 None
=> Position
::last_child_of(self.syntax()),
539 let indent
= IndentLevel
::from_node(self.syntax()) + 1;
540 elements
.push(make
::tokens
::whitespace(&format
!("\n{indent}")).into());
541 elements
.push(arm
.syntax().clone().into());
542 if needs_comma(&arm
) {
543 ted
::append_child(arm
.syntax(), make
::token(SyntaxKind
::COMMA
));
545 ted
::insert_all(position
, elements
);
547 fn needs_comma(arm
: &ast
::MatchArm
) -> bool
{
548 arm
.expr().map_or(false, |e
| !e
.is_block_like()) && arm
.comma_token().is_none()
553 impl ast
::RecordExprFieldList
{
554 pub fn add_field(&self, field
: ast
::RecordExprField
) {
555 let is_multiline
= self.syntax().text().contains_char('
\n'
);
556 let whitespace
= if is_multiline
{
557 let indent
= IndentLevel
::from_node(self.syntax()) + 1;
558 make
::tokens
::whitespace(&format
!("\n{indent}"))
560 make
::tokens
::single_space()
564 normalize_ws_between_braces(self.syntax());
567 let position
= match self.fields().last() {
568 Some(last_field
) => {
569 let comma
= get_or_insert_comma_after(last_field
.syntax());
570 Position
::after(comma
)
572 None
=> match self.l_curly_token() {
573 Some(it
) => Position
::after(it
),
574 None
=> Position
::last_child_of(self.syntax()),
578 ted
::insert_all(position
, vec
![whitespace
.into(), field
.syntax().clone().into()]);
580 ted
::insert(Position
::after(field
.syntax()), ast
::make
::token(T
![,]));
585 impl ast
::RecordExprField
{
586 /// This will either replace the initializer, or in the case that this is a shorthand convert
587 /// the initializer into the name ref and insert the expr as the new initializer.
588 pub fn replace_expr(&self, expr
: ast
::Expr
) {
589 if self.name_ref().is_some() {
591 Some(prev
) => ted
::replace(prev
.syntax(), expr
.syntax()),
592 None
=> ted
::append_child(self.syntax(), expr
.syntax()),
596 // this is a shorthand
597 if let Some(ast
::Expr
::PathExpr(path_expr
)) = self.expr() {
598 if let Some(path
) = path_expr
.path() {
599 if let Some(name_ref
) = path
.as_single_name_ref() {
600 path_expr
.syntax().detach();
602 name_ref
.syntax().clone().into(),
603 ast
::make
::token(T
![:]).into(),
604 ast
::make
::tokens
::single_space().into(),
605 expr
.syntax().clone().into(),
607 ted
::insert_all_raw(Position
::last_child_of(self.syntax()), children
);
614 impl ast
::RecordPatFieldList
{
615 pub fn add_field(&self, field
: ast
::RecordPatField
) {
616 let is_multiline
= self.syntax().text().contains_char('
\n'
);
617 let whitespace
= if is_multiline
{
618 let indent
= IndentLevel
::from_node(self.syntax()) + 1;
619 make
::tokens
::whitespace(&format
!("\n{indent}"))
621 make
::tokens
::single_space()
625 normalize_ws_between_braces(self.syntax());
628 let position
= match self.fields().last() {
629 Some(last_field
) => {
630 let syntax
= last_field
.syntax();
631 let comma
= get_or_insert_comma_after(syntax
);
632 Position
::after(comma
)
634 None
=> match self.l_curly_token() {
635 Some(it
) => Position
::after(it
),
636 None
=> Position
::last_child_of(self.syntax()),
640 ted
::insert_all(position
, vec
![whitespace
.into(), field
.syntax().clone().into()]);
642 ted
::insert(Position
::after(field
.syntax()), ast
::make
::token(T
![,]));
647 fn get_or_insert_comma_after(syntax
: &SyntaxNode
) -> SyntaxToken
{
649 .siblings_with_tokens(Direction
::Next
)
650 .filter_map(|it
| it
.into_token())
651 .find(|it
| it
.kind() == T
![,])
655 let comma
= ast
::make
::token(T
![,]);
656 ted
::insert(Position
::after(syntax
), &comma
);
663 pub fn push_front(&self, statement
: ast
::Stmt
) {
664 ted
::insert(Position
::after(self.l_curly_token().unwrap()), statement
.syntax());
668 impl ast
::VariantList
{
669 pub fn add_variant(&self, variant
: ast
::Variant
) {
670 let (indent
, position
) = match self.variants().last() {
672 IndentLevel
::from_node(last_item
.syntax()),
673 Position
::after(get_or_insert_comma_after(last_item
.syntax())),
675 None
=> match self.l_curly_token() {
677 normalize_ws_between_braces(self.syntax());
678 (IndentLevel
::from_token(&l_curly
) + 1, Position
::after(&l_curly
))
680 None
=> (IndentLevel
::single(), Position
::last_child_of(self.syntax())),
683 let elements
: Vec
<SyntaxElement
<_
>> = vec
![
684 make
::tokens
::whitespace(&format
!("{}{indent}", "\n")).into(),
685 variant
.syntax().clone().into(),
686 ast
::make
::token(T
![,]).into(),
688 ted
::insert_all(position
, elements
);
692 fn normalize_ws_between_braces(node
: &SyntaxNode
) -> Option
<()> {
694 .children_with_tokens()
695 .filter_map(|it
| it
.into_token())
696 .find(|it
| it
.kind() == T
!['
{'
])?
;
698 .children_with_tokens()
699 .filter_map(|it
| it
.into_token())
700 .find(|it
| it
.kind() == T
!['
}'
])?
;
702 let indent
= IndentLevel
::from_node(node
);
704 match l
.next_sibling_or_token() {
705 Some(ws
) if ws
.kind() == SyntaxKind
::WHITESPACE
=> {
706 if ws
.next_sibling_or_token()?
.into_token()?
== r
{
707 ted
::replace(ws
, make
::tokens
::whitespace(&format
!("\n{indent}")));
710 Some(ws
) if ws
.kind() == T
!['
}'
] => {
711 ted
::insert(Position
::after(l
), make
::tokens
::whitespace(&format
!("\n{indent}")));
718 pub trait Indent
: AstNode
+ Clone
+ Sized
{
719 fn indent_level(&self) -> IndentLevel
{
720 IndentLevel
::from_node(self.syntax())
722 fn indent(&self, by
: IndentLevel
) {
723 by
.increase_indent(self.syntax());
725 fn dedent(&self, by
: IndentLevel
) {
726 by
.decrease_indent(self.syntax());
728 fn reindent_to(&self, target_level
: IndentLevel
) {
729 let current_level
= IndentLevel
::from_node(self.syntax());
730 self.dedent(current_level
);
731 self.indent(target_level
);
735 impl<N
: AstNode
+ Clone
> Indent
for N {}
741 use stdx
::trim_indent
;
742 use test_utils
::assert_eq_text
;
744 use crate::SourceFile
;
748 fn ast_mut_from_text
<N
: AstNode
>(text
: &str) -> N
{
749 let parse
= SourceFile
::parse(text
);
750 parse
.tree().syntax().descendants().find_map(N
::cast
).unwrap().clone_for_update()
754 fn test_create_generic_param_list() {
755 fn check_create_gpl
<N
: GenericParamsOwnerEdit
+ fmt
::Display
>(before
: &str, after
: &str) {
756 let gpl_owner
= ast_mut_from_text
::<N
>(before
);
757 gpl_owner
.get_or_create_generic_param_list();
758 assert_eq
!(gpl_owner
.to_string(), after
);
761 check_create_gpl
::<ast
::Fn
>("fn foo", "fn foo<>");
762 check_create_gpl
::<ast
::Fn
>("fn foo() {}", "fn foo<>() {}");
764 check_create_gpl
::<ast
::Impl
>("impl", "impl<>");
765 check_create_gpl
::<ast
::Impl
>("impl Struct {}", "impl<> Struct {}");
766 check_create_gpl
::<ast
::Impl
>("impl Trait for Struct {}", "impl<> Trait for Struct {}");
768 check_create_gpl
::<ast
::Trait
>("trait Trait<>", "trait Trait<>");
769 check_create_gpl
::<ast
::Trait
>("trait Trait<> {}", "trait Trait<> {}");
771 check_create_gpl
::<ast
::Struct
>("struct A", "struct A<>");
772 check_create_gpl
::<ast
::Struct
>("struct A;", "struct A<>;");
773 check_create_gpl
::<ast
::Struct
>("struct A();", "struct A<>();");
774 check_create_gpl
::<ast
::Struct
>("struct A {}", "struct A<> {}");
776 check_create_gpl
::<ast
::Enum
>("enum E", "enum E<>");
777 check_create_gpl
::<ast
::Enum
>("enum E {", "enum E<> {");
781 fn test_increase_indent() {
782 let arm_list
= ast_mut_from_text
::<ast
::Fn
>(
788 arm_list
.indent(IndentLevel(2));
790 arm_list
.to_string(),
799 fn add_variant_to_empty_enum() {
800 let variant
= make
::variant(make
::name("Bar"), None
).clone_for_update();
816 fn add_variant_to_non_empty_enum() {
817 let variant
= make
::variant(make
::name("Baz"), None
).clone_for_update();
836 fn add_variant_with_tuple_field_list() {
837 let variant
= make
::variant(
839 Some(ast
::FieldList
::TupleFieldList(make
::tuple_field_list(std
::iter
::once(
840 make
::tuple_field(None
, make
::ty("bool")),
862 fn add_variant_with_record_field_list() {
863 let variant
= make
::variant(
865 Some(ast
::FieldList
::RecordFieldList(make
::record_field_list(std
::iter
::once(
866 make
::record_field(None
, make
::name("x"), make
::ty("bool")),
887 fn check_add_variant(before
: &str, expected
: &str, variant
: ast
::Variant
) {
888 let enum_
= ast_mut_from_text
::<ast
::Enum
>(before
);
889 enum_
.variant_list().map(|it
| it
.add_variant(variant
));
890 let after
= enum_
.to_string();
891 assert_eq_text
!(&trim_indent(expected
.trim()), &trim_indent(after
.trim()));