aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_syntax/src/ast/edit.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_syntax/src/ast/edit.rs')
-rw-r--r--crates/ra_syntax/src/ast/edit.rs78
1 files changed, 53 insertions, 25 deletions
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs
index 26e4576ff..29eb3fcb9 100644
--- a/crates/ra_syntax/src/ast/edit.rs
+++ b/crates/ra_syntax/src/ast/edit.rs
@@ -1,7 +1,10 @@
1//! This module contains functions for editing syntax trees. As the trees are 1//! This module contains functions for editing syntax trees. As the trees are
2//! immutable, all function here return a fresh copy of the tree, instead of 2//! immutable, all function here return a fresh copy of the tree, instead of
3//! doing an in-place modification. 3//! doing an in-place modification.
4use std::{iter, ops::RangeInclusive}; 4use std::{
5 fmt, iter,
6 ops::{self, RangeInclusive},
7};
5 8
6use arrayvec::ArrayVec; 9use arrayvec::ArrayVec;
7 10
@@ -28,7 +31,7 @@ impl ast::BinExpr {
28 31
29impl ast::FnDef { 32impl ast::FnDef {
30 #[must_use] 33 #[must_use]
31 pub fn with_body(&self, body: ast::Block) -> ast::FnDef { 34 pub fn with_body(&self, body: ast::BlockExpr) -> ast::FnDef {
32 let mut to_insert: ArrayVec<[SyntaxElement; 2]> = ArrayVec::new(); 35 let mut to_insert: ArrayVec<[SyntaxElement; 2]> = ArrayVec::new();
33 let old_body_or_semi: SyntaxElement = if let Some(old_body) = self.body() { 36 let old_body_or_semi: SyntaxElement = if let Some(old_body) = self.body() {
34 old_body.syntax().clone().into() 37 old_body.syntax().clone().into()
@@ -79,7 +82,7 @@ where
79 82
80impl ast::ItemList { 83impl ast::ItemList {
81 #[must_use] 84 #[must_use]
82 pub fn append_items(&self, items: impl IntoIterator<Item = ast::ImplItem>) -> ast::ItemList { 85 pub fn append_items(&self, items: impl IntoIterator<Item = ast::AssocItem>) -> ast::ItemList {
83 let mut res = self.clone(); 86 let mut res = self.clone();
84 if !self.syntax().text().contains_char('\n') { 87 if !self.syntax().text().contains_char('\n') {
85 res = make_multiline(res); 88 res = make_multiline(res);
@@ -89,8 +92,8 @@ impl ast::ItemList {
89 } 92 }
90 93
91 #[must_use] 94 #[must_use]
92 pub fn append_item(&self, item: ast::ImplItem) -> ast::ItemList { 95 pub fn append_item(&self, item: ast::AssocItem) -> ast::ItemList {
93 let (indent, position) = match self.impl_items().last() { 96 let (indent, position) = match self.assoc_items().last() {
94 Some(it) => ( 97 Some(it) => (
95 leading_indent(it.syntax()).unwrap_or_default().to_string(), 98 leading_indent(it.syntax()).unwrap_or_default().to_string(),
96 InsertPosition::After(it.syntax().clone().into()), 99 InsertPosition::After(it.syntax().clone().into()),
@@ -437,6 +440,28 @@ impl From<u8> for IndentLevel {
437 } 440 }
438} 441}
439 442
443impl fmt::Display for IndentLevel {
444 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
445 let spaces = " ";
446 let buf;
447 let len = self.0 as usize * 4;
448 let indent = if len <= spaces.len() {
449 &spaces[..len]
450 } else {
451 buf = iter::repeat(' ').take(len).collect::<String>();
452 &buf
453 };
454 fmt::Display::fmt(indent, f)
455 }
456}
457
458impl ops::Add<u8> for IndentLevel {
459 type Output = IndentLevel;
460 fn add(self, rhs: u8) -> IndentLevel {
461 IndentLevel(self.0 + rhs)
462 }
463}
464
440impl IndentLevel { 465impl IndentLevel {
441 pub fn from_node(node: &SyntaxNode) -> IndentLevel { 466 pub fn from_node(node: &SyntaxNode) -> IndentLevel {
442 let first_token = match node.first_token() { 467 let first_token = match node.first_token() {
@@ -453,11 +478,15 @@ impl IndentLevel {
453 IndentLevel(0) 478 IndentLevel(0)
454 } 479 }
455 480
456 pub fn increase_indent<N: AstNode>(self, node: N) -> N { 481 /// XXX: this intentionally doesn't change the indent of the very first token.
457 N::cast(self._increase_indent(node.syntax().clone())).unwrap() 482 /// Ie, in something like
458 } 483 /// ```
459 484 /// fn foo() {
460 fn _increase_indent(self, node: SyntaxNode) -> SyntaxNode { 485 /// 92
486 /// }
487 /// ```
488 /// if you indent the block, the `{` token would stay put.
489 fn increase_indent(self, node: SyntaxNode) -> SyntaxNode {
461 let mut rewriter = SyntaxRewriter::default(); 490 let mut rewriter = SyntaxRewriter::default();
462 node.descendants_with_tokens() 491 node.descendants_with_tokens()
463 .filter_map(|el| el.into_token()) 492 .filter_map(|el| el.into_token())
@@ -467,22 +496,13 @@ impl IndentLevel {
467 text.contains('\n') 496 text.contains('\n')
468 }) 497 })
469 .for_each(|ws| { 498 .for_each(|ws| {
470 let new_ws = make::tokens::whitespace(&format!( 499 let new_ws = make::tokens::whitespace(&format!("{}{}", ws.syntax(), self,));
471 "{}{:width$}",
472 ws.syntax().text(),
473 "",
474 width = self.0 as usize * 4
475 ));
476 rewriter.replace(ws.syntax(), &new_ws) 500 rewriter.replace(ws.syntax(), &new_ws)
477 }); 501 });
478 rewriter.rewrite(&node) 502 rewriter.rewrite(&node)
479 } 503 }
480 504
481 pub fn decrease_indent<N: AstNode>(self, node: N) -> N { 505 fn decrease_indent(self, node: SyntaxNode) -> SyntaxNode {
482 N::cast(self._decrease_indent(node.syntax().clone())).unwrap()
483 }
484
485 fn _decrease_indent(self, node: SyntaxNode) -> SyntaxNode {
486 let mut rewriter = SyntaxRewriter::default(); 506 let mut rewriter = SyntaxRewriter::default();
487 node.descendants_with_tokens() 507 node.descendants_with_tokens()
488 .filter_map(|el| el.into_token()) 508 .filter_map(|el| el.into_token())
@@ -493,7 +513,7 @@ impl IndentLevel {
493 }) 513 })
494 .for_each(|ws| { 514 .for_each(|ws| {
495 let new_ws = make::tokens::whitespace( 515 let new_ws = make::tokens::whitespace(
496 &ws.syntax().text().replace(&format!("\n{:1$}", "", self.0 as usize * 4), "\n"), 516 &ws.syntax().text().replace(&format!("\n{}", self), "\n"),
497 ); 517 );
498 rewriter.replace(ws.syntax(), &new_ws) 518 rewriter.replace(ws.syntax(), &new_ws)
499 }); 519 });
@@ -521,7 +541,7 @@ fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> {
521 iter::successors(Some(token), |token| token.prev_token()) 541 iter::successors(Some(token), |token| token.prev_token())
522} 542}
523 543
524pub trait AstNodeEdit: AstNode + Sized { 544pub trait AstNodeEdit: AstNode + Clone + Sized {
525 #[must_use] 545 #[must_use]
526 fn insert_children( 546 fn insert_children(
527 &self, 547 &self,
@@ -558,9 +578,17 @@ pub trait AstNodeEdit: AstNode + Sized {
558 } 578 }
559 rewriter.rewrite_ast(self) 579 rewriter.rewrite_ast(self)
560 } 580 }
581 #[must_use]
582 fn indent(&self, indent: IndentLevel) -> Self {
583 Self::cast(indent.increase_indent(self.syntax().clone())).unwrap()
584 }
585 #[must_use]
586 fn dedent(&self, indent: IndentLevel) -> Self {
587 Self::cast(indent.decrease_indent(self.syntax().clone())).unwrap()
588 }
561} 589}
562 590
563impl<N: AstNode> AstNodeEdit for N {} 591impl<N: AstNode + Clone> AstNodeEdit for N {}
564 592
565fn single_node(element: impl Into<SyntaxElement>) -> RangeInclusive<SyntaxElement> { 593fn single_node(element: impl Into<SyntaxElement>) -> RangeInclusive<SyntaxElement> {
566 let element = element.into(); 594 let element = element.into();
@@ -580,7 +608,7 @@ fn test_increase_indent() {
580 _ => (), 608 _ => (),
581}" 609}"
582 ); 610 );
583 let indented = IndentLevel(2).increase_indent(arm_list); 611 let indented = arm_list.indent(IndentLevel(2));
584 assert_eq!( 612 assert_eq!(
585 indented.syntax().to_string(), 613 indented.syntax().to_string(),
586 "{ 614 "{