⭐️ A friendly language for building type-safe, scalable systems!

stop referring to 'simple constants' and give a more accurate description

authored by giacomocavalieri.me and committed by Louis Pilfold 82af3b71 a6a6f22c

Changed files
+102 -29
compiler-core
+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
··· 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
··· 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
··· 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_(",", ", ")