Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions crates/ide/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,7 @@ fn test_fn() {
struct TestStruct { one: i32, two: i64 }

fn test_fn() {
let s = TestStruct { one: (), two: ()};
let s = TestStruct { one: (), two: () };
}
"#,
);
Expand All @@ -599,7 +599,7 @@ impl TestStruct {
struct TestStruct { one: i32 }

impl TestStruct {
fn test_fn() { let s = Self { one: ()}; }
fn test_fn() { let s = Self { one: () }; }
}
"#,
);
Expand Down Expand Up @@ -792,7 +792,7 @@ fn main() {
pub struct Foo { pub a: i32, pub b: i32 }
"#,
r#"
fn some(, b: ()) {}
fn some(, b: () ) {}
fn items() {}
fn here() {}

Expand Down
7 changes: 4 additions & 3 deletions crates/ide/src/diagnostics/fixes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,12 @@ impl DiagnosticWithFix for MissingFields {
let root = sema.db.parse_or_expand(self.file)?;
let field_list_parent = self.field_list_parent.to_node(&root);
let old_field_list = field_list_parent.record_expr_field_list()?;
let mut new_field_list = old_field_list.clone();
let new_field_list = old_field_list.clone_for_update();
for f in self.missed_fields.iter() {
let field =
make::record_expr_field(make::name_ref(&f.to_string()), Some(make::expr_unit()));
new_field_list = new_field_list.append_field(&field);
make::record_expr_field(make::name_ref(&f.to_string()), Some(make::expr_unit()))
.clone_for_update();
new_field_list.add_field(field);
}

let edit = {
Expand Down
100 changes: 2 additions & 98 deletions crates/syntax/src/ast/edit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,8 @@ use arrayvec::ArrayVec;

use crate::{
algo,
ast::{
self,
make::{self, tokens},
AstNode,
},
ted, AstToken, Direction, InsertPosition, NodeOrToken, SmolStr, SyntaxElement, SyntaxKind,
ast::{self, make, AstNode},
ted, AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxKind,
SyntaxKind::{ATTR, COMMENT, WHITESPACE},
SyntaxNode, SyntaxToken, T,
};
Expand All @@ -29,82 +25,6 @@ impl ast::BinExpr {
}
}

impl ast::RecordExprFieldList {
#[must_use]
pub fn append_field(&self, field: &ast::RecordExprField) -> ast::RecordExprFieldList {
self.insert_field(InsertPosition::Last, field)
}

#[must_use]
pub fn insert_field(
&self,
position: InsertPosition<&'_ ast::RecordExprField>,
field: &ast::RecordExprField,
) -> ast::RecordExprFieldList {
let is_multiline = self.syntax().text().contains_char('\n');
let ws;
let space = if is_multiline {
ws = tokens::WsBuilder::new(&format!(
"\n{} ",
leading_indent(self.syntax()).unwrap_or_default()
));
ws.ws()
} else {
tokens::single_space()
};

let mut to_insert: ArrayVec<SyntaxElement, 4> = ArrayVec::new();
to_insert.push(space.into());
to_insert.push(field.syntax().clone().into());
to_insert.push(make::token(T![,]).into());

macro_rules! after_l_curly {
() => {{
let anchor = match self.l_curly_token() {
Some(it) => it.into(),
None => return self.clone(),
};
InsertPosition::After(anchor)
}};
}

macro_rules! after_field {
($anchor:expr) => {
if let Some(comma) = $anchor
.syntax()
.siblings_with_tokens(Direction::Next)
.find(|it| it.kind() == T![,])
{
InsertPosition::After(comma)
} else {
to_insert.insert(0, make::token(T![,]).into());
InsertPosition::After($anchor.syntax().clone().into())
}
};
}

let position = match position {
InsertPosition::First => after_l_curly!(),
InsertPosition::Last => {
if !is_multiline {
// don't insert comma before curly
to_insert.pop();
}
match self.fields().last() {
Some(it) => after_field!(it),
None => after_l_curly!(),
}
}
InsertPosition::Before(anchor) => {
InsertPosition::Before(anchor.syntax().clone().into())
}
InsertPosition::After(anchor) => after_field!(anchor),
};

self.insert_children(position, to_insert)
}
}

impl ast::Path {
#[must_use]
pub fn with_segment(&self, segment: ast::PathSegment) -> ast::Path {
Expand Down Expand Up @@ -308,22 +228,6 @@ impl IndentLevel {
}
}

// FIXME: replace usages with IndentLevel above
fn leading_indent(node: &SyntaxNode) -> Option<SmolStr> {
for token in prev_tokens(node.first_token()?) {
if let Some(ws) = ast::Whitespace::cast(token.clone()) {
let ws_text = ws.text();
if let Some(pos) = ws_text.rfind('\n') {
return Some(ws_text[pos + 1..].into());
}
}
if token.text().contains('\n') {
break;
}
}
None
}

fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> {
iter::successors(Some(token), |token| token.prev_token())
}
Expand Down
40 changes: 40 additions & 0 deletions crates/syntax/src/ast/edit_in_place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,46 @@ impl ast::MatchArmList {
}
}

impl ast::RecordExprFieldList {
pub fn add_field(&self, field: ast::RecordExprField) {
let is_multiline = self.syntax().text().contains_char('\n');
let whitespace = if is_multiline {
let indent = IndentLevel::from_node(self.syntax()) + 1;
make::tokens::whitespace(&format!("\n{}", indent))
} else {
make::tokens::single_space()
};

let position = match self.fields().last() {
Some(last_field) => {
let comma = match last_field
.syntax()
.siblings_with_tokens(Direction::Next)
.filter_map(|it| it.into_token())
.find(|it| it.kind() == T![,])
{
Some(it) => it,
None => {
let comma = ast::make::token(T![,]);
ted::insert(Position::after(last_field.syntax()), &comma);
comma
}
};
Position::after(comma)
}
None => match self.l_curly_token() {
Some(it) => Position::after(it),
None => Position::last_child_of(self.syntax()),
},
};

ted::insert_all(position, vec![whitespace.into(), field.syntax().clone().into()]);
if is_multiline {
ted::insert(Position::after(field.syntax()), ast::make::token(T![,]));
}
}
}

fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> {
let l = node
.children_with_tokens()
Expand Down