Skip to content

Commit 794c9fa

Browse files
Change binary operation formatting (#2473)
Co-authored-by: Louis Pilfold <[email protected]>
1 parent 2baa086 commit 794c9fa

File tree

3 files changed

+172
-45
lines changed

3 files changed

+172
-45
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,18 @@
22

33
## Unreleased
44

5+
### Formatter
6+
7+
- The formatter now tries to split long chains of binary operations around the
8+
operator itself, rather than around other elements like lists or function
9+
calls.
10+
511
### Bug fixes
612

713
- Fixed a bug where string prefix aliases defined in alternative case branches
814
would all be bound to the same constant.
915

16+
1017
## v0.33.0-rc2 - 2023-12-07
1118

1219
### Language changes

compiler-core/src/format.rs

Lines changed: 64 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -958,14 +958,50 @@ impl<'comments> Formatter<'comments> {
958958
let precedence = name.precedence();
959959
let left_precedence = left.binop_precedence();
960960
let right_precedence = right.binop_precedence();
961-
let left = self.expr(left);
962-
let right = self.expr(right);
963-
self.operator_side(left, precedence, left_precedence)
964-
.append(name)
965-
.append(self.operator_side(right, precedence, right_precedence - 1))
961+
let left_doc = self.expr(left);
962+
let right_doc = self.expr(right);
963+
964+
let mut doc = docvec!();
965+
doc = match Self::binop_name(left) {
966+
// In case the left side is a binary operation as well and it can
967+
// be grouped together with the current binary operation, the two
968+
// docs are simply concatenated, so that they will end up in the
969+
// same group and the formatter will try to keep those on a single
970+
// line.
971+
Some(left_name) if Self::should_be_grouped_together(left_name, name) => {
972+
doc.append(left_doc)
973+
}
974+
// In case the binary operations cannot be grouped together the left
975+
// side is treated as a group on its own so that this operation can
976+
// be broken independently.
977+
_ => doc.append(self.operator_side(left_doc.group(), precedence, left_precedence)),
978+
};
979+
doc = doc.append(break_("", " ")).append(name).append(" ");
980+
doc = match Self::binop_name(right) {
981+
// This works the same as the left side
982+
Some(right_name) if Self::should_be_grouped_together(right_name, name) => {
983+
doc.append(right_doc)
984+
}
985+
_ => doc.append(self.operator_side(right_doc.group(), precedence, right_precedence)),
986+
};
987+
doc
988+
}
989+
990+
fn binop_name(expr: &UntypedExpr) -> Option<&BinOp> {
991+
match expr {
992+
UntypedExpr::BinOp { name, .. } => Some(name),
993+
_ => None,
994+
}
995+
}
996+
997+
fn should_be_grouped_together(one: &BinOp, other: &BinOp) -> bool {
998+
// Try to keep on a single line groups of binary operations with the
999+
// same name, or binary operations on booleans.
1000+
let is_boolean_binop = |binop| matches!(binop, BinOp::And | BinOp::Or);
1001+
one == other || (is_boolean_binop(*one) && is_boolean_binop(*other))
9661002
}
9671003

968-
pub fn operator_side<'a>(&mut self, doc: Document<'a>, op: u8, side: u8) -> Document<'a> {
1004+
pub fn operator_side<'a>(&self, doc: Document<'a>, op: u8, side: u8) -> Document<'a> {
9691005
if op > side {
9701006
wrap_block(doc).group()
9711007
} else {
@@ -1676,28 +1712,28 @@ impl<'a> Documentable<'a> for &'a UnqualifiedImport {
16761712
impl<'a> Documentable<'a> for &'a BinOp {
16771713
fn to_doc(self) -> Document<'a> {
16781714
match self {
1679-
BinOp::And => " && ",
1680-
BinOp::Or => " || ",
1681-
BinOp::LtInt => " < ",
1682-
BinOp::LtEqInt => " <= ",
1683-
BinOp::LtFloat => " <. ",
1684-
BinOp::LtEqFloat => " <=. ",
1685-
BinOp::Eq => " == ",
1686-
BinOp::NotEq => " != ",
1687-
BinOp::GtEqInt => " >= ",
1688-
BinOp::GtInt => " > ",
1689-
BinOp::GtEqFloat => " >=. ",
1690-
BinOp::GtFloat => " >. ",
1691-
BinOp::AddInt => " + ",
1692-
BinOp::AddFloat => " +. ",
1693-
BinOp::SubInt => " - ",
1694-
BinOp::SubFloat => " -. ",
1695-
BinOp::MultInt => " * ",
1696-
BinOp::MultFloat => " *. ",
1697-
BinOp::DivInt => " / ",
1698-
BinOp::DivFloat => " /. ",
1699-
BinOp::RemainderInt => " % ",
1700-
BinOp::Concatenate => " <> ",
1715+
BinOp::And => "&&",
1716+
BinOp::Or => "||",
1717+
BinOp::LtInt => "<",
1718+
BinOp::LtEqInt => "<=",
1719+
BinOp::LtFloat => "<.",
1720+
BinOp::LtEqFloat => "<=.",
1721+
BinOp::Eq => "==",
1722+
BinOp::NotEq => "!=",
1723+
BinOp::GtEqInt => ">=",
1724+
BinOp::GtInt => ">",
1725+
BinOp::GtEqFloat => ">=.",
1726+
BinOp::GtFloat => ">.",
1727+
BinOp::AddInt => "+",
1728+
BinOp::AddFloat => "+.",
1729+
BinOp::SubInt => "-",
1730+
BinOp::SubFloat => "-.",
1731+
BinOp::MultInt => "*",
1732+
BinOp::MultFloat => "*.",
1733+
BinOp::DivInt => "/",
1734+
BinOp::DivFloat => "/.",
1735+
BinOp::RemainderInt => "%",
1736+
BinOp::Concatenate => "<>",
17011737
}
17021738
.to_doc()
17031739
}

compiler-core/src/format/tests.rs

Lines changed: 101 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3696,7 +3696,8 @@ pub fn main(
36963696
fn commented_binop() {
36973697
assert_format!(
36983698
"fn main() {
3699-
1 + // hello
3699+
1
3700+
+ // hello
37003701
2
37013702
}
37023703
"
@@ -3705,8 +3706,10 @@ fn commented_binop() {
37053706
assert_format!(
37063707
"fn main() {
37073708
// one
3708-
1 + // two
3709-
2 + // three
3709+
1
3710+
+ // two
3711+
2
3712+
+ // three
37103713
3
37113714
}
37123715
"
@@ -3971,7 +3974,8 @@ fn binary_operator_precedence() {
39713974

39723975
assert_format!(
39733976
"fn main() {
3974-
3 * {
3977+
3
3978+
* {
39753979
1
39763980
|> inc
39773981
}
@@ -3984,7 +3988,8 @@ fn binary_operator_precedence() {
39843988
{
39853989
1
39863990
|> inc
3987-
} * 3
3991+
}
3992+
* 3
39883993
}
39893994
"
39903995
);
@@ -4999,13 +5004,35 @@ fn wrap_long_line_with_int_negation() {
49995004
r#"pub fn main() {
50005005
let a = 3
50015006
let b =
5002-
a * a * a * a * a * a * a * a * a * a * a * a * a * {
5003-
a * a * a * a * a * a * a * a * a * a
5004-
}
5007+
a
5008+
* a
5009+
* a
5010+
* a
5011+
* a
5012+
* a
5013+
* a
5014+
* a
5015+
* a
5016+
* a
5017+
* a
5018+
* a
5019+
* a
5020+
* { a * a * a * a * a * a * a * a * a * a }
50055021
let c =
5006-
c * c * c * c * c * c * c * c * c * c * c * c * c * -{
5007-
c * c * c * c * c * c * c * c * c * c
5008-
}
5022+
c
5023+
* c
5024+
* c
5025+
* c
5026+
* c
5027+
* c
5028+
* c
5029+
* c
5030+
* c
5031+
* c
5032+
* c
5033+
* c
5034+
* c
5035+
* -{ c * c * c * c * c * c * c * c * c * c }
50095036
}
50105037
"#
50115038
);
@@ -5023,13 +5050,35 @@ fn wrap_long_line_with_bool_negation() {
50235050
r#"pub fn main() {
50245051
let a = True
50255052
let b =
5026-
a || a || a || a || a || a || a || a || a || a || a || a || a || {
5027-
a || a || a || a || a || a || a || a || a || a
5028-
}
5053+
a
5054+
|| a
5055+
|| a
5056+
|| a
5057+
|| a
5058+
|| a
5059+
|| a
5060+
|| a
5061+
|| a
5062+
|| a
5063+
|| a
5064+
|| a
5065+
|| a
5066+
|| { a || a || a || a || a || a || a || a || a || a }
50295067
let c =
5030-
c || c || c || c || c || c || c || c || c || c || c || c || c || !{
5031-
c || c || c || c || c || c || c || c || c || c
5032-
}
5068+
c
5069+
|| c
5070+
|| c
5071+
|| c
5072+
|| c
5073+
|| c
5074+
|| c
5075+
|| c
5076+
|| c
5077+
|| c
5078+
|| c
5079+
|| c
5080+
|| c
5081+
|| !{ c || c || c || c || c || c || c || c || c || c }
50335082
}
50345083
"#
50355084
);
@@ -5291,3 +5340,38 @@ fn single_argument_call_nested_nested() {
52915340
"#
52925341
);
52935342
}
5343+
5344+
#[test]
5345+
pub fn long_binary_operation_sequence() {
5346+
assert_format!(
5347+
r#"pub fn main() {
5348+
int.to_string(color.red)
5349+
<> ", "
5350+
<> int.to_string(color.green)
5351+
<> ", "
5352+
<> int.to_string(color.blue)
5353+
<> ", "
5354+
<> float.to_string(color.alpha)
5355+
}
5356+
"#
5357+
);
5358+
}
5359+
5360+
#[test]
5361+
pub fn long_comparison_chain() {
5362+
assert_format!(
5363+
r#"pub fn main() {
5364+
trying_a_comparison(this, is, a, function) > with_ints
5365+
&& trying_other_comparisons < with_ints
5366+
|| trying_other_comparisons <= with_ints
5367+
&& trying_other_comparisons >= with_ints
5368+
|| and_now_an_equality_check == with_a_function(foo, bar)
5369+
&& trying_other_comparisons >. with_floats
5370+
|| trying_other_comparisons <. with_floats(baz)
5371+
&& trying_other_comparisons <=. with_floats
5372+
|| trying_other_comparisons(foo, bar) >=. with_floats
5373+
&& foo <> bar
5374+
}
5375+
"#
5376+
);
5377+
}

0 commit comments

Comments
 (0)