aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ide/src/diagnostics.rs6
-rw-r--r--crates/ide/src/diagnostics/fixes.rs7
-rw-r--r--crates/syntax/src/ast/edit.rs100
-rw-r--r--crates/syntax/src/ast/edit_in_place.rs40
4 files changed, 49 insertions, 104 deletions
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 273d8cfbb..d5fba6740 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -579,7 +579,7 @@ fn test_fn() {
579struct TestStruct { one: i32, two: i64 } 579struct TestStruct { one: i32, two: i64 }
580 580
581fn test_fn() { 581fn test_fn() {
582 let s = TestStruct { one: (), two: ()}; 582 let s = TestStruct { one: (), two: () };
583} 583}
584"#, 584"#,
585 ); 585 );
@@ -599,7 +599,7 @@ impl TestStruct {
599struct TestStruct { one: i32 } 599struct TestStruct { one: i32 }
600 600
601impl TestStruct { 601impl TestStruct {
602 fn test_fn() { let s = Self { one: ()}; } 602 fn test_fn() { let s = Self { one: () }; }
603} 603}
604"#, 604"#,
605 ); 605 );
@@ -792,7 +792,7 @@ fn main() {
792pub struct Foo { pub a: i32, pub b: i32 } 792pub struct Foo { pub a: i32, pub b: i32 }
793"#, 793"#,
794 r#" 794 r#"
795fn some(, b: ()) {} 795fn some(, b: () ) {}
796fn items() {} 796fn items() {}
797fn here() {} 797fn here() {}
798 798
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs
index 15821500f..695b59e27 100644
--- a/crates/ide/src/diagnostics/fixes.rs
+++ b/crates/ide/src/diagnostics/fixes.rs
@@ -100,11 +100,12 @@ impl DiagnosticWithFix for MissingFields {
100 let root = sema.db.parse_or_expand(self.file)?; 100 let root = sema.db.parse_or_expand(self.file)?;
101 let field_list_parent = self.field_list_parent.to_node(&root); 101 let field_list_parent = self.field_list_parent.to_node(&root);
102 let old_field_list = field_list_parent.record_expr_field_list()?; 102 let old_field_list = field_list_parent.record_expr_field_list()?;
103 let mut new_field_list = old_field_list.clone(); 103 let new_field_list = old_field_list.clone_for_update();
104 for f in self.missed_fields.iter() { 104 for f in self.missed_fields.iter() {
105 let field = 105 let field =
106 make::record_expr_field(make::name_ref(&f.to_string()), Some(make::expr_unit())); 106 make::record_expr_field(make::name_ref(&f.to_string()), Some(make::expr_unit()))
107 new_field_list = new_field_list.append_field(&field); 107 .clone_for_update();
108 new_field_list.add_field(field);
108 } 109 }
109 110
110 let edit = { 111 let edit = {
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()