diff options
-rw-r--r-- | crates/ra_assists/src/assist_ctx.rs | 1 | ||||
-rw-r--r-- | crates/ra_assists/src/assists/fill_match_arms.rs | 18 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/edit.rs | 91 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/make.rs | 6 |
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 @@ | |||
3 | use std::iter; | 3 | use std::iter; |
4 | 4 | ||
5 | use hir::{db::HirDatabase, Adt, HasSource}; | 5 | use hir::{db::HirDatabase, Adt, HasSource}; |
6 | use ra_syntax::ast::{self, make, AstNode, NameOwner}; | 6 | use ra_syntax::ast::{self, edit::IndentLevel, make, AstNode, NameOwner}; |
7 | 7 | ||
8 | use crate::{Assist, AssistCtx, AssistId}; | 8 | use 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 | ||
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() |