aboutsummaryrefslogtreecommitdiff
path: root/crates/syntax
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2021-05-16 16:10:56 +0100
committerAleksey Kladov <[email protected]>2021-05-16 16:10:56 +0100
commit1859df37fd6e308ea4304f69baae038ec09fe424 (patch)
tree6a8235bfb9867ed912236e7ebb67af118321061e /crates/syntax
parent9df0a2336829c54fbbc57ee8c8585aff345d9e47 (diff)
internal: use mutable syntax trees when filling fields
Diffstat (limited to 'crates/syntax')
-rw-r--r--crates/syntax/src/ast/edit.rs100
-rw-r--r--crates/syntax/src/ast/edit_in_place.rs40
2 files changed, 42 insertions, 98 deletions
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs
index 5e6c1d44e..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
11use crate::{ 11use 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,82 +25,6 @@ impl ast::BinExpr {
29 } 25 }
30} 26}
31 27
32impl ast::RecordExprFieldList {
33 #[must_use]
34 pub fn append_field(&self, field: &ast::RecordExprField) -> ast::RecordExprFieldList {
35 self.insert_field(InsertPosition::Last, field)
36 }
37
38 #[must_use]
39 pub fn insert_field(
40 &self,
41 position: InsertPosition<&'_ ast::RecordExprField>,
42 field: &ast::RecordExprField,
43 ) -> ast::RecordExprFieldList {
44 let is_multiline = self.syntax().text().contains_char('\n');
45 let ws;
46 let space = if is_multiline {
47 ws = tokens::WsBuilder::new(&format!(
48 "\n{} ",
49 leading_indent(self.syntax()).unwrap_or_default()
50 ));
51 ws.ws()
52 } else {
53 tokens::single_space()
54 };
55
56 let mut to_insert: ArrayVec<SyntaxElement, 4> = ArrayVec::new();
57 to_insert.push(space.into());
58 to_insert.push(field.syntax().clone().into());
59 to_insert.push(make::token(T![,]).into());
60
61 macro_rules! after_l_curly {
62 () => {{
63 let anchor = match self.l_curly_token() {
64 Some(it) => it.into(),
65 None => return self.clone(),
66 };
67 InsertPosition::After(anchor)
68 }};
69 }
70
71 macro_rules! after_field {
72 ($anchor:expr) => {
73 if let Some(comma) = $anchor
74 .syntax()
75 .siblings_with_tokens(Direction::Next)
76 .find(|it| it.kind() == T![,])
77 {
78 InsertPosition::After(comma)
79 } else {
80 to_insert.insert(0, make::token(T![,]).into());
81 InsertPosition::After($anchor.syntax().clone().into())
82 }
83 };
84 }
85
86 let position = match position {
87 InsertPosition::First => after_l_curly!(),
88 InsertPosition::Last => {
89 if !is_multiline {
90 // don't insert comma before curly
91 to_insert.pop();
92 }
93 match self.fields().last() {
94 Some(it) => after_field!(it),
95 None => after_l_curly!(),
96 }
97 }
98 InsertPosition::Before(anchor) => {
99 InsertPosition::Before(anchor.syntax().clone().into())
100 }
101 InsertPosition::After(anchor) => after_field!(anchor),
102 };
103
104 self.insert_children(position, to_insert)
105 }
106}
107
108impl ast::Path { 28impl ast::Path {
109 #[must_use] 29 #[must_use]
110 pub fn with_segment(&self, segment: ast::PathSegment) -> ast::Path { 30 pub fn with_segment(&self, segment: ast::PathSegment) -> ast::Path {
@@ -308,22 +228,6 @@ impl IndentLevel {
308 } 228 }
309} 229}
310 230
311// FIXME: replace usages with IndentLevel above
312fn leading_indent(node: &SyntaxNode) -> Option<SmolStr> {
313 for token in prev_tokens(node.first_token()?) {
314 if let Some(ws) = ast::Whitespace::cast(token.clone()) {
315 let ws_text = ws.text();
316 if let Some(pos) = ws_text.rfind('\n') {
317 return Some(ws_text[pos + 1..].into());
318 }
319 }
320 if token.text().contains('\n') {
321 break;
322 }
323 }
324 None
325}
326
327fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> { 231fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> {
328 iter::successors(Some(token), |token| token.prev_token()) 232 iter::successors(Some(token), |token| token.prev_token())
329} 233}
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs
index abab0269a..14624c682 100644
--- a/crates/syntax/src/ast/edit_in_place.rs
+++ b/crates/syntax/src/ast/edit_in_place.rs
@@ -368,6 +368,46 @@ impl ast::MatchArmList {
368 } 368 }
369} 369}
370 370
371impl ast::RecordExprFieldList {
372 pub fn add_field(&self, field: ast::RecordExprField) {
373 let is_multiline = self.syntax().text().contains_char('\n');
374 let whitespace = if is_multiline {
375 let indent = IndentLevel::from_node(self.syntax()) + 1;
376 make::tokens::whitespace(&format!("\n{}", indent))
377 } else {
378 make::tokens::single_space()
379 };
380
381 let position = match self.fields().last() {
382 Some(last_field) => {
383 let comma = match last_field
384 .syntax()
385 .siblings_with_tokens(Direction::Next)
386 .filter_map(|it| it.into_token())
387 .find(|it| it.kind() == T![,])
388 {
389 Some(it) => it,
390 None => {
391 let comma = ast::make::token(T![,]);
392 ted::insert(Position::after(last_field.syntax()), &comma);
393 comma
394 }
395 };
396 Position::after(comma)
397 }
398 None => match self.l_curly_token() {
399 Some(it) => Position::after(it),
400 None => Position::last_child_of(self.syntax()),
401 },
402 };
403
404 ted::insert_all(position, vec![whitespace.into(), field.syntax().clone().into()]);
405 if is_multiline {
406 ted::insert(Position::after(field.syntax()), ast::make::token(T![,]));
407 }
408 }
409}
410
371fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> { 411fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> {
372 let l = node 412 let l = node
373 .children_with_tokens() 413 .children_with_tokens()