aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2021-05-16 13:10:18 +0100
committerAleksey Kladov <[email protected]>2021-05-16 13:10:18 +0100
commite4a7b44e554e183fc66474bcbd8f7ace541c5536 (patch)
treee606c6fe7990c468ce58049b3074aa70542ce906 /crates
parent4e142757e167ac16ce65ba1c743e131aba83cdc4 (diff)
internal: use mutable trees when filling match arms
Diffstat (limited to 'crates')
-rw-r--r--crates/ide_assists/src/handlers/fill_match_arms.rs28
-rw-r--r--crates/syntax/src/ast/edit.rs105
-rw-r--r--crates/syntax/src/ast/edit_in_place.rs90
3 files changed, 89 insertions, 134 deletions
diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs
index be927cc1c..f66a9b54b 100644
--- a/crates/ide_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ide_assists/src/handlers/fill_match_arms.rs
@@ -71,6 +71,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
71 .filter_map(|variant| build_pat(ctx.db(), module, variant)) 71 .filter_map(|variant| build_pat(ctx.db(), module, variant))
72 .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) 72 .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat))
73 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) 73 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
74 .map(|it| it.clone_for_update())
74 .collect::<Vec<_>>(); 75 .collect::<Vec<_>>();
75 if Some(enum_def) 76 if Some(enum_def)
76 == FamousDefs(&ctx.sema, Some(module.krate())) 77 == FamousDefs(&ctx.sema, Some(module.krate()))
@@ -99,6 +100,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
99 }) 100 })
100 .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) 101 .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat))
101 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) 102 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
103 .map(|it| it.clone_for_update())
102 .collect() 104 .collect()
103 } else { 105 } else {
104 return None; 106 return None;
@@ -114,10 +116,20 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
114 "Fill match arms", 116 "Fill match arms",
115 target, 117 target,
116 |builder| { 118 |builder| {
117 let new_arm_list = match_arm_list.remove_placeholder(); 119 let new_match_arm_list = match_arm_list.clone_for_update();
118 let n_old_arms = new_arm_list.arms().count(); 120
119 let new_arm_list = new_arm_list.append_arms(missing_arms); 121 let catch_all_arm = new_match_arm_list
120 let first_new_arm = new_arm_list.arms().nth(n_old_arms); 122 .arms()
123 .find(|arm| matches!(arm.pat(), Some(ast::Pat::WildcardPat(_))));
124 if let Some(arm) = catch_all_arm {
125 arm.remove()
126 }
127 let mut first_new_arm = None;
128 for arm in missing_arms {
129 first_new_arm.get_or_insert_with(|| arm.clone());
130 new_match_arm_list.add_arm(arm);
131 }
132
121 let old_range = ctx.sema.original_range(match_arm_list.syntax()).range; 133 let old_range = ctx.sema.original_range(match_arm_list.syntax()).range;
122 match (first_new_arm, ctx.config.snippet_cap) { 134 match (first_new_arm, ctx.config.snippet_cap) {
123 (Some(first_new_arm), Some(cap)) => { 135 (Some(first_new_arm), Some(cap)) => {
@@ -131,10 +143,10 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
131 } 143 }
132 None => Cursor::Before(first_new_arm.syntax()), 144 None => Cursor::Before(first_new_arm.syntax()),
133 }; 145 };
134 let snippet = render_snippet(cap, new_arm_list.syntax(), cursor); 146 let snippet = render_snippet(cap, new_match_arm_list.syntax(), cursor);
135 builder.replace_snippet(cap, old_range, snippet); 147 builder.replace_snippet(cap, old_range, snippet);
136 } 148 }
137 _ => builder.replace(old_range, new_arm_list.to_string()), 149 _ => builder.replace(old_range, new_match_arm_list.to_string()),
138 } 150 }
139 }, 151 },
140 ) 152 )
@@ -919,8 +931,8 @@ fn main() {
919 match a { 931 match a {
920 // foo bar baz 932 // foo bar baz
921 A::One => {} 933 A::One => {}
922 // This is where the rest should be
923 $0A::Two => {} 934 $0A::Two => {}
935 // This is where the rest should be
924 } 936 }
925 } 937 }
926 "#, 938 "#,
@@ -943,9 +955,9 @@ fn main() {
943 enum A { One, Two } 955 enum A { One, Two }
944 fn foo(a: A) { 956 fn foo(a: A) {
945 match a { 957 match a {
946 // foo bar baz
947 $0A::One => {} 958 $0A::One => {}
948 A::Two => {} 959 A::Two => {}
960 // foo bar baz
949 } 961 }
950 } 962 }
951 "#, 963 "#,
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs
index 4b5f5c571..5e6c1d44e 100644
--- a/crates/syntax/src/ast/edit.rs
+++ b/crates/syntax/src/ast/edit.rs
@@ -29,38 +29,6 @@ impl ast::BinExpr {
29 } 29 }
30} 30}
31 31
32fn make_multiline<N>(node: N) -> N
33where
34 N: AstNode + Clone,
35{
36 let l_curly = match node.syntax().children_with_tokens().find(|it| it.kind() == T!['{']) {
37 Some(it) => it,
38 None => return node,
39 };
40 let sibling = match l_curly.next_sibling_or_token() {
41 Some(it) => it,
42 None => return node,
43 };
44 let existing_ws = match sibling.as_token() {
45 None => None,
46 Some(tok) if tok.kind() != WHITESPACE => None,
47 Some(ws) => {
48 if ws.text().contains('\n') {
49 return node;
50 }
51 Some(ws.clone())
52 }
53 };
54
55 let indent = leading_indent(node.syntax()).unwrap_or_default();
56 let ws = tokens::WsBuilder::new(&format!("\n{}", indent));
57 let to_insert = iter::once(ws.ws().into());
58 match existing_ws {
59 None => node.insert_children(InsertPosition::After(l_curly), to_insert),
60 Some(ws) => node.replace_children(single_node(ws), to_insert),
61 }
62}
63
64impl ast::RecordExprFieldList { 32impl ast::RecordExprFieldList {
65 #[must_use] 33 #[must_use]
66 pub fn append_field(&self, field: &ast::RecordExprField) -> ast::RecordExprFieldList { 34 pub fn append_field(&self, field: &ast::RecordExprField) -> ast::RecordExprFieldList {
@@ -214,79 +182,6 @@ impl ast::UseTree {
214 } 182 }
215} 183}
216 184
217impl ast::MatchArmList {
218 #[must_use]
219 pub fn append_arms(&self, items: impl IntoIterator<Item = ast::MatchArm>) -> ast::MatchArmList {
220 let mut res = self.clone();
221 res = res.strip_if_only_whitespace();
222 if !res.syntax().text().contains_char('\n') {
223 res = make_multiline(res);
224 }
225 items.into_iter().for_each(|it| res = res.append_arm(it));
226 res
227 }
228
229 fn strip_if_only_whitespace(&self) -> ast::MatchArmList {
230 let mut iter = self.syntax().children_with_tokens().skip_while(|it| it.kind() != T!['{']);
231 iter.next(); // Eat the curly
232 let mut inner = iter.take_while(|it| it.kind() != T!['}']);
233 if !inner.clone().all(|it| it.kind() == WHITESPACE) {
234 return self.clone();
235 }
236 let start = match inner.next() {
237 Some(s) => s,
238 None => return self.clone(),
239 };
240 let end = match inner.last() {
241 Some(s) => s,
242 None => start.clone(),
243 };
244 self.replace_children(start..=end, &mut iter::empty())
245 }
246
247 #[must_use]
248 pub fn remove_placeholder(&self) -> ast::MatchArmList {
249 let placeholder =
250 self.arms().find(|arm| matches!(arm.pat(), Some(ast::Pat::WildcardPat(_))));
251 if let Some(placeholder) = placeholder {
252 self.remove_arm(&placeholder)
253 } else {
254 self.clone()
255 }
256 }
257
258 #[must_use]
259 fn remove_arm(&self, arm: &ast::MatchArm) -> ast::MatchArmList {
260 let start = arm.syntax().clone();
261 let end = if let Some(comma) = start
262 .siblings_with_tokens(Direction::Next)
263 .skip(1)
264 .find(|it| !it.kind().is_trivia())
265 .filter(|it| it.kind() == T![,])
266 {
267 comma
268 } else {
269 start.clone().into()
270 };
271 self.replace_children(start.into()..=end, None)
272 }
273
274 #[must_use]
275 pub fn append_arm(&self, item: ast::MatchArm) -> ast::MatchArmList {
276 let r_curly = match self.syntax().children_with_tokens().find(|it| it.kind() == T!['}']) {
277 Some(t) => t,
278 None => return self.clone(),
279 };
280 let position = InsertPosition::Before(r_curly);
281 let arm_ws = tokens::WsBuilder::new(" ");
282 let match_indent = &leading_indent(self.syntax()).unwrap_or_default();
283 let match_ws = tokens::WsBuilder::new(&format!("\n{}", match_indent));
284 let to_insert: ArrayVec<SyntaxElement, 3> =
285 [arm_ws.ws().into(), item.syntax().clone().into(), match_ws.ws().into()].into();
286 self.insert_children(position, to_insert)
287 }
288}
289
290#[must_use] 185#[must_use]
291pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { 186pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N {
292 N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap() 187 N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap()
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs
index ca777d057..abab0269a 100644
--- a/crates/syntax/src/ast/edit_in_place.rs
+++ b/crates/syntax/src/ast/edit_in_place.rs
@@ -13,7 +13,7 @@ use crate::{
13 make, GenericParamsOwner, 13 make, GenericParamsOwner,
14 }, 14 },
15 ted::{self, Position}, 15 ted::{self, Position},
16 AstNode, AstToken, Direction, 16 AstNode, AstToken, Direction, SyntaxNode,
17}; 17};
18 18
19use super::NameOwner; 19use super::NameOwner;
@@ -297,7 +297,7 @@ impl ast::AssocItemList {
297 ), 297 ),
298 None => match self.l_curly_token() { 298 None => match self.l_curly_token() {
299 Some(l_curly) => { 299 Some(l_curly) => {
300 self.normalize_ws_between_braces(); 300 normalize_ws_between_braces(self.syntax());
301 (IndentLevel::from_token(&l_curly) + 1, Position::after(&l_curly), "\n") 301 (IndentLevel::from_token(&l_curly) + 1, Position::after(&l_curly), "\n")
302 } 302 }
303 None => (IndentLevel::single(), Position::last_child_of(self.syntax()), "\n"), 303 None => (IndentLevel::single(), Position::last_child_of(self.syntax()), "\n"),
@@ -309,25 +309,6 @@ impl ast::AssocItemList {
309 ]; 309 ];
310 ted::insert_all(position, elements); 310 ted::insert_all(position, elements);
311 } 311 }
312
313 fn normalize_ws_between_braces(&self) -> Option<()> {
314 let l = self.l_curly_token()?;
315 let r = self.r_curly_token()?;
316 let indent = IndentLevel::from_node(self.syntax());
317
318 match l.next_sibling_or_token() {
319 Some(ws) if ws.kind() == SyntaxKind::WHITESPACE => {
320 if ws.next_sibling_or_token()?.into_token()? == r {
321 ted::replace(ws, make::tokens::whitespace(&format!("\n{}", indent)));
322 }
323 }
324 Some(ws) if ws.kind() == T!['}'] => {
325 ted::insert(Position::after(l), make::tokens::whitespace(&format!("\n{}", indent)));
326 }
327 _ => (),
328 }
329 Some(())
330 }
331} 312}
332 313
333impl ast::Fn { 314impl ast::Fn {
@@ -346,6 +327,73 @@ impl ast::Fn {
346 } 327 }
347} 328}
348 329
330impl ast::MatchArm {
331 pub fn remove(&self) {
332 if let Some(sibling) = self.syntax().prev_sibling_or_token() {
333 if sibling.kind() == SyntaxKind::WHITESPACE {
334 ted::remove(sibling);
335 }
336 }
337 if let Some(sibling) = self.syntax().next_sibling_or_token() {
338 if sibling.kind() == T![,] {
339 ted::remove(sibling);
340 }
341 }
342 ted::remove(self.syntax());
343 }
344}
345
346impl ast::MatchArmList {
347 pub fn add_arm(&self, arm: ast::MatchArm) {
348 normalize_ws_between_braces(self.syntax());
349 let position = match self.arms().last() {
350 Some(last_arm) => {
351 let curly = last_arm
352 .syntax()
353 .siblings_with_tokens(Direction::Next)
354 .find(|it| it.kind() == T![,]);
355 Position::after(curly.unwrap_or_else(|| last_arm.syntax().clone().into()))
356 }
357 None => match self.l_curly_token() {
358 Some(it) => Position::after(it),
359 None => Position::last_child_of(self.syntax()),
360 },
361 };
362 let indent = IndentLevel::from_node(self.syntax()) + 1;
363 let elements = vec![
364 make::tokens::whitespace(&format!("\n{}", indent)).into(),
365 arm.syntax().clone().into(),
366 ];
367 ted::insert_all(position, elements);
368 }
369}
370
371fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> {
372 let l = node
373 .children_with_tokens()
374 .filter_map(|it| it.into_token())
375 .find(|it| it.kind() == T!['{'])?;
376 let r = node
377 .children_with_tokens()
378 .filter_map(|it| it.into_token())
379 .find(|it| it.kind() == T!['}'])?;
380
381 let indent = IndentLevel::from_node(node);
382
383 match l.next_sibling_or_token() {
384 Some(ws) if ws.kind() == SyntaxKind::WHITESPACE => {
385 if ws.next_sibling_or_token()?.into_token()? == r {
386 ted::replace(ws, make::tokens::whitespace(&format!("\n{}", indent)));
387 }
388 }
389 Some(ws) if ws.kind() == T!['}'] => {
390 ted::insert(Position::after(l), make::tokens::whitespace(&format!("\n{}", indent)));
391 }
392 _ => (),
393 }
394 Some(())
395}
396
349#[cfg(test)] 397#[cfg(test)]
350mod tests { 398mod tests {
351 use std::fmt; 399 use std::fmt;