diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-10-12 20:08:27 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2019-10-12 20:08:27 +0100 |
commit | 77f2dd96a122e59a8d8df8afb53a741df9b1af76 (patch) | |
tree | 6d66bed9aa538f34ea40b988af43774c16df909c /crates/ra_syntax/src/ast | |
parent | 264a07975d23ad4d7cb41b309ba4a4c0a507a028 (diff) | |
parent | c00f298fd26d4982e9fe092ee004facf9cef6906 (diff) |
Merge #2005
2005: add syntax-tree based indents r=matklad a=matklad
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_syntax/src/ast')
-rw-r--r-- | crates/ra_syntax/src/ast/edit.rs | 91 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/make.rs | 6 |
2 files changed, 91 insertions, 6 deletions
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs index 03f3b5fbb..ea92284b8 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,64 @@ 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 | |||
289 | // FIXME: replace usages with IndentLevel above | ||
237 | fn leading_indent(node: &SyntaxNode) -> Option<SmolStr> { | 290 | fn leading_indent(node: &SyntaxNode) -> Option<SmolStr> { |
238 | let prev_tokens = std::iter::successors(node.first_token(), |token| token.prev_token()); | 291 | for token in prev_tokens(node.first_token()?) { |
239 | for token in prev_tokens { | ||
240 | if let Some(ws) = ast::Whitespace::cast(token.clone()) { | 292 | if let Some(ws) = ast::Whitespace::cast(token.clone()) { |
241 | let ws_text = ws.text(); | 293 | let ws_text = ws.text(); |
242 | if let Some(pos) = ws_text.rfind('\n') { | 294 | if let Some(pos) = ws_text.rfind('\n') { |
@@ -250,6 +302,10 @@ fn leading_indent(node: &SyntaxNode) -> Option<SmolStr> { | |||
250 | None | 302 | None |
251 | } | 303 | } |
252 | 304 | ||
305 | fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> { | ||
306 | iter::successors(Some(token), |token| token.prev_token()) | ||
307 | } | ||
308 | |||
253 | #[must_use] | 309 | #[must_use] |
254 | fn insert_children<N: AstNode>( | 310 | fn insert_children<N: AstNode>( |
255 | parent: &N, | 311 | parent: &N, |
@@ -269,3 +325,26 @@ fn replace_children<N: AstNode>( | |||
269 | let new_syntax = algo::replace_children(parent.syntax(), to_replace, &mut to_insert); | 325 | let new_syntax = algo::replace_children(parent.syntax(), to_replace, &mut to_insert); |
270 | N::cast(new_syntax).unwrap() | 326 | N::cast(new_syntax).unwrap() |
271 | } | 327 | } |
328 | |||
329 | #[test] | ||
330 | fn test_increase_indent() { | ||
331 | let arm_list = { | ||
332 | let arm = make::match_arm(iter::once(make::placeholder_pat().into()), make::expr_unit()); | ||
333 | make::match_arm_list(vec![arm.clone(), arm].into_iter()) | ||
334 | }; | ||
335 | assert_eq!( | ||
336 | arm_list.syntax().to_string(), | ||
337 | "{ | ||
338 | _ => (), | ||
339 | _ => (), | ||
340 | }" | ||
341 | ); | ||
342 | let indented = IndentLevel(2).increase_indent(arm_list); | ||
343 | assert_eq!( | ||
344 | indented.syntax().to_string(), | ||
345 | "{ | ||
346 | _ => (), | ||
347 | _ => (), | ||
348 | }" | ||
349 | ); | ||
350 | } | ||
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs index 287a40bee..143835172 100644 --- a/crates/ra_syntax/src/ast/make.rs +++ b/crates/ra_syntax/src/ast/make.rs | |||
@@ -160,6 +160,12 @@ pub mod tokens { | |||
160 | .unwrap() | 160 | .unwrap() |
161 | } | 161 | } |
162 | 162 | ||
163 | pub fn whitespace(text: &str) -> SyntaxToken { | ||
164 | assert!(text.trim().is_empty()); | ||
165 | let sf = SourceFile::parse(text).ok().unwrap(); | ||
166 | sf.syntax().first_child_or_token().unwrap().into_token().unwrap() | ||
167 | } | ||
168 | |||
163 | pub fn single_newline() -> SyntaxToken { | 169 | pub fn single_newline() -> SyntaxToken { |
164 | SOURCE_FILE | 170 | SOURCE_FILE |
165 | .tree() | 171 | .tree() |