+6
-5
CHANGELOG.md
+6
-5
CHANGELOG.md
···
49
49
50
50
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
51
51
52
-
- The formatter now allows more control over how lists made up of simple
53
-
constants are formatted. If a list is split with multiple elements on the same
54
-
line, removing the trailing comma will make sure the formatter keeps each item
55
-
on its own line:
52
+
- The formatter now allows more control over how lists are formatted.
53
+
If a list is split with multiple elements on the same line, removing the
54
+
trailing comma will make sure the formatter keeps each item on its own line:
56
55
57
56
```gleam
58
57
pub fn my_favourite_pokemon() -> List(String) {
···
79
78
80
79
- The formatter no longer removes empty lines between list items. In case an
81
80
empty line is added between list items they will all be split on multiple
82
-
lines.
81
+
lines. For example:
83
82
84
83
```gleam
85
84
pub fn main() {
···
103
102
]
104
103
}
105
104
```
105
+
106
+
([Giacomo Cavalieri](https://github.com/giacomocavalieri))
106
107
107
108
### Compiler
108
109
+15
-5
compiler-core/src/ast/constant.rs
+15
-5
compiler-core/src/ast/constant.rs
···
200
200
}
201
201
}
202
202
203
-
pub fn is_simple(&self) -> bool {
204
-
matches!(
205
-
self,
206
-
Self::Int { .. } | Self::Float { .. } | Self::String { .. }
207
-
)
203
+
#[must_use]
204
+
pub fn can_have_multiple_per_line(&self) -> bool {
205
+
match self {
206
+
Constant::Int { .. }
207
+
| Constant::Float { .. }
208
+
| Constant::String { .. }
209
+
| Constant::Var { .. } => true,
210
+
211
+
Constant::Tuple { .. }
212
+
| Constant::List { .. }
213
+
| Constant::Record { .. }
214
+
| Constant::BitArray { .. }
215
+
| Constant::StringConcatenation { .. }
216
+
| Constant::Invalid { .. } => false,
217
+
}
208
218
}
209
219
}
210
220
+29
-5
compiler-core/src/ast/untyped.rs
+29
-5
compiler-core/src/ast/untyped.rs
···
202
202
}
203
203
}
204
204
205
-
pub fn is_simple_constant(&self) -> bool {
206
-
matches!(
207
-
self,
208
-
Self::String { .. } | Self::Int { .. } | Self::Float { .. }
209
-
)
205
+
pub fn can_have_multiple_per_line(&self) -> bool {
206
+
match self {
207
+
UntypedExpr::Int { .. }
208
+
| UntypedExpr::Float { .. }
209
+
| UntypedExpr::String { .. }
210
+
| UntypedExpr::Var { .. } => true,
211
+
212
+
UntypedExpr::NegateBool { value, .. }
213
+
| UntypedExpr::NegateInt { value, .. }
214
+
| UntypedExpr::FieldAccess {
215
+
container: value, ..
216
+
} => value.can_have_multiple_per_line(),
217
+
218
+
UntypedExpr::Block { .. }
219
+
| UntypedExpr::Fn { .. }
220
+
| UntypedExpr::List { .. }
221
+
| UntypedExpr::Call { .. }
222
+
| UntypedExpr::BinOp { .. }
223
+
| UntypedExpr::PipeLine { .. }
224
+
| UntypedExpr::Case { .. }
225
+
| UntypedExpr::Tuple { .. }
226
+
| UntypedExpr::TupleIndex { .. }
227
+
| UntypedExpr::Todo { .. }
228
+
| UntypedExpr::Panic { .. }
229
+
| UntypedExpr::Echo { .. }
230
+
| UntypedExpr::BitArray { .. }
231
+
| UntypedExpr::RecordUpdate { .. }
232
+
| UntypedExpr::Placeholder { .. } => false,
233
+
}
210
234
}
211
235
212
236
pub fn is_tuple(&self) -> bool {
+52
-14
compiler-core/src/format.rs
+52
-14
compiler-core/src/format.rs
···
483
483
484
484
self.bit_array(
485
485
segment_docs,
486
-
segments.iter().all(|s| s.value.is_simple()),
486
+
segments
487
+
.iter()
488
+
.all(|s| s.value.can_have_multiple_per_line()),
487
489
location,
488
490
)
489
491
}
···
578
580
};
579
581
}
580
582
581
-
let list_packing =
582
-
self.list_items_packing(elements, None, |element| element.is_simple(), *location);
583
+
let list_packing = self.list_items_packing(
584
+
elements,
585
+
None,
586
+
|element| element.can_have_multiple_per_line(),
587
+
*location,
588
+
);
583
589
let comma = match list_packing {
584
590
ListItemsPacking::FitMultiplePerLine => flex_break(",", ", "),
585
591
ListItemsPacking::FitOnePerLine | ListItemsPacking::BreakOnePerLine => {
···
1071
1077
1072
1078
self.bit_array(
1073
1079
segment_docs,
1074
-
segments.iter().all(|s| s.value.is_simple_constant()),
1080
+
segments
1081
+
.iter()
1082
+
.all(|s| s.value.can_have_multiple_per_line()),
1075
1083
location,
1076
1084
)
1077
1085
}
···
2002
2010
};
2003
2011
}
2004
2012
2005
-
let list_packing =
2006
-
self.list_items_packing(elements, tail, UntypedExpr::is_simple_constant, *location);
2013
+
let list_packing = self.list_items_packing(
2014
+
elements,
2015
+
tail,
2016
+
UntypedExpr::can_have_multiple_per_line,
2017
+
*location,
2018
+
);
2007
2019
2008
2020
let comma = match list_packing {
2009
2021
ListItemsPacking::FitMultiplePerLine => flex_break(",", ", "),
···
2089
2101
&self,
2090
2102
items: &'a [T],
2091
2103
tail: Option<&'a T>,
2092
-
is_simple_constant: impl Fn(&'a T) -> bool,
2104
+
can_have_multiple_per_line: impl Fn(&'a T) -> bool,
2093
2105
list_location: SrcSpan,
2094
2106
) -> ListItemsPacking {
2095
2107
let ends_with_trailing_comma = tail
···
2099
2111
self.has_trailing_comma(last_element_end, list_location.end)
2100
2112
});
2101
2113
2102
-
let is_simple_constant_list = tail.is_none() && items.iter().all(is_simple_constant);
2103
2114
let has_multiple_elements_per_line =
2104
2115
self.has_items_on_the_same_line(items.iter().chain(tail));
2105
2116
···
2120
2131
// a single line; if we can't we'll put one item per line, no matter
2121
2132
// the content of the list.
2122
2133
ListItemsPacking::FitOnePerLine
2123
-
} else if is_simple_constant_list
2134
+
} else if tail.is_none()
2135
+
&& items.iter().all(can_have_multiple_per_line)
2124
2136
&& has_multiple_elements_per_line
2125
2137
&& self.spans_multiple_lines(list_location.start, list_location.end)
2126
2138
{
2127
-
// If there's a trailing comma, the list is only made of simple
2128
-
// constants and there's already multiple items per line, we try
2129
-
// and pack as many items as possible on each line.
2139
+
// If there's a trailing comma, we can have multiple items per line,
2140
+
// and there's already multiple items per line, we try and pack as
2141
+
// many items as possible on each line.
2142
+
//
2143
+
// Note how we only ever try and pack lists where all items are
2144
+
// unbreakable primitives. To pack a list we need to use put
2145
+
// `flex_break`s between each item.
2146
+
// If the items themselves had breaks we could end up in a situation
2147
+
// where an item gets broken making it span multiple lines and the
2148
+
// spaces are not, for example:
2149
+
//
2150
+
// ```gleam
2151
+
// [Constructor("wibble", "lorem ipsum dolor sit amet something something"), Other(1)]
2152
+
// ```
2153
+
//
2154
+
// If we used flex breaks here the list would be formatted as:
2155
+
//
2156
+
// ```gleam
2157
+
// [
2158
+
// Constructor(
2159
+
// "wibble",
2160
+
// "lorem ipsum dolor sit amet something something",
2161
+
// ), Other(1)
2162
+
// ]
2163
+
// ```
2164
+
//
2165
+
// The first item is broken, meaning that once we get to the flex
2166
+
// space separating it from the following one the formatter is not
2167
+
// going to break it since there's enough space in the current line!
2130
2168
ListItemsPacking::FitMultiplePerLine
2131
2169
} else {
2132
2170
// If it ends with a trailing comma we will force the list on
···
2509
2547
fn bit_array<'a>(
2510
2548
&mut self,
2511
2549
segments: Vec<Document<'a>>,
2512
-
is_simple: bool,
2550
+
can_have_multiple_per_line: bool,
2513
2551
location: &SrcSpan,
2514
2552
) -> Document<'a> {
2515
2553
let comments = self.pop_comments(location.end);
···
2535
2573
.force_break(),
2536
2574
};
2537
2575
}
2538
-
let comma = if is_simple {
2576
+
let comma = if can_have_multiple_per_line {
2539
2577
flex_break(",", ", ")
2540
2578
} else {
2541
2579
break_(",", ", ")