diff options
Diffstat (limited to 'crates/ra_syntax/src')
-rw-r--r-- | crates/ra_syntax/src/ast/edit.rs | 119 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/make.rs | 14 | ||||
-rw-r--r-- | crates/ra_syntax/src/grammar.ron | 2 |
3 files changed, 128 insertions, 7 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 | } | ||
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 | ||
131 | pub 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 | |||
131 | fn ast_from_text<N: AstNode>(text: &str) -> N { | 139 | fn 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() |
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 25e6f64ce..0a8fd0612 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron | |||
@@ -1,5 +1,5 @@ | |||
1 | // Stores definitions which must be used in multiple places | 1 | // Stores definitions which must be used in multiple places |
2 | // See `cargo gen-syntax` (defined in crates/ra_tools/src/main.rs) | 2 | // See `cargo xtask codegen` (defined in xtasks/src/main.rs) |
3 | Grammar( | 3 | Grammar( |
4 | punct: [ | 4 | punct: [ |
5 | (";", "SEMI"), | 5 | (";", "SEMI"), |