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.rs119
1 files changed, 113 insertions, 6 deletions
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs
index 03f3b5fbb..47bdbb81a 100644
--- a/crates/ra_syntax/src/ast/edit.rs
+++ b/crates/ra_syntax/src/ast/edit.rs
@@ -15,7 +15,7 @@ use crate::{
15 }, 15 },
16 AstToken, Direction, InsertPosition, SmolStr, SyntaxElement, 16 AstToken, Direction, InsertPosition, SmolStr, SyntaxElement,
17 SyntaxKind::{ATTR, COMMENT, WHITESPACE}, 17 SyntaxKind::{ATTR, COMMENT, WHITESPACE},
18 SyntaxNode, T, 18 SyntaxNode, SyntaxToken, T,
19}; 19};
20 20
21impl ast::FnDef { 21impl ast::FnDef {
@@ -231,12 +231,92 @@ pub fn replace_descendants<N: AstNode, D: AstNode>(
231 N::cast(new_syntax).unwrap() 231 N::cast(new_syntax).unwrap()
232} 232}
233 233
234// Note this is copy-pasted from fmt. It seems like fmt should be a separate 234#[derive(Debug, Clone, Copy)]
235// crate, but basic tree building should be this crate. However, tree building 235pub struct IndentLevel(pub u8);
236// might want to call into fmt... 236
237impl From<u8> for IndentLevel {
238 fn from(level: u8) -> IndentLevel {
239 IndentLevel(level)
240 }
241}
242
243impl IndentLevel {
244 pub fn from_node(node: &SyntaxNode) -> IndentLevel {
245 let first_token = match node.first_token() {
246 Some(it) => it,
247 None => return IndentLevel(0),
248 };
249 for ws in prev_tokens(first_token).filter_map(ast::Whitespace::cast) {
250 let text = ws.syntax().text();
251 if let Some(pos) = text.rfind('\n') {
252 let level = text[pos + 1..].chars().count() / 4;
253 return IndentLevel(level as u8);
254 }
255 }
256 IndentLevel(0)
257 }
258
259 pub fn increase_indent<N: AstNode>(self, node: N) -> N {
260 N::cast(self._increase_indent(node.syntax().clone())).unwrap()
261 }
262
263 fn _increase_indent(self, node: SyntaxNode) -> SyntaxNode {
264 let replacements: FxHashMap<SyntaxElement, SyntaxElement> = node
265 .descendants_with_tokens()
266 .filter_map(|el| el.into_token())
267 .filter_map(ast::Whitespace::cast)
268 .filter(|ws| {
269 let text = ws.syntax().text();
270 text.contains('\n')
271 })
272 .map(|ws| {
273 (
274 ws.syntax().clone().into(),
275 make::tokens::whitespace(&format!(
276 "{}{:width$}",
277 ws.syntax().text(),
278 "",
279 width = self.0 as usize * 4
280 ))
281 .into(),
282 )
283 })
284 .collect();
285 algo::replace_descendants(&node, &replacements)
286 }
287
288 pub fn decrease_indent<N: AstNode>(self, node: N) -> N {
289 N::cast(self._decrease_indent(node.syntax().clone())).unwrap()
290 }
291
292 fn _decrease_indent(self, node: SyntaxNode) -> SyntaxNode {
293 let replacements: FxHashMap<SyntaxElement, SyntaxElement> = node
294 .descendants_with_tokens()
295 .filter_map(|el| el.into_token())
296 .filter_map(ast::Whitespace::cast)
297 .filter(|ws| {
298 let text = ws.syntax().text();
299 text.contains('\n')
300 })
301 .map(|ws| {
302 (
303 ws.syntax().clone().into(),
304 make::tokens::whitespace(
305 &ws.syntax()
306 .text()
307 .replace(&format!("\n{:1$}", "", self.0 as usize * 4), "\n"),
308 )
309 .into(),
310 )
311 })
312 .collect();
313 algo::replace_descendants(&node, &replacements)
314 }
315}
316
317// FIXME: replace usages with IndentLevel above
237fn leading_indent(node: &SyntaxNode) -> Option<SmolStr> { 318fn leading_indent(node: &SyntaxNode) -> Option<SmolStr> {
238 let prev_tokens = std::iter::successors(node.first_token(), |token| token.prev_token()); 319 for token in prev_tokens(node.first_token()?) {
239 for token in prev_tokens {
240 if let Some(ws) = ast::Whitespace::cast(token.clone()) { 320 if let Some(ws) = ast::Whitespace::cast(token.clone()) {
241 let ws_text = ws.text(); 321 let ws_text = ws.text();
242 if let Some(pos) = ws_text.rfind('\n') { 322 if let Some(pos) = ws_text.rfind('\n') {
@@ -250,6 +330,10 @@ fn leading_indent(node: &SyntaxNode) -> Option<SmolStr> {
250 None 330 None
251} 331}
252 332
333fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> {
334 iter::successors(Some(token), |token| token.prev_token())
335}
336
253#[must_use] 337#[must_use]
254fn insert_children<N: AstNode>( 338fn insert_children<N: AstNode>(
255 parent: &N, 339 parent: &N,
@@ -269,3 +353,26 @@ fn replace_children<N: AstNode>(
269 let new_syntax = algo::replace_children(parent.syntax(), to_replace, &mut to_insert); 353 let new_syntax = algo::replace_children(parent.syntax(), to_replace, &mut to_insert);
270 N::cast(new_syntax).unwrap() 354 N::cast(new_syntax).unwrap()
271} 355}
356
357#[test]
358fn test_increase_indent() {
359 let arm_list = {
360 let arm = make::match_arm(iter::once(make::placeholder_pat().into()), make::expr_unit());
361 make::match_arm_list(vec![arm.clone(), arm].into_iter())
362 };
363 assert_eq!(
364 arm_list.syntax().to_string(),
365 "{
366 _ => (),
367 _ => (),
368}"
369 );
370 let indented = IndentLevel(2).increase_indent(arm_list);
371 assert_eq!(
372 indented.syntax().to_string(),
373 "{
374 _ => (),
375 _ => (),
376 }"
377 );
378}