Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

allow pattern matching on discard in case branches

author
Giacomo Cavalieri
committer
Louis Pilfold
date (May 18, 2026, 3:51 PM +0100) commit 1ec479c1 parent 8383864c change-id wtnllusy
+136 -3
+29
CHANGELOG.md
··· 18 18 ``` 19 19 20 20 The compiler will display this error message: 21 + 21 22 ```text 22 23 error: Unknown variable 23 24 ┌─ /path/to/project/src/project.gleam:4:3 ··· 192 193 193 194 - The language server now presents quick fix code actions before refactoring 194 195 ones. 196 + ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) 197 + 198 + - The language server now allows to further pattern match on a discard by 199 + replacing it with the patterns it is discarding. 200 + For example: 201 + 202 + ```gleam 203 + pub fn list_names(x: Result(List(String), Nil)) { 204 + case x { 205 + Error(Nil) -> io.println("no names") 206 + Ok(_) -> todo 207 + // ^ Triggering the code action here 208 + } 209 + } 210 + ``` 211 + 212 + Triggering the code action will result in the following code: 213 + 214 + ```gleam 215 + pub fn list_names(x: Result(List(String), Nil)) { 216 + case x { 217 + Error(Nil) -> io.println("no names") 218 + Ok([]) -> todo 219 + Ok([first, ..rest]) -> todo 220 + } 221 + } 222 + ``` 223 + 195 224 ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) 196 225 197 226 - The language server no longer shows completions for deprecated values from
+43 -3
language-server/src/code_action.rs
··· 5522 5522 /// This location covers the entire list tail pattern, including the `..` 5523 5523 location: SrcSpan, 5524 5524 }, 5525 + /// When the pattern being matched is a discard. For example: 5526 + /// ```gleam 5527 + /// case wibble { 5528 + /// Ok(_) -> todo 5529 + /// // ^ Hovering this! 5530 + /// } 5531 + /// ``` 5532 + Discard { location: SrcSpan }, 5525 5533 } 5526 5534 5527 5535 impl PatternLocation { 5528 5536 fn regular(location: SrcSpan) -> Self { 5529 5537 Self::Regular { location } 5538 + } 5539 + 5540 + fn is_discard(&self) -> bool { 5541 + match self { 5542 + PatternLocation::Regular { .. } | PatternLocation::ListTail { .. } => false, 5543 + PatternLocation::Discard { .. } => true, 5544 + } 5530 5545 } 5531 5546 } 5532 5547 ··· 5581 5596 clause_location, 5582 5597 bound_variables, 5583 5598 }) => { 5599 + let title = if variable_location.is_discard() { 5600 + "Pattern match on value" 5601 + } else { 5602 + "Pattern match on variable" 5603 + }; 5604 + 5584 5605 self.match_on_clause_variable( 5585 5606 variable_type, 5586 5607 variable_location, 5587 5608 clause_location, 5588 5609 &bound_variables, 5589 5610 ); 5590 - "Pattern match on variable" 5611 + 5612 + title 5591 5613 } 5592 5614 5593 5615 None => return vec![], ··· 5739 5761 let nesting = " ".repeat(clause_range.start.character as usize); 5740 5762 5741 5763 let variable_location = match variable_location { 5742 - PatternLocation::Regular { location } => location, 5743 - PatternLocation::ListTail { location } => location, 5764 + PatternLocation::Regular { location } 5765 + | PatternLocation::ListTail { location } 5766 + | PatternLocation::Discard { location } => location, 5744 5767 }; 5745 5768 5746 5769 let variable_start = (variable_location.start - clause_location.start) as usize; ··· 6109 6132 } 6110 6133 6111 6134 ast::visit::visit_typed_use(self, use_); 6135 + } 6136 + 6137 + fn visit_typed_pattern_discard( 6138 + &mut self, 6139 + location: &'ast SrcSpan, 6140 + name: &'ast EcoString, 6141 + type_: &'ast Arc<Type>, 6142 + ) { 6143 + if within( 6144 + self.params.range, 6145 + self.edits.src_span_to_lsp_range(*location), 6146 + ) { 6147 + let location = PatternLocation::Discard { 6148 + location: *location, 6149 + }; 6150 + self.pattern_variable_under_cursor = Some((name, location, type_.clone())); 6151 + } 6112 6152 } 6113 6153 6114 6154 fn visit_typed_pattern_variable(
+42
language-server/src/tests/action.rs
··· 179 179 const GENERATE_TO_JSON_FUNCTION: &str = "Generate to-JSON function"; 180 180 const PATTERN_MATCH_ON_ARGUMENT: &str = "Pattern match on argument"; 181 181 const PATTERN_MATCH_ON_VARIABLE: &str = "Pattern match on variable"; 182 + const PATTERN_MATCH_ON_VALUE: &str = "Pattern match on value"; 182 183 const GENERATE_FUNCTION: &str = "Generate function"; 183 184 const CONVERT_TO_FUNCTION_CALL: &str = "Convert to function call"; 184 185 const INLINE_VARIABLE: &str = "Inline variable"; ··· 10827 10828 } 10828 10829 }", 10829 10830 find_position_of("else_").to_selection() 10831 + ); 10832 + } 10833 + 10834 + #[test] 10835 + fn pattern_match_on_discard_pattern_in_branch() { 10836 + assert_code_action!( 10837 + PATTERN_MATCH_ON_VALUE, 10838 + "pub fn main(x: Result(List(a), Nil)) { 10839 + case x { 10840 + Error(Nil) -> todo 10841 + Ok(_) -> todo 10842 + } 10843 + }", 10844 + find_position_of("_").to_selection() 10845 + ); 10846 + } 10847 + 10848 + #[test] 10849 + fn cannot_pattern_match_on_discard_on_the_left_of_an_assignment() { 10850 + assert_no_code_actions!( 10851 + PATTERN_MATCH_ON_VALUE, 10852 + "pub fn main(x: Result(List(a), Nil)) { 10853 + let _ = x 10854 + }", 10855 + find_position_of("_").to_selection() 10856 + ); 10857 + } 10858 + 10859 + #[test] 10860 + fn cannot_pattern_match_on_discard_on_the_left_of_a_use() { 10861 + assert_no_code_actions!( 10862 + PATTERN_MATCH_ON_VALUE, 10863 + "pub fn main() { 10864 + use _ <- wibble() 10865 + } 10866 + 10867 + fn wibble(fun: fn(Result(Int, Nil)) -> Nil) { 10868 + todo 10869 + } 10870 + ", 10871 + find_position_of("_").to_selection() 10830 10872 ); 10831 10873 } 10832 10874
+22
language-server/src/tests/snapshots/gleam_language_server__tests__action__pattern_match_on_discard_pattern_in_branch.snap
··· 1 + --- 2 + source: language-server/src/tests/action.rs 3 + expression: "pub fn main(x: Result(List(a), Nil)) {\n case x {\n Error(Nil) -> todo\n Ok(_) -> todo\n }\n}" 4 + --- 5 + ----- BEFORE ACTION 6 + pub fn main(x: Result(List(a), Nil)) { 7 + case x { 8 + Error(Nil) -> todo 9 + Ok(_) -> todo 10 + 11 + } 12 + } 13 + 14 + 15 + ----- AFTER ACTION 16 + pub fn main(x: Result(List(a), Nil)) { 17 + case x { 18 + Error(Nil) -> todo 19 + Ok([]) -> todo 20 + Ok([first, ..rest]) -> todo 21 + } 22 + }