aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_syntax/src/ast
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2019-10-12 20:07:47 +0100
committerAleksey Kladov <[email protected]>2019-10-12 20:07:47 +0100
commitc00f298fd26d4982e9fe092ee004facf9cef6906 (patch)
tree6d66bed9aa538f34ea40b988af43774c16df909c /crates/ra_syntax/src/ast
parent264a07975d23ad4d7cb41b309ba4a4c0a507a028 (diff)
add syntax-tree based indents
Diffstat (limited to 'crates/ra_syntax/src/ast')
-rw-r--r--crates/ra_syntax/src/ast/edit.rs91
-rw-r--r--crates/ra_syntax/src/ast/make.rs6
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
21impl ast::FnDef { 21impl 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 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
289// FIXME: replace usages with IndentLevel above
237fn leading_indent(node: &SyntaxNode) -> Option<SmolStr> { 290fn 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
305fn 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]
254fn insert_children<N: AstNode>( 310fn 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]
330fn 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()