aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_syntax/src/ast
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_syntax/src/ast')
-rw-r--r--crates/ra_syntax/src/ast/edit.rs119
-rw-r--r--crates/ra_syntax/src/ast/make.rs14
2 files changed, 127 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}
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs
index 287a40bee..00422ea91 100644
--- a/crates/ra_syntax/src/ast/make.rs
+++ b/crates/ra_syntax/src/ast/make.rs
@@ -128,6 +128,14 @@ pub fn where_clause(preds: impl Iterator<Item = ast::WherePred>) -> ast::WhereCl
128 } 128 }
129} 129}
130 130
131pub fn if_expression(condition: &ast::Expr, statement: &str) -> ast::IfExpr {
132 return ast_from_text(&format!(
133 "fn f() {{ if !{} {{\n {}\n}}\n}}",
134 condition.syntax().text(),
135 statement
136 ));
137}
138
131fn ast_from_text<N: AstNode>(text: &str) -> N { 139fn ast_from_text<N: AstNode>(text: &str) -> N {
132 let parse = SourceFile::parse(text); 140 let parse = SourceFile::parse(text);
133 let res = parse.tree().syntax().descendants().find_map(N::cast).unwrap(); 141 let res = parse.tree().syntax().descendants().find_map(N::cast).unwrap();
@@ -160,6 +168,12 @@ pub mod tokens {
160 .unwrap() 168 .unwrap()
161 } 169 }
162 170
171 pub fn whitespace(text: &str) -> SyntaxToken {
172 assert!(text.trim().is_empty());
173 let sf = SourceFile::parse(text).ok().unwrap();
174 sf.syntax().first_child_or_token().unwrap().into_token().unwrap()
175 }
176
163 pub fn single_newline() -> SyntaxToken { 177 pub fn single_newline() -> SyntaxToken {
164 SOURCE_FILE 178 SOURCE_FILE
165 .tree() 179 .tree()