Skip to content

Commit e4a7b44

Browse files
committed
internal: use mutable trees when filling match arms
1 parent 4e14275 commit e4a7b44

File tree

3 files changed

+89
-134
lines changed

3 files changed

+89
-134
lines changed

crates/ide_assists/src/handlers/fill_match_arms.rs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
7171
.filter_map(|variant| build_pat(ctx.db(), module, variant))
7272
.filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat))
7373
.map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
74+
.map(|it| it.clone_for_update())
7475
.collect::<Vec<_>>();
7576
if Some(enum_def)
7677
== FamousDefs(&ctx.sema, Some(module.krate()))
@@ -99,6 +100,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
99100
})
100101
.filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat))
101102
.map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
103+
.map(|it| it.clone_for_update())
102104
.collect()
103105
} else {
104106
return None;
@@ -114,10 +116,20 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
114116
"Fill match arms",
115117
target,
116118
|builder| {
117-
let new_arm_list = match_arm_list.remove_placeholder();
118-
let n_old_arms = new_arm_list.arms().count();
119-
let new_arm_list = new_arm_list.append_arms(missing_arms);
120-
let first_new_arm = new_arm_list.arms().nth(n_old_arms);
119+
let new_match_arm_list = match_arm_list.clone_for_update();
120+
121+
let catch_all_arm = new_match_arm_list
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+
121133
let old_range = ctx.sema.original_range(match_arm_list.syntax()).range;
122134
match (first_new_arm, ctx.config.snippet_cap) {
123135
(Some(first_new_arm), Some(cap)) => {
@@ -131,10 +143,10 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
131143
}
132144
None => Cursor::Before(first_new_arm.syntax()),
133145
};
134-
let snippet = render_snippet(cap, new_arm_list.syntax(), cursor);
146+
let snippet = render_snippet(cap, new_match_arm_list.syntax(), cursor);
135147
builder.replace_snippet(cap, old_range, snippet);
136148
}
137-
_ => builder.replace(old_range, new_arm_list.to_string()),
149+
_ => builder.replace(old_range, new_match_arm_list.to_string()),
138150
}
139151
},
140152
)
@@ -919,8 +931,8 @@ fn main() {
919931
match a {
920932
// foo bar baz
921933
A::One => {}
922-
// This is where the rest should be
923934
$0A::Two => {}
935+
// This is where the rest should be
924936
}
925937
}
926938
"#,
@@ -943,9 +955,9 @@ fn main() {
943955
enum A { One, Two }
944956
fn foo(a: A) {
945957
match a {
946-
// foo bar baz
947958
$0A::One => {}
948959
A::Two => {}
960+
// foo bar baz
949961
}
950962
}
951963
"#,

crates/syntax/src/ast/edit.rs

Lines changed: 0 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -29,38 +29,6 @@ impl ast::BinExpr {
2929
}
3030
}
3131

32-
fn make_multiline<N>(node: N) -> N
33-
where
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-
6432
impl ast::RecordExprFieldList {
6533
#[must_use]
6634
pub fn append_field(&self, field: &ast::RecordExprField) -> ast::RecordExprFieldList {
@@ -214,79 +182,6 @@ impl ast::UseTree {
214182
}
215183
}
216184

217-
impl 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-
290185
#[must_use]
291186
pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N {
292187
N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap()

crates/syntax/src/ast/edit_in_place.rs

Lines changed: 69 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::{
1313
make, GenericParamsOwner,
1414
},
1515
ted::{self, Position},
16-
AstNode, AstToken, Direction,
16+
AstNode, AstToken, Direction, SyntaxNode,
1717
};
1818

1919
use super::NameOwner;
@@ -297,7 +297,7 @@ impl ast::AssocItemList {
297297
),
298298
None => match self.l_curly_token() {
299299
Some(l_curly) => {
300-
self.normalize_ws_between_braces();
300+
normalize_ws_between_braces(self.syntax());
301301
(IndentLevel::from_token(&l_curly) + 1, Position::after(&l_curly), "\n")
302302
}
303303
None => (IndentLevel::single(), Position::last_child_of(self.syntax()), "\n"),
@@ -309,25 +309,6 @@ impl ast::AssocItemList {
309309
];
310310
ted::insert_all(position, elements);
311311
}
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-
}
331312
}
332313

333314
impl ast::Fn {
@@ -346,6 +327,73 @@ impl ast::Fn {
346327
}
347328
}
348329

330+
impl 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+
346+
impl 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+
371+
fn 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+
349397
#[cfg(test)]
350398
mod tests {
351399
use std::fmt;

0 commit comments

Comments
 (0)