diff options
Diffstat (limited to 'crates/syntax/src/ast/edit.rs')
-rw-r--r-- | crates/syntax/src/ast/edit.rs | 205 |
1 files changed, 2 insertions, 203 deletions
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs index 4b5f5c571..61952377f 100644 --- a/crates/syntax/src/ast/edit.rs +++ b/crates/syntax/src/ast/edit.rs | |||
@@ -10,12 +10,8 @@ use arrayvec::ArrayVec; | |||
10 | 10 | ||
11 | use crate::{ | 11 | use crate::{ |
12 | algo, | 12 | algo, |
13 | ast::{ | 13 | ast::{self, make, AstNode}, |
14 | self, | 14 | ted, AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxKind, |
15 | make::{self, tokens}, | ||
16 | AstNode, | ||
17 | }, | ||
18 | ted, AstToken, Direction, InsertPosition, NodeOrToken, SmolStr, SyntaxElement, SyntaxKind, | ||
19 | SyntaxKind::{ATTR, COMMENT, WHITESPACE}, | 15 | SyntaxKind::{ATTR, COMMENT, WHITESPACE}, |
20 | SyntaxNode, SyntaxToken, T, | 16 | SyntaxNode, SyntaxToken, T, |
21 | }; | 17 | }; |
@@ -29,114 +25,6 @@ impl ast::BinExpr { | |||
29 | } | 25 | } |
30 | } | 26 | } |
31 | 27 | ||
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 { | ||
65 | #[must_use] | ||
66 | pub fn append_field(&self, field: &ast::RecordExprField) -> ast::RecordExprFieldList { | ||
67 | self.insert_field(InsertPosition::Last, field) | ||
68 | } | ||
69 | |||
70 | #[must_use] | ||
71 | pub fn insert_field( | ||
72 | &self, | ||
73 | position: InsertPosition<&'_ ast::RecordExprField>, | ||
74 | field: &ast::RecordExprField, | ||
75 | ) -> ast::RecordExprFieldList { | ||
76 | let is_multiline = self.syntax().text().contains_char('\n'); | ||
77 | let ws; | ||
78 | let space = if is_multiline { | ||
79 | ws = tokens::WsBuilder::new(&format!( | ||
80 | "\n{} ", | ||
81 | leading_indent(self.syntax()).unwrap_or_default() | ||
82 | )); | ||
83 | ws.ws() | ||
84 | } else { | ||
85 | tokens::single_space() | ||
86 | }; | ||
87 | |||
88 | let mut to_insert: ArrayVec<SyntaxElement, 4> = ArrayVec::new(); | ||
89 | to_insert.push(space.into()); | ||
90 | to_insert.push(field.syntax().clone().into()); | ||
91 | to_insert.push(make::token(T![,]).into()); | ||
92 | |||
93 | macro_rules! after_l_curly { | ||
94 | () => {{ | ||
95 | let anchor = match self.l_curly_token() { | ||
96 | Some(it) => it.into(), | ||
97 | None => return self.clone(), | ||
98 | }; | ||
99 | InsertPosition::After(anchor) | ||
100 | }}; | ||
101 | } | ||
102 | |||
103 | macro_rules! after_field { | ||
104 | ($anchor:expr) => { | ||
105 | if let Some(comma) = $anchor | ||
106 | .syntax() | ||
107 | .siblings_with_tokens(Direction::Next) | ||
108 | .find(|it| it.kind() == T![,]) | ||
109 | { | ||
110 | InsertPosition::After(comma) | ||
111 | } else { | ||
112 | to_insert.insert(0, make::token(T![,]).into()); | ||
113 | InsertPosition::After($anchor.syntax().clone().into()) | ||
114 | } | ||
115 | }; | ||
116 | } | ||
117 | |||
118 | let position = match position { | ||
119 | InsertPosition::First => after_l_curly!(), | ||
120 | InsertPosition::Last => { | ||
121 | if !is_multiline { | ||
122 | // don't insert comma before curly | ||
123 | to_insert.pop(); | ||
124 | } | ||
125 | match self.fields().last() { | ||
126 | Some(it) => after_field!(it), | ||
127 | None => after_l_curly!(), | ||
128 | } | ||
129 | } | ||
130 | InsertPosition::Before(anchor) => { | ||
131 | InsertPosition::Before(anchor.syntax().clone().into()) | ||
132 | } | ||
133 | InsertPosition::After(anchor) => after_field!(anchor), | ||
134 | }; | ||
135 | |||
136 | self.insert_children(position, to_insert) | ||
137 | } | ||
138 | } | ||
139 | |||
140 | impl ast::Path { | 28 | impl ast::Path { |
141 | #[must_use] | 29 | #[must_use] |
142 | pub fn with_segment(&self, segment: ast::PathSegment) -> ast::Path { | 30 | pub fn with_segment(&self, segment: ast::PathSegment) -> ast::Path { |
@@ -214,79 +102,6 @@ impl ast::UseTree { | |||
214 | } | 102 | } |
215 | } | 103 | } |
216 | 104 | ||
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] | 105 | #[must_use] |
291 | pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { | 106 | pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { |
292 | N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap() | 107 | N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap() |
@@ -413,22 +228,6 @@ impl IndentLevel { | |||
413 | } | 228 | } |
414 | } | 229 | } |
415 | 230 | ||
416 | // FIXME: replace usages with IndentLevel above | ||
417 | fn leading_indent(node: &SyntaxNode) -> Option<SmolStr> { | ||
418 | for token in prev_tokens(node.first_token()?) { | ||
419 | if let Some(ws) = ast::Whitespace::cast(token.clone()) { | ||
420 | let ws_text = ws.text(); | ||
421 | if let Some(pos) = ws_text.rfind('\n') { | ||
422 | return Some(ws_text[pos + 1..].into()); | ||
423 | } | ||
424 | } | ||
425 | if token.text().contains('\n') { | ||
426 | break; | ||
427 | } | ||
428 | } | ||
429 | None | ||
430 | } | ||
431 | |||
432 | fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> { | 231 | fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> { |
433 | iter::successors(Some(token), |token| token.prev_token()) | 232 | iter::successors(Some(token), |token| token.prev_token()) |
434 | } | 233 | } |