Skip to content
Open
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
2 changes: 1 addition & 1 deletion compiler/formatter/src/existing_whitespace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ impl<'a> ExistingWhitespace<'a> {
&FormattingInfo {
indentation,
trailing_comma_condition: None,
is_single_expression_in_assignment_body: false,
supports_sandwich_like_formatting: false,
},
)
.split();
Expand Down
432 changes: 298 additions & 134 deletions compiler/formatter/src/format.rs

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions compiler/formatter/src/format_collection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@ pub fn format_collection<'a>(
is_comma_required_for_single_item: bool,
info: &FormattingInfo,
) -> FormattedCst<'a> {
let info = info.resolve_for_expression_with_indented_lines(
previous_width,
SinglelineWidth::PARENTHESIS.into(),
);
let (info, uses_sandwich_like_multiline_formatting) =
info.resolve_for_sandwich_like(previous_width, SinglelineWidth::PARENTHESIS.into());

let opening_punctuation = format_cst(edits, previous_width, opening_punctuation, &info);
let closing_punctuation = format_cst(
Expand Down Expand Up @@ -100,7 +98,7 @@ pub fn format_collection<'a>(

let last_item_index = items.len().checked_sub(1);
let (closing_punctuation_width, whitespace) = closing_punctuation.split();
FormattedCst::new(
FormattedCst::new_maybe_sandwich_like_multiline_formatting(
opening_punctuation.into_trailing(edits, opening_punctuation_trailing)
+ items
.into_iter()
Expand All @@ -117,6 +115,8 @@ pub fn format_collection<'a>(
})
.sum::<Width>()
+ closing_punctuation_width,
uses_sandwich_like_multiline_formatting,
uses_sandwich_like_multiline_formatting,
whitespace,
)
}
Expand Down
39 changes: 39 additions & 0 deletions compiler/formatter/src/formatted_cst.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,42 @@ pub struct FormattedCst<'a> {
/// If there are trailing comments, this is [Width::Multiline]. Otherwise, it's the child's own
/// width.
child_width: Width,

/// Whether this CST node was formatted as a multiline sandwich-like.
///
/// This means the previous width had sufficient space to fit the sandwich-like's opening
/// character(s) (e.g., the opening parenthesis of a function call or the opening quote(s) of a
/// text) and the rest of the sandwich-like expression is formatted over multiple lines.
is_sandwich_like_multiline_formatting: bool,

/// Whether this CST node is mostly singleline and ends with a CST node formatted as a multiline
/// sandwich-like.
///
/// For example, if the single expression of an assignment is a call with a trailing multiline
/// list argument, it can start on the same line as the assignment but end on a new line.
ends_with_sandwich_like_multiline_formatting: bool,

pub whitespace: ExistingWhitespace<'a>,
}
impl<'a> FormattedCst<'a> {
pub const fn new(child_width: Width, whitespace: ExistingWhitespace<'a>) -> Self {
Self {
child_width,
is_sandwich_like_multiline_formatting: false,
ends_with_sandwich_like_multiline_formatting: false,
whitespace,
}
}
pub const fn new_maybe_sandwich_like_multiline_formatting(
child_width: Width,
is_sandwich_like_multiline_formatting: bool,
ends_with_sandwich_like_multiline_formatting: bool,
whitespace: ExistingWhitespace<'a>,
) -> Self {
Self {
child_width,
is_sandwich_like_multiline_formatting,
ends_with_sandwich_like_multiline_formatting,
whitespace,
}
}
Expand All @@ -45,6 +75,15 @@ impl<'a> FormattedCst<'a> {
}
}

#[must_use]
pub const fn is_sandwich_like_multiline_formatting(&self) -> bool {
self.is_sandwich_like_multiline_formatting
}
#[must_use]
pub const fn ends_with_sandwich_like_multiline_formatting(&self) -> bool {
self.ends_with_sandwich_like_multiline_formatting
}

pub fn split(self) -> (Width, ExistingWhitespace<'a>) {
(self.child_width, self.whitespace)
}
Expand Down
1 change: 1 addition & 0 deletions compiler/formatter/src/width.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ pub struct SinglelineWidth(usize);
impl SinglelineWidth {
pub const SPACE: Self = Self(1);
pub const PERCENT: Self = Self(1);
pub const DOUBLE_QUOTE: Self = Self(1);

pub const fn new_const(width: usize) -> Self {
Self(width)
Expand Down
6 changes: 4 additions & 2 deletions compiler/frontend/src/cst/kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -859,8 +859,10 @@ where
builder.push_cst_kind("StructField", |builder| {
builder.push_cst_kind_property_name("key_and_colon");
if let Some(box (key, colon)) = key_and_colon {
builder.push_cst_kind_property("key", key);
builder.push_cst_kind_property("colon", colon);
builder.push_indented_foldable(|builder| {
builder.push_cst_kind_property("key", key);
builder.push_cst_kind_property("colon", colon);
});
} else {
builder.push_simple(" None");
}
Expand Down
10 changes: 5 additions & 5 deletions compiler/frontend/src/string_to_rcst/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1462,11 +1462,11 @@ mod test {
fields:
StructField:
key_and_colon:
key: Symbol "Foo"
colon: TrailingWhitespace:
child: Colon
whitespace:
Whitespace " "
key: Symbol "Foo"
colon: TrailingWhitespace:
child: Colon
whitespace:
Whitespace " "
value: Identifier "foo"
comma: None
closing_bracket: ClosingBracket
Expand Down
164 changes: 146 additions & 18 deletions compiler/frontend/src/string_to_rcst/struct_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ pub fn struct_(input: &str, indentation: usize, allow_function: bool) -> Option<

// Whitespace between colon and value.
let (input, whitespace) = whitespaces_and_newlines(input, fields_indentation + 1, true);
let whitespace_is_multiline = whitespace.is_multiline();
if whitespace.is_multiline() {
fields_indentation = indentation + 1;
}
Expand All @@ -79,7 +80,11 @@ pub fn struct_(input: &str, indentation: usize, allow_function: bool) -> Option<
// Value.
let (input, value, has_value) = match expression(
input,
fields_indentation + 1,
if whitespace_is_multiline {
fields_indentation + 1
} else {
fields_indentation
},
ExpressionParsingOptions {
allow_assignment: false,
allow_call: true,
Expand Down Expand Up @@ -220,8 +225,8 @@ mod test {
fields:
StructField:
key_and_colon:
key: Identifier "foo"
colon: Colon
key: Identifier "foo"
colon: Colon
value: Identifier "bar"
comma: None
closing_bracket: ClosingBracket
Expand All @@ -237,8 +242,8 @@ mod test {
comma: Comma
StructField:
key_and_colon:
key: Identifier "bar"
colon: Colon
key: Identifier "bar"
colon: Colon
value: Identifier "baz"
comma: None
closing_bracket: ClosingBracket
Expand Down Expand Up @@ -275,11 +280,11 @@ mod test {
TrailingWhitespace:
child: StructField:
key_and_colon:
key: Identifier "foo"
colon: TrailingWhitespace:
child: Colon
whitespace:
Whitespace " "
key: Identifier "foo"
colon: TrailingWhitespace:
child: Colon
whitespace:
Whitespace " "
value: Identifier "bar"
comma: Comma
whitespace:
Expand All @@ -288,14 +293,14 @@ mod test {
TrailingWhitespace:
child: StructField:
key_and_colon:
key: Int:
radix_prefix: None
value: 4
string: "4"
colon: TrailingWhitespace:
child: Colon
whitespace:
Whitespace " "
key: Int:
radix_prefix: None
value: 4
string: "4"
colon: TrailingWhitespace:
child: Colon
whitespace:
Whitespace " "
value: Text:
opening: OpeningText:
opening_single_quotes:
Expand All @@ -310,5 +315,128 @@ mod test {
Newline "\n"
closing_bracket: ClosingBracket
"###);
// https://github.com/candy-lang/candy/issues/828
assert_rich_ir_snapshot!(
struct_(
"[\n State: [YieldedAfterLastMatch: True]\n]",
0,
true
),
@r###"
Remaining input: ""
Parsed: Struct:
opening_bracket: TrailingWhitespace:
child: OpeningBracket
whitespace:
Newline "\n"
Whitespace " "
fields:
TrailingWhitespace:
child: StructField:
key_and_colon:
key: Symbol "State"
colon: TrailingWhitespace:
child: Colon
whitespace:
Whitespace " "
value: Struct:
opening_bracket: OpeningBracket
fields:
StructField:
key_and_colon:
key: Symbol "YieldedAfterLastMatch"
colon: TrailingWhitespace:
child: Colon
whitespace:
Whitespace " "
value: Symbol "True"
comma: None
closing_bracket: ClosingBracket
comma: None
whitespace:
Newline "\n"
closing_bracket: ClosingBracket
"###
);
assert_rich_ir_snapshot!(
struct_(
"[\n YieldedAfterLastMatch: True,\n]",
0,
true
),
@r###"
Remaining input: ""
Parsed: Struct:
opening_bracket: TrailingWhitespace:
child: OpeningBracket
whitespace:
Newline "\n"
Whitespace " "
fields:
TrailingWhitespace:
child: StructField:
key_and_colon:
key: Symbol "YieldedAfterLastMatch"
colon: TrailingWhitespace:
child: Colon
whitespace:
Whitespace " "
value: Symbol "True"
comma: Comma
whitespace:
Newline "\n"
closing_bracket: ClosingBracket
"###
);
assert_rich_ir_snapshot!(
struct_(
"[\n State: [\n YieldedAfterLastMatch: True,\n ]\n]",
0,
true
),
@r###"
Remaining input: ""
Parsed: Struct:
opening_bracket: TrailingWhitespace:
child: OpeningBracket
whitespace:
Newline "\n"
Whitespace " "
fields:
TrailingWhitespace:
child: StructField:
key_and_colon:
key: Symbol "State"
colon: TrailingWhitespace:
child: Colon
whitespace:
Whitespace " "
value: Struct:
opening_bracket: TrailingWhitespace:
child: OpeningBracket
whitespace:
Newline "\n"
Whitespace " "
fields:
TrailingWhitespace:
child: StructField:
key_and_colon:
key: Symbol "YieldedAfterLastMatch"
colon: TrailingWhitespace:
child: Colon
whitespace:
Whitespace " "
value: Symbol "True"
comma: Comma
whitespace:
Newline "\n"
Whitespace " "
closing_bracket: ClosingBracket
comma: None
whitespace:
Newline "\n"
closing_bracket: ClosingBracket
"###
);
}
}