diff options
Diffstat (limited to 'crates/ra_syntax/src/ast/edit.rs')
-rw-r--r-- | crates/ra_syntax/src/ast/edit.rs | 119 |
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 | ||
21 | impl ast::FnDef { | 21 | impl 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 | 235 | pub struct IndentLevel(pub u8); |
236 | // might want to call into fmt... | 236 | |
237 | impl From<u8> for IndentLevel { | ||
238 | fn from(level: u8) -> IndentLevel { | ||
239 | IndentLevel(level) | ||
240 | } | ||
241 | } | ||
242 | |||
243 | impl 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 | ||
237 | fn leading_indent(node: &SyntaxNode) -> Option<SmolStr> { | 318 | fn 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 | ||
333 | fn 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] |
254 | fn insert_children<N: AstNode>( | 338 | fn 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] | ||
358 | fn 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 | } | ||