+148
-30
compiler-core/src/format.rs
+148
-30
compiler-core/src/format.rs
···
578
578
};
579
579
}
580
580
581
-
let comma = match elements.first() {
582
-
// If the list is made of non-simple constants and it gets too long we want to
583
-
// have each record on its own line instead of trying to fit as much
584
-
// as possible in each line. For example:
585
-
//
586
-
// [
587
-
// Some("wibble wobble"),
588
-
// None,
589
-
// Some("wobble wibble"),
590
-
// ]
591
-
//
592
-
Some(el) if !el.is_simple() => break_(",", ", "),
593
-
// For simple constants(String, Int, Float), if we have to break the list we still try to
594
-
// fit as much as possible into a single line instead of putting
595
-
// each item on its own separate line. For example:
596
-
//
597
-
// [
598
-
// 1, 2, 3, 4,
599
-
// 5, 6, 7,
600
-
// ]
601
-
//
602
-
Some(_) | None => flex_break(",", ", "),
581
+
let list_packing =
582
+
self.list_items_packing(elements, None, |element| element.is_simple(), *location);
583
+
let comma = match list_packing {
584
+
ListItemsPacking::FitMultiplePerLine => flex_break(",", ", "),
585
+
ListItemsPacking::FitOnePerLine | ListItemsPacking::BreakOnePerLine => {
586
+
break_(",", ", ")
587
+
}
603
588
};
604
589
605
590
let elements = join(
···
615
600
// of moving those out of the list.
616
601
// Otherwise those would be moved out of the list.
617
602
let comments = self.pop_comments(location.end);
618
-
match printed_comments(comments, false) {
619
-
None => doc.append(break_(",", "")).append("]").group(),
603
+
let doc = match printed_comments(comments, false) {
604
+
None => doc.append(break_(",", "")).append("]"),
620
605
Some(comment) => doc
621
606
.append(break_(",", "").nest(INDENT))
622
607
// ^ See how here we're adding the missing indentation to the
···
626
611
.append(line())
627
612
.append("]")
628
613
.force_break(),
614
+
};
615
+
616
+
match list_packing {
617
+
ListItemsPacking::FitOnePerLine | ListItemsPacking::FitMultiplePerLine => doc.group(),
618
+
ListItemsPacking::BreakOnePerLine => doc.force_break(),
629
619
}
630
620
}
631
621
···
1488
1478
.is_ok()
1489
1479
}
1490
1480
1481
+
/// Returns true if there's a trailing comma between `start` and `end`.
1482
+
///
1483
+
fn has_trailing_comma(&self, start: u32, end: u32) -> bool {
1484
+
dbg!(self.trailing_commas)
1485
+
.binary_search_by(|comma| {
1486
+
if *comma < start {
1487
+
Ordering::Less
1488
+
} else if *comma > end {
1489
+
Ordering::Greater
1490
+
} else {
1491
+
Ordering::Equal
1492
+
}
1493
+
})
1494
+
.is_ok()
1495
+
}
1496
+
1491
1497
fn pipeline<'a>(
1492
1498
&mut self,
1493
1499
expressions: &'a Vec1<UntypedExpr>,
···
1977
1983
};
1978
1984
}
1979
1985
1980
-
let comma = if tail.is_none() && elements.iter().all(UntypedExpr::is_simple_constant) {
1981
-
flex_break(",", ", ")
1982
-
} else {
1983
-
break_(",", ", ")
1986
+
let list_packing =
1987
+
self.list_items_packing(elements, tail, UntypedExpr::is_simple_constant, *location);
1988
+
1989
+
let comma = match list_packing {
1990
+
ListItemsPacking::FitMultiplePerLine => flex_break(",", ", "),
1991
+
ListItemsPacking::FitOnePerLine | ListItemsPacking::BreakOnePerLine => {
1992
+
break_(",", ", ")
1993
+
}
1984
1994
};
1985
1995
1986
1996
let list_size = elements.len()
···
2021
2031
// of moving those out of the list.
2022
2032
// Otherwise those would be moved out of the list.
2023
2033
let comments = self.pop_comments(location.end);
2024
-
match printed_comments(comments, false) {
2025
-
None => doc.append(last_break).append("]").group(),
2034
+
let doc = match printed_comments(comments, false) {
2035
+
None => doc.append(last_break).append("]"),
2026
2036
Some(comment) => doc
2027
2037
.append(last_break.nest(INDENT))
2028
2038
// ^ See how here we're adding the missing indentation to the
···
2032
2042
.append(line())
2033
2043
.append("]")
2034
2044
.force_break(),
2045
+
};
2046
+
2047
+
match list_packing {
2048
+
ListItemsPacking::FitOnePerLine | ListItemsPacking::FitMultiplePerLine => doc.group(),
2049
+
ListItemsPacking::BreakOnePerLine => doc.force_break(),
2035
2050
}
2036
2051
}
2037
2052
2053
+
fn list_items_packing<'a, T: HasLocation>(
2054
+
&self,
2055
+
items: &'a [T],
2056
+
tail: Option<&'a T>,
2057
+
is_simple_constant: impl Fn(&'a T) -> bool,
2058
+
list_location: SrcSpan,
2059
+
) -> ListItemsPacking {
2060
+
let ends_with_trailing_comma = tail
2061
+
.map(|tail| tail.location().end)
2062
+
.or_else(|| items.last().map(|last| last.location().end))
2063
+
.is_some_and(|last_element_end| {
2064
+
self.has_trailing_comma(last_element_end, list_location.end)
2065
+
});
2066
+
2067
+
let is_simple_constant_list = tail.is_none() && items.iter().all(is_simple_constant);
2068
+
let has_multiple_elements_per_line =
2069
+
self.has_items_on_the_same_line(items.iter().chain(tail));
2070
+
2071
+
if !ends_with_trailing_comma {
2072
+
// If the list doesn't end with a trailing comma we try and pack it in
2073
+
// a single line; if we can't we'll put one item per line, no matter
2074
+
// the content of the list.
2075
+
ListItemsPacking::FitOnePerLine
2076
+
} else if is_simple_constant_list
2077
+
&& has_multiple_elements_per_line
2078
+
&& self.spans_multiple_lines(list_location.start, list_location.end)
2079
+
{
2080
+
// If there's a trailing comma, the list is only made of simple
2081
+
// constants and there's already multiple items per line, we try
2082
+
// and pack as many items as possible on each line.
2083
+
ListItemsPacking::FitMultiplePerLine
2084
+
} else {
2085
+
// If it ends with a trailing comma we will force the list on
2086
+
// multiple lines, with one item per line.
2087
+
ListItemsPacking::BreakOnePerLine
2088
+
}
2089
+
}
2090
+
2091
+
fn has_items_on_the_same_line<'a, L: HasLocation + 'a, T: Iterator<Item = &'a L>>(
2092
+
&self,
2093
+
items: T,
2094
+
) -> bool {
2095
+
let mut previous: Option<SrcSpan> = None;
2096
+
for item in items {
2097
+
let item_location = item.location();
2098
+
// A list has multiple items on the same line if two consecutive
2099
+
// ones do not span multiple lines.
2100
+
if let Some(previous) = previous {
2101
+
if !self.spans_multiple_lines(previous.end, item_location.start) {
2102
+
return true;
2103
+
}
2104
+
}
2105
+
previous = Some(item_location);
2106
+
}
2107
+
false
2108
+
}
2109
+
2038
2110
/// Pretty prints an expression to be used in a comma separated list; for
2039
2111
/// example as a list item, a tuple item or as an argument of a function call.
2040
2112
fn comma_separated_item<'a>(
···
2761
2833
}
2762
2834
.to_doc()
2763
2835
}
2836
+
}
2837
+
2838
+
enum ListItemsPacking {
2839
+
/// Try and fit everything on a single line; if the items don't fit, break
2840
+
/// the list putting each item into its own line.
2841
+
///
2842
+
/// ```gleam
2843
+
/// // unbroken
2844
+
/// [1, 2, 3]
2845
+
///
2846
+
/// // broken
2847
+
/// [
2848
+
/// 1,
2849
+
/// 2,
2850
+
/// 3,
2851
+
/// ]
2852
+
/// ```
2853
+
///
2854
+
FitOnePerLine,
2855
+
2856
+
/// Try and fit everything on a single line; if the items don't fit, break
2857
+
/// the list putting as many items as possible in a single line.
2858
+
///
2859
+
/// ```gleam
2860
+
/// // unbroken
2861
+
/// [1, 2, 3]
2862
+
///
2863
+
/// // broken
2864
+
/// [
2865
+
/// 1, 2, 3, ...
2866
+
/// 4, 100,
2867
+
/// ]
2868
+
/// ```
2869
+
///
2870
+
FitMultiplePerLine,
2871
+
2872
+
/// Always break the list, putting each item into its own line:
2873
+
///
2874
+
/// ```gleam
2875
+
/// [
2876
+
/// 1,
2877
+
/// 2,
2878
+
/// 3,
2879
+
/// ]
2880
+
/// ```
2881
+
BreakOnePerLine,
2764
2882
}
2765
2883
2766
2884
pub fn break_block(doc: Document<'_>) -> Document<'_> {
+4
-2
compiler-core/src/format/tests.rs
+4
-2
compiler-core/src/format/tests.rs
···
5992
5992
r#"const x = "some long string 1"
5993
5993
<> "some long string 2"
5994
5994
<> [
5995
-
"here is a list", "with several elements",
5995
+
"here is a list",
5996
+
"with several elements",
5996
5997
"in order to make it be too long to fit on one line",
5997
-
"so we can see how it breaks", "onto multiple lines",
5998
+
"so we can see how it breaks",
5999
+
"onto multiple lines",
5998
6000
]
5999
6001
<> "and a last string"
6000
6002
"#,