aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2019-10-12 20:08:27 +0100
committerGitHub <[email protected]>2019-10-12 20:08:27 +0100
commit77f2dd96a122e59a8d8df8afb53a741df9b1af76 (patch)
tree6d66bed9aa538f34ea40b988af43774c16df909c
parent264a07975d23ad4d7cb41b309ba4a4c0a507a028 (diff)
parentc00f298fd26d4982e9fe092ee004facf9cef6906 (diff)
Merge #2005
2005: add syntax-tree based indents r=matklad a=matklad Co-authored-by: Aleksey Kladov <[email protected]>
-rw-r--r--crates/ra_assists/src/assist_ctx.rs1
-rw-r--r--crates/ra_assists/src/assists/fill_match_arms.rs18
-rw-r--r--crates/ra_syntax/src/ast/edit.rs91
-rw-r--r--crates/ra_syntax/src/ast/make.rs6
4 files changed, 103 insertions, 13 deletions
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs
index 189cad7d0..e270c5d60 100644
--- a/crates/ra_assists/src/assist_ctx.rs
+++ b/crates/ra_assists/src/assist_ctx.rs
@@ -138,6 +138,7 @@ impl AssistBuilder {
138 138
139 /// Replaces specified `node` of text with a given string, reindenting the 139 /// Replaces specified `node` of text with a given string, reindenting the
140 /// string to maintain `node`'s existing indent. 140 /// string to maintain `node`'s existing indent.
141 // FIXME: remove in favor of ra_syntax::edit::IndentLevel::increase_indent
141 pub(crate) fn replace_node_and_indent( 142 pub(crate) fn replace_node_and_indent(
142 &mut self, 143 &mut self,
143 node: &SyntaxNode, 144 node: &SyntaxNode,
diff --git a/crates/ra_assists/src/assists/fill_match_arms.rs b/crates/ra_assists/src/assists/fill_match_arms.rs
index 7335cce09..e3f30b5de 100644
--- a/crates/ra_assists/src/assists/fill_match_arms.rs
+++ b/crates/ra_assists/src/assists/fill_match_arms.rs
@@ -3,7 +3,7 @@
3use std::iter; 3use std::iter;
4 4
5use hir::{db::HirDatabase, Adt, HasSource}; 5use hir::{db::HirDatabase, Adt, HasSource};
6use ra_syntax::ast::{self, make, AstNode, NameOwner}; 6use ra_syntax::ast::{self, edit::IndentLevel, make, AstNode, NameOwner};
7 7
8use crate::{Assist, AssistCtx, AssistId}; 8use crate::{Assist, AssistCtx, AssistId};
9 9
@@ -30,15 +30,19 @@ pub(crate) fn fill_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<As
30 let variant_list = enum_def.variant_list()?; 30 let variant_list = enum_def.variant_list()?;
31 31
32 ctx.add_action(AssistId("fill_match_arms"), "fill match arms", |edit| { 32 ctx.add_action(AssistId("fill_match_arms"), "fill match arms", |edit| {
33 let variants = variant_list.variants(); 33 let indent_level = IndentLevel::from_node(match_arm_list.syntax());
34 let arms = variants 34
35 .filter_map(build_pat) 35 let new_arm_list = {
36 .map(|pat| make::match_arm(iter::once(pat), make::expr_unit())); 36 let variants = variant_list.variants();
37 let new_arm_list = make::match_arm_list(arms); 37 let arms = variants
38 .filter_map(build_pat)
39 .map(|pat| make::match_arm(iter::once(pat), make::expr_unit()));
40 indent_level.increase_indent(make::match_arm_list(arms))
41 };
38 42
39 edit.target(match_expr.syntax().text_range()); 43 edit.target(match_expr.syntax().text_range());
40 edit.set_cursor(expr.syntax().text_range().start()); 44 edit.set_cursor(expr.syntax().text_range().start());
41 edit.replace_node_and_indent(match_arm_list.syntax(), new_arm_list.syntax().text()); 45 edit.replace_ast(match_arm_list, new_arm_list);
42 }); 46 });
43 47
44 ctx.build() 48 ctx.build()
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()