diff options
Diffstat (limited to 'crates/syntax')
-rw-r--r-- | crates/syntax/src/ast/edit.rs | 105 | ||||
-rw-r--r-- | crates/syntax/src/ast/edit_in_place.rs | 90 |
2 files changed, 69 insertions, 126 deletions
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 | ||
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 | |||
64 | impl ast::RecordExprFieldList { | 32 | impl 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 | ||
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 | |||
290 | #[must_use] | 185 | #[must_use] |
291 | pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { | 186 | pub 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 | ||
19 | use super::NameOwner; | 19 | use 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 | ||
333 | impl ast::Fn { | 314 | impl ast::Fn { |
@@ -346,6 +327,73 @@ impl ast::Fn { | |||
346 | } | 327 | } |
347 | } | 328 | } |
348 | 329 | ||
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 | |||
349 | #[cfg(test)] | 397 | #[cfg(test)] |
350 | mod tests { | 398 | mod tests { |
351 | use std::fmt; | 399 | use std::fmt; |