1use std::{fmt, iter, ops};
5
6use crate::{
7 AstToken, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken,
8 ast::{self, AstNode, make},
9 syntax_editor::{SyntaxEditor, SyntaxMappingBuilder},
10 ted,
11};
12
13use super::syntax_factory::SyntaxFactory;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub struct IndentLevel(pub u8);
17
18impl From<u8> for IndentLevel {
19 fn from(level: u8) -> IndentLevel {
20 IndentLevel(level)
21 }
22}
23
24impl fmt::Display for IndentLevel {
25 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26 let spaces = " ";
27 let buf;
28 let len = self.0 as usize * 4;
29 let indent = if len <= spaces.len() {
30 &spaces[..len]
31 } else {
32 buf = " ".repeat(len);
33 &buf
34 };
35 fmt::Display::fmt(indent, f)
36 }
37}
38
39impl ops::Add<u8> for IndentLevel {
40 type Output = IndentLevel;
41 fn add(self, rhs: u8) -> IndentLevel {
42 IndentLevel(self.0 + rhs)
43 }
44}
45
46impl ops::AddAssign<u8> for IndentLevel {
47 fn add_assign(&mut self, rhs: u8) {
48 self.0 += rhs;
49 }
50}
51
52impl IndentLevel {
53 pub fn zero() -> IndentLevel {
54 IndentLevel(0)
55 }
56 pub fn is_zero(&self) -> bool {
57 self.0 == 0
58 }
59 pub fn from_element(element: &SyntaxElement) -> IndentLevel {
60 match element {
61 rowan::NodeOrToken::Node(it) => IndentLevel::from_node(it),
62 rowan::NodeOrToken::Token(it) => IndentLevel::from_token(it),
63 }
64 }
65
66 pub fn from_node(node: &SyntaxNode) -> IndentLevel {
67 match node.first_token() {
68 Some(it) => Self::from_token(&it),
69 None => IndentLevel(0),
70 }
71 }
72
73 pub fn from_token(token: &SyntaxToken) -> IndentLevel {
74 for ws in prev_tokens(token.clone()).filter_map(ast::Whitespace::cast) {
75 let text = ws.syntax().text();
76 if let Some(pos) = text.rfind('\n') {
77 let level = text[pos + 1..].chars().count() / 4;
78 return IndentLevel(level as u8);
79 }
80 }
81 IndentLevel(0)
82 }
83
84 pub(super) fn increase_indent(self, node: &SyntaxNode) {
93 let tokens = node.preorder_with_tokens().filter_map(|event| match event {
94 rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it),
95 _ => None,
96 });
97 for token in tokens {
98 if let Some(ws) = ast::Whitespace::cast(token)
99 && ws.text().contains('\n')
100 {
101 let new_ws = make::tokens::whitespace(&format!("{}{self}", ws.syntax()));
102 ted::replace(ws.syntax(), &new_ws);
103 }
104 }
105 }
106
107 pub(super) fn clone_increase_indent(self, node: &SyntaxNode) -> SyntaxNode {
108 let node = node.clone_subtree();
109 let mut editor = SyntaxEditor::new(node.clone());
110 let tokens = node
111 .preorder_with_tokens()
112 .filter_map(|event| match event {
113 rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it),
114 _ => None,
115 })
116 .filter_map(ast::Whitespace::cast)
117 .filter(|ws| ws.text().contains('\n'));
118 for ws in tokens {
119 let new_ws = make::tokens::whitespace(&format!("{}{self}", ws.syntax()));
120 editor.replace(ws.syntax(), &new_ws);
121 }
122 editor.finish().new_root().clone()
123 }
124
125 pub(super) fn decrease_indent(self, node: &SyntaxNode) {
126 let tokens = node.preorder_with_tokens().filter_map(|event| match event {
127 rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it),
128 _ => None,
129 });
130 for token in tokens {
131 if let Some(ws) = ast::Whitespace::cast(token)
132 && ws.text().contains('\n')
133 {
134 let new_ws = make::tokens::whitespace(
135 &ws.syntax().text().replace(&format!("\n{self}"), "\n"),
136 );
137 ted::replace(ws.syntax(), &new_ws);
138 }
139 }
140 }
141
142 pub(super) fn clone_decrease_indent(self, node: &SyntaxNode) -> SyntaxNode {
143 let node = node.clone_subtree();
144 let mut editor = SyntaxEditor::new(node.clone());
145 let tokens = node
146 .preorder_with_tokens()
147 .filter_map(|event| match event {
148 rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it),
149 _ => None,
150 })
151 .filter_map(ast::Whitespace::cast)
152 .filter(|ws| ws.text().contains('\n'));
153 for ws in tokens {
154 let new_ws =
155 make::tokens::whitespace(&ws.syntax().text().replace(&format!("\n{self}"), "\n"));
156 editor.replace(ws.syntax(), &new_ws);
157 }
158 editor.finish().new_root().clone()
159 }
160}
161
162fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> {
163 iter::successors(Some(token), |token| token.prev_token())
164}
165
166pub trait AstNodeEdit: AstNode + Clone + Sized {
167 fn indent_level(&self) -> IndentLevel {
168 IndentLevel::from_node(self.syntax())
169 }
170 #[must_use]
171 fn indent(&self, level: IndentLevel) -> Self {
172 Self::cast(level.clone_increase_indent(self.syntax())).unwrap()
173 }
174 #[must_use]
175 fn indent_with_mapping(&self, level: IndentLevel, make: &SyntaxFactory) -> Self {
176 let new_node = self.indent(level);
177 if let Some(mut mapping) = make.mappings() {
178 let mut builder = SyntaxMappingBuilder::new(new_node.syntax().clone());
179 for (old, new) in self.syntax().children().zip(new_node.syntax().children()) {
180 builder.map_node(old, new);
181 }
182 builder.finish(&mut mapping);
183 }
184 new_node
185 }
186 #[must_use]
187 fn dedent(&self, level: IndentLevel) -> Self {
188 Self::cast(level.clone_decrease_indent(self.syntax())).unwrap()
189 }
190 #[must_use]
191 fn reset_indent(&self) -> Self {
192 let level = IndentLevel::from_node(self.syntax());
193 self.dedent(level)
194 }
195}
196
197impl<N: AstNode + Clone> AstNodeEdit for N {}
198
199#[test]
200fn test_increase_indent() {
201 let arm_list = {
202 let arm = make::match_arm(make::wildcard_pat().into(), None, make::ext::expr_unit());
203 make::match_arm_list([arm.clone(), arm])
204 };
205 assert_eq!(
206 arm_list.syntax().to_string(),
207 "{
208 _ => (),
209 _ => (),
210}"
211 );
212 let indented = arm_list.indent(IndentLevel(2));
213 assert_eq!(
214 indented.syntax().to_string(),
215 "{
216 _ => (),
217 _ => (),
218 }"
219 );
220}