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

Configure Feed

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

add code action to replace underscore with corresponding type

author
Giacomo Cavalieri
committer
Louis Pilfold
date (Mar 19, 2026, 11:56 AM UTC) commit 505b260c parent f0b906ac change-id qmwlrqzw
+514 -54
+23
CHANGELOG.md
··· 17 17 branches. 18 18 ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) 19 19 20 + - The language server now has a code action to replace a `_` in a type 21 + annotation with the corresponding type. For example: 22 + 23 + ```gleam 24 + pub fn load_user(id: Int) -> Result(_, Error) { 25 + // ^ 26 + // Triggering the code action here 27 + sql.find_by_id(id) 28 + |> result.map_error(CannotLoadUser) 29 + } 30 + ``` 31 + 32 + Triggering the code action over the `_` will result in the following code: 33 + 34 + ```gleam 35 + pub fn load_user(id: Int) -> Result(User, Error) { 36 + sql.find_by_id(id) 37 + |> result.map_error(CannotLoadUser) 38 + } 39 + ``` 40 + 41 + ([Giacomo Cavalieri](https://github.com/giacomocavalieri)) 42 + 20 43 ### Formatter 21 44 22 45 ### Bug fixes
+118 -36
compiler-core/src/ast/visit.rs
··· 594 594 visit_typed_pattern_invalid(self, location, type_); 595 595 } 596 596 597 - fn visit_type_ast(&mut self, node: &'ast TypeAst) { 598 - visit_type_ast(self, node); 597 + fn visit_type_ast( 598 + &mut self, 599 + node: &'ast TypeAst, 600 + // The type inferred for this annotation. 601 + // It might not always be possible to infer one in presence of type 602 + // errors, so this is optional! 603 + inferred_type: Option<Arc<Type>>, 604 + ) { 605 + visit_type_ast(self, node, inferred_type); 599 606 } 600 607 601 608 fn visit_type_ast_constructor( ··· 604 611 name_location: &'ast SrcSpan, 605 612 module: &'ast Option<(EcoString, SrcSpan)>, 606 613 name: &'ast EcoString, 607 - arguments: &'ast Vec<TypeAst>, 614 + arguments: &'ast [TypeAst], 615 + inferred_arguments_types: Option<Vec<Arc<Type>>>, 608 616 ) { 609 - visit_type_ast_constructor(self, location, name_location, module, name, arguments); 617 + visit_type_ast_constructor( 618 + self, 619 + location, 620 + name_location, 621 + module, 622 + name, 623 + arguments, 624 + inferred_arguments_types, 625 + ); 610 626 } 611 627 612 628 fn visit_type_ast_fn( 613 629 &mut self, 614 630 location: &'ast SrcSpan, 615 - arguments: &'ast Vec<TypeAst>, 631 + arguments: &'ast [TypeAst], 632 + arguments_types: Option<Vec<Arc<Type>>>, 616 633 return_: &'ast TypeAst, 634 + return_type: Option<Arc<Type>>, 617 635 ) { 618 - visit_type_ast_fn(self, location, arguments, return_); 636 + visit_type_ast_fn( 637 + self, 638 + location, 639 + arguments, 640 + arguments_types, 641 + return_, 642 + return_type, 643 + ); 619 644 } 620 645 621 646 fn visit_type_ast_var(&mut self, location: &'ast SrcSpan, name: &'ast EcoString) { 622 647 visit_type_ast_var(self, location, name); 623 648 } 624 649 625 - fn visit_type_ast_tuple(&mut self, location: &'ast SrcSpan, elements: &'ast Vec<TypeAst>) { 626 - visit_type_ast_tuple(self, location, elements); 650 + fn visit_type_ast_tuple( 651 + &mut self, 652 + location: &'ast SrcSpan, 653 + elements: &'ast [TypeAst], 654 + elements_types: Option<&Vec<Arc<Type>>>, 655 + ) { 656 + visit_type_ast_tuple(self, location, elements, elements_types); 627 657 } 628 658 629 - fn visit_type_ast_hole(&mut self, location: &'ast SrcSpan, name: &'ast EcoString) { 630 - visit_type_ast_hole(self, location, name); 659 + fn visit_type_ast_hole( 660 + &mut self, 661 + location: &'ast SrcSpan, 662 + name: &'ast EcoString, 663 + type_: Option<Arc<Type>>, 664 + ) { 665 + visit_type_ast_hole(self, location, name, type_); 631 666 } 632 667 633 668 fn visit_typed_constant(&mut self, constant: &'ast TypedConstant) { ··· 925 960 { 926 961 for argument in fun.arguments.iter() { 927 962 if let Some(annotation) = &argument.annotation { 928 - v.visit_type_ast(annotation); 963 + v.visit_type_ast(annotation, Some(argument.type_.clone())); 929 964 } 930 965 } 931 966 if let Some(annotation) = &fun.return_annotation { 932 - v.visit_type_ast(annotation); 967 + v.visit_type_ast(annotation, Some(fun.return_type.clone())); 933 968 } 934 969 935 970 for statement in &fun.body { ··· 937 972 } 938 973 } 939 974 940 - pub fn visit_type_ast<'a, V>(v: &mut V, node: &'a TypeAst) 975 + pub fn visit_type_ast<'a, V>(v: &mut V, node: &'a TypeAst, type_: Option<Arc<Type>>) 941 976 where 942 977 V: Visit<'a> + ?Sized, 943 978 { ··· 950 985 name, 951 986 start_parentheses: _, 952 987 }) => { 953 - v.visit_type_ast_constructor(location, name_location, module, name, arguments); 988 + v.visit_type_ast_constructor( 989 + location, 990 + name_location, 991 + module, 992 + name, 993 + arguments, 994 + type_.and_then(|type_| type_.constructor_types()), 995 + ); 954 996 } 955 997 TypeAst::Fn(super::TypeAstFn { 956 998 location, 957 999 arguments, 958 1000 return_, 959 1001 }) => { 960 - v.visit_type_ast_fn(location, arguments, return_); 1002 + let (arguments_types, return_type) = match type_.and_then(|type_| type_.fn_types()) { 1003 + Some((arguments_types, return_type)) => (Some(arguments_types), Some(return_type)), 1004 + None => (None, None), 1005 + }; 1006 + 1007 + v.visit_type_ast_fn(location, arguments, arguments_types, return_, return_type); 961 1008 } 962 1009 TypeAst::Var(super::TypeAstVar { location, name }) => { 963 1010 v.visit_type_ast_var(location, name); 964 1011 } 965 1012 TypeAst::Tuple(super::TypeAstTuple { location, elements }) => { 966 - v.visit_type_ast_tuple(location, elements); 1013 + let elements_types = if let Some(Type::Tuple { elements }) = type_.as_deref() { 1014 + Some(elements) 1015 + } else { 1016 + None 1017 + }; 1018 + v.visit_type_ast_tuple(location, elements, elements_types); 967 1019 } 968 1020 TypeAst::Hole(super::TypeAstHole { location, name }) => { 969 - v.visit_type_ast_hole(location, name); 1021 + v.visit_type_ast_hole(location, name, type_); 970 1022 } 971 1023 } 972 1024 } ··· 977 1029 _name_location: &'a SrcSpan, 978 1030 _module: &'a Option<(EcoString, SrcSpan)>, 979 1031 _name: &'a EcoString, 980 - arguments: &'a Vec<TypeAst>, 1032 + arguments: &'a [TypeAst], 1033 + inferred_arguments_types: Option<Vec<Arc<Type>>>, 981 1034 ) where 982 1035 V: Visit<'a> + ?Sized, 983 1036 { 984 - for argument in arguments { 985 - v.visit_type_ast(argument); 1037 + for (i, argument) in arguments.iter().enumerate() { 1038 + let argument_type = inferred_arguments_types 1039 + .as_ref() 1040 + .and_then(|types| types.get(i)); 1041 + v.visit_type_ast(argument, argument_type.cloned()); 986 1042 } 987 1043 } 988 1044 989 1045 pub fn visit_type_ast_fn<'a, V>( 990 1046 v: &mut V, 991 1047 _location: &'a SrcSpan, 992 - arguments: &'a Vec<TypeAst>, 1048 + arguments: &'a [TypeAst], 1049 + arguments_types: Option<Vec<Arc<Type>>>, 993 1050 return_: &'a TypeAst, 1051 + return_type: Option<Arc<Type>>, 994 1052 ) where 995 1053 V: Visit<'a> + ?Sized, 996 1054 { 997 - for argument in arguments { 998 - v.visit_type_ast(argument); 1055 + for (i, argument) in arguments.iter().enumerate() { 1056 + v.visit_type_ast( 1057 + argument, 1058 + arguments_types 1059 + .as_ref() 1060 + .and_then(|types| types.get(i)) 1061 + .cloned(), 1062 + ); 999 1063 } 1000 - v.visit_type_ast(return_); 1064 + v.visit_type_ast(return_, return_type.clone()); 1001 1065 } 1002 1066 1003 1067 pub fn visit_type_ast_var<'a, V>(_v: &mut V, _location: &'a SrcSpan, _name: &'a EcoString) ··· 1007 1071 // No further traversal needed for variables 1008 1072 } 1009 1073 1010 - pub fn visit_type_ast_tuple<'a, V>(v: &mut V, _location: &'a SrcSpan, elements: &'a Vec<TypeAst>) 1011 - where 1074 + pub fn visit_type_ast_tuple<'a, V>( 1075 + v: &mut V, 1076 + _location: &'a SrcSpan, 1077 + elements: &'a [TypeAst], 1078 + elements_types: Option<&Vec<Arc<Type>>>, 1079 + ) where 1012 1080 V: Visit<'a> + ?Sized, 1013 1081 { 1014 - for element in elements { 1015 - v.visit_type_ast(element); 1082 + for (i, element) in elements.iter().enumerate() { 1083 + let element_type = elements_types 1084 + .as_ref() 1085 + .and_then(|types| types.get(i)) 1086 + .cloned(); 1087 + v.visit_type_ast(element, element_type); 1016 1088 } 1017 1089 } 1018 1090 1019 - pub fn visit_type_ast_hole<'a, V>(_v: &mut V, _location: &'a SrcSpan, _name: &'a EcoString) 1020 - where 1091 + pub fn visit_type_ast_hole<'a, V>( 1092 + _v: &mut V, 1093 + _location: &'a SrcSpan, 1094 + _name: &'a EcoString, 1095 + _type_: Option<Arc<Type>>, 1096 + ) where 1021 1097 V: Visit<'a> + ?Sized, 1022 1098 { 1023 1099 // No further traversal needed for holes ··· 1027 1103 where 1028 1104 V: Visit<'a> + ?Sized, 1029 1105 { 1106 + if let Some(annotation) = &constant.annotation { 1107 + v.visit_type_ast(annotation, Some(constant.type_.clone())); 1108 + } 1030 1109 v.visit_typed_constant(&constant.value); 1031 1110 } 1032 1111 ··· 1124 1203 { 1125 1204 for record in &custom_type.constructors { 1126 1205 for argument in &record.arguments { 1127 - v.visit_type_ast(&argument.ast); 1206 + v.visit_type_ast(&argument.ast, Some(argument.type_.clone())); 1128 1207 } 1129 1208 } 1130 1209 } ··· 1133 1212 where 1134 1213 V: Visit<'a> + ?Sized, 1135 1214 { 1136 - v.visit_type_ast(&type_alias.type_ast); 1215 + v.visit_type_ast(&type_alias.type_ast, Some(type_alias.type_.clone())); 1137 1216 } 1138 1217 1139 1218 pub fn visit_typed_import<'a, V>(_v: &mut V, _import: &'a TypedImport) ··· 1390 1469 pub fn visit_typed_expr_fn<'a, V>( 1391 1470 v: &mut V, 1392 1471 _location: &'a SrcSpan, 1393 - _type_: &'a Arc<Type>, 1472 + type_: &'a Arc<Type>, 1394 1473 _kind: &'a FunctionLiteralKind, 1395 1474 arguments: &'a [TypedArg], 1396 1475 body: &'a Vec1<TypedStatement>, ··· 1400 1479 { 1401 1480 for argument in arguments { 1402 1481 if let Some(annotation) = &argument.annotation { 1403 - v.visit_type_ast(annotation); 1482 + v.visit_type_ast(annotation, Some(argument.type_.clone())); 1404 1483 } 1405 1484 } 1406 1485 if let Some(return_) = return_annotation { 1407 - v.visit_type_ast(return_); 1486 + v.visit_type_ast( 1487 + return_, 1488 + type_.fn_types().map(|(_, return_)| return_.clone()), 1489 + ); 1408 1490 } 1409 1491 1410 1492 for statement in body { ··· 1643 1725 V: Visit<'a> + ?Sized, 1644 1726 { 1645 1727 if let Some(annotation) = &assignment.annotation { 1646 - v.visit_type_ast(annotation); 1728 + v.visit_type_ast(annotation, Some(assignment.type_())); 1647 1729 } 1648 1730 v.visit_typed_expr(&assignment.value); 1649 1731 v.visit_typed_pattern(&assignment.pattern);
+97 -4
language-server/src/code_action.rs
··· 1750 1750 name_location: &'ast SrcSpan, 1751 1751 module: &'ast Option<(EcoString, SrcSpan)>, 1752 1752 name: &'ast EcoString, 1753 - arguments: &'ast Vec<ast::TypeAst>, 1753 + arguments: &'ast [ast::TypeAst], 1754 + arguments_types: Option<Vec<Arc<Type>>>, 1754 1755 ) { 1755 1756 let range = src_span_to_lsp_range(*location, self.line_numbers); 1756 1757 if overlaps(self.params.range, range) ··· 1771 1772 module, 1772 1773 name, 1773 1774 arguments, 1775 + arguments_types, 1774 1776 ); 1775 1777 } 1776 1778 ··· 2001 2003 name_location: &'ast SrcSpan, 2002 2004 module: &'ast Option<(EcoString, SrcSpan)>, 2003 2005 name: &'ast EcoString, 2004 - arguments: &'ast Vec<ast::TypeAst>, 2006 + arguments: &'ast [ast::TypeAst], 2007 + arguments_types: Option<Vec<Arc<Type>>>, 2005 2008 ) { 2006 2009 if let Some((module_name, _)) = module { 2007 2010 let QualifiedConstructor { ··· 2022 2025 module, 2023 2026 name, 2024 2027 arguments, 2028 + arguments_types, 2025 2029 ); 2026 2030 } 2027 2031 ··· 2265 2269 name_location: &'ast SrcSpan, 2266 2270 module: &'ast Option<(EcoString, SrcSpan)>, 2267 2271 name: &'ast EcoString, 2268 - arguments: &'ast Vec<ast::TypeAst>, 2272 + arguments: &'ast [ast::TypeAst], 2273 + arguments_types: Option<Vec<Arc<Type>>>, 2269 2274 ) { 2270 2275 if module.is_none() 2271 2276 && overlaps( ··· 2283 2288 module, 2284 2289 name, 2285 2290 arguments, 2291 + arguments_types, 2286 2292 ); 2287 2293 } 2288 2294 ··· 2507 2513 name_location: &'ast SrcSpan, 2508 2514 module: &'ast Option<(EcoString, SrcSpan)>, 2509 2515 name: &'ast EcoString, 2510 - arguments: &'ast Vec<ast::TypeAst>, 2516 + arguments: &'ast [ast::TypeAst], 2517 + arguments_types: Option<Vec<Arc<Type>>>, 2511 2518 ) { 2512 2519 if module.is_none() { 2513 2520 let UnqualifiedConstructor { ··· 2524 2531 module, 2525 2532 name, 2526 2533 arguments, 2534 + arguments_types, 2527 2535 ); 2528 2536 } 2529 2537 ··· 10996 11004 } 10997 11005 } 10998 11006 } 11007 + 11008 + /// Code action to replace a `_` with its actual type in an annotation. 11009 + /// 11010 + /// Before: 11011 + /// ```gleam 11012 + /// fn wibble() -> Ok(_) { Ok(1) } 11013 + /// // ^ Trigger it here 11014 + /// ``` 11015 + /// 11016 + /// After: 11017 + /// ```gleam 11018 + /// fn wibble() -> Ok(Int) { Ok(1) } 11019 + /// ``` 11020 + /// 11021 + pub struct ReplaceUnderscoreWithType<'a> { 11022 + module: &'a Module, 11023 + params: &'a CodeActionParams, 11024 + edits: TextEdits<'a>, 11025 + hovered_hole: Option<HoveredHole>, 11026 + } 11027 + 11028 + struct HoveredHole { 11029 + type_: Arc<Type>, 11030 + location: SrcSpan, 11031 + } 11032 + 11033 + impl<'a> ReplaceUnderscoreWithType<'a> { 11034 + pub fn new( 11035 + module: &'a Module, 11036 + line_numbers: &'a LineNumbers, 11037 + params: &'a CodeActionParams, 11038 + ) -> Self { 11039 + Self { 11040 + module, 11041 + params, 11042 + edits: TextEdits::new(line_numbers), 11043 + hovered_hole: None, 11044 + } 11045 + } 11046 + 11047 + pub fn code_actions(mut self) -> Vec<CodeAction> { 11048 + self.visit_typed_module(&self.module.ast); 11049 + 11050 + let mut action = Vec::with_capacity(1); 11051 + 11052 + let Some(HoveredHole { type_, location }) = self.hovered_hole else { 11053 + return vec![]; 11054 + }; 11055 + let mut printer = Printer::new(&self.module.ast.names); 11056 + self.edits 11057 + .replace(location, format!("{}", printer.print_type(&type_))); 11058 + 11059 + CodeActionBuilder::new("Replace `_` with type") 11060 + .kind(CodeActionKind::QUICKFIX) 11061 + .changes(self.params.text_document.uri.clone(), self.edits.edits) 11062 + .preferred(true) 11063 + .push_to(&mut action); 11064 + action 11065 + } 11066 + } 11067 + 11068 + impl<'ast> ast::visit::Visit<'ast> for ReplaceUnderscoreWithType<'ast> { 11069 + fn visit_type_ast(&mut self, node: &'ast ast::TypeAst, inferred_type: Option<Arc<Type>>) { 11070 + // We never traverse a type annotation we're not hovering 11071 + let node_location = self.edits.src_span_to_lsp_range(node.location()); 11072 + if !within(self.params.range, node_location) { 11073 + return; 11074 + } 11075 + ast::visit::visit_type_ast(self, node, inferred_type); 11076 + } 11077 + 11078 + fn visit_type_ast_hole( 11079 + &mut self, 11080 + location: &'ast SrcSpan, 11081 + _name: &'ast EcoString, 11082 + type_: Option<Arc<Type>>, 11083 + ) { 11084 + if let Some(type_) = type_ { 11085 + self.hovered_hole = Some(HoveredHole { 11086 + type_, 11087 + location: *location, 11088 + }) 11089 + } 11090 + } 11091 + }
+6 -5
language-server/src/engine.rs
··· 32 32 }; 33 33 use std::{collections::HashSet, sync::Arc}; 34 34 35 - use crate::rename::rename_module_alias; 35 + use crate::{code_action::ReplaceUnderscoreWithType, rename::rename_module_alias}; 36 36 37 37 use super::{ 38 38 DownloadDependencies, MakeLocker, ··· 474 474 actions 475 475 .extend(AnnotateTopLevelDefinitions::new(module, &lines, &params).code_actions()); 476 476 actions.extend(AddMissingTypeParameter::new(module, &lines, &params).code_actions()); 477 + actions.extend(ReplaceUnderscoreWithType::new(module, &lines, &params).code_actions()); 477 478 Ok(if actions.is_empty() { 478 479 None 479 480 } else { ··· 1020 1021 .get_module_interface(module_name.as_str()) 1021 1022 .and_then(|module_interface| { 1022 1023 if is_type { 1023 - module_interface.types.get(name).map(|t| { 1024 + module_interface.types.get(name).map(|constructor| { 1024 1025 hover_for_annotation( 1025 1026 *location, 1026 - t.type_.as_ref(), 1027 - Some(t), 1027 + constructor.type_.as_ref(), 1028 + Some(constructor), 1028 1029 lines, 1029 1030 module, 1030 1031 ) ··· 1457 1458 ) -> Hover { 1458 1459 let empty_str = EcoString::from(""); 1459 1460 let documentation = type_constructor 1460 - .and_then(|t| t.documentation.as_ref()) 1461 + .and_then(|constructor| constructor.documentation.as_ref()) 1461 1462 .unwrap_or(&empty_str); 1462 1463 // If a user is hovering an annotation, it's not very useful to show the 1463 1464 // local representation of that type, since that's probably what they see
+3 -9
language-server/src/reference.rs
··· 920 920 name_location: &'ast SrcSpan, 921 921 module: &'ast Option<(EcoString, SrcSpan)>, 922 922 name: &'ast EcoString, 923 - arguments: &'ast Vec<ast::TypeAst>, 923 + arguments: &'ast [ast::TypeAst], 924 + arguments_types: Option<Vec<std::sync::Arc<Type>>>, 924 925 ) { 925 926 if let Some((module_alias, module_location)) = module 926 927 && module_alias == self.module_alias ··· 938 939 module, 939 940 name, 940 941 arguments, 942 + arguments_types, 941 943 ); 942 944 } 943 945 ··· 1007 1009 type_, 1008 1010 field_map, 1009 1011 ) 1010 - } 1011 - 1012 - fn visit_typed_module_constant(&mut self, constant: &'ast ast::TypedModuleConstant) { 1013 - if let Some(annotation) = &constant.annotation { 1014 - ast::visit::visit_type_ast(self, annotation); 1015 - } 1016 - 1017 - ast::visit::visit_typed_constant(self, &constant.value); 1018 1012 } 1019 1013 1020 1014 fn visit_typed_constant_var(
+105
language-server/src/tests/action.rs
··· 139 139 const EXTRACT_FUNCTION: &str = "Extract function"; 140 140 const MERGE_CASE_BRANCHES: &str = "Merge case branches"; 141 141 const ADD_MISSING_TYPE_PARAMETER: &str = "Add missing type parameter"; 142 + const REPLACE_UNDERSCORE_WITH_TYPE: &str = "Replace `_` with type"; 142 143 143 144 macro_rules! assert_code_action { 144 145 ($title:expr, $code:literal, $range:expr $(,)?) => { ··· 12701 12702 .select_until(find_position_of("}").nth_occurrence(2)) 12702 12703 ); 12703 12704 } 12705 + 12706 + #[test] 12707 + fn replace_underscore_with_function_return_type() { 12708 + assert_code_action!( 12709 + REPLACE_UNDERSCORE_WITH_TYPE, 12710 + r#" 12711 + pub fn wibble() -> _ { 12712 + 12 12713 + } 12714 + "#, 12715 + find_position_of("_").to_selection() 12716 + ); 12717 + } 12718 + 12719 + #[test] 12720 + fn replace_nested_underscore_with_generic_type() { 12721 + assert_code_action!( 12722 + REPLACE_UNDERSCORE_WITH_TYPE, 12723 + r#" 12724 + pub fn wibble() -> Result(a, _) { 12725 + Ok(todo) 12726 + } 12727 + "#, 12728 + find_position_of("_").to_selection() 12729 + ); 12730 + } 12731 + 12732 + #[test] 12733 + fn replace_nested_underscore_with_function_return_type() { 12734 + assert_code_action!( 12735 + REPLACE_UNDERSCORE_WITH_TYPE, 12736 + r#" 12737 + pub fn wibble() -> Result(Result(_, Nil), Int) { 12738 + Ok(Ok("hello")) 12739 + } 12740 + "#, 12741 + find_position_of("_").to_selection() 12742 + ); 12743 + } 12744 + 12745 + #[test] 12746 + fn replace_nested_underscore_in_let_annotation() { 12747 + assert_code_action!( 12748 + REPLACE_UNDERSCORE_WITH_TYPE, 12749 + r#" 12750 + pub fn wibble() { 12751 + let a: Result(Result(_, Nil), Nil) = Ok(Ok("hello")) 12752 + } 12753 + "#, 12754 + find_position_of("_").to_selection() 12755 + ); 12756 + } 12757 + 12758 + #[test] 12759 + fn replace_underscore_in_let_annotation() { 12760 + assert_code_action!( 12761 + REPLACE_UNDERSCORE_WITH_TYPE, 12762 + r#" 12763 + pub fn wibble() { 12764 + let a: _ = Ok(Ok("hello")) 12765 + } 12766 + "#, 12767 + find_position_of("_").to_selection() 12768 + ); 12769 + } 12770 + 12771 + #[test] 12772 + fn replace_nested_underscore_with_tuple_type() { 12773 + assert_code_action!( 12774 + REPLACE_UNDERSCORE_WITH_TYPE, 12775 + r#" 12776 + pub fn wibble() { 12777 + let a: #(Int, _, Int) = #(1, "hello", 2) 12778 + } 12779 + "#, 12780 + find_position_of("_").to_selection() 12781 + ); 12782 + } 12783 + 12784 + #[test] 12785 + fn replace_underscore_in_function_argument() { 12786 + assert_code_action!( 12787 + REPLACE_UNDERSCORE_WITH_TYPE, 12788 + r#" 12789 + pub fn wibble(a: _) -> Int { 12790 + a + 2 12791 + } 12792 + "#, 12793 + find_position_of("_").to_selection() 12794 + ); 12795 + } 12796 + 12797 + #[test] 12798 + fn replace_underscore_in_fn_expr_argument() { 12799 + assert_code_action!( 12800 + REPLACE_UNDERSCORE_WITH_TYPE, 12801 + r#" 12802 + pub fn wibble() { 12803 + fn(a: _) { a + 2 } 12804 + } 12805 + "#, 12806 + find_position_of("_").to_selection() 12807 + ); 12808 + }
+18
language-server/src/tests/snapshots/gleam_language_server__tests__action__replace_nested_underscore_in_let_annotation.snap
··· 1 + --- 2 + source: language-server/src/tests/action.rs 3 + expression: "\npub fn wibble() {\n let a: Result(Result(_, Nil), Nil) = Ok(Ok(\"hello\"))\n}\n " 4 + --- 5 + ----- BEFORE ACTION 6 + 7 + pub fn wibble() { 8 + let a: Result(Result(_, Nil), Nil) = Ok(Ok("hello")) 9 + 10 + } 11 + 12 + 13 + 14 + ----- AFTER ACTION 15 + 16 + pub fn wibble() { 17 + let a: Result(Result(String, Nil), Nil) = Ok(Ok("hello")) 18 + }
+18
language-server/src/tests/snapshots/gleam_language_server__tests__action__replace_nested_underscore_with_function_return_type.snap
··· 1 + --- 2 + source: language-server/src/tests/action.rs 3 + expression: "\npub fn wibble() -> Result(Result(_, Nil), Int) {\n Ok(Ok(\"hello\"))\n}\n " 4 + --- 5 + ----- BEFORE ACTION 6 + 7 + pub fn wibble() -> Result(Result(_, Nil), Int) { 8 + 9 + Ok(Ok("hello")) 10 + } 11 + 12 + 13 + 14 + ----- AFTER ACTION 15 + 16 + pub fn wibble() -> Result(Result(String, Nil), Int) { 17 + Ok(Ok("hello")) 18 + }
+18
language-server/src/tests/snapshots/gleam_language_server__tests__action__replace_nested_underscore_with_generic_type.snap
··· 1 + --- 2 + source: language-server/src/tests/action.rs 3 + expression: "\npub fn wibble() -> Result(a, _) {\n Ok(todo)\n}\n " 4 + --- 5 + ----- BEFORE ACTION 6 + 7 + pub fn wibble() -> Result(a, _) { 8 + 9 + Ok(todo) 10 + } 11 + 12 + 13 + 14 + ----- AFTER ACTION 15 + 16 + pub fn wibble() -> Result(a, b) { 17 + Ok(todo) 18 + }
+18
language-server/src/tests/snapshots/gleam_language_server__tests__action__replace_nested_underscore_with_tuple_type.snap
··· 1 + --- 2 + source: language-server/src/tests/action.rs 3 + expression: "\npub fn wibble() {\n let a: #(Int, _, Int) = #(1, \"hello\", 2)\n}\n " 4 + --- 5 + ----- BEFORE ACTION 6 + 7 + pub fn wibble() { 8 + let a: #(Int, _, Int) = #(1, "hello", 2) 9 + 10 + } 11 + 12 + 13 + 14 + ----- AFTER ACTION 15 + 16 + pub fn wibble() { 17 + let a: #(Int, String, Int) = #(1, "hello", 2) 18 + }
+18
language-server/src/tests/snapshots/gleam_language_server__tests__action__replace_underscore_in_fn_expr_argument.snap
··· 1 + --- 2 + source: language-server/src/tests/action.rs 3 + expression: "\npub fn wibble() {\n fn(a: _) { a + 2 }\n}\n " 4 + --- 5 + ----- BEFORE ACTION 6 + 7 + pub fn wibble() { 8 + fn(a: _) { a + 2 } 9 + 10 + } 11 + 12 + 13 + 14 + ----- AFTER ACTION 15 + 16 + pub fn wibble() { 17 + fn(a: Int) { a + 2 } 18 + }
+18
language-server/src/tests/snapshots/gleam_language_server__tests__action__replace_underscore_in_function_argument.snap
··· 1 + --- 2 + source: language-server/src/tests/action.rs 3 + expression: "\npub fn wibble(a: _) -> Int {\n a + 2\n}\n " 4 + --- 5 + ----- BEFORE ACTION 6 + 7 + pub fn wibble(a: _) -> Int { 8 + 9 + a + 2 10 + } 11 + 12 + 13 + 14 + ----- AFTER ACTION 15 + 16 + pub fn wibble(a: Int) -> Int { 17 + a + 2 18 + }
+18
language-server/src/tests/snapshots/gleam_language_server__tests__action__replace_underscore_in_let_annotation.snap
··· 1 + --- 2 + source: language-server/src/tests/action.rs 3 + expression: "\npub fn wibble() {\n let a: _ = Ok(Ok(\"hello\"))\n}\n " 4 + --- 5 + ----- BEFORE ACTION 6 + 7 + pub fn wibble() { 8 + let a: _ = Ok(Ok("hello")) 9 + 10 + } 11 + 12 + 13 + 14 + ----- AFTER ACTION 15 + 16 + pub fn wibble() { 17 + let a: Result(Result(String, a), b) = Ok(Ok("hello")) 18 + }
+18
language-server/src/tests/snapshots/gleam_language_server__tests__action__replace_underscore_with_function_return_type.snap
··· 1 + --- 2 + source: language-server/src/tests/action.rs 3 + expression: "\npub fn wibble() -> _ {\n 12\n}\n " 4 + --- 5 + ----- BEFORE ACTION 6 + 7 + pub fn wibble() -> _ { 8 + 9 + 12 10 + } 11 + 12 + 13 + 14 + ----- AFTER ACTION 15 + 16 + pub fn wibble() -> Int { 17 + 12 18 + }
+18
language-server/src/tests/snapshots/gleam_language_server__tests__action__replace_underscore_with_type.snap
··· 1 + --- 2 + source: language-server/src/tests/action.rs 3 + expression: "\npub fn wibble() -> Result(Result(_, Nil), Int) {\n Ok(Ok(\"hello\"))\n}\n " 4 + --- 5 + ----- BEFORE ACTION 6 + 7 + pub fn wibble() -> Result(Result(_, Nil), Int) { 8 + 9 + Ok(Ok("hello")) 10 + } 11 + 12 + 13 + 14 + ----- AFTER ACTION 15 + 16 + pub fn wibble() -> Result(Result(String, Nil), Int) { 17 + Ok(Ok("hello")) 18 + }