Fork of daniellemaywood.uk/gleam — Wasm codegen work
294 kB
14340 lines
1use itertools::Itertools;
2use lsp_types::{
3 CodeActionContext, CodeActionParams, DocumentChangeOperation, DocumentChanges,
4 PartialResultParams, Position, ResourceOp, Url, WorkDoneProgressParams,
5};
6
7use super::*;
8use crate::path;
9
10fn code_actions(
11 tester: &TestProject<'_>,
12 origin: Origin,
13 module: &str,
14 range_selector: RangeSelector,
15) -> Vec<lsp_types::CodeAction> {
16 let position = Position {
17 line: 0,
18 character: 0,
19 };
20
21 tester.in_module_at(origin, module, position, |engine, params, code| {
22 let range = range_selector.find_range(&code);
23 let params = CodeActionParams {
24 text_document: params.text_document,
25 range,
26 context: CodeActionContext::default(),
27 work_done_progress_params: WorkDoneProgressParams::default(),
28 partial_result_params: PartialResultParams::default(),
29 };
30 engine
31 .code_actions(params)
32 .result
33 .unwrap()
34 .unwrap_or_default()
35 })
36}
37
38fn actions_with_title(
39 titles: Vec<&str>,
40 tester: &TestProject<'_>,
41 origin: Origin,
42 module: &str,
43 range_selector: RangeSelector,
44) -> Vec<lsp_types::CodeAction> {
45 code_actions(tester, origin, module, range_selector)
46 .into_iter()
47 .filter(|action| titles.contains(&action.title.as_str()))
48 .collect_vec()
49}
50
51fn apply_code_action(
52 title: &str,
53 tester: &TestProject<'_>,
54 origin: Origin,
55 module: &str,
56 range_selector: RangeSelector,
57) -> (String, String) {
58 let titles = vec![title];
59 let actions = actions_with_title(titles, &tester, origin, module, range_selector);
60 let changes = actions
61 .last()
62 .expect("No action with the given title")
63 .edit
64 .as_ref()
65 .and_then(|edit| edit.changes.as_ref())
66 .cloned()
67 .unwrap_or_default();
68 let code_change = apply_code_edit(tester, changes);
69 let file_operations = format_code_action_file_operations(&actions);
70 (code_change, file_operations)
71}
72
73fn apply_code_edit(
74 tester: &TestProject<'_>,
75 changes: HashMap<Url, Vec<lsp_types::TextEdit>>,
76) -> String {
77 let mut changed_files: HashMap<Url, String> = HashMap::new();
78 for (uri, change) in changes {
79 let code = match changed_files.get(&uri) {
80 Some(code) => code,
81 None => tester
82 .src_from_module_url(&uri)
83 .unwrap_or_else(|| panic!("no src for url {uri:?}")),
84 };
85 let code = super::apply_code_edit(code, change);
86 let _ = changed_files.insert(uri, code);
87 }
88
89 show_code_edits(tester, changed_files)
90}
91
92fn show_code_edits(tester: &TestProject<'_>, changed_files: HashMap<Url, String>) -> String {
93 let format_code = |url: &Url, code: &String| {
94 format!(
95 "// --- Edits applied to module '{}'\n{}",
96 tester.module_name_from_url(url).expect("a module"),
97 code
98 )
99 };
100
101 // If the file that changed is the main one we just show its code.
102 if changed_files.len() == 1 {
103 let mut changed = changed_files.iter().peekable();
104 let (url, code) = changed.peek().unwrap();
105 if tester.module_name_from_url(url) == Some("app".into()) {
106 code.to_string()
107 } else {
108 format_code(url, code)
109 }
110 } else {
111 // If more than a single file changed we want to add the name of the
112 // file before each!
113 changed_files
114 .iter()
115 .map(|(url, code)| format_code(url, code))
116 .join("\n")
117 }
118}
119
120fn format_code_action_file_operations<'a>(actions: &[lsp_types::CodeAction]) -> String {
121 // Display path the same on linux or windows so tests don't fail between targets
122 let normalized_path = |uri: &Url| {
123 format!(
124 "/{}",
125 path(uri)
126 .components()
127 .filter(|component| match component {
128 camino::Utf8Component::Prefix(_) | camino::Utf8Component::RootDir => false,
129 camino::Utf8Component::CurDir
130 | camino::Utf8Component::ParentDir
131 | camino::Utf8Component::Normal(_) => true,
132 })
133 .join("/")
134 )
135 };
136
137 actions
138 .iter()
139 .filter_map(|action| {
140 if let Some(DocumentChanges::Operations(operations)) = action
141 .edit
142 .as_ref()
143 .and_then(|edit| edit.document_changes.as_ref())
144 {
145 Some(operations)
146 } else {
147 None
148 }
149 })
150 .flat_map(|operations| {
151 operations.into_iter().filter_map(|op| match op {
152 DocumentChangeOperation::Op(op) => Some(op),
153 DocumentChangeOperation::Edit(_) => None,
154 })
155 })
156 .map(|op| match op {
157 ResourceOp::Create(create) => {
158 format!("- Create {}", normalized_path(&create.uri))
159 }
160 ResourceOp::Rename(rename) => {
161 format!(
162 "- Rename {} to {}",
163 normalized_path(&rename.old_uri),
164 normalized_path(&rename.new_uri)
165 )
166 }
167 ResourceOp::Delete(delete) => {
168 format!("- Delete {}", normalized_path(&delete.uri))
169 }
170 })
171 .join("\n")
172}
173
174const REMOVE_UNUSED_IMPORTS: &str = "Remove unused imports";
175const REMOVE_REDUNDANT_TUPLES: &str = "Remove redundant tuples";
176const CONVERT_TO_CASE: &str = "Convert to case";
177const USE_LABEL_SHORTHAND_SYNTAX: &str = "Use label shorthand syntax";
178const FILL_LABELS: &str = "Fill labels";
179const ASSIGN_UNUSED_RESULT: &str = "Assign unused Result value to `_`";
180const ADD_MISSING_PATTERNS: &str = "Add missing patterns";
181const ADD_ANNOTATION: &str = "Add type annotation";
182const ADD_ANNOTATIONS: &str = "Add type annotations";
183const ANNOTATE_TOP_LEVEL_DEFINITIONS: &str = "Annotate all top level definitions";
184const CONVERT_FROM_USE: &str = "Convert from `use`";
185const CONVERT_TO_USE: &str = "Convert to `use`";
186const EXTRACT_VARIABLE: &str = "Extract variable";
187const EXTRACT_CONSTANT: &str = "Extract constant";
188const EXPAND_FUNCTION_CAPTURE: &str = "Expand function capture";
189const GENERATE_DYNAMIC_DECODER: &str = "Generate dynamic decoder";
190const GENERATE_TO_JSON_FUNCTION: &str = "Generate to-JSON function";
191const PATTERN_MATCH_ON_ARGUMENT: &str = "Pattern match on argument";
192const PATTERN_MATCH_ON_VARIABLE: &str = "Pattern match on variable";
193const GENERATE_FUNCTION: &str = "Generate function";
194const CONVERT_TO_FUNCTION_CALL: &str = "Convert to function call";
195const INLINE_VARIABLE: &str = "Inline variable";
196const CONVERT_TO_PIPE: &str = "Convert to pipe";
197const INTERPOLATE_STRING: &str = "Interpolate string";
198const FILL_UNUSED_FIELDS: &str = "Fill unused fields";
199const REMOVE_ALL_ECHOS_FROM_THIS_MODULE: &str = "Remove all `echo`s from this module";
200const WRAP_IN_BLOCK: &str = "Wrap in block";
201const GENERATE_VARIANT: &str = "Generate variant";
202const REMOVE_BLOCK: &str = "Remove block";
203const REMOVE_OPAQUE_FROM_PRIVATE_TYPE: &str = "Remove opaque from private type";
204const COLLAPSE_NESTED_CASE: &str = "Collapse nested case";
205const REMOVE_UNREACHABLE_CLAUSES: &str = "Remove unreachable clauses";
206const ADD_OMITTED_LABELS: &str = "Add omitted labels";
207const EXTRACT_FUNCTION: &str = "Extract function";
208const MERGE_CASE_BRANCHES: &str = "Merge case branches";
209const ADD_MISSING_TYPE_PARAMETER: &str = "Add missing type parameter";
210const REPLACE_UNDERSCORE_WITH_TYPE: &str = "Replace `_` with type";
211const WRAP_IN_ANONYMOUS_FUNCTION: &str = "Wrap in anonymous function";
212const UNWRAP_ANONYMOUS_FUNCTION: &str = "Remove anonymous function wrapper";
213const REMOVE_REDUNDANT_RECORD_UPDATE: &str = "Remove redundant record update";
214
215macro_rules! assert_code_action {
216 ($title:expr, $code:literal, $range_selector:expr $(,)?) => {
217 let project = TestProject::for_source($code);
218 assert_code_action!($title, project, $range_selector);
219 };
220
221 ($title:expr, $project:expr, $range_selector:expr $(,)?) => {
222 assert_code_action!(
223 $title,
224 $project,
225 Origin::Src,
226 LSP_TEST_ROOT_PACKAGE_NAME,
227 $range_selector
228 );
229 };
230
231 ($title:expr, $code:literal, $origin:expr, $module:expr, $range_selector:expr $(,)?) => {
232 let project = TestProject::for_source($code);
233 assert_code_action!($title, project, $origin, $module, $range_selector);
234 };
235
236 ($title:expr, $project:expr, $origin:expr, $module:expr, $range_selector:expr $(,)?) => {
237 let project = &$project;
238 let src = project
239 .src_from_origin_and_module_name($origin, $module)
240 .unwrap();
241 let range = $range_selector.find_range(src);
242 let (updated_src, file_operations) =
243 apply_code_action($title, project, $origin, $module, $range_selector);
244
245 let mut output = format!(
246 "----- BEFORE ACTION\n{}\n\n----- AFTER ACTION\n{}",
247 hover::show_hover(src, range, range.end),
248 updated_src
249 );
250
251 if !file_operations.is_empty() {
252 output.push_str(&format!("\n----- FILE OPERATIONS -----\n{file_operations}"));
253 }
254
255 insta::assert_snapshot!(insta::internals::AutoName, output, src);
256 };
257}
258
259macro_rules! assert_no_code_actions {
260 ($title:ident $(| $titles:ident)*, $code:literal, $range_selector:expr $(,)?) => {
261 let project = TestProject::for_source($code);
262 assert_no_code_actions!($title $(| $titles)*, project, $range_selector);
263 };
264
265 ($title:ident $(| $titles:ident)*, $project:expr, $range_selector:expr $(,)?) => {
266 let all_titles = vec![$title $(, $titles)*];
267 let expected: Vec<lsp_types::CodeAction> = vec![];
268 let result = actions_with_title(
269 all_titles,
270 &$project,
271 Origin::Src,
272 LSP_TEST_ROOT_PACKAGE_NAME,
273 $range_selector
274 );
275 assert_eq!(expected, result);
276 };
277
278 ($title:literal $(| $titles:literal)*, $code:literal, $range_selector:expr $(,)?) => {
279 let project = TestProject::for_source($code);
280 assert_no_code_actions!($title $(| $titles)*, project, $range_selector);
281 };
282
283 ($title:literal $(| $titles:literal)*, $project:expr, $range_selector:expr $(,)?) => {
284 let all_titles = vec![$title $(, $titles)*];
285 let expected: Vec<lsp_types::CodeAction> = vec![];
286 let result = actions_with_title(
287 all_titles,
288 &$project,
289 Origin::Src,
290 LSP_TEST_ROOT_PACKAGE_NAME,
291 $range_selector
292 );
293 assert_eq!(expected, result);
294 };
295}
296
297#[test]
298fn fix_truncated_segment_1() {
299 let name = "Replace with `1`";
300 assert_code_action!(
301 name,
302 r#"
303pub fn main() {
304 <<1, 257, 259:size(1)>>
305}"#,
306 find_position_of("257").to_selection()
307 );
308}
309
310#[test]
311fn fix_truncated_segment_2() {
312 let name = "Replace with `0`";
313 assert_code_action!(
314 name,
315 r#"
316pub fn main() {
317 <<1, 1024:size(10)>>
318}"#,
319 find_position_of("size").to_selection()
320 );
321}
322
323#[test]
324fn generate_variant_with_fields_in_same_module() {
325 assert_code_action!(
326 GENERATE_VARIANT,
327 r#"
328pub type Wibble {
329 Wibble
330}
331
332pub fn main() -> Wibble {
333 Wobble(1)
334}"#,
335 find_position_of("Wobble").to_selection()
336 );
337}
338
339#[test]
340fn generate_variant_with_no_fields_in_same_module() {
341 assert_code_action!(
342 GENERATE_VARIANT,
343 r#"
344pub type Wibble {
345 Wibble
346}
347
348pub fn main() -> Wibble {
349 Wobble
350}"#,
351 find_position_of("Wobble").to_selection()
352 );
353}
354
355#[test]
356fn generate_variant_with_labels_in_same_module() {
357 assert_code_action!(
358 GENERATE_VARIANT,
359 r#"
360pub type Wibble {
361 Wibble
362}
363
364pub fn main() -> Wibble {
365 Wobble("hello", label: 1)
366}"#,
367 find_position_of("Wobble").to_selection()
368 );
369}
370
371#[test]
372fn generate_variant_from_pattern_with_fields() {
373 assert_code_action!(
374 GENERATE_VARIANT,
375 r#"
376pub type Wibble {
377 Wibble
378}
379
380pub fn new() { Wibble }
381
382pub fn main() -> Wibble {
383 let assert Wobble(1) = new()
384}
385
386"#,
387 find_position_of("Wobble").to_selection()
388 );
389}
390
391#[test]
392fn generate_variant_from_pattern_with_labelled_fields() {
393 assert_code_action!(
394 GENERATE_VARIANT,
395 r#"
396pub type Wibble {
397 Wibble
398}
399
400pub fn new() { Wibble }
401
402pub fn main() -> Wibble {
403 let assert Wobble("hello", label: 1) = new()
404}
405
406"#,
407 find_position_of("Wobble").to_selection()
408 );
409}
410
411#[test]
412fn generate_variant_from_pattern_with_no_fields() {
413 assert_code_action!(
414 GENERATE_VARIANT,
415 r#"
416pub type Wibble {
417 Wibble
418}
419
420pub fn new() { Wibble }
421
422pub fn main() -> Wibble {
423 let assert Wobble = new()
424}
425
426"#,
427 find_position_of("Wobble").to_selection()
428 );
429}
430
431#[test]
432fn generate_unqualified_variant_in_other_module() {
433 let src = r#"
434import other
435
436pub fn main() -> other.Wibble {
437 let assert Wobble = new()
438}
439
440pub fn new() -> other.Wibble { todo }
441"#;
442
443 assert_code_action!(
444 GENERATE_VARIANT,
445 TestProject::for_source(src).add_module("other", "pub type Wibble"),
446 find_position_of("Wobble").to_selection()
447 );
448}
449
450#[test]
451fn generate_qualified_variant_in_other_module() {
452 let src = r#"
453import other
454
455pub fn main() -> other.Wibble {
456 let assert other.Wobble = new()
457}
458
459pub fn new() -> other.Wibble { todo }
460"#;
461 assert_code_action!(
462 GENERATE_VARIANT,
463 TestProject::for_source(src).add_module("other", "pub type Wibble"),
464 find_position_of("Wobble").to_selection()
465 );
466}
467
468#[test]
469fn do_not_generate_variant_if_one_with_the_same_name_exists() {
470 assert_no_code_actions!(
471 GENERATE_VARIANT,
472 r#"
473pub fn main() -> Wibble {
474 let assert Wobble = new()
475}
476
477pub type Wibble {
478 Wobble(n: Int)
479}
480
481pub fn new() -> Wibble { todo }
482"#,
483 find_position_of("Wobble").to_selection()
484 );
485}
486
487#[test]
488fn do_not_generate_variant_if_one_with_the_same_name_exists_in_other_module() {
489 let src = r#"
490import other.{type Wibble}
491
492pub fn main() -> Wibble {
493 let assert Wobble = new()
494}
495
496pub fn new() -> Wibble { todo }
497"#;
498 assert_no_code_actions!(
499 GENERATE_VARIANT,
500 TestProject::for_source(src).add_module("other", "pub type Wibble { Wobble(String) }"),
501 find_position_of("Wobble").to_selection()
502 );
503}
504
505#[test]
506fn do_not_generate_qualified_variant_if_one_with_the_same_name_exists_in_other_module() {
507 let src = r#"
508import other.{type Wibble}
509
510pub fn main() -> Wibble {
511 let assert other.Wobble = new()
512}
513
514pub fn new() -> Wibble { todo }
515"#;
516 assert_no_code_actions!(
517 GENERATE_VARIANT,
518 TestProject::for_source(src).add_module("other", "pub type Wibble { Wobble(String) }"),
519 find_position_of("Wobble").to_selection()
520 );
521}
522
523#[test]
524fn fill_unused_fields_with_ignored_labelled_fields() {
525 assert_code_action!(
526 FILL_UNUSED_FIELDS,
527 r#"
528pub type Wibble { Wibble(Int, label1: String, label2: Int) }
529
530pub fn main() {
531 let Wibble(_, ..) = todo
532}"#,
533 find_position_of("..").to_selection()
534 );
535}
536
537#[test]
538fn fill_unused_fields_with_ignored_positional_fields() {
539 assert_code_action!(
540 FILL_UNUSED_FIELDS,
541 r#"
542pub type Wibble { Wibble(Int, label1: String, label2: Int) }
543
544pub fn main() {
545 let Wibble(label1:, label2:, ..) = todo
546}"#,
547 find_position_of("..").to_selection()
548 );
549}
550
551#[test]
552fn fill_unused_fields_with_all_positional_fields() {
553 assert_code_action!(
554 FILL_UNUSED_FIELDS,
555 r#"
556pub type Wibble { Wibble(Int, String) }
557
558pub fn main() {
559 let Wibble(..) = todo
560}"#,
561 find_position_of("..").to_selection()
562 );
563}
564
565#[test]
566fn fill_unused_fields_with_ignored_mixed_fields() {
567 assert_code_action!(
568 FILL_UNUSED_FIELDS,
569 r#"
570pub type Wibble { Wibble(Int, String, label1: String, label2: Int) }
571
572pub fn main() {
573 let Wibble(_, label2:, ..) = todo
574}"#,
575 find_position_of("..").to_selection()
576 );
577}
578
579#[test]
580fn fill_unused_fields_with_all_ignored_fields() {
581 assert_code_action!(
582 FILL_UNUSED_FIELDS,
583 r#"
584pub type Wibble { Wibble(Int, label1: String, label2: Int) }
585
586pub fn main() {
587 let Wibble(..) = todo
588}"#,
589 find_position_of("..").to_selection()
590 );
591}
592
593#[test]
594fn fill_unused_fields_with_ignored_fields_never_calls_a_positional_arg_as_a_labelled_one() {
595 assert_code_action!(
596 FILL_UNUSED_FIELDS,
597 r#"
598pub type Wibble { Wibble(Int, int: Int) }
599
600pub fn main() {
601 let Wibble(..) = todo
602}"#,
603 find_position_of("..").to_selection()
604 );
605}
606
607#[test]
608fn remove_echo() {
609 assert_code_action!(
610 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
611 "pub fn main() {
612 echo 1 + 2
613}",
614 find_position_of("echo").to_selection()
615 );
616}
617
618#[test]
619fn remove_echo_with_message() {
620 assert_code_action!(
621 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
622 r#"pub fn main() {
623 echo 1 + 2 as "message"
624}"#,
625 find_position_of("echo").to_selection()
626 );
627}
628
629#[test]
630fn remove_echo_with_message_and_comment() {
631 assert_code_action!(
632 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
633 r#"pub fn main() {
634 echo 1 + 2
635 // Hello!
636 as "message"
637}"#,
638 find_position_of("echo").to_selection()
639 );
640}
641
642#[test]
643fn remove_echo_with_message_and_comment_2() {
644 assert_code_action!(
645 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
646 r#"pub fn main() {
647 echo 1 + 2 as
648 // Hello!
649 "message"
650}"#,
651 find_position_of("echo").to_selection()
652 );
653}
654
655#[test]
656fn remove_echo_with_message_and_comment_3() {
657 assert_code_action!(
658 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
659 r#"pub fn main() {
660 echo 1 + 2 as
661 // Hello!
662 "message"
663
664 Nil
665}"#,
666 find_position_of("echo").to_selection()
667 );
668}
669
670#[test]
671fn remove_echo_selecting_expression() {
672 assert_code_action!(
673 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
674 "pub fn main() {
675 echo 1 + 2
676}",
677 find_position_of("1").select_until(find_position_of("2"))
678 );
679}
680
681#[test]
682fn remove_echo_selecting_message() {
683 assert_code_action!(
684 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
685 r#"pub fn main() {
686 echo 1 + 2 as "message"
687}"#,
688 find_position_of("message").to_selection()
689 );
690}
691
692#[test]
693fn remove_echo_as_function_arg() {
694 assert_code_action!(
695 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
696 "pub fn main() {
697 wibble([], echo 1 + 2)
698}",
699 find_position_of("1").to_selection()
700 );
701}
702
703#[test]
704fn remove_echo_in_pipeline_step() {
705 assert_code_action!(
706 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
707 "pub fn main() {
708 [1, 2, 3]
709 |> echo
710 |> wibble
711}",
712 find_position_of("echo").to_selection()
713 );
714}
715
716#[test]
717fn remove_echo_in_pipeline_step_with_message() {
718 assert_code_action!(
719 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
720 r#"pub fn main() {
721 [1, 2, 3]
722 |> echo as message
723 |> wibble
724}"#,
725 find_position_of("echo").to_selection()
726 );
727}
728
729#[test]
730fn remove_echo_in_single_line_pipeline_step() {
731 assert_code_action!(
732 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
733 "pub fn main() {
734 [1, 2, 3] |> echo |> wibble
735}",
736 find_position_of("echo").to_selection()
737 );
738}
739
740#[test]
741fn remove_echo_in_single_line_pipeline_step_with_message() {
742 assert_code_action!(
743 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
744 r#"pub fn main() {
745 [1, 2, 3] |> echo as "message" |> wibble
746}"#,
747 find_position_of("echo").to_selection()
748 );
749}
750
751#[test]
752fn remove_echo_last_in_long_pipeline_step() {
753 assert_code_action!(
754 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
755 "pub fn main() {
756 [1, 2, 3]
757 |> wibble
758 |> echo
759}",
760 find_position_of("echo").to_selection()
761 );
762}
763
764#[test]
765fn remove_echo_last_in_long_pipeline_step_with_message() {
766 assert_code_action!(
767 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
768 r#"pub fn main() {
769 [1, 2, 3]
770 |> wibble
771 |> echo as "message"
772}"#,
773 find_position_of("echo").to_selection()
774 );
775}
776
777#[test]
778fn remove_echo_last_in_short_pipeline_step() {
779 assert_code_action!(
780 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
781 "pub fn main() {
782 [1, 2, 3]
783 |> echo
784}",
785 find_position_of("echo").to_selection()
786 );
787}
788
789#[test]
790fn remove_echo_last_in_short_pipeline_step_with_message() {
791 assert_code_action!(
792 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
793 r#"pub fn main() {
794 [1, 2, 3]
795 |> echo as "message"
796}"#,
797 find_position_of("echo").to_selection()
798 );
799}
800
801#[test]
802fn remove_echo_before_pipeline() {
803 assert_code_action!(
804 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
805 "pub fn main() {
806 echo [1, 2, 3] |> wibble
807}",
808 find_position_of("echo").to_selection()
809 );
810}
811
812#[test]
813fn remove_echo_before_pipeline_selecting_step() {
814 assert_code_action!(
815 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
816 "pub fn main() {
817 echo [1, 2, 3] |> wibble
818}",
819 find_position_of("wibble").to_selection()
820 );
821}
822
823#[test]
824fn remove_echo_removes_all_echos() {
825 assert_code_action!(
826 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
827 "pub fn main() {
828 echo wibble(echo 1, 2)
829}",
830 find_position_of("echo").nth_occurrence(2).to_selection()
831 );
832}
833
834#[test]
835fn remove_echo_removes_all_echos_1() {
836 assert_code_action!(
837 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
838 "pub fn main() {
839 echo 1 |> echo |> echo |> wibble |> echo
840 echo wibble(echo 1, echo 2)
841 echo 1
842}",
843 find_position_of("echo").nth_occurrence(2).to_selection()
844 );
845}
846
847#[test]
848fn remove_echo_removes_entire_echo_statement_used_with_literals() {
849 assert_code_action!(
850 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
851 "pub fn main() {
852 echo 1
853 Nil
854}",
855 find_position_of("echo").to_selection()
856 );
857}
858
859#[test]
860fn remove_echo_removes_entire_echo_statement_used_with_literals_and_message() {
861 assert_code_action!(
862 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
863 r#"pub fn main() {
864 echo 1 as "message"
865 Nil
866}"#,
867 find_position_of("echo").to_selection()
868 );
869}
870
871#[test]
872fn remove_echo_removes_entire_echo_statement_used_with_a_var() {
873 assert_code_action!(
874 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
875 "pub fn main() {
876 let a = 1
877 echo a
878 Nil
879}",
880 find_position_of("echo").to_selection()
881 );
882}
883
884#[test]
885fn remove_echo_removes_multiple_entire_echo_statement_used_with_literals() {
886 assert_code_action!(
887 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
888 r#"pub fn main() {
889 echo 1
890 echo "wibble"
891 Nil
892}"#,
893 find_position_of("echo").to_selection()
894 );
895}
896
897#[test]
898fn remove_echo_removes_multiple_entire_echo_statement_used_with_literals_but_stops_at_comments() {
899 assert_code_action!(
900 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
901 r#"pub fn main() {
902 echo 1
903
904 // Oh no I hope I'm not deleted by the code action!!
905 Nil
906}"#,
907 find_position_of("echo").to_selection()
908 );
909}
910
911#[test]
912fn remove_echo_removes_entire_echo_statement_used_with_literals_in_a_fn() {
913 assert_code_action!(
914 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
915 "pub fn main() {
916 fn() {
917 echo 1
918 Nil
919 }
920}",
921 find_position_of("echo").to_selection()
922 );
923}
924
925#[test]
926fn remove_echo_removes_multiple_entire_echo_statement_used_with_literals_in_a_fn() {
927 assert_code_action!(
928 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
929 r#"pub fn main() {
930 fn() {
931 echo 1
932 echo "wibble"
933 Nil
934 }
935}"#,
936 find_position_of("echo").to_selection()
937 );
938}
939
940#[test]
941fn remove_echo_removes_does_not_remove_entire_echo_statement_if_its_the_return() {
942 assert_code_action!(
943 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
944 "pub fn main() {
945 echo 1
946}",
947 find_position_of("echo").to_selection()
948 );
949}
950
951#[test]
952fn remove_echo_with_message_removes_does_not_remove_entire_echo_statement_if_its_the_return() {
953 assert_code_action!(
954 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
955 r#"pub fn main() {
956 echo 1 as "message"
957}"#,
958 find_position_of("echo").to_selection()
959 );
960}
961
962#[test]
963fn remove_echo_removes_does_not_remove_entire_echo_statement_if_its_the_return_of_a_fn() {
964 assert_code_action!(
965 REMOVE_ALL_ECHOS_FROM_THIS_MODULE,
966 r#"pub fn main() {
967 fn() {
968 echo 1
969 }
970}"#,
971 find_position_of("echo").to_selection()
972 );
973}
974
975#[test]
976fn split_string() {
977 assert_code_action!(
978 INTERPOLATE_STRING,
979 r#"pub fn main() {
980 "wibble wobble woo"
981}"#,
982 find_position_of("wobble").to_selection()
983 );
984}
985
986#[test]
987fn no_split_string_right_at_the_start() {
988 assert_no_code_actions!(
989 INTERPOLATE_STRING,
990 r#"pub fn main() {
991 "wibble wobble woo"
992}"#,
993 find_position_of("wibble").to_selection()
994 );
995}
996
997#[test]
998fn no_split_string_right_at_the_end() {
999 assert_no_code_actions!(
1000 INTERPOLATE_STRING,
1001 r#"pub fn main() {
1002 "wibble wobble woo"
1003}"#,
1004 find_position_of("\"").nth_occurrence(2).to_selection()
1005 );
1006}
1007
1008#[test]
1009fn no_split_string_before_the_start() {
1010 assert_no_code_actions!(
1011 INTERPOLATE_STRING,
1012 r#"pub fn main() {
1013 "wibble wobble woo"
1014}"#,
1015 find_position_of("\"").to_selection()
1016 );
1017}
1018
1019#[test]
1020fn no_split_string_after_the_end() {
1021 assert_no_code_actions!(
1022 INTERPOLATE_STRING,
1023 r#"pub fn main() {
1024 "wibble wobble woo"//we need this comment so we can put the cursor _after_ the closing quote
1025}"#,
1026 find_position_of("\"/").under_last_char().to_selection()
1027 );
1028}
1029
1030#[test]
1031fn interpolate_string_inside_string() {
1032 assert_code_action!(
1033 INTERPOLATE_STRING,
1034 r#"pub fn main() {
1035 "wibble wobble woo"
1036}"#,
1037 find_position_of("wobble").select_until(find_position_of("wobble ").under_last_char()),
1038 );
1039}
1040
1041#[test]
1042fn splitting_string_as_first_pipeline_step_inserts_brackets() {
1043 assert_code_action!(
1044 INTERPOLATE_STRING,
1045 r#"pub fn main() {
1046 "wibble wobble" |> io.println
1047}"#,
1048 find_position_of(" wobble").to_selection(),
1049 );
1050}
1051
1052#[test]
1053fn interpolating_string_as_first_pipeline_step_inserts_brackets() {
1054 assert_code_action!(
1055 INTERPOLATE_STRING,
1056 r#"pub fn main() {
1057 "wibble wobble woo" |> io.println
1058}"#,
1059 find_position_of("wobble ").select_until(find_position_of("wobble ").under_last_char()),
1060 );
1061}
1062
1063#[test]
1064fn test_remove_unused_simple() {
1065 let src = "
1066// test
1067import // comment
1068list as lispy
1069import result
1070import option
1071
1072pub fn main() {
1073 result.is_ok
1074}
1075";
1076
1077 assert_code_action!(
1078 REMOVE_UNUSED_IMPORTS,
1079 TestProject::for_source(src)
1080 .add_hex_module("list", "")
1081 .add_hex_module("result", "")
1082 .add_hex_module("option", ""),
1083 find_position_of("// test").select_until(find_position_of("option")),
1084 );
1085}
1086
1087#[test]
1088fn test_remove_unused_start_of_file() {
1089 let src = "import option
1090import result
1091
1092pub fn main() {
1093 result.is_ok
1094}
1095";
1096 assert_code_action!(
1097 REMOVE_UNUSED_IMPORTS,
1098 TestProject::for_source(src)
1099 .add_hex_module("option", "")
1100 .add_hex_module("result", ""),
1101 find_position_of("import").select_until(find_position_of("pub")),
1102 );
1103}
1104
1105#[test]
1106fn test_remove_unused_alias() {
1107 let src = "
1108// test
1109import result.{is_ok} as res
1110import option
1111
1112pub fn main() {
1113 is_ok
1114}
1115";
1116 assert_code_action!(
1117 REMOVE_UNUSED_IMPORTS,
1118 TestProject::for_source(src)
1119 .add_hex_module("result", "pub fn is_ok() {}")
1120 .add_hex_module("option", ""),
1121 find_position_of("// test").select_until(find_position_of("pub")),
1122 );
1123}
1124
1125#[test]
1126fn test_remove_unused_value() {
1127 let src = "
1128// test
1129import result.{is_ok}
1130import option
1131
1132pub fn main() {
1133 result.is_ok
1134}
1135";
1136 assert_code_action!(
1137 REMOVE_UNUSED_IMPORTS,
1138 TestProject::for_source(src)
1139 .add_hex_module("result", "pub fn is_ok() {}")
1140 .add_hex_module("option", ""),
1141 find_position_of("// test").select_until(find_position_of("pub")),
1142 );
1143}
1144
1145#[test]
1146fn test_remove_aliased_unused_value() {
1147 let src = "
1148// test
1149import result.{is_ok as ok}
1150import option
1151
1152pub fn main() {
1153 result.is_ok
1154}
1155";
1156 assert_code_action!(
1157 REMOVE_UNUSED_IMPORTS,
1158 TestProject::for_source(src)
1159 .add_hex_module("result", "pub fn is_ok() {}")
1160 .add_hex_module("option", ""),
1161 find_position_of("// test").select_until(find_position_of("pub")),
1162 );
1163}
1164
1165#[test]
1166fn test_remove_multiple_unused_values() {
1167 let src = "
1168// test
1169import result.{type Unused, used, unused, unused_again, type Used, used_again}
1170
1171pub fn main(x: Used) {
1172 #(used, used_again)
1173}
1174";
1175 assert_code_action!(
1176 REMOVE_UNUSED_IMPORTS,
1177 TestProject::for_source(src).add_hex_module(
1178 "result",
1179 "
1180pub const used = 1
1181pub const unused = 2
1182pub const unused_again = 3
1183pub const used_again = 4
1184pub type Unused
1185pub type Used
1186"
1187 ),
1188 find_position_of("// test").select_until(find_position_of("pub")),
1189 );
1190}
1191
1192#[test]
1193fn test_remove_multiple_unused_values_2() {
1194 let src = "
1195// test
1196import result.{type Unused, used, unused, type Used, unused_again}
1197
1198pub fn main(x: Used) {
1199 used
1200}
1201";
1202 assert_code_action!(
1203 REMOVE_UNUSED_IMPORTS,
1204 TestProject::for_source(src).add_hex_module(
1205 "result",
1206 "
1207pub const used = 1
1208pub const unused = 2
1209pub const unused_again = 3
1210pub type Unused
1211pub type Used
1212"
1213 ),
1214 find_position_of("// test").select_until(find_position_of("pub")),
1215 );
1216}
1217
1218#[test]
1219fn test_remove_entire_unused_import() {
1220 let src = "
1221// test
1222import result.{unused, unused_again}
1223
1224pub fn main() {
1225 todo
1226}
1227";
1228 assert_code_action!(
1229 REMOVE_UNUSED_IMPORTS,
1230 TestProject::for_source(src).add_hex_module(
1231 "result",
1232 "
1233pub const used = 1
1234pub const unused = 2
1235pub const unused_again = 3
1236pub type Unused
1237pub type Used
1238"
1239 ),
1240 find_position_of("// test").select_until(find_position_of("pub")),
1241 );
1242}
1243
1244#[test]
1245fn test_remove_redundant_tuple_in_case_subject_simple() {
1246 assert_code_action!(
1247 REMOVE_REDUNDANT_TUPLES,
1248 "pub fn main() {
1249 case #(1) { #(a) -> 0 }
1250 case #(1, 2) { #(a, b) -> 0 }
1251}",
1252 find_position_of("case").select_until(find_position_of("#(1, 2)").under_last_char())
1253 );
1254}
1255
1256#[test]
1257fn test_remove_redundant_tuple_with_catch_all_pattern() {
1258 assert_code_action!(
1259 REMOVE_REDUNDANT_TUPLES,
1260 "pub fn main() {
1261 case #(1, 2) {
1262 #(1, 2) -> 0
1263 _ -> 1
1264 }
1265}",
1266 find_position_of("case").select_until(find_position_of("#(1, 2)").under_last_char())
1267 );
1268}
1269
1270#[test]
1271fn test_remove_multiple_redundant_tuple_with_catch_all_pattern() {
1272 assert_code_action!(
1273 REMOVE_REDUNDANT_TUPLES,
1274 "pub fn main() {
1275 case #(1, 2), #(3, 4) {
1276 #(2, 2), #(2, 2) -> 0
1277 #(1, 2), _ -> 0
1278 _, #(1, 2) -> 0
1279 _, _ -> 1
1280 }
1281}",
1282 find_position_of("case").select_until(find_position_of("#(3, 4)"))
1283 );
1284}
1285
1286#[test]
1287fn test_remove_redundant_tuple_in_case_subject_nested() {
1288 assert_code_action!(
1289 REMOVE_REDUNDANT_TUPLES,
1290 "pub fn main() {
1291 case #(case #(0) { #(a) -> 0 }) { #(b) -> 0 }
1292}",
1293 find_position_of("case").select_until(find_position_of("#(b)"))
1294 );
1295}
1296
1297#[test]
1298fn test_remove_redundant_tuple_in_case_retain_extras() {
1299 assert_code_action!(
1300 REMOVE_REDUNDANT_TUPLES,
1301 "
1302pub fn main() {
1303 case
1304 #(
1305 // first comment
1306 1,
1307 // second comment
1308 2,
1309 3 // third comment before comma
1310
1311 ,
1312
1313 // fourth comment after comma
1314
1315 )
1316 {
1317 #(
1318 // first comment
1319 a,
1320 // second comment
1321 b,
1322 c // third comment before comma
1323
1324 ,
1325
1326 // fourth comment after comma
1327
1328 ) -> 0
1329 }
1330}
1331",
1332 find_position_of("#").select_until(find_position_of("// first"))
1333 );
1334}
1335
1336#[test]
1337fn test_remove_redundant_tuple_in_case_subject_ignore_empty_tuple() {
1338 assert_no_code_actions!(
1339 REMOVE_REDUNDANT_TUPLES,
1340 "
1341pub fn main() {
1342 case #() { #() -> 0 }
1343}
1344",
1345 find_position_of("case").select_until(find_position_of("0"))
1346 );
1347}
1348
1349#[test]
1350fn test_remove_redundant_tuple_in_case_subject_only_safe_remove() {
1351 assert_code_action!(
1352 REMOVE_REDUNDANT_TUPLES,
1353 "
1354pub fn main() {
1355 case #(0), #(1) {
1356 #(1), #(b) -> 0
1357 a, #(0) -> 1 // The first of this clause is not a tuple
1358 #(a), #(b) -> 2
1359 }
1360}
1361",
1362 find_position_of("#(0)").select_until(find_position_of("#(1)"))
1363 );
1364}
1365
1366#[test]
1367fn rename_invalid_const() {
1368 assert_code_action!(
1369 "Rename to my_invalid_constant",
1370 "const myInvalid_Constant = 42",
1371 find_position_of("_Constant").to_selection(),
1372 );
1373}
1374
1375#[test]
1376fn rename_invalid_parameter() {
1377 assert_code_action!(
1378 "Rename to num_a",
1379 "fn add(numA: Int, num_b: Int) { numA + num_b }",
1380 find_position_of("numA").to_selection()
1381 );
1382}
1383
1384#[test]
1385fn rename_invalid_parameter_name2() {
1386 assert_code_action!(
1387 "Rename to param_name",
1388 "fn pass(label paramName: Bool) { paramName }",
1389 find_position_of("paramName").to_selection()
1390 );
1391}
1392
1393#[test]
1394fn rename_invalid_parameter_name3() {
1395 assert_code_action!(
1396 "Rename to num_a",
1397 "pub fn main() {
1398 let add = fn(numA: Int, num_b: Int) { numA + num_b }
1399}",
1400 find_position_of("let add").select_until(find_position_of("num_b"))
1401 );
1402}
1403
1404#[test]
1405fn rename_invalid_parameter_discard() {
1406 assert_code_action!(
1407 "Rename to _ignore_me",
1408 "fn ignore(_ignoreMe: Bool) { 98 }",
1409 find_position_of("ignore").select_until(find_position_of("98"))
1410 );
1411}
1412
1413#[test]
1414fn rename_invalid_parameter_discard_name2() {
1415 assert_code_action!(
1416 "Rename to _ignore_me",
1417 "fn ignore(labelled_discard _ignoreMe: Bool) { 98 }",
1418 find_position_of("ignore").select_until(find_position_of("98"))
1419 );
1420}
1421
1422#[test]
1423fn rename_invalid_parameter_discard_name3() {
1424 assert_code_action!(
1425 "Rename to _ignore_me",
1426 "pub fn main() {
1427 let ignore = fn(_ignoreMe: Bool) { 98 }
1428}",
1429 find_position_of("ignore").select_until(find_position_of("98"))
1430 );
1431}
1432
1433#[test]
1434fn rename_invalid_parameter_label() {
1435 assert_code_action!(
1436 "Rename to this_is_a_label",
1437 "fn func(thisIsALabel param: Int) { param }",
1438 find_position_of("thisIs").select_until(find_position_of("Int"))
1439 );
1440}
1441
1442#[test]
1443fn rename_invalid_parameter_label2() {
1444 assert_code_action!(
1445 "Rename to this_is_a_label",
1446 "fn ignore(thisIsALabel _ignore: Int) { 25 }",
1447 find_position_of("thisIs").under_char('i').to_selection()
1448 );
1449}
1450
1451#[test]
1452fn rename_invalid_constructor() {
1453 assert_code_action!(
1454 "Rename to TheConstructor",
1455 "type MyType { The_Constructor(Int) }",
1456 find_position_of("The_").under_char('h').to_selection(),
1457 );
1458}
1459
1460#[test]
1461fn rename_invalid_constructor_arg() {
1462 assert_code_action!(
1463 "Rename to inner_int",
1464 "type IntWrapper { IntWrapper(innerInt: Int) }",
1465 find_position_of("IntWrapper")
1466 .nth_occurrence(2)
1467 .select_until(find_position_of(": Int"))
1468 );
1469}
1470
1471#[test]
1472fn rename_invalid_custom_type() {
1473 assert_code_action!(
1474 "Rename to BoxedValue",
1475 "type Boxed_value { Box(Int) }",
1476 find_position_of("Box").select_until(find_position_of("_value"))
1477 );
1478}
1479
1480#[test]
1481fn rename_invalid_type_alias() {
1482 assert_code_action!(
1483 "Rename to FancyBool",
1484 "type Fancy_Bool = Bool",
1485 find_position_of("Fancy")
1486 .under_char('a')
1487 .select_until(find_position_of("="))
1488 );
1489}
1490
1491#[test]
1492fn rename_invalid_function() {
1493 assert_code_action!(
1494 "Rename to do_stuff",
1495 "fn doStuff() {}",
1496 find_position_of("fn").select_until(find_position_of("{}"))
1497 );
1498}
1499
1500#[test]
1501fn rename_invalid_variable() {
1502 assert_code_action!(
1503 "Rename to the_answer",
1504 "pub fn main() {
1505 let theAnswer = 42
1506}",
1507 find_position_of("theAnswer").select_until(find_position_of("Answer"))
1508 );
1509}
1510
1511#[test]
1512fn rename_invalid_variable_discard() {
1513 assert_code_action!(
1514 "Rename to _boring_number",
1515 "pub fn main() {
1516 let _boringNumber = 72
1517}",
1518 find_position_of("let").select_until(find_position_of("72"))
1519 );
1520}
1521
1522#[test]
1523fn rename_invalid_use() {
1524 assert_code_action!(
1525 "Rename to use_var",
1526 "fn use_test(f) { f(Nil) }
1527pub fn main() {use useVar <- use_test()}",
1528 find_position_of("use")
1529 .nth_occurrence(2)
1530 .select_until(find_position_of("use_test()"))
1531 );
1532}
1533
1534#[test]
1535fn rename_invalid_use_discard() {
1536 assert_code_action!(
1537 "Rename to _discard_var",
1538 "fn use_test(f) { f(Nil) }
1539pub fn main() {use _discardVar <- use_test()}",
1540 find_position_of("_discardVar")
1541 .under_last_char()
1542 .to_selection()
1543 );
1544}
1545
1546#[test]
1547fn rename_invalid_pattern_assignment() {
1548 assert_code_action!(
1549 "Rename to the_answer",
1550 "pub fn main() {
1551 let assert 42 as theAnswer = 42
1552}",
1553 find_position_of("let").select_until(find_position_of("= 42"))
1554 );
1555}
1556
1557#[test]
1558fn rename_invalid_list_pattern() {
1559 assert_code_action!(
1560 "Rename to the_element",
1561 "pub fn main() {
1562 let assert [theElement] = [9.4]
1563}",
1564 find_position_of("assert").select_until(find_position_of("9.4"))
1565 );
1566}
1567
1568#[test]
1569fn rename_invalid_list_pattern_discard() {
1570 assert_code_action!(
1571 "Rename to _elem_one",
1572 "pub fn main() {
1573 let assert [_elemOne] = [False]
1574}",
1575 find_position_of("[_elemOne]")
1576 .under_char('O')
1577 .to_selection()
1578 );
1579}
1580
1581#[test]
1582fn rename_invalid_constructor_pattern() {
1583 assert_code_action!(
1584 "Rename to inner_value",
1585 "pub type Box { Box(Int) }
1586pub fn main() {
1587 let Box(innerValue) = Box(203)
1588}",
1589 find_position_of("innerValue").to_selection()
1590 );
1591}
1592
1593#[test]
1594fn rename_invalid_constructor_pattern_discard() {
1595 assert_code_action!(
1596 "Rename to _ignored_inner",
1597 "pub type Box { Box(Int) }
1598pub fn main() {
1599 let Box(_ignoredInner) = Box(203)
1600}",
1601 find_position_of("_").select_until(find_position_of("203"))
1602 );
1603}
1604
1605#[test]
1606fn rename_invalid_tuple_pattern() {
1607 assert_code_action!(
1608 "Rename to second_value",
1609 "pub fn main() {
1610 let #(a, secondValue) = #(1, 2)
1611}",
1612 find_position_of("secondValue")
1613 .select_until(find_position_of("secondValue").under_char('n'))
1614 );
1615}
1616
1617#[test]
1618fn rename_invalid_tuple_pattern_discard() {
1619 assert_code_action!(
1620 "Rename to _second_value",
1621 "pub fn main() {
1622 let #(a, _secondValue) = #(1, 2)
1623}",
1624 find_position_of("_secondValue")
1625 .under_char('_')
1626 .select_until(find_position_of("#(1, 2)"))
1627 );
1628}
1629
1630#[test]
1631fn rename_invalid_bit_array_pattern() {
1632 assert_code_action!(
1633 "Rename to bit_value",
1634 "pub fn main() {
1635 let assert <<bitValue>> = <<73>>
1636}",
1637 find_position_of("<<").select_until(find_position_of(">>"))
1638 );
1639}
1640
1641#[test]
1642fn rename_invalid_bit_array_pattern_discard() {
1643 assert_code_action!(
1644 "Rename to _i_dont_care",
1645 "pub fn main() {
1646 let assert <<_iDontCare>> = <<97>>
1647}",
1648 find_position_of("<<").select_until(find_position_of("Care"))
1649 );
1650}
1651
1652#[test]
1653fn rename_invalid_string_prefix_pattern() {
1654 assert_code_action!(
1655 "Rename to cool_suffix",
1656 r#"pub fn main() {
1657 let assert "prefix" <> coolSuffix = "prefix-suffix"
1658}"#,
1659 find_position_of("<>").select_until(find_position_of("-suffix"))
1660 );
1661}
1662
1663#[test]
1664fn rename_invalid_string_prefix_pattern_discard() {
1665 assert_code_action!(
1666 "Rename to _boring_suffix",
1667 r#"pub fn main() {
1668 let assert "prefix" <> _boringSuffix = "prefix-suffix"
1669}"#,
1670 find_position_of("<>").select_until(find_position_of("Suffix"))
1671 );
1672}
1673
1674#[test]
1675fn rename_invalid_string_prefix_pattern_alias() {
1676 assert_code_action!(
1677 "Rename to the_prefix",
1678 r#"pub fn main() {
1679 let assert "prefix" as thePrefix <> _suffix = "prefix-suffix"
1680}"#,
1681 find_position_of("prefix").select_until(find_position_of("-suffix"))
1682 );
1683}
1684
1685#[test]
1686fn rename_invalid_case_variable() {
1687 assert_code_action!(
1688 "Rename to twenty_one",
1689 "pub fn main() {
1690 case 21 { twentyOne -> {Nil} }
1691}",
1692 find_position_of("case").select_until(find_position_of("Nil"))
1693 );
1694}
1695
1696#[test]
1697fn rename_invalid_case_variable_discard() {
1698 assert_code_action!(
1699 "Rename to _twenty_one",
1700 "pub fn main() {
1701 case 21 { _twentyOne -> {Nil} }
1702}",
1703 find_position_of("21").select_until(find_position_of("->"))
1704 );
1705}
1706
1707#[test]
1708fn rename_invalid_type_parameter_name() {
1709 assert_code_action!(
1710 "Rename to inner_type",
1711 "type Wrapper(innerType) {}",
1712 find_position_of("innerType").select_until(find_position_of(")"))
1713 );
1714}
1715
1716#[test]
1717fn rename_invalid_type_alias_parameter_name() {
1718 assert_code_action!(
1719 "Rename to phantom_type",
1720 "type Phantom(phantomType) = Int",
1721 find_position_of("phantomType").select_until(find_position_of(")"))
1722 );
1723}
1724
1725#[test]
1726fn rename_invalid_function_type_parameter_name() {
1727 assert_code_action!(
1728 "Rename to some_type",
1729 "fn identity(value: someType) { value }",
1730 find_position_of("someType").select_until(find_position_of(")"))
1731 );
1732}
1733
1734#[test]
1735fn test_convert_assert_result_to_case() {
1736 assert_code_action!(
1737 CONVERT_TO_CASE,
1738 "pub fn main() {
1739 let assert Ok(value) = Ok(1)
1740}",
1741 find_position_of("assert").select_until(find_position_of("assert").under_char('r')),
1742 );
1743}
1744
1745#[test]
1746fn test_convert_let_assert_to_case_indented() {
1747 assert_code_action!(
1748 CONVERT_TO_CASE,
1749 "pub fn main() {
1750 {
1751 let assert Ok(value) = Ok(1)
1752 }
1753}",
1754 find_position_of("Ok").to_selection()
1755 );
1756}
1757
1758#[test]
1759fn test_convert_let_assert_to_case_multi_variables() {
1760 assert_code_action!(
1761 CONVERT_TO_CASE,
1762 "pub fn main() {
1763 let assert [var1, var2, _var3, var4] = [1, 2, 3, 4]
1764}",
1765 find_position_of("var1").select_until(find_position_of("_var").under_last_char())
1766 );
1767}
1768
1769#[test]
1770fn test_convert_let_assert_to_case_discard() {
1771 assert_code_action!(
1772 CONVERT_TO_CASE,
1773 "pub fn main() {
1774 let assert [_elem] = [6]
1775}",
1776 find_position_of("assert").select_until(find_position_of("[6]").under_last_char()),
1777 );
1778}
1779
1780#[test]
1781fn test_convert_let_assert_to_case_no_variables() {
1782 assert_code_action!(
1783 CONVERT_TO_CASE,
1784 "pub fn main() {
1785 let assert [] = []
1786}",
1787 find_position_of("[]").to_selection(),
1788 );
1789}
1790
1791#[test]
1792fn test_convert_let_assert_alias_to_case() {
1793 assert_code_action!(
1794 CONVERT_TO_CASE,
1795 "pub fn main() {
1796 let assert 10 as ten = 10
1797}",
1798 find_position_of("as").select_until(find_position_of("ten")),
1799 );
1800}
1801
1802#[test]
1803fn test_convert_let_assert_tuple_to_case() {
1804 assert_code_action!(
1805 CONVERT_TO_CASE,
1806 "pub fn main() {
1807 let assert #(first, 10, third) = #(5, 10, 15)
1808}
1809",
1810 find_position_of("let").to_selection(),
1811 );
1812}
1813
1814#[test]
1815fn test_convert_let_assert_bit_array_to_case() {
1816 assert_code_action!(
1817 CONVERT_TO_CASE,
1818 "pub fn main() {
1819 let assert <<bits1, bits2>> = <<73, 98>>
1820}",
1821 find_position_of("bits").select_until(find_position_of("2")),
1822 );
1823}
1824
1825#[test]
1826fn test_convert_let_assert_string_prefix_to_case() {
1827 assert_code_action!(
1828 CONVERT_TO_CASE,
1829 r#"pub fn main() {
1830 let assert "_" <> thing = "_Hello"
1831}"#,
1832 find_position_of("_").to_selection()
1833 );
1834}
1835
1836#[test]
1837fn test_convert_let_assert_string_prefix_pattern_alias_to_case() {
1838 assert_code_action!(
1839 CONVERT_TO_CASE,
1840 r#"pub fn main() {
1841 let assert "123" as one_two_three <> rest = "123456"
1842}"#,
1843 find_position_of("123").select_until(find_position_of("123456")),
1844 );
1845}
1846
1847#[test]
1848fn test_convert_inner_let_assert_to_case() {
1849 assert_code_action!(
1850 CONVERT_TO_CASE,
1851 r#"pub fn main() {
1852 let assert [wibble] = {
1853 let assert Ok(wobble) = {
1854 Ok(1)
1855 }
1856 [wobble]
1857 }
1858}"#,
1859 find_position_of("wobble").under_char('l').to_selection()
1860 );
1861}
1862
1863#[test]
1864fn test_convert_outer_let_assert_to_case() {
1865 assert_code_action!(
1866 CONVERT_TO_CASE,
1867 r#"pub fn main() {
1868 let assert [wibble] = {
1869 let assert Ok(wobble) = {
1870 Ok(1)
1871 }
1872 [wobble]
1873 }
1874}"#,
1875 find_position_of("wibble")
1876 .under_char('i')
1877 .select_until(find_position_of("= {")),
1878 );
1879}
1880
1881#[test]
1882fn test_convert_let_assert_with_message_to_case() {
1883 assert_code_action!(
1884 CONVERT_TO_CASE,
1885 r#"
1886pub fn expect(value, message) {
1887 let assert Ok(inner) = value as message
1888 inner
1889}
1890"#,
1891 find_position_of("assert").select_until(find_position_of("=")),
1892 );
1893}
1894
1895#[test]
1896fn test_convert_assert_custom_type_with_label_shorthands_to_case() {
1897 assert_code_action!(
1898 CONVERT_TO_CASE,
1899 "
1900pub type Wibble { Wibble(arg: Int, arg2: Float) }
1901pub fn main() {
1902 let assert Wibble(arg2:, ..) = Wibble(arg: 1, arg2: 1.0)
1903}
1904",
1905 find_position_of("arg2:,").select_until(find_position_of("1.0")),
1906 );
1907}
1908
1909#[test]
1910fn test_convert_assert_does_not_appear_if_the_entire_module_is_selected() {
1911 assert_no_code_actions!(
1912 CONVERT_TO_CASE,
1913 "
1914pub type Wibble { Wibble(arg: Int, arg2: Float) }
1915pub fn main() {
1916 let assert Wibble(arg2:, ..) = Wibble(arg: 1, arg2: 1.0)
1917 let assert Wibble(arg2:, ..) = Wibble(arg: 1, arg2: 1.0)
1918}
1919// end
1920",
1921 find_position_of("pub").select_until(find_position_of("// end")),
1922 );
1923}
1924
1925#[test]
1926fn label_shorthand_action_works_on_labelled_call_args() {
1927 assert_code_action!(
1928 USE_LABEL_SHORTHAND_SYNTAX,
1929 r#"
1930pub fn main() {
1931 let arg1 = 1
1932 let arg2 = 2
1933 wibble(arg2: arg2, arg1: arg1)
1934}
1935
1936pub fn wibble(arg1 arg1, arg2 arg2) { Nil }
1937"#,
1938 find_position_of("wibble")
1939 .under_char('i')
1940 .select_until(find_position_of("arg1: arg1")),
1941 );
1942}
1943
1944#[test]
1945fn label_shorthand_action_works_on_labelled_constructor_call_args() {
1946 assert_code_action!(
1947 USE_LABEL_SHORTHAND_SYNTAX,
1948 r#"
1949pub fn main() {
1950 let arg1 = 1
1951 let arg2 = 2
1952 Wibble(arg2: arg2, arg1: arg1)
1953}
1954
1955pub type Wibble { Wibble(arg1: Int, arg2: Int) }
1956"#,
1957 find_position_of("Wibble").select_until(find_position_of("arg1: arg1").under_char(':')),
1958 );
1959}
1960
1961#[test]
1962fn label_shorthand_action_only_applies_to_selected_args() {
1963 assert_code_action!(
1964 USE_LABEL_SHORTHAND_SYNTAX,
1965 r#"
1966pub fn main() {
1967 let arg1 = 1
1968 let arg2 = 2
1969 Wibble(arg2: arg2, arg1: arg1)
1970}
1971
1972pub type Wibble { Wibble(arg1: Int, arg2: Int) }
1973"#,
1974 find_position_of("Wibble").select_until(find_position_of("arg2: arg2").under_char(':')),
1975 );
1976}
1977
1978#[test]
1979fn label_shorthand_action_works_on_labelled_update_call_args() {
1980 assert_code_action!(
1981 USE_LABEL_SHORTHAND_SYNTAX,
1982 r#"
1983pub fn main() {
1984 let arg1 = 1
1985 Wibble(..todo, arg1: arg1)
1986}
1987
1988pub type Wibble { Wibble(arg1: Int, arg2: Int) }
1989"#,
1990 find_position_of("..todo").select_until(find_position_of("arg1: arg1").under_last_char()),
1991 );
1992}
1993
1994#[test]
1995fn label_shorthand_action_works_on_labelled_pattern_call_args() {
1996 assert_code_action!(
1997 USE_LABEL_SHORTHAND_SYNTAX,
1998 r#"
1999pub fn main() {
2000 let Wibble(arg1: arg1, arg2: arg2) = todo
2001 arg1 + arg2
2002}
2003
2004pub type Wibble { Wibble(arg1: Int, arg2: Int) }
2005"#,
2006 find_position_of("let").select_until(find_position_of("todo").under_last_char()),
2007 );
2008}
2009
2010#[test]
2011fn label_shorthand_action_doesnt_come_up_for_arguments_with_different_label() {
2012 assert_no_code_actions!(
2013 USE_LABEL_SHORTHAND_SYNTAX,
2014 r#"
2015pub fn main() {
2016 let Wibble(arg1: arg_1, arg2: arg_2) = todo
2017 arg_1 + arg_2
2018}
2019
2020pub type Wibble { Wibble(arg1: Int, arg2: Int) }
2021"#,
2022 find_position_of("arg_1").select_until(find_position_of("arg_2").under_last_char())
2023 );
2024}
2025
2026#[test]
2027fn fill_in_labelled_args_with_some_arguments_already_supplied() {
2028 assert_code_action!(
2029 FILL_LABELS,
2030 r#"
2031pub fn main() {
2032 wibble(1,)
2033}
2034
2035pub fn wibble(arg1 arg1, arg2 arg2) { Nil }
2036 "#,
2037 find_position_of("wibble(").under_char('b').to_selection(),
2038 );
2039}
2040
2041#[test]
2042fn fill_in_labelled_args_with_some_arguments_already_supplied_2() {
2043 assert_code_action!(
2044 FILL_LABELS,
2045 r#"
2046pub fn main() {
2047 wibble(arg2: 1)
2048}
2049
2050pub fn wibble(arg1 arg1, arg2 arg2) { Nil }
2051 "#,
2052 find_position_of("wibble(").to_selection(),
2053 );
2054}
2055
2056#[test]
2057fn fill_in_labelled_args_with_some_arguments_already_supplied_3() {
2058 assert_code_action!(
2059 FILL_LABELS,
2060 r#"
2061pub fn main() {
2062 wibble(1, arg3: 2)
2063}
2064
2065pub fn wibble(arg1 arg1, arg2 arg2, arg3 arg3) { Nil }
2066 "#,
2067 find_position_of("wibble(").to_selection(),
2068 );
2069}
2070
2071#[test]
2072fn fill_in_labelled_args_works_with_regular_function() {
2073 assert_code_action!(
2074 FILL_LABELS,
2075 r#"
2076pub fn main() {
2077 wibble()
2078}
2079
2080pub fn wibble(arg1 arg1, arg2 arg2) { Nil }
2081 "#,
2082 find_position_of("wibble(").to_selection(),
2083 );
2084}
2085
2086#[test]
2087fn fill_in_labelled_args_works_with_record_constructor() {
2088 assert_code_action!(
2089 FILL_LABELS,
2090 r#"
2091pub fn main() {
2092 Wibble()
2093}
2094
2095pub type Wibble { Wibble(arg1: Int, arg2: String) }
2096 "#,
2097 find_position_of("Wibble").select_until(find_position_of("Wibble()").under_last_char()),
2098 );
2099}
2100
2101#[test]
2102fn fill_in_labelled_args_works_with_pattern_and_no_parentheses() {
2103 assert_code_action!(
2104 FILL_LABELS,
2105 r#"
2106pub fn main() {
2107 let assert Ok(Wibble) = Wibble(1, "2")
2108}
2109
2110pub type Wibble { Wibble(arg1: Int, arg2: String) }
2111 "#,
2112 find_position_of("Wibble").select_until(find_position_of("Wibble").under_last_char()),
2113 );
2114}
2115
2116#[test]
2117fn fill_in_labelled_args_works_with_pattern_and_parentheses() {
2118 assert_code_action!(
2119 FILL_LABELS,
2120 r#"
2121pub fn main() {
2122 let assert Ok(Wibble()) = Wibble(1, "2")
2123}
2124
2125pub type Wibble { Wibble(arg1: Int, arg2: String) }
2126 "#,
2127 find_position_of("Wibble").select_until(find_position_of("Wibble").under_last_char()),
2128 );
2129}
2130
2131#[test]
2132fn fill_in_labelled_args_works_with_pattern_and_parentheses_with_spaces() {
2133 assert_code_action!(
2134 FILL_LABELS,
2135 r#"
2136pub fn main() {
2137 let assert Ok(Wibble ()) = Wibble(1, "2")
2138}
2139
2140pub type Wibble { Wibble(arg1: Int, arg2: String) }
2141 "#,
2142 find_position_of("Wibble").select_until(find_position_of("Wibble").under_last_char()),
2143 );
2144}
2145
2146#[test]
2147fn fill_in_labelled_args_works_with_pipes() {
2148 assert_code_action!(
2149 FILL_LABELS,
2150 r#"
2151pub fn main() {
2152 1 |> wibble()
2153}
2154
2155pub fn wibble(arg1 arg1, arg2 arg2) { Nil }
2156 "#,
2157 find_position_of("wibble()")
2158 .under_last_char()
2159 .to_selection(),
2160 );
2161}
2162
2163#[test]
2164fn fill_in_labelled_args_works_with_pipes_2() {
2165 assert_code_action!(
2166 FILL_LABELS,
2167 r#"
2168pub fn main() {
2169 1 |> wibble()
2170}
2171
2172pub fn wibble(not_labelled, arg1 arg1, arg2 arg2) { Nil }
2173 "#,
2174 find_position_of("wibble()")
2175 .under_last_char()
2176 .to_selection(),
2177 );
2178}
2179
2180#[test]
2181fn fill_in_labelled_args_works_with_use() {
2182 assert_code_action!(
2183 FILL_LABELS,
2184 r#"
2185pub fn main() {
2186 use <- wibble()
2187 todo
2188}
2189
2190pub fn wibble(arg1 arg1, arg2 arg2) { Nil }
2191 "#,
2192 find_position_of("wibble(").select_until(find_position_of("wibble()").under_last_char()),
2193 );
2194}
2195
2196#[test]
2197fn fill_in_labelled_args_works_with_use_2() {
2198 assert_code_action!(
2199 FILL_LABELS,
2200 r#"
2201pub fn main() {
2202 use <- wibble(arg1: 1)
2203 todo
2204}
2205
2206pub fn wibble(arg1 arg1, arg2 arg2, arg3 arg3) { Nil }
2207 "#,
2208 find_position_of("wibble(").select_until(find_position_of("1").under_last_char()),
2209 );
2210}
2211
2212#[test]
2213fn fill_in_labelled_args_works_with_use_3() {
2214 assert_code_action!(
2215 FILL_LABELS,
2216 r#"
2217pub fn main() {
2218 use <- wibble(arg2: 2)
2219 todo
2220}
2221
2222pub fn wibble(arg1 arg1, arg2 arg2, arg3 arg3) { Nil }
2223 "#,
2224 find_position_of("wibble(").select_until(find_position_of("2").under_last_char()),
2225 );
2226}
2227
2228#[test]
2229fn fill_in_labelled_args_selects_innermost_function() {
2230 assert_code_action!(
2231 FILL_LABELS,
2232 r#"
2233pub fn main() {
2234 wibble(
2235 wibble()
2236 )
2237}
2238
2239pub fn wibble(arg1 arg1, arg2 arg2) { Nil }
2240 "#,
2241 find_position_of("wibble()")
2242 .under_last_char()
2243 .to_selection(),
2244 );
2245}
2246
2247#[test]
2248fn fill_labels_uses_variable_in_scope_with_matching_type() {
2249 assert_code_action!(
2250 FILL_LABELS,
2251 r#"
2252pub type Player {
2253 Player(name: String, team: String)
2254}
2255
2256pub fn main() {
2257 let name = "Priya"
2258 Player(team: "BLU")
2259}
2260"#,
2261 find_position_of("Player").nth_occurrence(3).to_selection(),
2262 );
2263}
2264
2265#[test]
2266fn fill_labels_falls_back_to_todo_when_type_does_not_match() {
2267 assert_code_action!(
2268 FILL_LABELS,
2269 r#"
2270pub type Player {
2271 Player(name: String, team: String)
2272}
2273
2274pub fn main() {
2275 let name = 1
2276 Player(team: "BLU")
2277}
2278"#,
2279 find_position_of("Player").nth_occurrence(3).to_selection(),
2280 );
2281}
2282
2283#[test]
2284fn fill_labels_uses_function_argument_in_scope() {
2285 assert_code_action!(
2286 FILL_LABELS,
2287 r#"
2288pub type Player {
2289 Player(name: String, team: String)
2290}
2291
2292pub fn create_player(name: String) {
2293 Player(team: "BLU")
2294}
2295"#,
2296 find_position_of("Player").nth_occurrence(3).to_selection(),
2297 );
2298}
2299
2300#[test]
2301fn fill_labels_ignores_variable_defined_after_call() {
2302 assert_code_action!(
2303 FILL_LABELS,
2304 r#"
2305pub type Player {
2306 Player(name: String, team: String)
2307}
2308
2309pub fn main() {
2310 Player(team: "BLU")
2311 let name = "Priya"
2312}
2313"#,
2314 find_position_of("Player").nth_occurrence(3).to_selection(),
2315 );
2316}
2317
2318#[test]
2319fn fill_labels_multiple_fields_some_matching() {
2320 assert_code_action!(
2321 FILL_LABELS,
2322 r#"
2323pub type Player {
2324 Player(name: String, age: Int, team: String)
2325}
2326
2327pub fn main() {
2328 let name = "Priya"
2329 let age = "not an int"
2330 Player()
2331}
2332"#,
2333 find_position_of("Player").nth_occurrence(3).to_selection(),
2334 );
2335}
2336
2337#[test]
2338fn fill_labels_all_fields_have_matching_variables() {
2339 assert_code_action!(
2340 FILL_LABELS,
2341 r#"
2342pub type Player {
2343 Player(name: String, age: Int, team: String)
2344}
2345
2346pub fn main() {
2347 let name = "Priya"
2348 let age = 25
2349 let team = "BLU"
2350 Player()
2351}
2352"#,
2353 find_position_of("Player").nth_occurrence(3).to_selection(),
2354 );
2355}
2356
2357#[test]
2358fn fill_labels_inside_anonymous_function() {
2359 assert_code_action!(
2360 FILL_LABELS,
2361 r#"
2362pub type Player {
2363 Player(name: String, team: String)
2364}
2365
2366pub fn main() {
2367 let name = "Outer"
2368 let callback = fn() {
2369 let name = "Inner"
2370 Player(team: "BLU")
2371 }
2372}
2373"#,
2374 find_position_of("Player").nth_occurrence(3).to_selection(),
2375 );
2376}
2377
2378#[test]
2379fn fill_labels_variable_from_outer_scope_not_shadowed() {
2380 assert_code_action!(
2381 FILL_LABELS,
2382 r#"
2383pub type Player {
2384 Player(name: String, team: String)
2385}
2386
2387pub fn main() {
2388 let name = "Outer"
2389 let callback = fn() {
2390 Player(team: "BLU")
2391 }
2392}
2393"#,
2394 find_position_of("Player").nth_occurrence(3).to_selection(),
2395 );
2396}
2397
2398#[test]
2399fn fill_labels_inside_assignment_with_same_name_as_field() {
2400 assert_code_action!(
2401 FILL_LABELS,
2402 r#"
2403pub type Player {
2404 Player(name: String, team: String)
2405}
2406
2407pub fn main() {
2408 let name = Player(team: "BLU")
2409}
2410"#,
2411 find_position_of("Player").nth_occurrence(3).to_selection(),
2412 );
2413}
2414
2415#[test]
2416fn fill_labels_ignores_underscore_prefixed_variables() {
2417 assert_code_action!(
2418 FILL_LABELS,
2419 r#"
2420pub type Player {
2421 Player(name: String, team: String)
2422}
2423
2424pub fn main() {
2425 let _name = "Priya"
2426 Player(team: "BLU")
2427}
2428"#,
2429 find_position_of("Player").nth_occurrence(3).to_selection(),
2430 );
2431}
2432
2433#[test]
2434fn fill_labels_variable_out_of_scope_in_block() {
2435 assert_code_action!(
2436 FILL_LABELS,
2437 r#"
2438pub type Player {
2439 Player(name: String, team: String)
2440}
2441
2442pub fn main() {
2443 {
2444 let name = "Priya"
2445 }
2446 Player(team: "BLU")
2447}
2448"#,
2449 find_position_of("Player").nth_occurrence(3).to_selection(),
2450 );
2451}
2452
2453#[test]
2454fn fill_labels_variable_in_scope_from_case_pattern() {
2455 assert_code_action!(
2456 FILL_LABELS,
2457 r#"
2458pub type Player {
2459 Player(name: String, team: String)
2460}
2461
2462pub fn main(result: Result(String, Nil)) {
2463 case result {
2464 Ok(name) -> Player(team: "BLU")
2465 Error(_) -> Player(team: "RED", name: "Unknown")
2466 }
2467}
2468"#,
2469 find_position_of("Player").nth_occurrence(3).to_selection(),
2470 );
2471}
2472
2473#[test]
2474fn fill_labels_generic_type_matching() {
2475 assert_code_action!(
2476 FILL_LABELS,
2477 r#"
2478pub type Container(a) {
2479 Container(value: a, label: String)
2480}
2481
2482pub fn main() {
2483 let value = 42
2484 let label = "test"
2485 Container()
2486}
2487"#,
2488 find_position_of("Container")
2489 .nth_occurrence(3)
2490 .to_selection(),
2491 );
2492}
2493
2494#[test]
2495fn use_label_shorthand_works_for_nested_calls() {
2496 assert_code_action!(
2497 USE_LABEL_SHORTHAND_SYNTAX,
2498 r#"
2499pub fn wibble(arg arg: Int) -> Int { arg }
2500
2501pub fn main() {
2502 let arg = 1
2503 wibble(wibble(arg: arg))
2504}
2505 "#,
2506 find_position_of("main").select_until(find_position_of("}").nth_occurrence(2)),
2507 );
2508}
2509
2510#[test]
2511fn use_label_shorthand_works_for_nested_record_updates() {
2512 assert_code_action!(
2513 USE_LABEL_SHORTHAND_SYNTAX,
2514 r#"
2515pub type Wibble { Wibble(arg: Int, arg2: Wobble) }
2516pub type Wobble { Wobble(arg: Int, arg2: String) }
2517
2518pub fn main() {
2519 let arg = 1
2520 let arg2 = "a"
2521 Wibble(..todo, arg2: Wobble(arg: arg, arg2: arg2))
2522}
2523 "#,
2524 find_position_of("todo").select_until(find_position_of("arg2: arg2")),
2525 );
2526}
2527
2528#[test]
2529fn use_label_shorthand_works_for_nested_patterns() {
2530 assert_code_action!(
2531 USE_LABEL_SHORTHAND_SYNTAX,
2532 r#"
2533pub type Wibble { Wibble(arg: Int, arg2: Wobble) }
2534pub type Wobble { Wobble(arg: Int, arg2: String) }
2535
2536pub fn main() {
2537 let Wibble(arg2: Wobble(arg: arg, arg2: arg2), ..) = todo
2538}
2539 "#,
2540 find_position_of("main").select_until(find_position_of("todo")),
2541 );
2542}
2543
2544#[test]
2545fn use_label_shorthand_works_for_alternative_patterns() {
2546 assert_code_action!(
2547 USE_LABEL_SHORTHAND_SYNTAX,
2548 r#"
2549pub type Wibble { Wibble(arg: Int, arg2: String) }
2550
2551pub fn main() {
2552 case Wibble(1, "wibble") {
2553 Wibble(arg2: arg2, ..) | Wibble(arg: 1, arg2: arg2) -> todo
2554 }
2555}
2556 "#,
2557 find_position_of("main").select_until(find_position_of("todo")),
2558 );
2559}
2560
2561#[test]
2562fn test_assign_unused_result() {
2563 assert_code_action!(
2564 ASSIGN_UNUSED_RESULT,
2565 r#"
2566pub fn main() {
2567 let x = 1
2568 Ok(x)
2569 Nil
2570}
2571"#,
2572 find_position_of("Ok").select_until(find_position_of("(x)")),
2573 );
2574}
2575
2576#[test]
2577fn test_assign_unused_result_in_block() {
2578 assert_code_action!(
2579 ASSIGN_UNUSED_RESULT,
2580 r#"
2581pub fn main() {
2582 {
2583 let x = 1
2584 Ok(x)
2585 Nil
2586 }
2587 Nil
2588}
2589"#,
2590 find_position_of("Ok").select_until(find_position_of("(x)")),
2591 );
2592}
2593
2594#[test]
2595fn test_assign_unused_result_on_block_start() {
2596 assert_code_action!(
2597 ASSIGN_UNUSED_RESULT,
2598 r#"
2599pub fn main() {
2600 {
2601 let x = 1
2602 Ok(x)
2603 Ok(x)
2604 }
2605 Nil
2606}
2607"#,
2608 find_position_of("{").nth_occurrence(2).to_selection()
2609 );
2610}
2611
2612#[test]
2613fn test_assign_unused_result_on_block_end() {
2614 assert_code_action!(
2615 ASSIGN_UNUSED_RESULT,
2616 r#"
2617pub fn main() {
2618 {
2619 let x = 1
2620 Ok(x)
2621 Ok(x)
2622 }
2623 Nil
2624}
2625"#,
2626 find_position_of("}").to_selection()
2627 );
2628}
2629
2630#[test]
2631#[should_panic(expected = "No action with the given title")]
2632fn test_assign_unused_result_inside_block() {
2633 assert_code_action!(
2634 ASSIGN_UNUSED_RESULT,
2635 r#"
2636pub fn main() {
2637 {
2638 let x = 1
2639 Nil
2640 Ok(x)
2641 }
2642}
2643"#,
2644 find_position_of("Ok").select_until(find_position_of("(x)"))
2645 );
2646}
2647
2648#[test]
2649fn test_assign_unused_result_only_first_action() {
2650 assert_code_action!(
2651 ASSIGN_UNUSED_RESULT,
2652 r#"
2653pub fn main() {
2654 let x = 1
2655 Ok(x)
2656 Ok(x)
2657 Nil
2658}
2659"#,
2660 find_position_of("Ok").select_until(find_position_of("(x)"))
2661 );
2662}
2663
2664#[test]
2665#[should_panic(expected = "No action with the given title")]
2666fn test_assign_unused_result_not_on_return_value() {
2667 assert_code_action!(
2668 ASSIGN_UNUSED_RESULT,
2669 r#"
2670pub fn main() {
2671 let x = 1
2672 Ok(x)
2673}
2674"#,
2675 find_position_of("Ok").select_until(find_position_of("(x)"))
2676 );
2677}
2678
2679#[test]
2680#[should_panic(expected = "No action with the given title")]
2681fn test_assign_unused_result_not_on_return_value_in_block() {
2682 assert_code_action!(
2683 ASSIGN_UNUSED_RESULT,
2684 r#"
2685pub fn main() {
2686 let _ = {
2687 let x = 1
2688 Ok(x)
2689 }
2690 Nil
2691}"#,
2692 find_position_of("Ok").select_until(find_position_of("(x)"))
2693 );
2694}
2695
2696#[test]
2697fn test_import_module_from_function() {
2698 let src = "
2699pub fn main() {
2700 result.is_ok()
2701}
2702";
2703
2704 assert_code_action!(
2705 "Import `result`",
2706 TestProject::for_source(src).add_hex_module("result", "pub fn is_ok() {}"),
2707 find_position_of("result").select_until(find_position_of("."))
2708 );
2709}
2710
2711#[test]
2712fn test_import_path_module_from_function() {
2713 let src = r#"
2714pub fn main() {
2715 io.println("Hello, world!")
2716}
2717"#;
2718
2719 assert_code_action!(
2720 "Import `gleam/io`",
2721 TestProject::for_source(src)
2722 .add_hex_module("gleam/io", "pub fn println(message: String) {}"),
2723 find_position_of("io").select_until(find_position_of("."))
2724 );
2725}
2726
2727#[test]
2728fn test_import_module_from_type() {
2729 let src = "type Wobble = wibble.Wubble";
2730
2731 assert_code_action!(
2732 "Import `mod/wibble`",
2733 TestProject::for_source(src).add_hex_module("mod/wibble", "pub type Wubble { Wubble }"),
2734 find_position_of("wibble").select_until(find_position_of("."))
2735 );
2736}
2737
2738#[test]
2739fn test_import_module_from_constructor() {
2740 let src = "
2741pub fn main() {
2742 let value = values.Value(10)
2743}
2744";
2745
2746 assert_code_action!(
2747 "Import `values`",
2748 TestProject::for_source(src).add_hex_module("values", "pub type Value { Value(Int) }"),
2749 find_position_of("values").select_until(find_position_of("."))
2750 );
2751}
2752
2753#[test]
2754fn test_rename_module_for_imported() {
2755 let src = r#"
2756import gleam/io
2757
2758pub fn main() {
2759 i.println("Hello, world!")
2760}
2761"#;
2762
2763 assert_code_action!(
2764 "Did you mean `io`",
2765 TestProject::for_source(src)
2766 .add_hex_module("gleam/io", "pub fn println(message: String) {}"),
2767 find_position_of("i.").select_until(find_position_of("println"))
2768 );
2769}
2770
2771#[test]
2772fn test_import_similar_module() {
2773 let src = "
2774pub fn main() {
2775 reult.is_ok()
2776}
2777";
2778
2779 assert_code_action!(
2780 "Import `result`",
2781 TestProject::for_source(src).add_hex_module("result", "pub fn is_ok() {}"),
2782 find_position_of("reult").select_until(find_position_of("."))
2783 );
2784}
2785
2786#[test]
2787fn test_no_action_to_import_module_without_value() {
2788 // The language server should not suggest a code action
2789 // to import a module if it doesn't have a value with
2790 // the same name as we are trying to access
2791 let src = "
2792pub fn main() {
2793 io.hello_world()
2794}
2795";
2796
2797 let title = "Import `io`";
2798
2799 assert_no_code_actions!(
2800 title,
2801 TestProject::for_source(src).add_hex_module("io", "pub fn println() {}"),
2802 find_position_of("io").select_until(find_position_of("."))
2803 );
2804}
2805
2806#[test]
2807fn test_no_action_to_import_module_without_type() {
2808 let src = "type Name = int.String";
2809
2810 let title = "Import `int`";
2811
2812 assert_no_code_actions!(
2813 title,
2814 TestProject::for_source(src).add_hex_module("int", ""),
2815 find_position_of("int").select_until(find_position_of("."))
2816 );
2817}
2818
2819#[test]
2820fn test_no_action_to_import_module_with_private_value() {
2821 // The language server should not suggest a code action
2822 // to import a module if the value we are trying to
2823 // access is private.
2824 let src = "
2825pub fn main() {
2826 mod.internal()
2827}
2828";
2829
2830 let title = "Import `mod`";
2831
2832 assert_no_code_actions!(
2833 title,
2834 TestProject::for_source(src).add_hex_module("mod", "fn internal() {}"),
2835 find_position_of("mod").select_until(find_position_of("."))
2836 );
2837}
2838
2839#[test]
2840fn test_no_action_to_import_module_with_private_type() {
2841 let src = "type T = module.T";
2842
2843 let title = "Import `module`";
2844
2845 assert_no_code_actions!(
2846 title,
2847 TestProject::for_source(src).add_hex_module("module", "type T { T }"),
2848 find_position_of("module").select_until(find_position_of("."))
2849 );
2850}
2851
2852#[test]
2853fn test_no_action_to_import_module_with_constructor_named_same_as_type() {
2854 let src = "type NotAType = shapes.Rectangle";
2855
2856 let title = "Import `shapes`";
2857
2858 assert_no_code_actions!(
2859 title,
2860 TestProject::for_source(src)
2861 .add_hex_module("shapes", "pub type Shape { Rectangle, Circle }"),
2862 find_position_of("shapes").select_until(find_position_of("."))
2863 );
2864}
2865
2866#[test]
2867fn add_missing_patterns_bool() {
2868 assert_code_action!(
2869 ADD_MISSING_PATTERNS,
2870 "
2871pub fn main(bool: Bool) {
2872 case bool {}
2873}
2874",
2875 find_position_of("case").select_until(find_position_of("bool {"))
2876 );
2877}
2878
2879#[test]
2880fn add_missing_patterns_does_not_remove_comment() {
2881 assert_code_action!(
2882 ADD_MISSING_PATTERNS,
2883 "
2884pub fn main(bool: Bool) {
2885 case bool {
2886 // Here is some comment that should not be deleted
2887 }
2888}
2889",
2890 find_position_of("case").select_until(find_position_of("bool {"))
2891 );
2892}
2893
2894#[test]
2895fn add_missing_patterns_does_not_remove_comment_2() {
2896 assert_code_action!(
2897 ADD_MISSING_PATTERNS,
2898 "
2899pub fn main(bool: Bool) {
2900 case bool {
2901 // Here is some comment that should not be deleted
2902
2903 // Here is some other separate comment!
2904 // And loads of white space that has to be deleted.
2905
2906
2907
2908
2909
2910 }
2911}
2912",
2913 find_position_of("case").select_until(find_position_of("bool {"))
2914 );
2915}
2916
2917#[test]
2918fn add_missing_patterns_custom_type() {
2919 assert_code_action!(
2920 ADD_MISSING_PATTERNS,
2921 "
2922type Wibble {
2923 Wibble
2924 Wobble
2925 Wubble
2926}
2927
2928pub fn main(wibble: Wibble) {
2929 case wibble {
2930 Wobble -> Nil
2931 }
2932}
2933",
2934 find_position_of("case").select_until(find_position_of("wibble {"))
2935 );
2936}
2937
2938#[test]
2939fn add_missing_patterns_tuple() {
2940 assert_code_action!(
2941 ADD_MISSING_PATTERNS,
2942 "
2943pub fn main(two_at_once: #(Bool, Result(Int, Nil))) {
2944 case two_at_once {
2945 #(False, Error(_)) -> Nil
2946 }
2947}
2948",
2949 find_position_of("case").select_until(find_position_of("two_at_once {"))
2950 );
2951}
2952
2953#[test]
2954fn add_missing_patterns_list() {
2955 assert_code_action!(
2956 ADD_MISSING_PATTERNS,
2957 "
2958pub fn main() {
2959 let list = [1, 2, 3]
2960 case list {
2961 [a, b, c, 4 as d] -> d
2962 }
2963}
2964",
2965 find_position_of("case").select_until(find_position_of("list {"))
2966 );
2967}
2968
2969#[test]
2970fn add_missing_patterns_infinite() {
2971 assert_code_action!(
2972 ADD_MISSING_PATTERNS,
2973 r#"
2974pub fn main() {
2975 let value = 3
2976 case value {
2977 1 -> "one"
2978 2 -> "two"
2979 3 -> "three"
2980 }
2981}
2982"#,
2983 find_position_of("case").select_until(find_position_of("value {"))
2984 );
2985}
2986
2987#[test]
2988fn add_missing_patterns_multi() {
2989 assert_code_action!(
2990 ADD_MISSING_PATTERNS,
2991 r#"
2992pub fn main(a: Bool) {
2993 let b = 1
2994 case a, b {
2995
2996 }
2997}
2998"#,
2999 find_position_of("case").select_until(find_position_of("b {"))
3000 );
3001}
3002
3003#[test]
3004fn add_missing_patterns_inline() {
3005 // Ensure we correctly detect the indentation, if the case expression
3006 // does not start at the beginning of the line
3007 assert_code_action!(
3008 ADD_MISSING_PATTERNS,
3009 r#"
3010pub fn main(a: Bool) {
3011 let value = case a {}
3012}
3013"#,
3014 find_position_of("case").select_until(find_position_of("a {"))
3015 );
3016}
3017
3018#[test]
3019fn import_module_from_pattern() {
3020 let src = "
3021pub fn main(res) {
3022 case res {
3023 result.Ok(_) -> Nil
3024 result.Error(_) -> Nil
3025 }
3026}
3027";
3028
3029 assert_code_action!(
3030 "Import `result`",
3031 TestProject::for_source(src)
3032 .add_hex_module("result", "pub type Result(v, e) { Ok(v) Error(e) }"),
3033 find_position_of("result").select_until(find_position_of("."))
3034 );
3035}
3036
3037#[test]
3038fn annotate_function() {
3039 assert_code_action!(
3040 ADD_ANNOTATIONS,
3041 r#"
3042pub fn add_one(thing) {
3043 thing + 1
3044}
3045"#,
3046 find_position_of("fn").select_until(find_position_of("("))
3047 );
3048}
3049
3050#[test]
3051fn annotate_function_with_annotated_return_type() {
3052 assert_code_action!(
3053 ADD_ANNOTATION,
3054 r#"
3055pub fn add_one(thing) -> Int {
3056 thing + 1
3057}
3058"#,
3059 find_position_of("fn").select_until(find_position_of("("))
3060 );
3061}
3062
3063#[test]
3064fn annotate_function_with_partially_annotated_parameters() {
3065 assert_code_action!(
3066 ADD_ANNOTATION,
3067 r#"
3068pub fn add(a: Float, b) -> Float {
3069 a +. b
3070}
3071"#,
3072 find_position_of("fn").select_until(find_position_of("("))
3073 );
3074}
3075
3076#[test]
3077fn no_code_action_for_fully_annotated_function() {
3078 assert_no_code_actions!(
3079 ADD_ANNOTATION | ADD_ANNOTATIONS,
3080 r#"
3081pub fn do_a_thing(a: Int, b: Float) -> String {
3082 todo
3083}
3084"#,
3085 find_position_of("fn").select_until(find_position_of("("))
3086 );
3087}
3088
3089#[test]
3090fn annotate_anonymous_function() {
3091 assert_code_action!(
3092 ADD_ANNOTATIONS,
3093 r#"
3094pub fn add_curry(a) {
3095 fn(b) { a + b }
3096}
3097"#,
3098 find_position_of("fn(").select_until(find_position_of("b)"))
3099 );
3100}
3101
3102#[test]
3103fn annotate_anonymous_function_with_annotated_return_type() {
3104 assert_code_action!(
3105 ADD_ANNOTATION,
3106 r#"
3107pub fn add_curry(a) {
3108 fn(b) -> Int { a + b }
3109}
3110"#,
3111 find_position_of("fn(").select_until(find_position_of("b)"))
3112 );
3113}
3114
3115#[test]
3116fn annotate_anonymous_function_with_partially_annotated_parameters() {
3117 assert_code_action!(
3118 ADD_ANNOTATIONS,
3119 r#"
3120pub fn main() {
3121 fn(a, b: Int, c) { a + b + c }
3122}
3123"#,
3124 find_position_of("fn(").select_until(find_position_of("c)"))
3125 );
3126}
3127
3128#[test]
3129fn no_code_action_for_fully_annotated_anonymous_function() {
3130 assert_no_code_actions!(
3131 ADD_ANNOTATION | ADD_ANNOTATIONS,
3132 r#"
3133pub fn main() {
3134 fn(a: Int, b: Int) -> Int { a - b }
3135}
3136"#,
3137 find_position_of("fn(").select_until(find_position_of("Int)"))
3138 );
3139}
3140
3141#[test]
3142fn annotate_use() {
3143 assert_code_action!(
3144 ADD_ANNOTATIONS,
3145 r#"
3146pub fn wibble(wobble: fn(Int, Int) -> Int) {
3147 wobble(1, 2)
3148}
3149
3150pub fn main() {
3151 use a, b <- wibble
3152 a + b
3153}
3154"#,
3155 find_position_of("use").select_until(find_position_of("<-"))
3156 );
3157}
3158
3159#[test]
3160fn annotate_use_with_partially_annotated_parameters() {
3161 assert_code_action!(
3162 ADD_ANNOTATION,
3163 r#"
3164pub fn wibble(wobble: fn(Int, Int) -> Int) {
3165 wobble(1, 2)
3166}
3167
3168pub fn main() {
3169 use a: Int, b <- wibble
3170 a + b
3171}
3172"#,
3173 find_position_of("use").select_until(find_position_of("<-"))
3174 );
3175}
3176
3177#[test]
3178fn no_code_action_for_fully_annotated_use() {
3179 assert_no_code_actions!(
3180 ADD_ANNOTATION | ADD_ANNOTATIONS,
3181 r#"
3182pub fn wibble(wobble: fn(Int, Int) -> Int) {
3183 wobble(1, 2)
3184}
3185
3186pub fn main() {
3187 use a: Int, b: Int <- wibble
3188 a + b
3189}
3190"#,
3191 find_position_of("use").select_until(find_position_of("<-"))
3192 );
3193}
3194
3195#[test]
3196fn annotate_constant() {
3197 assert_code_action!(
3198 ADD_ANNOTATION,
3199 r#"
3200pub const my_constant = 20
3201"#,
3202 find_position_of("const").select_until(find_position_of("="))
3203 );
3204}
3205
3206#[test]
3207fn no_code_action_for_annotated_constant() {
3208 assert_no_code_actions!(
3209 ADD_ANNOTATION | ADD_ANNOTATIONS,
3210 r#"
3211pub const PI: Float = 3.14159
3212"#,
3213 find_position_of("const").select_until(find_position_of("="))
3214 );
3215}
3216
3217#[test]
3218fn annotate_local_variable() {
3219 assert_code_action!(
3220 ADD_ANNOTATION,
3221 r#"
3222pub fn main() {
3223 let my_value = 10
3224}
3225"#,
3226 find_position_of("let").select_until(find_position_of("="))
3227 );
3228}
3229
3230#[test]
3231fn annotate_local_variable_with_pattern() {
3232 assert_code_action!(
3233 ADD_ANNOTATION,
3234 r#"
3235type Wibble {
3236 Wibble(a: Int, b: Int, c: Int)
3237}
3238
3239pub fn main() {
3240 let Wibble(a, b, c) = Wibble(1, 2, 3)
3241}
3242"#,
3243 find_position_of("let").select_until(find_position_of("="))
3244 );
3245}
3246
3247#[test]
3248fn annotate_local_variable_with_pattern2() {
3249 assert_code_action!(
3250 ADD_ANNOTATION,
3251 r#"
3252pub fn main(values) {
3253 let #(left, right) = values
3254}
3255"#,
3256 find_position_of("let").select_until(find_position_of("="))
3257 );
3258}
3259
3260#[test]
3261fn annotate_local_variable_let_assert() {
3262 assert_code_action!(
3263 ADD_ANNOTATION,
3264 r#"
3265pub fn fallible() -> Result(Int, Nil) {
3266 todo
3267}
3268
3269pub fn main() {
3270 let assert Ok(value) = fallible()
3271}
3272"#,
3273 find_position_of("let").select_until(find_position_of("="))
3274 );
3275}
3276
3277#[test]
3278fn annotate_nested_local_variable() {
3279 assert_code_action!(
3280 ADD_ANNOTATION,
3281 r#"
3282pub fn main() {
3283 let a = {
3284 let b = 10
3285 b + 1
3286 }
3287}
3288"#,
3289 find_position_of("let b").select_until(find_position_of("b ="))
3290 );
3291}
3292
3293#[test]
3294fn no_code_action_for_annotated_local_variable() {
3295 assert_no_code_actions!(
3296 ADD_ANNOTATION | ADD_ANNOTATIONS,
3297 r#"
3298pub fn main() {
3299 let typed: Int = 1.2
3300}
3301"#,
3302 find_position_of("let").select_until(find_position_of("="))
3303 );
3304}
3305
3306#[test]
3307fn adding_annotations_correctly_prints_type_variables() {
3308 assert_code_action!(
3309 ADD_ANNOTATIONS,
3310 r#"
3311pub fn map_result(input, function) {
3312 case input {
3313 Ok(value) -> Ok(function(value))
3314 Error(error) -> Error(error)
3315 }
3316}
3317"#,
3318 find_position_of("fn").select_until(find_position_of("("))
3319 );
3320}
3321
3322#[test]
3323fn add_multiple_annotations() {
3324 assert_code_action!(
3325 ADD_ANNOTATIONS,
3326 r#"
3327pub const my_constant = 20
3328
3329pub fn add_my_constant(value) {
3330 let result = value + my_constant
3331 result
3332}
3333"#,
3334 find_position_of("pub const").select_until(find_position_of("}"))
3335 );
3336}
3337
3338#[test]
3339fn different_annotations_create_compatible_type_variables() {
3340 assert_code_action!(
3341 ADD_ANNOTATIONS,
3342 r#"
3343pub fn do_generic_things(a, b) {
3344 let a_value = a
3345 let b_value = b
3346 let other_value = a_value
3347}
3348"#,
3349 find_position_of("let a_value").select_until(find_position_of("}"))
3350 );
3351}
3352
3353#[test]
3354fn adding_annotations_prints_type_variable_names() {
3355 assert_code_action!(
3356 ADD_ANNOTATIONS,
3357 r#"
3358pub fn do_generic_things(a: type_a, b: type_b) {
3359 let a_value = a
3360 let b_value = b
3361 let other_value = a_value
3362}
3363"#,
3364 find_position_of("let a_value").select_until(find_position_of("}"))
3365 );
3366}
3367
3368#[test]
3369fn adding_annotations_prints_contextual_types() {
3370 assert_code_action!(
3371 ADD_ANNOTATION,
3372 r#"
3373pub type IntAlias = Int
3374
3375pub fn main() {
3376 let value = 20
3377}
3378"#,
3379 find_position_of("let").select_until(find_position_of("value"))
3380 );
3381}
3382
3383#[test]
3384fn adding_annotations_prints_contextual_types2() {
3385 assert_code_action!(
3386 ADD_ANNOTATION,
3387 r#"
3388pub type Result
3389
3390pub fn main() {
3391 let value = Ok(12)
3392}
3393"#,
3394 find_position_of("let").select_until(find_position_of("="))
3395 );
3396}
3397
3398#[test]
3399fn adding_annotations_prints_contextual_types3() {
3400 let src = r#"
3401import wibble
3402
3403pub fn main() {
3404 let value = wibble.Wibble
3405}
3406"#;
3407
3408 assert_code_action!(
3409 ADD_ANNOTATION,
3410 TestProject::for_source(src).add_hex_module("wibble", "pub type Wibble { Wibble }"),
3411 find_position_of("let").select_until(find_position_of("="))
3412 );
3413}
3414
3415#[test]
3416fn add_annotation_triggers_on_function_curly_brace() {
3417 assert_code_action!(
3418 ADD_ANNOTATION,
3419 "pub fn main() { 1 }",
3420 find_position_of("{").to_selection(),
3421 );
3422}
3423
3424#[test]
3425fn add_annotation_triggers_on_empty_space_before_function_curly_brace() {
3426 assert_code_action!(
3427 ADD_ANNOTATION,
3428 "pub fn main() { 1 }",
3429 find_position_of(" ").nth_occurrence(3).to_selection(),
3430 );
3431}
3432
3433#[test]
3434fn adding_annotations_prints_contextual_types4() {
3435 let src = r#"
3436import wibble as wobble
3437
3438pub fn main() {
3439 let value = wobble.Wibble
3440}
3441"#;
3442
3443 assert_code_action!(
3444 ADD_ANNOTATION,
3445 TestProject::for_source(src).add_hex_module("wibble", "pub type Wibble { Wibble }"),
3446 find_position_of("let").select_until(find_position_of("="))
3447 );
3448}
3449
3450#[test]
3451fn adding_annotations_prints_contextual_types5() {
3452 let src = r#"
3453import wibble.{type Wibble as Wobble}
3454
3455pub fn main() {
3456 let value = wibble.Wibble
3457}
3458"#;
3459
3460 assert_code_action!(
3461 ADD_ANNOTATION,
3462 TestProject::for_source(src).add_hex_module("wibble", "pub type Wibble { Wibble }"),
3463 find_position_of("let").select_until(find_position_of("="))
3464 );
3465}
3466
3467#[test]
3468// https://github.com/gleam-lang/gleam/issues/3789
3469fn no_code_actions_to_add_annotations_for_pipe() {
3470 assert_no_code_actions!(
3471 ADD_ANNOTATION | ADD_ANNOTATIONS,
3472 r#"
3473fn do_something(a: Int) { a }
3474
3475pub fn main() {
3476 10 |> do_something
3477}
3478"#,
3479 find_position_of("10").select_until(find_position_of("|>"))
3480 );
3481}
3482
3483#[test]
3484// https://github.com/gleam-lang/gleam/issues/3789#issuecomment-2455805734
3485fn add_correct_type_annotation_for_non_variable_use() {
3486 assert_code_action!(
3487 ADD_ANNOTATION,
3488 r#"
3489fn usable(f) {
3490 f(#(1, 2))
3491}
3492
3493pub fn main() {
3494 use #(a, b) <- usable
3495 a + b
3496}
3497"#,
3498 find_position_of("use").select_until(find_position_of("b)"))
3499 );
3500}
3501
3502#[test]
3503fn test_qualified_to_unqualified_import_basic_with_argument() {
3504 let src = r#"
3505import option
3506
3507pub fn main() {
3508 option.Some(1)
3509}
3510"#;
3511 assert_code_action!(
3512 "Unqualify option.Some",
3513 TestProject::for_source(src)
3514 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
3515 find_position_of(".Some").select_until(find_position_of("(1)")),
3516 );
3517}
3518
3519#[test]
3520fn test_qualified_to_unqualified_import_basic_record_without_argument() {
3521 let src = r#"
3522import wobble
3523
3524pub fn main() {
3525 wobble.Wibble
3526}
3527"#;
3528 assert_code_action!(
3529 "Unqualify wobble.Wibble",
3530 TestProject::for_source(src).add_hex_module("wobble", "pub type Wobble { Wibble }"),
3531 find_position_of(".W").select_until(find_position_of("ibble"))
3532 );
3533}
3534
3535#[test]
3536fn test_qualified_to_unqualified_import_custom_type_record_declaration() {
3537 let src = r#"
3538import wobble
3539
3540pub type Wibble {
3541 Wibble(wibble: wobble.Wobble)
3542}
3543"#;
3544 assert_code_action!(
3545 "Unqualify wobble.Wobble",
3546 TestProject::for_source(src).add_hex_module("wobble", "pub type Wobble { Wibble }"),
3547 find_position_of(".").select_until(find_position_of("Wobble"))
3548 );
3549}
3550
3551#[test]
3552fn test_qualified_to_unqualified_import_basic_type_without_argument() {
3553 let src = r#"
3554import wobble
3555
3556pub fn identity(x: wobble.Wobble) -> wobble.Wobble {
3557 x
3558}
3559"#;
3560 assert_code_action!(
3561 "Unqualify wobble.Wobble",
3562 TestProject::for_source(src).add_hex_module("wobble", "pub type Wobble { Wibble }"),
3563 find_position_of(".").select_until(find_position_of("Wobble"))
3564 );
3565}
3566
3567#[test]
3568fn test_qualified_to_unqualified_record_value_constructor_module_name() {
3569 let src = r#"
3570import option
3571
3572pub fn main() {
3573 option.Some(1)
3574}
3575"#;
3576 assert_code_action!(
3577 "Unqualify option.Some",
3578 TestProject::for_source(src)
3579 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
3580 find_position_of("option").nth_occurrence(2).to_selection()
3581 );
3582}
3583
3584#[test]
3585fn test_qualified_to_unqualified_import_basic_multiple() {
3586 let src = r#"
3587import option
3588
3589pub fn main() {
3590 option.Some(1)
3591 option.Some(1)
3592 todo
3593}
3594"#;
3595 assert_code_action!(
3596 "Unqualify option.Some",
3597 TestProject::for_source(src)
3598 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
3599 find_position_of(".Some").select_until(find_position_of("(1)")),
3600 );
3601}
3602
3603#[test]
3604fn test_qualified_to_unqualified_import_when_unqualified_exists() {
3605 let src = r#"
3606import option.{Some}
3607
3608pub fn main() {
3609 option.Some(1)
3610}
3611"#;
3612 assert_code_action!(
3613 "Unqualify option.Some",
3614 TestProject::for_source(src)
3615 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
3616 find_position_of(".Some").select_until(find_position_of("(1)")),
3617 );
3618}
3619
3620#[test]
3621fn test_qualified_to_unqualified_import_with_comma() {
3622 let src = r#"
3623import option.{None, }
3624
3625pub fn main() {
3626 option.Some(1)
3627}
3628"#;
3629 assert_code_action!(
3630 "Unqualify option.Some",
3631 TestProject::for_source(src)
3632 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
3633 find_position_of(".Some").select_until(find_position_of("(1)")),
3634 );
3635}
3636
3637#[test]
3638fn test_qualified_to_unqualified_import_with_comma_pos_not_end() {
3639 let src = r#"
3640import option.{None, } as opt
3641
3642pub fn main() {
3643 opt.Some(1)
3644}
3645"#;
3646 assert_code_action!(
3647 "Unqualify opt.Some",
3648 TestProject::for_source(src)
3649 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
3650 find_position_of(".Some").select_until(find_position_of("(1)")),
3651 );
3652}
3653
3654#[test]
3655fn test_qualified_to_unqualified_import_different_constructors() {
3656 let src = r#"
3657import option
3658
3659pub fn main() {
3660 option.Some(1)
3661 option.None
3662}"#;
3663 assert_code_action!(
3664 "Unqualify option.Some",
3665 TestProject::for_source(src)
3666 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
3667 find_position_of(".Some").select_until(find_position_of("(1)")),
3668 );
3669}
3670
3671#[test]
3672fn test_qualified_to_unqualified_import_no_action_when_already_unqualified() {
3673 let src = r#"
3674import option.{Some, None}
3675
3676pub fn main() {
3677 Some(1)
3678 Some(1)
3679 todo
3680}
3681"#;
3682 let title = "Unqualify option.Some";
3683 assert_no_code_actions!(
3684 title,
3685 TestProject::for_source(src)
3686 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
3687 find_position_of("Some(").select_until(find_position_of("1)")),
3688 );
3689}
3690
3691#[test]
3692fn test_qualified_to_unqualified_import_with_alias() {
3693 let src = r#"
3694import option as opt
3695
3696pub fn main() {
3697 opt.Some(1)
3698}
3699"#;
3700 assert_code_action!(
3701 "Unqualify opt.Some",
3702 TestProject::for_source(src)
3703 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
3704 find_position_of("opt.Some").select_until(find_position_of("(1)")),
3705 );
3706}
3707
3708#[test]
3709fn test_qualified_to_unqualified_import_with_alias_multiple() {
3710 let src = r#"
3711import option as opt
3712
3713pub fn main() {
3714 opt.Some(1)
3715 opt.Some(1)
3716}
3717
3718pub fn identity(x: opt.Option(Int)) -> opt.Option(Int) {
3719 opt.Some(1)
3720 x
3721}
3722"#;
3723 assert_code_action!(
3724 "Unqualify opt.Some",
3725 TestProject::for_source(src)
3726 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
3727 find_position_of("opt.Some").select_until(find_position_of("(1)")),
3728 );
3729}
3730
3731#[test]
3732fn test_qualified_to_unqualified_import_multiple_imports() {
3733 let src = r#"
3734import option
3735import wobble
3736
3737pub fn main() {
3738 option.Some(2)
3739 wobble.Wibble(1)
3740}
3741"#;
3742 assert_code_action!(
3743 "Unqualify wobble.Wibble",
3744 TestProject::for_source(src)
3745 .add_hex_module("option", "pub type Option(v) { Some(v) None }")
3746 .add_hex_module("wobble", "pub type Wobble { Wibble(Int)} "),
3747 find_position_of(".Wibble").select_until(find_position_of("(1)")),
3748 );
3749}
3750
3751#[test]
3752fn test_qualified_to_unqualified_import_in_case_with_argument() {
3753 let src = r#"
3754import option
3755
3756pub fn main(x) {
3757 case option.Some(1) {
3758 option.Some(value) -> value
3759 option.None -> 0
3760 }
3761}
3762"#;
3763 assert_code_action!(
3764 "Unqualify option.Some",
3765 TestProject::for_source(src)
3766 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
3767 find_position_of(".Some(").select_until(find_position_of("(1)"))
3768 );
3769}
3770
3771#[test]
3772fn test_qualified_to_unqualified_import_in_case_without_argument() {
3773 let src = r#"
3774import wobble
3775
3776pub fn main() {
3777 case wobble.Wibble {
3778 wobble.Wibble -> 1
3779 wobble.Wubble(1) -> 2
3780 }
3781}
3782"#;
3783 assert_code_action!(
3784 "Unqualify wobble.Wibble",
3785 TestProject::for_source(src)
3786 .add_hex_module("wobble", "pub type Wobble { Wibble Wubble(Int) }"),
3787 find_position_of(".W").select_until(find_position_of("ibble"))
3788 );
3789}
3790
3791#[test]
3792fn test_qualified_to_unqualified_import_in_pattern() {
3793 let src = r#"
3794import option
3795
3796pub fn main() -> Int {
3797 case option.Some(1) {
3798 option.Some(value) -> value
3799 option.None -> 0
3800 }
3801}
3802"#;
3803 assert_code_action!(
3804 "Unqualify option.Some",
3805 TestProject::for_source(src)
3806 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
3807 find_position_of(".Some(va").select_until(find_position_of("lue)")),
3808 );
3809}
3810
3811#[test]
3812fn test_qualified_to_unqualified_import_in_pattern_without_argument() {
3813 let src = r#"
3814import wobble
3815
3816pub fn main() {
3817 case wobble.Wibble {
3818 wobble.Wibble -> 1
3819 wobble.Wubble(1) -> 2
3820 }
3821 let wob = wobble.Wibble
3822 todo
3823}
3824"#;
3825 assert_code_action!(
3826 "Unqualify wobble.Wibble",
3827 TestProject::for_source(src)
3828 .add_hex_module("wobble", "pub type Wobble { Wibble Wubble(Int) }"),
3829 find_position_of("wobble.W").select_until(find_position_of("ibble"))
3830 );
3831}
3832
3833#[test]
3834fn test_qualified_to_unqualified_import_type() {
3835 let src = r#"
3836import option
3837
3838pub fn main(x) -> option.Option(Int) {
3839 option.Some(1)
3840}
3841"#;
3842 assert_code_action!(
3843 "Unqualify option.Option",
3844 TestProject::for_source(src)
3845 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
3846 find_position_of(".Option").select_until(find_position_of("(Int)")),
3847 );
3848}
3849
3850#[test]
3851fn test_qualified_to_unqualified_only_triggers_within_qualified_value() {
3852 let src = r#"
3853import option
3854
3855pub fn main(x) -> option.Option(Int) {
3856 option.Some(1)
3857}
3858// end
3859"#;
3860 assert_no_code_actions!(
3861 "Unqualify option.Option",
3862 TestProject::for_source(src)
3863 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
3864 find_position_of("import").select_until(find_position_of("// end")),
3865 );
3866}
3867
3868#[test]
3869fn test_qualified_to_unqualified_import_nested_type_outer() {
3870 let src = r#"
3871import option
3872import wobble
3873pub fn main(x) -> option.Option(wobble.Wibble) {
3874 todo
3875}
3876"#;
3877 assert_code_action!(
3878 "Unqualify option.Option",
3879 TestProject::for_source(src)
3880 .add_hex_module("option", "pub type Option(v) { Some(v) None }")
3881 .add_hex_module("wobble", "pub type Wibble { Wobble(Int) }"),
3882 find_position_of(".O").select_until(find_position_of("ption(")),
3883 );
3884}
3885
3886#[test]
3887fn test_qualified_to_unqualified_import_nested_constructor_outer() {
3888 let src = r#"
3889import option
3890import wobble
3891pub fn main(x) -> option.Option(wobble.Wibble) {
3892 option.Some(wobble.Wobble(1))
3893}
3894"#;
3895 assert_code_action!(
3896 "Unqualify option.Some",
3897 TestProject::for_source(src)
3898 .add_hex_module("option", "pub type Option(v) { Some(v) None }")
3899 .add_hex_module("wobble", "pub type Wibble { Wobble(Int) }"),
3900 find_position_of(".S").select_until(find_position_of("ome(")),
3901 );
3902}
3903
3904#[test]
3905fn test_qualified_to_unqualified_import_nested_constructor_inner() {
3906 let src = r#"
3907import option
3908import wobble
3909
3910pub fn main(x) -> option.Option(wobble.Wibble) {
3911 option.Some(wobble.Wobble(1))
3912}
3913"#;
3914 assert_code_action!(
3915 "Unqualify wobble.Wobble",
3916 TestProject::for_source(src)
3917 .add_hex_module("option", "pub type Option(v) { Some(v) None }")
3918 .add_hex_module("wobble", "pub type Wibble { Wobble(Int) }"),
3919 find_position_of(".Wobble").select_until(find_position_of("(1)")),
3920 );
3921}
3922
3923#[test]
3924fn test_qualified_to_unqualified_import_nested_type_inner() {
3925 let src = r#"
3926import option
3927import wobble
3928
3929pub fn main(x) -> option.Option(wobble.Wibble) {
3930 todo
3931}
3932"#;
3933 assert_code_action!(
3934 "Unqualify wobble.Wibble",
3935 TestProject::for_source(src)
3936 .add_hex_module("option", "pub type Option(v) { Some(v) None }")
3937 .add_hex_module("wobble", "pub type Wibble { Wobble(Int) }"),
3938 find_position_of("wobble.").select_until(find_position_of("Wibble")),
3939 );
3940}
3941
3942#[test]
3943fn test_qualified_to_unqualified_aliased_type() {
3944 let src = r#"
3945import wobble
3946
3947pub fn main(x) -> wobble.Wibble(a) {
3948 todo
3949}
3950"#;
3951 assert_code_action!(
3952 "Unqualify wobble.Wibble",
3953 TestProject::for_source(src).add_hex_module("wobble", "pub type Wibble(a) = List(a)"),
3954 find_position_of("wobble.").select_until(find_position_of("Wibble")),
3955 );
3956}
3957
3958#[test]
3959fn test_qualified_to_unqualified_aliased_type_with_multiple_imports() {
3960 let src = r#"
3961import other/wobble as other
3962import wibble/wobble
3963
3964pub fn main(x) -> wobble.Wibble(a) {
3965 todo
3966}
3967"#;
3968 assert_code_action!(
3969 "Unqualify wobble.Wibble",
3970 TestProject::for_source(src)
3971 .add_hex_module("wibble/wobble", "pub type Wibble(a) = List(a)")
3972 .add_hex_module("other/wobble", "pub type Wibble(a) = List(a)"),
3973 find_position_of("wobble.").select_until(find_position_of("Wibble")),
3974 );
3975}
3976
3977#[test]
3978fn test_qualified_aliased_to_unqualified_aliased_type() {
3979 let src = r#"
3980import wobble as wob
3981
3982pub fn main(x) -> wob.Wibble(a) {
3983 todo
3984}
3985"#;
3986 assert_code_action!(
3987 "Unqualify wob.Wibble",
3988 TestProject::for_source(src).add_hex_module("wobble", "pub type Wibble(a) = List(a)"),
3989 find_position_of("wob.").select_until(find_position_of("Wibble")),
3990 );
3991}
3992
3993#[test]
3994fn test_qualified_to_unqualified_import_below_constructor() {
3995 let src = r#"
3996
3997pub fn main() {
3998 option.Some(1)
3999}
4000
4001import option
4002"#;
4003 assert_code_action!(
4004 "Unqualify option.Some",
4005 TestProject::for_source(src)
4006 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
4007 find_position_of(".Some").select_until(find_position_of("(1)")),
4008 );
4009}
4010
4011#[test]
4012fn test_qualified_to_unqualified_import_between_constructors() {
4013 let src = r#"
4014
4015pub fn main() {
4016 option.Some(1)
4017}
4018
4019import option
4020
4021pub fn identity(x: option.Option(Int)) -> option.Option(Int) {
4022 option.Some(1)
4023 x
4024}
4025"#;
4026 assert_code_action!(
4027 "Unqualify option.Some",
4028 TestProject::for_source(src)
4029 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
4030 find_position_of(".Some").select_until(find_position_of("(1)")),
4031 );
4032}
4033
4034#[test]
4035fn test_qualified_to_unqualified_import_multiple_line() {
4036 let src = r#"
4037import option.{
4038 type Option,
4039 None,
4040}
4041
4042pub fn main() {
4043 option.Some(1)
4044}
4045"#;
4046 assert_code_action!(
4047 "Unqualify option.Some",
4048 TestProject::for_source(src)
4049 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
4050 find_position_of(".Some").select_until(find_position_of("(1)")),
4051 );
4052}
4053
4054#[test]
4055fn test_qualified_to_unqualified_import_multiple_line_bad_format_with_trailing_comma() {
4056 let src = r#"
4057import option.{type Option,
4058 None,
4059
4060}
4061
4062pub fn main() {
4063 option.Some(1)
4064}
4065"#;
4066 assert_code_action!(
4067 "Unqualify option.Some",
4068 TestProject::for_source(src)
4069 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
4070 find_position_of(".Some").select_until(find_position_of("(1)")),
4071 );
4072}
4073
4074#[test]
4075fn test_qualified_to_unqualified_import_multiple_line_bad_format_multiple_whitespace() {
4076 let src = r#"
4077import option.{ }
4078
4079pub fn main() {
4080 option.Some(1)
4081}
4082"#;
4083 assert_code_action!(
4084 "Unqualify option.Some",
4085 TestProject::for_source(src)
4086 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
4087 find_position_of(".Some").select_until(find_position_of("(1)")),
4088 );
4089}
4090#[test]
4091fn test_qualified_to_unqualified_import_multiple_line_bad_format_without_trailing_comma() {
4092 let src = r#"
4093import option.{type Option,
4094 None
4095
4096}
4097
4098pub fn main() {
4099 option.Some(1)
4100}
4101"#;
4102 assert_code_action!(
4103 "Unqualify option.Some",
4104 TestProject::for_source(src)
4105 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
4106 find_position_of(".Some").select_until(find_position_of("(1)")),
4107 );
4108}
4109#[test]
4110fn test_qualified_to_unqualified_import_multiple_line_aliased() {
4111 let src = r#"
4112import option.{
4113 type Option,
4114 None} as opt
4115
4116pub fn main() {
4117 opt.Some(1)
4118}
4119"#;
4120 assert_code_action!(
4121 "Unqualify opt.Some",
4122 TestProject::for_source(src)
4123 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
4124 find_position_of(".Some").select_until(find_position_of("(1)")),
4125 );
4126}
4127
4128#[test]
4129fn test_qualified_to_unqualified_import_in_list_and_tuple() {
4130 let src = r#"
4131import option
4132
4133pub fn main() {
4134 let list = [option.Some(1), option.None]
4135 let tuple = #(option.Some(2), option.None)
4136}
4137"#;
4138 assert_code_action!(
4139 "Unqualify option.Some",
4140 TestProject::for_source(src)
4141 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
4142 find_position_of("option.Some").select_until(find_position_of("(1)")),
4143 );
4144}
4145
4146#[test]
4147fn test_qualified_to_unqualified_import_multiple_generic_type() {
4148 let src = r#"
4149import result
4150
4151pub fn main() -> result.Result(Int, String) {
4152 result.Ok(1)
4153}
4154"#;
4155 assert_code_action!(
4156 "Unqualify result.Result",
4157 TestProject::for_source(src)
4158 .add_hex_module("result", "pub type Result(a, e) { Ok(a) Error(e) }"),
4159 find_position_of(".Result").select_until(find_position_of("(Int")),
4160 );
4161}
4162
4163#[test]
4164fn test_qualified_to_unqualified_import_constructor_as_argument() {
4165 let src = r#"
4166import option
4167
4168pub fn main() {
4169 option.map(option.Some(1), fn(x) { x + 1 })
4170}
4171"#;
4172 assert_code_action!(
4173 "Unqualify option.Some",
4174 TestProject::for_source(src).add_hex_module(
4175 "option",
4176 "
4177 pub type Option(v) { Some(v) None }
4178 pub fn map(a, f) { todo }
4179 "
4180 ),
4181 find_position_of("option.Some").select_until(find_position_of("(1)")),
4182 );
4183}
4184
4185#[test]
4186fn test_qualified_to_unqualified_import_constructor_different_module_same_type_inner() {
4187 let src = r#"
4188import option
4189import opt
4190
4191pub fn main() -> option.Option(opt.Option(Int)) {
4192 todo
4193}
4194"#;
4195 assert_code_action!(
4196 "Unqualify opt.Option",
4197 TestProject::for_source(src)
4198 .add_hex_module("option", "pub type Option(v) { Some(v) None }")
4199 .add_hex_module("opt", "pub type Option(v) { Some(v) None }"),
4200 find_position_of("opt.Option").select_until(find_position_of("(Int)")),
4201 );
4202}
4203#[test]
4204fn test_qualified_to_unqualified_import_constructor_different_module_same_type_outer() {
4205 let src = r#"
4206import option
4207import opt
4208
4209pub fn main() -> option.Option(opt.Option(Int)) {
4210 todo
4211}
4212"#;
4213 assert_code_action!(
4214 "Unqualify option.Option",
4215 TestProject::for_source(src)
4216 .add_hex_module("option", "pub type Option(v) { Some(v) None }")
4217 .add_hex_module("opt", "pub type Option(v) { Some(v) None }"),
4218 find_position_of("option.").select_until(find_position_of("Option(")),
4219 );
4220}
4221#[test]
4222fn test_qualified_to_unqualified_import_constructor_different_module_same_name_inner() {
4223 let src = r#"
4224import option
4225import opt
4226
4227pub fn main() {
4228 option.Some(opt.Some(1))
4229 todo
4230}
4231"#;
4232 assert_code_action!(
4233 "Unqualify opt.Some",
4234 TestProject::for_source(src)
4235 .add_hex_module("option", "pub type Option(v) { Some(v) None }")
4236 .add_hex_module("opt", "pub type Option(v) { Some(v) None }"),
4237 find_position_of("opt.Some").select_until(find_position_of("(1)")),
4238 );
4239}
4240#[test]
4241fn test_qualified_to_unqualified_import_constructor_different_module_same_name_outer() {
4242 let src = r#"
4243import option
4244import opt
4245
4246pub fn main() {
4247 option.Some(opt.Some(1))
4248}
4249"#;
4250 assert_code_action!(
4251 "Unqualify option.Some",
4252 TestProject::for_source(src)
4253 .add_hex_module("option", "pub type Option(v) { Some(v) None }")
4254 .add_hex_module("opt", "pub type Option(v) { Some(v) None }"),
4255 find_position_of("option.").select_until(find_position_of("Some(")),
4256 );
4257}
4258
4259#[test]
4260fn test_qualified_to_unqualified_import_constructor_complex_pattern() {
4261 let src = r#"
4262import option
4263
4264pub fn main() {
4265 case [option.Some(1), option.None] {
4266 [option.None, ..] -> todo
4267 [option.Some(_), ..] -> todo
4268 _ -> todo
4269 }
4270 case option.Some(1), option.Some(2) {
4271 option.None, option.Some(_) -> todo
4272 option.Some(_), option.Some(val) -> todo
4273 _ -> todo
4274 }
4275}
4276"#;
4277 assert_code_action!(
4278 "Unqualify option.Some",
4279 TestProject::for_source(src)
4280 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
4281 find_position_of("option.").select_until(find_position_of("Some(")),
4282 );
4283}
4284
4285#[test]
4286fn test_qualified_to_unqualified_import_constructor_constructor_name_exists() {
4287 let src = r#"
4288import option.{Some}
4289import opt
4290
4291pub fn main() -> option.Option(opt.Option(Int)) {
4292 Some(opt.Some(1))
4293}
4294"#;
4295 let title = "Unqualify opt.Some";
4296 assert_no_code_actions!(
4297 title,
4298 TestProject::for_source(src)
4299 .add_hex_module("option", "pub type Option(v) { Some(v) None }")
4300 .add_hex_module("opt", "pub type Option(v) { Some(v) None }"),
4301 find_position_of("opt.").select_until(find_position_of(".Some(")),
4302 );
4303}
4304
4305#[test]
4306fn test_qualified_to_unqualified_import_constructor_constructor_name_exists_below() {
4307 let src = r#"
4308import opt
4309
4310pub fn main() -> option.Option(opt.Option(Int)) {
4311 Some(opt.Some(1))
4312}
4313import option.{Some}
4314"#;
4315 let title = "Unqualify opt.Some";
4316 assert_no_code_actions!(
4317 title,
4318 TestProject::for_source(src)
4319 .add_hex_module("option", "pub type Option(v) { Some(v) None }")
4320 .add_hex_module("opt", "pub type Option(v) { Some(v) None }"),
4321 find_position_of("opt.").select_until(find_position_of(".Some(")),
4322 );
4323}
4324
4325#[test]
4326fn test_qualified_to_unqualified_import_type_constructor_constructor_name_exists() {
4327 let src = r#"
4328import option.{type Option}
4329import opt
4330
4331pub fn main() -> Option(opt.Option(Int)) {
4332 option.Some(opt.Some(1))
4333}
4334"#;
4335 let title = "Unqualify opt.Option";
4336 assert_no_code_actions!(
4337 title,
4338 TestProject::for_source(src)
4339 .add_hex_module("option", "pub type Option(v) { Some(v) None }")
4340 .add_hex_module("opt", "pub type Option(v) { Some(v) None }"),
4341 find_position_of("opt.").select_until(find_position_of(".Option(")),
4342 );
4343}
4344
4345#[test]
4346fn test_qualified_to_unqualified_import_type_constructor_constructor_name_exists_below() {
4347 let src = r#"
4348import opt
4349
4350pub fn main() -> Option(opt.Option(Int)) {
4351 option.Some(opt.Some(1))
4352}
4353import option.{type Option}
4354"#;
4355 let title = "Unqualify opt.Option";
4356 assert_no_code_actions!(
4357 title,
4358 TestProject::for_source(src)
4359 .add_hex_module("option", "pub type Option(v) { Some(v) None }")
4360 .add_hex_module("opt", "pub type Option(v) { Some(v) None }"),
4361 find_position_of("opt.").select_until(find_position_of(".Option(")),
4362 );
4363}
4364
4365#[test]
4366fn test_qualified_to_unqualified_import_in_constant_var() {
4367 let src = r#"
4368import option
4369
4370const none = option.None
4371"#;
4372 let title = "Unqualify option.None";
4373 assert_code_action!(
4374 title,
4375 TestProject::for_source(src)
4376 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
4377 find_position_of("None").to_selection(),
4378 );
4379}
4380
4381#[test]
4382fn test_qualified_to_unqualified_import_in_constant_record() {
4383 let src = r#"
4384import option
4385
4386const some = option.Some(1)
4387"#;
4388 let title = "Unqualify option.Some";
4389 assert_code_action!(
4390 title,
4391 TestProject::for_source(src)
4392 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
4393 find_position_of("Some").to_selection(),
4394 );
4395}
4396
4397#[test]
4398fn test_qualified_to_unqualified_import_in_nested_constant() {
4399 let src = r#"
4400import option
4401
4402type Result(a) {
4403 Result(expected: a, actual: option.Option(a))
4404}
4405
4406const zero = Result(0, option.None)
4407"#;
4408 let title = "Unqualify option.None";
4409 assert_code_action!(
4410 title,
4411 TestProject::for_source(src)
4412 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
4413 find_position_of("None").to_selection(),
4414 );
4415}
4416
4417#[test]
4418fn test_qualified_to_unqualified_import_constant_multiple_occurrences() {
4419 let src = r#"
4420import option
4421
4422const a = option.None
4423const b = option.Some(option.None)
4424"#;
4425 let title = "Unqualify option.None";
4426 assert_code_action!(
4427 title,
4428 TestProject::for_source(src)
4429 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
4430 find_position_of("None").nth_occurrence(1).to_selection(),
4431 );
4432}
4433
4434#[test]
4435fn test_qualified_to_unqualified_import_not_offered_for_function_in_constant() {
4436 let src = r#"
4437import list
4438
4439const mapper = list.map
4440"#;
4441
4442 let title = "Unqualify list.map";
4443 assert_no_code_actions!(
4444 title,
4445 TestProject::for_source(src).add_hex_module("list", "pub fn map(list, f) { todo }"),
4446 find_position_of("list.map").to_selection(),
4447 );
4448}
4449
4450#[test]
4451fn test_qualified_to_unqualified_import_not_offered_for_module_constant_in_constant() {
4452 let src = r#"
4453import mymath
4454
4455const x = mymath.pi
4456"#;
4457
4458 let title = "Unqualify mymath.pi";
4459 assert_no_code_actions!(
4460 title,
4461 TestProject::for_source(src).add_hex_module("mymath", "pub const pi = 3.14159"),
4462 find_position_of("pi").to_selection(),
4463 );
4464}
4465
4466#[test]
4467fn test_qualified_to_unqualified_import_from_constant_also_updates_functions() {
4468 let src = r#"
4469import option
4470
4471const default = option.None
4472
4473pub fn get_or_default(value: a) {
4474 case value {
4475 option.Some(x) -> x
4476 option.None -> value
4477 }
4478}
4479"#;
4480 let title = "Unqualify option.None";
4481 assert_code_action!(
4482 title,
4483 TestProject::for_source(src)
4484 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
4485 find_position_of("None").nth_occurrence(1).to_selection(),
4486 );
4487}
4488
4489#[test]
4490fn test_qualified_to_unqualified_import_from_function_also_updates_constants() {
4491 let src = r#"
4492import option
4493
4494const default = option.None
4495
4496pub fn get_or_default(value: a) {
4497 case value {
4498 option.Some(x) -> x
4499 option.None -> value
4500 }
4501}
4502"#;
4503 let title = "Unqualify option.None";
4504 assert_code_action!(
4505 title,
4506 TestProject::for_source(src)
4507 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
4508 find_position_of("None").nth_occurrence(2).to_selection(),
4509 );
4510}
4511
4512#[test]
4513fn test_unqualified_to_qualified_import_function() {
4514 let src = r#"
4515import list.{map}
4516
4517pub fn main() {
4518 let identity = map([1, 2, 3], fn(x) { x })
4519 let double = map([1, 2, 3], fn(x) { x * 2 })
4520}
4521"#;
4522 assert_code_action!(
4523 "Qualify map as list.map",
4524 TestProject::for_source(src).add_hex_module("list", "pub fn map(list, f) { todo }"),
4525 find_position_of("map").nth_occurrence(2).to_selection(),
4526 );
4527}
4528
4529#[test]
4530fn test_unqualified_to_qualified_only_triggers_when_within_an_expression() {
4531 let src = r#"
4532import list.{map}
4533
4534pub fn main() {
4535 let identity = map([1, 2, 3], fn(x) { x })
4536 let double = map([1, 2, 3], fn(x) { x * 2 })
4537}
4538// end
4539"#;
4540 assert_no_code_actions!(
4541 "Qualify map as list.map",
4542 TestProject::for_source(src).add_hex_module("list", "pub fn map(list, f) { todo }"),
4543 find_position_of("import").select_until(find_position_of("// end")),
4544 );
4545}
4546
4547#[test]
4548fn test_unqualified_to_qualified_import_constant() {
4549 let src = r#"
4550import mymath.{pi}
4551
4552pub fn circle_area(radius: Float) -> Float {
4553 pi *. radius *. radius
4554}
4555
4556pub fn circle_circumference(radius: Float) -> Float {
4557 2. *. pi *. radius
4558}
4559"#;
4560 assert_code_action!(
4561 "Qualify pi as mymath.pi",
4562 TestProject::for_source(src).add_hex_module("mymath", "pub const pi = 3.14159"),
4563 find_position_of("pi").nth_occurrence(2).to_selection(),
4564 );
4565}
4566
4567#[test]
4568fn test_unqualified_to_qualified_import_record_constructor() {
4569 let src = r#"
4570import user.{type User, User}
4571
4572pub fn create_user(name: String) -> User {
4573 User(name: name, id: 1)
4574}
4575"#;
4576 assert_code_action!(
4577 "Qualify User as user.User",
4578 TestProject::for_source(src)
4579 .add_hex_module("user", "pub type User { User(name: String, id: Int) }"),
4580 find_position_of("User")
4581 .nth_occurrence(4)
4582 .under_char('s')
4583 .to_selection(),
4584 );
4585}
4586
4587#[test]
4588fn test_unqualified_to_qualified_import_after_constructor() {
4589 let src = r#"
4590pub fn create_user(name: String) -> User {
4591 User(name: name, id: 1)
4592}
4593
4594import user.{type User, User}
4595"#;
4596 assert_code_action!(
4597 "Qualify User as user.User",
4598 TestProject::for_source(src)
4599 .add_hex_module("user", "pub type User { User(name: String, id: Int) }"),
4600 find_position_of("User").nth_occurrence(2).to_selection(),
4601 );
4602}
4603
4604#[test]
4605fn test_unqualified_to_qualified_import_between_constructors() {
4606 let src = r#"
4607pub fn create_user(name: String) -> User {
4608 User(name: name, id: 1)
4609}
4610
4611import user.{type User, User}
4612
4613pub fn user_list(users: List(User)) -> List(String) {
4614 [User(name: "John", id: 1),
4615 User(name: "Jane", id: 2)]
4616}
4617
4618"#;
4619 assert_code_action!(
4620 "Qualify User as user.User",
4621 TestProject::for_source(src)
4622 .add_hex_module("user", "pub type User { User(name: String, id: Int) }"),
4623 find_position_of("User").nth_occurrence(2).to_selection(),
4624 );
4625}
4626
4627#[test]
4628fn test_unqualified_to_qualified_import_multiple_occurrences() {
4629 let src = r#"
4630import list.{map, filter}
4631
4632pub fn process_list(items: List(Int)) -> List(Int) {
4633 items
4634 |> map(fn(x) { x + 1 })
4635 |> map(fn(x) { x * 2 })
4636}
4637"#;
4638 assert_code_action!(
4639 "Qualify map as list.map",
4640 TestProject::for_source(src).add_hex_module(
4641 "list",
4642 "pub fn map(list: List(a), with fun: fn(a) -> b) -> List(b) { todo }"
4643 ),
4644 find_position_of("map").nth_occurrence(2).to_selection(),
4645 );
4646}
4647
4648#[test]
4649fn test_unqualified_to_qualified_import_in_pattern_matching() {
4650 let src = r#"
4651import result.{type Result, Ok, Error}
4652
4653pub fn process_result(res: Result(Int, String)) -> Int {
4654 case res {
4655 Ok(value) -> value
4656 Error(_) -> 0
4657 }
4658}
4659"#;
4660 assert_code_action!(
4661 "Qualify Ok as result.Ok",
4662 TestProject::for_source(src)
4663 .add_hex_module("result", "pub type Result(a, e) { Ok(a) Error(e) }"),
4664 find_position_of("Ok").nth_occurrence(2).to_selection(),
4665 );
4666}
4667
4668#[test]
4669fn test_unqualified_to_qualified_import_type_annotation() {
4670 let src = r#"
4671import option.{type Option, Some}
4672
4673pub fn maybe_increment(x: Option(Int)) -> Option(Int) {
4674 case x {
4675 Some(value) -> Some(value + 1)
4676 _ -> x
4677 }
4678}
4679"#;
4680 assert_code_action!(
4681 "Qualify Option as option.Option",
4682 TestProject::for_source(src)
4683 .add_hex_module("option", "pub type Option(a) { Some(a) None }"),
4684 find_position_of("Opt")
4685 .nth_occurrence(2)
4686 .select_until(find_position_of("ion").nth_occurrence(3)),
4687 );
4688}
4689
4690#[test]
4691fn test_unqualified_to_qualified_import_nested_function_call() {
4692 let src = r#"
4693import list.{map, flatten}
4694import operation.{double}
4695
4696pub fn process_names(names: List(List(Int))) -> List(Int) {
4697 names
4698 |> flatten
4699 |> map(double)
4700}
4701"#;
4702 assert_code_action!(
4703 "Qualify double as operation.double",
4704 TestProject::for_source(src)
4705 .add_hex_module(
4706 "list",
4707 "pub fn map(list: List(a), with fun: fn(a) -> b) -> List(b) { todo }
4708pub fn flatten(lists: List(List(a))) -> List(a) { todo }"
4709 )
4710 .add_hex_module("operation", "pub fn double(s: Int) -> Int { todo }"),
4711 find_position_of("dou")
4712 .nth_occurrence(2)
4713 .select_until(find_position_of("ble").nth_occurrence(2)),
4714 );
4715}
4716
4717#[test]
4718fn test_unqualified_to_qualified_import_with_alias() {
4719 let src = r#"
4720import list.{map as transform}
4721
4722pub fn double_list(items: List(Int)) -> List(Int) {
4723 transform(items, fn(x) { x * 2 })
4724}
4725"#;
4726 assert_code_action!(
4727 "Qualify transform as list.map",
4728 TestProject::for_source(src).add_hex_module(
4729 "list",
4730 "pub fn map(list: List(a), with fun: fn(a) -> b) -> List(b) { todo }"
4731 ),
4732 find_position_of("transform")
4733 .nth_occurrence(2)
4734 .to_selection(),
4735 );
4736}
4737
4738#[test]
4739fn test_unqualified_to_qualified_import_with_alias_and_module_alias() {
4740 let src = r#"
4741import list.{map as transform} as lst
4742
4743pub fn double_list(items: List(Int)) -> List(Int) {
4744 transform(items, fn(x) { x * 2 })
4745}
4746"#;
4747 assert_code_action!(
4748 "Qualify transform as lst.map",
4749 TestProject::for_source(src).add_hex_module(
4750 "list",
4751 "pub fn map(list: List(a), with fun: fn(a) -> b) -> List(b) { todo }"
4752 ),
4753 find_position_of("transform")
4754 .nth_occurrence(2)
4755 .to_selection(),
4756 );
4757}
4758
4759#[test]
4760fn test_unqualified_to_qualified_import_import_discarded() {
4761 let src = r#"
4762import list.{map as transform} as _
4763
4764pub fn double_list(items: List(Int)) -> List(Int) {
4765 transform(items, fn(x) { x * 2 })
4766}
4767"#;
4768 let title = "Qualify transform as list.map";
4769 assert_no_code_actions!(
4770 title,
4771 TestProject::for_source(src).add_hex_module(
4772 "list",
4773 "pub fn map(list: List(a), with fun: fn(a) -> b) -> List(b) { todo }"
4774 ),
4775 find_position_of("transform")
4776 .nth_occurrence(2)
4777 .to_selection(),
4778 );
4779}
4780
4781#[test]
4782fn test_unqualified_to_qualified_import_bad_formatted_type_constructor() {
4783 let src = r#"
4784import option.{type Option, Some}
4785
4786pub fn maybe_increment(x: Option(Int)) -> Option(Int) {
4787 case x {
4788 Some(value) -> Some(value + 1)
4789 _ -> x
4790 }
4791}
4792"#;
4793 assert_code_action!(
4794 "Qualify Option as option.Option",
4795 TestProject::for_source(src)
4796 .add_hex_module("option", "pub type Option(a) { Some(a) None }"),
4797 find_position_of("Opt")
4798 .nth_occurrence(2)
4799 .select_until(find_position_of("ion").nth_occurrence(3)),
4800 );
4801}
4802
4803#[test]
4804fn test_unqualified_to_qualified_import_bad_formatted_type_constructor_with_alias() {
4805 let src = r#"
4806import option.{type Option as Maybe, Some}
4807
4808pub fn maybe_increment(x: Maybe(Int)) -> Maybe(Int) {
4809 case x {
4810 Some(value) -> Some(value + 1)
4811 _ -> x
4812 }
4813}
4814"#;
4815 assert_code_action!(
4816 "Qualify Maybe as option.Option",
4817 TestProject::for_source(src)
4818 .add_hex_module("option", "pub type Option(a) { Some(a) None }"),
4819 find_position_of("May")
4820 .nth_occurrence(2)
4821 .select_until(find_position_of("be").nth_occurrence(3)),
4822 );
4823}
4824
4825#[test]
4826fn test_unqualified_to_qualified_import_bad_formatted_comma() {
4827 let src = r#"
4828import option.{type Option , Some}
4829
4830pub fn maybe_increment(x: Option(Int)) -> Option(Int) {
4831 case x {
4832 Some(value) -> Some(value + 1)
4833 _ -> x
4834 }
4835}
4836"#;
4837 assert_code_action!(
4838 "Qualify Option as option.Option",
4839 TestProject::for_source(src)
4840 .add_hex_module("option", "pub type Option(a) { Some(a) None }"),
4841 find_position_of("Opt")
4842 .nth_occurrence(2)
4843 .select_until(find_position_of("ion").nth_occurrence(3)),
4844 );
4845}
4846
4847#[test]
4848fn test_unqualified_to_qualified_import_in_list_and_tuple() {
4849 let src = r#"
4850import option.{Some}
4851
4852pub fn main() {
4853 let list = [Some(1), option.None]
4854 let tuple = #(Some(2), option.None)
4855}
4856"#;
4857 assert_code_action!(
4858 "Qualify Some as option.Some",
4859 TestProject::for_source(src)
4860 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
4861 find_position_of("Some(1)").select_until(find_position_of("Some(1)").under_char('e')),
4862 );
4863}
4864#[test]
4865fn test_unqualified_to_qualified_import_constructor_complex_pattern() {
4866 let src = r#"
4867import option.{None, Some}
4868
4869pub fn main() {
4870 case [Some(1), None] {
4871 [None, ..] -> todo
4872 [Some(_), ..] -> todo
4873 _ -> todo
4874 }
4875 case Some(1), Some(2) {
4876 None, Some(_) -> todo
4877 Some(_), Some(val) -> todo
4878 _ -> todo
4879 }
4880}
4881"#;
4882 assert_code_action!(
4883 "Qualify Some as option.Some",
4884 TestProject::for_source(src)
4885 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
4886 find_position_of("Some(1)").select_until(find_position_of("Some(1)").under_char('e')),
4887 );
4888}
4889
4890#[test]
4891fn test_unqualified_to_qualified_import_multiple_line_aliased() {
4892 let src = r#"
4893import option.{
4894 type Option,
4895 None,
4896 Some
4897} as opt
4898
4899pub fn main() {
4900 Some(1)
4901}
4902"#;
4903 assert_code_action!(
4904 "Qualify Some as opt.Some",
4905 TestProject::for_source(src)
4906 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
4907 find_position_of("Some")
4908 .nth_occurrence(2)
4909 .select_until(find_position_of("(1)")),
4910 );
4911}
4912
4913#[test]
4914fn test_unqualified_to_qualified_import_multiple_line_bad_format_without_trailing_comma() {
4915 let src = r#"
4916import option.{type Option,
4917 Some
4918
4919}
4920
4921pub fn main() {
4922 Some(1)
4923}
4924"#;
4925 assert_code_action!(
4926 "Qualify Some as option.Some",
4927 TestProject::for_source(src)
4928 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
4929 find_position_of("Some")
4930 .nth_occurrence(2)
4931 .select_until(find_position_of("(1)")),
4932 );
4933}
4934
4935#[test]
4936fn test_unqualified_to_qualified_import_variable_shadowing() {
4937 let src = r#"
4938
4939import wibble.{wobble}
4940
4941pub fn example() {
4942 echo wobble
4943
4944 let wobble = 1
4945
4946 echo wobble
4947
4948 let _ = fn(wobble) {
4949 echo wobble
4950 }
4951
4952 todo
4953}
4954"#;
4955
4956 assert_code_action!(
4957 "Qualify wobble as wibble.wobble",
4958 TestProject::for_source(src).add_hex_module("wibble", "pub fn wobble() { todo }"),
4959 find_position_of("wob")
4960 .nth_occurrence(2)
4961 .select_until(find_position_of("ble").nth_occurrence(3))
4962 );
4963}
4964
4965#[test]
4966fn test_unqualified_to_qualified_import_in_constant_var() {
4967 let src = r#"
4968import option.{None}
4969
4970const none = None
4971"#;
4972 let title = "Qualify None as option.None";
4973 assert_code_action!(
4974 title,
4975 TestProject::for_source(src)
4976 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
4977 find_position_of("None").nth_occurrence(2).to_selection(),
4978 );
4979}
4980
4981#[test]
4982fn test_unqualified_to_qualified_import_in_constant_record() {
4983 let src = r#"
4984import option.{Some}
4985
4986const some = Some(1)
4987"#;
4988 let title = "Qualify Some as option.Some";
4989 assert_code_action!(
4990 title,
4991 TestProject::for_source(src)
4992 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
4993 find_position_of("Some").nth_occurrence(2).to_selection()
4994 );
4995}
4996
4997#[test]
4998fn test_unqualified_to_qualified_import_in_nested_constant() {
4999 let src = r#"
5000import option.{type Option, None}
5001
5002type Result(a) {
5003 Result(expected: a, actual: Option(a))
5004}
5005
5006const zero = Result(0, None)
5007"#;
5008 let title = "Qualify None as option.None";
5009 assert_code_action!(
5010 title,
5011 TestProject::for_source(src)
5012 .add_hex_module("option", "pub type Option(v) { Some(v) None }"),
5013 find_position_of("None").nth_occurrence(2).to_selection(),
5014 );
5015}
5016
5017/* TODO: implement qualified unused location
5018#[test]
5019fn test_remove_unused_qualified_action() {
5020 let code = "
5021// test
5022import map.{Map, delete}
5023";
5024 let expected = "
5025// test
5026
5027";
5028 assert_eq!(remove_unused_action(code), expected.to_string())
5029}
5030
5031#[test]
5032fn test_remove_unused_qualified_partial_action() {
5033 let code = "
5034// test
5035import result.{is_ok, is_err}
5036
5037pub fn main() {
5038 is_ok
5039}
5040";
5041 let expected = "
5042// test
5043import result.{is_ok}
5044
5045pub fn main() {
5046 is_ok
5047}
5048";
5049 assert_eq!(remove_unused_action(code), expected.to_string())
5050}
5051
5052#[test]
5053fn test_remove_unused_qualified_partial2_action() {
5054 let code = "
5055// test
5056import result.{all, is_ok, is_err}
5057
5058pub fn main() {
5059 is_ok
5060}
5061";
5062 let expected = "
5063// test
5064import result.{ is_ok}
5065
5066pub fn main() {
5067 is_ok
5068}
5069";
5070 assert_eq!(remove_unused_action(code), expected.to_string())
5071}
5072
5073#[test]
5074fn test_remove_unused_qualified_partial3_action() {
5075 let code = "
5076// test
5077import result.{all, is_ok, is_err} as res
5078
5079pub fn main() {
5080 is_ok
5081}
5082";
5083 let expected = "
5084// test
5085import result.{ is_ok} as res
5086
5087pub fn main() {
5088 is_ok
5089}
5090";
5091 assert_eq!(remove_unused_action(code), expected.to_string())
5092}
5093*/
5094
5095#[test]
5096fn convert_from_use_expression_with_no_parens() {
5097 let src = r#"
5098pub fn main() {
5099 use <- wibble
5100 todo
5101 todo
5102}
5103
5104fn wibble(f) {
5105 f()
5106}
5107"#;
5108 assert_code_action!(
5109 CONVERT_FROM_USE,
5110 TestProject::for_source(src),
5111 find_position_of("use").select_until(find_position_of("wibble")),
5112 );
5113}
5114
5115#[test]
5116fn convert_from_use_expression_with_empty_parens() {
5117 let src = r#"
5118pub fn main() {
5119 use <- wibble()
5120 todo
5121 todo
5122}
5123
5124fn wibble(f) {
5125 f()
5126}
5127"#;
5128 assert_code_action!(
5129 CONVERT_FROM_USE,
5130 TestProject::for_source(src),
5131 find_position_of("use").to_selection(),
5132 );
5133}
5134
5135#[test]
5136fn convert_from_use_expression_with_parens_and_other_args() {
5137 let src = r#"
5138pub fn main() {
5139 use <- wibble(1, 2)
5140 todo
5141 todo
5142}
5143
5144fn wibble(n, m, f) {
5145 f()
5146}
5147"#;
5148 assert_code_action!(
5149 CONVERT_FROM_USE,
5150 TestProject::for_source(src),
5151 find_position_of("wibble").select_until(find_position_of("1")),
5152 );
5153}
5154
5155#[test]
5156fn convert_from_use_expression_with_single_pattern() {
5157 let src = r#"
5158pub fn main() {
5159 use a <- wibble(1, 2)
5160 todo
5161 todo
5162}
5163
5164fn wibble(n, m, f) {
5165 f(1)
5166}
5167"#;
5168 assert_code_action!(
5169 CONVERT_FROM_USE,
5170 TestProject::for_source(src),
5171 find_position_of("a <-").to_selection(),
5172 );
5173}
5174
5175#[test]
5176fn convert_from_use_expression_with_multiple_patterns() {
5177 let src = r#"
5178pub fn main() {
5179 use a, b <- wibble(1, 2)
5180 todo
5181 todo
5182}
5183
5184fn wibble(n, m, f) {
5185 f(1, 2)
5186}
5187"#;
5188 assert_code_action!(
5189 CONVERT_FROM_USE,
5190 TestProject::for_source(src),
5191 find_position_of("wibble").to_selection(),
5192 );
5193}
5194
5195#[test]
5196fn desugar_nested_use_expressions_picks_inner_under_cursor() {
5197 let src = r#"
5198pub fn main() {
5199 use a, b <- wibble(1, 2)
5200 use a, b <- wibble(a, b)
5201 todo
5202}
5203
5204fn wibble(n, m, f) {
5205 f(1, 2)
5206}
5207"#;
5208 assert_code_action!(
5209 CONVERT_FROM_USE,
5210 TestProject::for_source(src),
5211 find_position_of("use").nth_occurrence(2).to_selection(),
5212 );
5213}
5214
5215#[test]
5216fn convert_from_use_only_triggers_on_the_use_line() {
5217 let src = r#"
5218pub fn main() {
5219 use a, b <- wibble(1, 2)
5220 todo
5221}
5222
5223fn wibble(n, m, f) {
5224 f(1, 2)
5225}
5226"#;
5227 assert_no_code_actions!(
5228 CONVERT_FROM_USE,
5229 TestProject::for_source(src),
5230 find_position_of("todo").to_selection(),
5231 );
5232}
5233
5234#[test]
5235fn desugar_nested_use_expressions_picks_inner_under_cursor_2() {
5236 let src = r#"
5237pub fn main() {
5238 use a, b <- wibble(1, 2)
5239 use a, b <- wibble(a, b)
5240 todo
5241}
5242
5243fn wibble(n, m, f) {
5244 f(1, 2)
5245}
5246"#;
5247 assert_code_action!(
5248 CONVERT_FROM_USE,
5249 TestProject::for_source(src),
5250 find_position_of("<-").select_until(find_position_of("wibble")),
5251 );
5252}
5253
5254#[test]
5255fn convert_from_use_expression_with_type_annotations() {
5256 let src = r#"
5257pub fn main() {
5258 use a: Int, b: Int <- wibble(1, 2)
5259 todo
5260}
5261
5262fn wibble(n, m, f) {
5263 f(1, 2)
5264}
5265"#;
5266 assert_code_action!(
5267 CONVERT_FROM_USE,
5268 TestProject::for_source(src),
5269 find_position_of("<-").select_until(find_position_of("wibble")),
5270 );
5271}
5272
5273#[test]
5274fn convert_from_use_expression_doesnt_work_with_complex_patterns() {
5275 let src = r#"
5276pub fn main() {
5277 use #(a, b), 1 <- wibble(1, 2)
5278 todo
5279}
5280
5281fn wibble(n, m, f) {
5282 f(todo, todo)
5283}
5284"#;
5285 assert_no_code_actions!(
5286 CONVERT_FROM_USE,
5287 TestProject::for_source(src),
5288 find_position_of("<-").select_until(find_position_of("wibble")),
5289 );
5290}
5291
5292#[test]
5293fn convert_from_use_with_labels() {
5294 let src = r#"
5295pub fn main() {
5296 use a <- wibble(one: 1, two: 2)
5297 todo
5298}
5299
5300fn wibble(one _, two _, three f) {
5301 f(1)
5302}
5303"#;
5304 assert_code_action!(
5305 CONVERT_FROM_USE,
5306 TestProject::for_source(src),
5307 find_position_of("use").to_selection(),
5308 );
5309}
5310
5311#[test]
5312fn convert_from_use_with_labels_2() {
5313 let src = r#"
5314pub fn main() {
5315 use a <- wibble(1, two: 2)
5316 todo
5317}
5318
5319fn wibble(one _, two _, three f) {
5320 f(1)
5321}
5322"#;
5323 assert_code_action!(
5324 CONVERT_FROM_USE,
5325 TestProject::for_source(src),
5326 find_position_of("use").to_selection(),
5327 );
5328}
5329
5330#[test]
5331fn convert_from_use_with_labels_3() {
5332 let src = r#"
5333pub fn main() {
5334 use a <- wibble(1, three: 3)
5335 todo
5336}
5337
5338fn wibble(one _, two f, three _) {
5339 f(1)
5340}
5341"#;
5342 assert_code_action!(
5343 CONVERT_FROM_USE,
5344 TestProject::for_source(src),
5345 find_position_of("use").to_selection(),
5346 );
5347}
5348
5349#[test]
5350fn convert_from_use_with_labels_4() {
5351 let src = r#"
5352pub fn main() {
5353 use a <- wibble(two: 2, three: 3)
5354 todo
5355}
5356
5357fn wibble(one f, two _, three _) {
5358 f(1)
5359}
5360"#;
5361 assert_code_action!(
5362 CONVERT_FROM_USE,
5363 TestProject::for_source(src),
5364 find_position_of("use").to_selection(),
5365 );
5366}
5367
5368#[test]
5369// https://github.com/gleam-lang/gleam/issues/4149
5370fn convert_from_use_with_trailing_comma() {
5371 let src = r#"
5372pub fn main() {
5373 use a, b <- wibble(1, 2,)
5374 todo
5375}
5376
5377fn wibble(n, m, f) {
5378 f(todo, todo)
5379}
5380"#;
5381 assert_code_action!(
5382 CONVERT_FROM_USE,
5383 TestProject::for_source(src),
5384 find_position_of("<-").select_until(find_position_of("wibble")),
5385 );
5386}
5387
5388#[test]
5389fn convert_from_use_with_trailing_comma_2() {
5390 let src = r#"
5391pub fn main() {
5392 use a, b <- wibble(
5393 1,
5394 2,
5395 )
5396 todo
5397}
5398
5399fn wibble(n, m, f) {
5400 f(todo, todo)
5401}
5402"#;
5403 assert_code_action!(
5404 CONVERT_FROM_USE,
5405 TestProject::for_source(src),
5406 find_position_of("<-").select_until(find_position_of("wibble")),
5407 );
5408}
5409
5410#[test]
5411fn convert_from_use_with_trailing_comma_and_label() {
5412 let src = r#"
5413pub fn main() {
5414 use a, b <- wibble(
5415 1,
5416 wibble: 2,
5417 )
5418 todo
5419}
5420
5421fn wibble(n, wibble m, wobble f) {
5422 f(todo, todo)
5423}
5424"#;
5425 assert_code_action!(
5426 CONVERT_FROM_USE,
5427 TestProject::for_source(src),
5428 find_position_of("<-").select_until(find_position_of("wibble")),
5429 );
5430}
5431
5432#[test]
5433fn convert_from_use_multiline_with_no_trailing_comma() {
5434 let src = r#"
5435pub fn main() {
5436 use a, b <- wibble(
5437 1,
5438 2
5439 )
5440 todo
5441}
5442
5443fn wibble(n, m, f) {
5444 f(todo, todo)
5445}
5446"#;
5447 assert_code_action!(
5448 CONVERT_FROM_USE,
5449 TestProject::for_source(src),
5450 find_position_of("<-").select_until(find_position_of("wibble")),
5451 );
5452}
5453
5454#[test]
5455fn turn_call_into_use_with_single_line_body() {
5456 let src = r#"
5457pub fn main() {
5458 wibble(fn(a, b) { todo })
5459}
5460
5461fn wibble(f) {
5462 f(todo, todo)
5463}
5464"#;
5465 assert_code_action!(
5466 CONVERT_TO_USE,
5467 TestProject::for_source(src),
5468 find_position_of("wibble").to_selection(),
5469 );
5470}
5471
5472#[test]
5473fn turn_call_into_use_with_fn_with_no_args() {
5474 let src = r#"
5475pub fn main() {
5476 wibble(fn() { todo })
5477}
5478
5479fn wibble(f) {
5480 f()
5481}
5482"#;
5483 assert_code_action!(
5484 CONVERT_TO_USE,
5485 TestProject::for_source(src),
5486 find_position_of("wibble").to_selection(),
5487 );
5488}
5489
5490#[test]
5491fn turn_call_with_multiple_arguments_into_use() {
5492 let src = r#"
5493pub fn main() {
5494 wibble(1, 2, fn(a) { todo })
5495}
5496
5497fn wibble(m, n, f) {
5498 f(1)
5499}
5500"#;
5501 assert_code_action!(
5502 CONVERT_TO_USE,
5503 TestProject::for_source(src),
5504 find_position_of("todo").to_selection(),
5505 );
5506}
5507
5508#[test]
5509fn turn_call_with_multiline_fn_into_use() {
5510 let src = r#"
5511pub fn main() {
5512 wibble(1, 2, fn(a) {
5513 todo
5514 case todo {
5515 _ -> todo
5516 }
5517 })
5518}
5519
5520fn wibble(m, n, f) {
5521 f(1)
5522}
5523"#;
5524 assert_code_action!(
5525 CONVERT_TO_USE,
5526 TestProject::for_source(src),
5527 find_position_of("1, 2").select_until(find_position_of("fn(a)")),
5528 );
5529}
5530
5531#[test]
5532fn turn_call_with_fn_with_type_annotations_into_use() {
5533 let src = r#"
5534pub fn main() {
5535 wibble(1, 2, fn(a: Int) {
5536 todo
5537 })
5538}
5539
5540fn wibble(m, n, f) {
5541 f(1)
5542}
5543"#;
5544 assert_code_action!(
5545 CONVERT_TO_USE,
5546 TestProject::for_source(src),
5547 find_position_of("wibble").select_until(find_position_of("a: ")),
5548 );
5549}
5550
5551#[test]
5552fn turn_call_into_use_only_works_on_last_call_in_a_block() {
5553 let src = r#"
5554pub fn main() {
5555 wibble(10, 20, fn(a) { todo })
5556 wibble(1, 2, fn(a) { todo })
5557}
5558
5559fn wibble(m, n, f) {
5560 f(1)
5561}
5562"#;
5563 assert_no_code_actions!(
5564 CONVERT_TO_USE,
5565 TestProject::for_source(src),
5566 find_position_of("10").to_selection(),
5567 );
5568}
5569
5570#[test]
5571fn turn_call_into_use_only_works_on_last_call_in_a_block_2() {
5572 let src = r#"
5573pub fn main() {
5574 {
5575 wibble(10, 20, fn(a) { todo })
5576 wibble(1, 2, fn(a) { todo })
5577 }
5578 Nil
5579}
5580
5581fn wibble(m, n, f) {
5582 f(1)
5583}
5584"#;
5585 assert_no_code_actions!(
5586 CONVERT_TO_USE,
5587 TestProject::for_source(src),
5588 find_position_of("10").to_selection(),
5589 );
5590}
5591
5592#[test]
5593fn turn_call_into_use_with_last_function_in_a_block() {
5594 let src = r#"
5595pub fn main() {
5596 {
5597 wibble(10, 20, fn(a) { todo })
5598 wibble(1, 11, fn(a) { todo })
5599 }
5600 Nil
5601}
5602
5603fn wibble(m, n, f) {
5604 f(1)
5605}
5606"#;
5607 assert_code_action!(
5608 CONVERT_TO_USE,
5609 TestProject::for_source(src),
5610 find_position_of("wibble(1,").select_until(find_position_of("11")),
5611 );
5612}
5613
5614#[test]
5615fn turn_call_into_use_starts_from_innermost_function() {
5616 let src = r#"
5617pub fn main() {
5618 wibble(10, 20, fn(a) {
5619 wibble(30, 40, fn(b) {
5620 a + b
5621 })
5622 })
5623}
5624
5625fn wibble(m, n, f) {
5626 f(1)
5627}
5628"#;
5629 assert_code_action!(
5630 CONVERT_TO_USE,
5631 TestProject::for_source(src),
5632 find_position_of("30").select_until(find_position_of("40")),
5633 );
5634}
5635
5636#[test]
5637fn turn_call_into_use_with_another_use_in_the_way() {
5638 let src = r#"
5639pub fn main() {
5640 wibble(10, 20, fn(a) {
5641 use b <- wibble(30, 40)
5642 a + b
5643 })
5644}
5645
5646fn wibble(m, n, f) {
5647 f(1)
5648}
5649"#;
5650 assert_code_action!(
5651 CONVERT_TO_USE,
5652 TestProject::for_source(src),
5653 find_position_of("use").to_selection(),
5654 );
5655}
5656
5657#[test]
5658fn turn_call_into_use_with_module_function() {
5659 let src = r#"
5660import other
5661pub fn main() {
5662 other.wibble(10, 20, fn(a) {
5663 todo
5664 a + b
5665 })
5666}
5667"#;
5668 assert_code_action!(
5669 CONVERT_TO_USE,
5670 TestProject::for_source(src).add_module("other", "pub fn wibble(n, m, f) { todo }"),
5671 find_position_of("wibble").to_selection(),
5672 );
5673}
5674
5675#[test]
5676// https://github.com/gleam-lang/gleam/issues/4498
5677fn turn_call_into_use_with_out_of_order_arguments() {
5678 assert_code_action!(
5679 CONVERT_TO_USE,
5680 r#"
5681pub fn main() {
5682 fold(0, over: [], with: fn (a, b) { todo })
5683}
5684
5685fn fold(over list: List(a), from acc: acc, with fun: fn(acc, a) -> acc) -> acc {
5686 todo
5687}
5688"#,
5689 find_position_of("fold").to_selection(),
5690 );
5691}
5692
5693#[test]
5694fn inexhaustive_let_result_to_case() {
5695 assert_code_action!(
5696 CONVERT_TO_CASE,
5697 "pub fn main(result) {
5698 let Ok(value) = result
5699}",
5700 find_position_of("let").select_until(find_position_of("=")),
5701 );
5702}
5703
5704#[test]
5705fn inexhaustive_let_to_case_indented() {
5706 assert_code_action!(
5707 CONVERT_TO_CASE,
5708 "pub fn main(result) {
5709 {
5710 let Ok(value) = result
5711 }
5712}",
5713 find_position_of("let").select_until(find_position_of("=")),
5714 );
5715}
5716
5717#[test]
5718fn inexhaustive_let_to_case_multi_variables() {
5719 assert_code_action!(
5720 CONVERT_TO_CASE,
5721 "pub fn main() {
5722 let [var1, var2, _var3, var4] = [1, 2, 3, 4]
5723}",
5724 find_position_of("let").select_until(find_position_of("=")),
5725 );
5726}
5727
5728#[test]
5729fn inexhaustive_let_to_case_discard() {
5730 assert_code_action!(
5731 CONVERT_TO_CASE,
5732 "pub fn main() {
5733 let [_elem] = [6]
5734}",
5735 find_position_of("let").select_until(find_position_of("=")),
5736 );
5737}
5738
5739#[test]
5740fn inexhaustive_let_to_case_no_variables() {
5741 assert_code_action!(
5742 CONVERT_TO_CASE,
5743 "pub fn main() {
5744 let [] = []
5745}",
5746 find_position_of("let").select_until(find_position_of("=")),
5747 );
5748}
5749
5750#[test]
5751fn inexhaustive_let_alias_to_case() {
5752 assert_code_action!(
5753 CONVERT_TO_CASE,
5754 "pub fn main() {
5755 let 10 as ten = 10
5756}",
5757 find_position_of("let").select_until(find_position_of("=")),
5758 );
5759}
5760
5761#[test]
5762fn cursor_must_be_within_let_assignment_to_trigger_action() {
5763 assert_no_code_actions!(
5764 CONVERT_TO_CASE,
5765 "pub fn main() {
5766 let 10 as ten = 10
5767}",
5768 find_position_of("pub").select_until(find_position_of("}")),
5769 );
5770}
5771
5772#[test]
5773fn inexhaustive_let_tuple_to_case() {
5774 assert_code_action!(
5775 CONVERT_TO_CASE,
5776 "pub fn main() {
5777 let #(first, 10, third) = #(5, 10, 15)
5778}
5779",
5780 find_position_of("let").select_until(find_position_of("=")),
5781 );
5782}
5783
5784#[test]
5785fn inexhaustive_let_bit_array_to_case() {
5786 assert_code_action!(
5787 CONVERT_TO_CASE,
5788 "pub fn main() {
5789 let <<bits1, bits2>> = <<73, 98>>
5790}",
5791 find_position_of("let").select_until(find_position_of("=")),
5792 );
5793}
5794
5795#[test]
5796fn inexhaustive_let_string_prefix_to_case() {
5797 assert_code_action!(
5798 CONVERT_TO_CASE,
5799 r#"pub fn main() {
5800 let "_" <> thing = "_Hello"
5801}"#,
5802 find_position_of("let").select_until(find_position_of("=")),
5803 );
5804}
5805
5806#[test]
5807fn inexhaustive_let_string_prefix_pattern_alias_to_case() {
5808 assert_code_action!(
5809 CONVERT_TO_CASE,
5810 r#"pub fn main() {
5811 let "123" as one_two_three <> rest = "123456"
5812}"#,
5813 find_position_of("let").select_until(find_position_of("=")),
5814 );
5815}
5816
5817#[test]
5818fn inner_inexhaustive_let_to_case() {
5819 assert_code_action!(
5820 CONVERT_TO_CASE,
5821 r#"pub fn main(result) {
5822 let [wibble] = {
5823 let Ok(wobble) = {
5824 result
5825 }
5826 [wobble]
5827 }
5828}"#,
5829 find_position_of("let Ok").select_until(find_position_of(") =")),
5830 );
5831}
5832
5833#[test]
5834fn outer_inexhaustive_let_to_case() {
5835 assert_code_action!(
5836 CONVERT_TO_CASE,
5837 r#"pub fn main(result) {
5838 let [wibble] = {
5839 let Ok(wobble) = {
5840 result
5841 }
5842 [wobble]
5843 }
5844}"#,
5845 find_position_of("let [").select_until(find_position_of("] =")),
5846 );
5847}
5848
5849#[test]
5850fn no_code_action_for_exhaustive_let_to_case() {
5851 assert_no_code_actions!(
5852 CONVERT_TO_CASE,
5853 r#"pub fn first(pair) {
5854 let #(first, second) = pair
5855 first
5856}"#,
5857 find_position_of("let").select_until(find_position_of("=")),
5858 );
5859}
5860
5861#[test]
5862fn extract_variable() {
5863 assert_code_action!(
5864 EXTRACT_VARIABLE,
5865 r#"pub fn main() {
5866 list.map([1, 2, 3], int.add(1, _))
5867}"#,
5868 find_position_of("[1").select_until(find_position_of("2"))
5869 );
5870}
5871
5872#[test]
5873fn extract_can_extract_number_used_as_call_argument() {
5874 assert_code_action!(
5875 EXTRACT_VARIABLE,
5876 r#"pub fn main() {
5877 wibble(label: 1)
5878}"#,
5879 find_position_of("1").to_selection()
5880 );
5881}
5882
5883#[test]
5884fn extract_can_extract_bool_used_as_call_argument() {
5885 assert_code_action!(
5886 EXTRACT_VARIABLE,
5887 r#"pub fn main() {
5888 wibble(label: True)
5889}"#,
5890 find_position_of("True").to_selection()
5891 );
5892}
5893
5894#[test]
5895fn extract_variable_ignores_names_in_other_branches() {
5896 assert_code_action!(
5897 EXTRACT_VARIABLE,
5898 r#"pub fn main(x: Result(_, _)) {
5899 case x {
5900 Ok(_) -> {
5901 let int = 1
5902 int + 2
5903 }
5904 Error(_) -> wibble(11)
5905 }
5906
5907}"#,
5908 find_position_of("11").to_selection()
5909 );
5910}
5911
5912#[test]
5913fn extract_variable_ignores_names_in_other_branches_2() {
5914 assert_code_action!(
5915 EXTRACT_VARIABLE,
5916 r#"
5917pub fn main(x: Result(_, _)) {
5918 case x {
5919 Ok(_) -> {
5920 let int = 1
5921 int + 2
5922 }
5923 Error(_) -> {
5924 wibble(11)
5925 }
5926 }
5927}
5928"#,
5929 find_position_of("11").to_selection()
5930 );
5931}
5932
5933#[test]
5934fn extract_variable_ignores_names_in_other_blocks() {
5935 assert_code_action!(
5936 EXTRACT_VARIABLE,
5937 r#"
5938pub fn main() {
5939 {
5940 let int = 1
5941 int + 2
5942 }
5943 wibble(11)
5944}
5945"#,
5946 find_position_of("11").to_selection()
5947 );
5948}
5949
5950#[test]
5951fn extract_variable_ignores_names_in_anonymous_functions() {
5952 assert_code_action!(
5953 EXTRACT_VARIABLE,
5954 r#"
5955pub fn main() {
5956 let fun = fn() {
5957 let int = 1
5958 int + 2
5959 }
5960
5961 wibble(11)
5962}
5963"#,
5964 find_position_of("11").to_selection()
5965 );
5966}
5967
5968#[test]
5969fn extract_variable_does_not_shadow_names_in_anonymous_function() {
5970 assert_code_action!(
5971 EXTRACT_VARIABLE,
5972 r#"
5973pub fn main() {
5974 let fun = fn() {
5975 let int = 1
5976 wibble(11)
5977 }
5978 fun()
5979}
5980"#,
5981 find_position_of("11").to_selection()
5982 );
5983}
5984
5985#[test]
5986fn extract_variable_does_not_shadow_name_in_same_branch() {
5987 assert_code_action!(
5988 EXTRACT_VARIABLE,
5989 r#"
5990pub fn main(x: Result(_, _)) {
5991 case x {
5992 Ok(_) -> {
5993 let int = 1
5994 wibble(11)
5995 }
5996 Error(_) -> {
5997 panic
5998 }
5999 }
6000}
6001"#,
6002 find_position_of("11").to_selection()
6003 );
6004}
6005
6006#[test]
6007fn extract_variable_does_not_shadow_name_in_same_block() {
6008 assert_code_action!(
6009 EXTRACT_VARIABLE,
6010 r#"
6011pub fn main() {
6012 let result = {
6013 let int = 1
6014 wibble(11)
6015 }
6016 result
6017}"#,
6018 find_position_of("11").to_selection()
6019 );
6020}
6021
6022#[test]
6023fn extract_variable_does_not_extract_a_variable() {
6024 assert_no_code_actions!(
6025 EXTRACT_VARIABLE,
6026 r#"pub fn main() {
6027 let z = 1
6028 let a = [1, 2, z]
6029}"#,
6030 find_position_of("z").nth_occurrence(2).to_selection()
6031 );
6032}
6033
6034#[test]
6035fn extract_variable_does_not_extract_top_level_statement() {
6036 assert_no_code_actions!(
6037 EXTRACT_VARIABLE,
6038 r#"pub fn main() {
6039 let wibble = 1
6040}"#,
6041 find_position_of("1").to_selection()
6042 );
6043}
6044
6045#[test]
6046fn extract_variable_does_not_extract_top_level_statement_inside_block() {
6047 assert_no_code_actions!(
6048 EXTRACT_VARIABLE,
6049 r#"pub fn main() {
6050 let x = {
6051 let y = "y"
6052 let w = "w" <> y
6053 w
6054 }
6055}"#,
6056 find_position_of("y").nth_occurrence(2).to_selection()
6057 );
6058}
6059
6060#[test]
6061fn extract_variable_does_not_extract_top_level_statement_inside_use() {
6062 assert_no_code_actions!(
6063 EXTRACT_VARIABLE,
6064 "
6065pub fn main() {
6066 use x <- try(Ok(1))
6067 let y = 2
6068 Ok(y + x)
6069}
6070pub fn try(result: Result(a, e), fun: fn(a) -> Result(b, e)) -> Result(b, e) { todo }
6071",
6072 find_position_of("2").to_selection()
6073 );
6074}
6075
6076#[test]
6077fn extract_variable_does_not_extract_use() {
6078 assert_no_code_actions!(
6079 EXTRACT_VARIABLE,
6080 "
6081pub fn main() {
6082 use x <- try(Ok(1))
6083 Ok(x)
6084}
6085pub fn try(result: Result(a, e), fun: fn(a) -> Result(b, e)) -> Result(b, e) { todo }
6086",
6087 find_position_of("use").to_selection()
6088 );
6089}
6090
6091#[test]
6092fn extract_variable_does_not_extract_panic() {
6093 assert_no_code_actions!(
6094 EXTRACT_VARIABLE,
6095 r#"pub fn main() {
6096 let x = 1
6097 panic
6098}"#,
6099 find_position_of("panic").to_selection()
6100 );
6101}
6102
6103#[test]
6104fn extract_variable_does_not_extract_echo() {
6105 assert_no_code_actions!(
6106 EXTRACT_VARIABLE,
6107 r#"pub fn main() {
6108 let x = 1
6109 echo x
6110}"#,
6111 find_position_of("echo").to_selection()
6112 );
6113}
6114
6115#[test]
6116fn extract_variable_does_not_extract_record_constructor_in_record_update() {
6117 assert_no_code_actions!(
6118 EXTRACT_VARIABLE,
6119 r#"
6120pub fn main() {
6121 Wibble(..todo, a: 1)
6122}
6123
6124pub type Wibble { Wibble(a: Int, b: Int) }
6125"#,
6126 find_position_of("Wibble").to_selection()
6127 );
6128}
6129
6130#[test]
6131fn extract_variable_does_not_extract_assignment() {
6132 assert_no_code_actions!(
6133 EXTRACT_VARIABLE,
6134 r#"pub fn main() {
6135 let x = 1
6136}"#,
6137 find_position_of("x").to_selection()
6138 );
6139}
6140
6141#[test]
6142fn extract_variable_does_not_extract_record_variable_in_record_update() {
6143 assert_no_code_actions!(
6144 EXTRACT_VARIABLE,
6145 r#"
6146type Wibble { Wibble(one: Int, two: Int) }
6147
6148pub fn main() {
6149 let wibble = todo
6150 Wibble(..wibble, one: 1)
6151}"#,
6152 find_position_of("wibble").nth_occurrence(2).to_selection()
6153 );
6154}
6155
6156#[test]
6157fn extract_variable_from_arg_in_pipelined_call() {
6158 assert_code_action!(
6159 EXTRACT_VARIABLE,
6160 "
6161pub fn main() {
6162 let adder = add
6163 let x = [4, 5, 6] |> map2([1, 2, 3], adder)
6164 x
6165}
6166pub fn map2(list1: List(a), list2: List(b), fun: fn(a, b) -> c) -> List(c) { todo }
6167pub fn add(a: Int, b: Int) -> Int { todo }
6168",
6169 find_position_of("[1").to_selection()
6170 );
6171}
6172
6173#[test]
6174fn extract_variable_from_arg_in_pipelined_call_to_capture() {
6175 assert_code_action!(
6176 EXTRACT_VARIABLE,
6177 "
6178pub fn main() {
6179 let adder = add
6180 let x = adder |> reduce([1, 2, 3], _)
6181 x
6182}
6183pub fn reduce(list: List(a), fun: fn(a, a) -> a) -> Result(a, Nil) { todo }
6184pub fn add(a: Int, b: Int) -> Int { todo }
6185",
6186 find_position_of("[1").to_selection()
6187 );
6188}
6189
6190#[test]
6191fn extract_variable_from_arg_in_pipelined_call_of_function_to_capture() {
6192 assert_code_action!(
6193 EXTRACT_VARIABLE,
6194 "
6195pub fn main() {
6196 fn(total, item) { total + item }
6197 |> fold(with: _, from: 0, over: [1, 2, 3])
6198}
6199pub fn fold(over l: List(a), from i: t, with f: fn(t, a) -> t) -> acc { todo }
6200",
6201 find_position_of("fold").to_selection()
6202 );
6203}
6204
6205#[test]
6206fn extract_variable_from_arg_in_nested_function_called_in_pipeline() {
6207 assert_code_action!(
6208 EXTRACT_VARIABLE,
6209 "
6210pub fn main() {
6211 let result =
6212 [1, 2, 3]
6213 |> map(add(_, 1))
6214 |> map(subtract(_, 9))
6215
6216 result
6217}
6218pub fn map(list: List(a), fun: fn(a) -> b) -> List(b) { todo }
6219pub fn add(a: Int, b: Int) -> Int { todo }
6220pub fn subtract(a: Int, b: Int) -> Int { todo }
6221",
6222 find_position_of("9").to_selection()
6223 );
6224}
6225
6226#[test]
6227fn extract_variable_does_not_extract_an_entire_pipeline_step() {
6228 assert_no_code_actions!(
6229 EXTRACT_VARIABLE,
6230 "
6231pub fn main() {
6232 [1, 2, 3]
6233 |> map(todo)
6234 |> map(todo)
6235}
6236
6237fn map(list, fun) { todo }
6238",
6239 find_position_of("map").to_selection()
6240 );
6241}
6242
6243#[test]
6244fn extract_variable_does_not_extract_the_last_pipeline_step() {
6245 assert_no_code_actions!(
6246 EXTRACT_VARIABLE,
6247 r#"pub fn main() {
6248 [1, 2, 3]
6249 |> map(todo)
6250 |> map(todo)
6251}
6252
6253fn map(list, fun) { todo }
6254"#,
6255 find_position_of("map").to_selection()
6256 );
6257}
6258
6259#[test]
6260fn extract_variable_2() {
6261 assert_code_action!(
6262 EXTRACT_VARIABLE,
6263 "
6264pub fn main() {
6265 map([1, 2, 3], add(1, _))
6266}
6267pub fn add(n, m) { todo }
6268pub fn map(l, f) { todo }
6269",
6270 find_position_of("add").to_selection()
6271 );
6272}
6273
6274#[test]
6275fn extract_variable_from_capture_arguments() {
6276 assert_no_code_actions!(
6277 EXTRACT_VARIABLE,
6278 r#"pub fn main() {
6279 int.add(1, _)
6280}"#,
6281 find_position_of("_").to_selection()
6282 );
6283}
6284
6285#[test]
6286fn extract_variable_from_capture_arguments_2() {
6287 assert_code_action!(
6288 EXTRACT_VARIABLE,
6289 r#"pub fn main() {
6290 int.add(11, _)
6291}"#,
6292 find_position_of("11").to_selection()
6293 );
6294}
6295
6296#[test]
6297fn extract_variable_3() {
6298 assert_code_action!(
6299 EXTRACT_VARIABLE,
6300 r#"pub fn main() {
6301 list.map([1, 2, 3], todo, todo)
6302}"#,
6303 find_position_of("todo")
6304 .nth_occurrence(2)
6305 .select_until(find_position_of("todo)").under_last_char())
6306 );
6307}
6308
6309#[test]
6310fn extract_variable_inside_multiline_function_call() {
6311 assert_code_action!(
6312 EXTRACT_VARIABLE,
6313 r#"pub fn main() {
6314 list.map(
6315 [1, 2, 3],
6316 int.add(1, _),
6317 )
6318}"#,
6319 find_position_of("[1").to_selection()
6320 );
6321}
6322
6323#[test]
6324fn extract_variable_in_case_branch() {
6325 assert_code_action!(
6326 EXTRACT_VARIABLE,
6327 r#"pub fn main() {
6328 case wibble {
6329 _ -> [1, 2, 3]
6330 }
6331}"#,
6332 find_position_of("[1").to_selection()
6333 );
6334}
6335
6336#[test]
6337fn extract_variable_in_multiline_case_subject_branch() {
6338 assert_code_action!(
6339 EXTRACT_VARIABLE,
6340 r#"pub fn main() {
6341 case
6342 list.map(
6343 [1, 2, 3],
6344 int.add(1, _)
6345 )
6346 {
6347 _ -> todo
6348 }
6349}"#,
6350 find_position_of("[1").to_selection()
6351 );
6352}
6353
6354#[test]
6355fn extract_variable_in_case_branch_using_var() {
6356 assert_code_action!(
6357 EXTRACT_VARIABLE,
6358 r#"pub fn main() {
6359 case todo {
6360 Ok(value) -> 2 * value + 1
6361 Error(_) -> panic
6362 }
6363}"#,
6364 find_position_of("2").select_until(find_position_of("value").nth_occurrence(2))
6365 );
6366}
6367
6368#[test]
6369fn extract_variable_in_case_branch_from_second_arg() {
6370 assert_code_action!(
6371 EXTRACT_VARIABLE,
6372 r#"pub fn main() {
6373 case todo {
6374 Ok(_) -> #(Ok(1), Error("s"))
6375 Error(_) -> panic
6376 }
6377}"#,
6378 find_position_of("E").to_selection()
6379 );
6380}
6381
6382#[test]
6383fn extract_variable_in_use() {
6384 assert_code_action!(
6385 EXTRACT_VARIABLE,
6386 r#"pub fn main() {
6387 use <- wibble([1, 2, 3])
6388 todo
6389}"#,
6390 find_position_of("[1").to_selection()
6391 );
6392}
6393
6394#[test]
6395fn extract_variable_inside_use_body() {
6396 assert_code_action!(
6397 EXTRACT_VARIABLE,
6398 r#"pub fn main() {
6399 use <- wibble(todo)
6400 list.map([1, 2, 3], int.add(1, _))
6401 todo
6402}"#,
6403 find_position_of("[1").to_selection()
6404 );
6405}
6406
6407#[test]
6408fn extract_variable_in_multiline_use() {
6409 assert_code_action!(
6410 EXTRACT_VARIABLE,
6411 r#"pub fn main() {
6412 use <- wibble(
6413 [1, 2, 3]
6414 )
6415 todo
6416}"#,
6417 find_position_of("[1").to_selection()
6418 );
6419}
6420
6421#[test]
6422fn extract_variable_after_nested_anonymous_function() {
6423 assert_code_action!(
6424 EXTRACT_VARIABLE,
6425 r#"pub fn main() {
6426 let f = fn() {
6427 let x = 1 + 2
6428 let ff = fn() {
6429 let y = x + 3
6430 let z = y + x
6431 z
6432 }
6433 let z = x * 4
6434 z
6435 }
6436 let y = 5 + 6
6437 f()
6438}"#,
6439 find_position_of("6").to_selection()
6440 );
6441}
6442
6443#[test]
6444fn extract_variable_in_nested_anonymous_function() {
6445 assert_code_action!(
6446 EXTRACT_VARIABLE,
6447 r#"pub fn main() {
6448 let f = fn() {
6449 let x = 1 + 2
6450 let ff = fn() {
6451 let y = x + 3
6452 let z = y + x
6453 z
6454 }
6455 let z = x * 4
6456 z
6457 }
6458 let y = 5 + 6
6459 f()
6460}"#,
6461 find_position_of("4").to_selection()
6462 );
6463}
6464
6465#[test]
6466fn extract_variable_in_double_nested_anonymous_function() {
6467 assert_code_action!(
6468 EXTRACT_VARIABLE,
6469 r#"pub fn main() {
6470 let f = fn() {
6471 let x = 1 + 2
6472 let ff = fn() {
6473 let y = x + 3
6474 let z = y + x
6475 z
6476 }
6477 let z = x * 4
6478 z
6479 }
6480 let y = 5 + 6
6481 f()
6482}"#,
6483 find_position_of("3").to_selection()
6484 );
6485}
6486
6487#[test]
6488fn extract_variable_in_block() {
6489 assert_code_action!(
6490 EXTRACT_VARIABLE,
6491 r#"pub fn main() {
6492 {
6493 todo
6494 wibble([1, 2, 3])
6495 todo
6496 }
6497}"#,
6498 find_position_of("2").select_until(find_position_of("3"))
6499 );
6500}
6501
6502#[test]
6503fn extract_variable_and_dont_shadow_existing_variable_in_operator() {
6504 let src = "import gleam/int
6505import random_import as int_2
6506
6507const int_3 = 3
6508
6509fn int_4() { 4 }
6510
6511fn isolated_scope() {
6512 let int_6 = 6
6513 int_6 + 1
6514}
6515
6516pub fn main() {
6517 let int_5 = 5
6518 let result = int_5 + 6
6519 result
6520}
6521";
6522
6523 assert_code_action!(
6524 EXTRACT_VARIABLE,
6525 TestProject::for_source(src)
6526 .add_hex_module("gleam/int", "")
6527 .add_hex_module("random_import", ""),
6528 find_position_of("6").nth_occurrence(4).to_selection(),
6529 );
6530}
6531
6532#[test]
6533fn extract_variable_and_dont_shadow_existing_variable_in_argument() {
6534 assert_code_action!(
6535 EXTRACT_VARIABLE,
6536 r#"fn wibble(a, b) {
6537 a + b
6538}
6539
6540fn main() {
6541 let int = 1
6542 wibble(int, 2)
6543}"#,
6544 find_position_of("2").to_selection()
6545 );
6546}
6547
6548#[test]
6549fn extract_constant_from_call_argument_with_bit_array() {
6550 assert_code_action!(
6551 EXTRACT_CONSTANT,
6552 r#"import gleam/io
6553
6554pub fn main() {
6555 io.debug(<<3:size(8)>>)
6556}"#,
6557 find_position_of("<").select_until(find_position_of("<").nth_occurrence(2))
6558 );
6559}
6560
6561#[test]
6562fn extract_constant_from_call_argument_with_float() {
6563 assert_code_action!(
6564 EXTRACT_CONSTANT,
6565 r#"import gleam/float
6566
6567pub fn main() {
6568 float.ceiling(1.9998)
6569}"#,
6570 find_position_of("1").select_until(find_position_of("8"))
6571 );
6572}
6573
6574#[test]
6575fn extract_constant_from_call_argument_with_int() {
6576 assert_code_action!(
6577 EXTRACT_CONSTANT,
6578 r#"import gleam/list
6579
6580pub fn main() {
6581 list.sample([4, 5, 6], 2)
6582}"#,
6583 find_position_of("2").to_selection()
6584 );
6585}
6586
6587#[test]
6588fn extract_constant_from_call_argument_with_list() {
6589 assert_code_action!(
6590 EXTRACT_CONSTANT,
6591 r#"import gleam/io
6592
6593pub fn main() {
6594 io.debug(["constant", "another constant"])
6595}"#,
6596 find_position_of("[").to_selection()
6597 );
6598}
6599
6600#[test]
6601fn extract_constant_from_call_argument_with_nested_inside() {
6602 assert_code_action!(
6603 EXTRACT_CONSTANT,
6604 r#"import gleam/list
6605
6606pub fn main() {
6607 list.unzip([#(1, 2), #(3, 4)])
6608}"#,
6609 find_position_of("#").select_until(find_position_of("(").nth_occurrence(3))
6610 );
6611}
6612
6613#[test]
6614fn extract_constant_from_call_argument_with_nested_outside() {
6615 assert_code_action!(
6616 EXTRACT_CONSTANT,
6617 r#"import gleam/list
6618
6619pub fn main() {
6620 list.unzip([#(1, 2), #(3, 4)])
6621}"#,
6622 find_position_of("[").to_selection()
6623 );
6624}
6625
6626#[test]
6627fn extract_constant_from_call_argument_with_string() {
6628 assert_code_action!(
6629 EXTRACT_CONSTANT,
6630 r#"import gleam/io
6631
6632pub fn main() {
6633 io.print("constant")
6634}"#,
6635 find_position_of("\"").select_until(find_position_of("\""))
6636 );
6637}
6638
6639#[test]
6640fn extract_constant_from_call_argument_with_tuple() {
6641 assert_code_action!(
6642 EXTRACT_CONSTANT,
6643 r#"import gleam/io
6644
6645pub fn main() {
6646 io.debug(#(1, 2, 3))
6647}"#,
6648 find_position_of("#").select_until(find_position_of("(").nth_occurrence(3))
6649 );
6650}
6651
6652#[test]
6653fn extract_constant_from_declaration_of_float() {
6654 assert_code_action!(
6655 EXTRACT_CONSTANT,
6656 r#"pub fn main() {
6657 let c = 3.1415
6658}"#,
6659 find_position_of("3").select_until(find_position_of("5"))
6660 );
6661}
6662
6663#[test]
6664fn extract_constant_from_whole_declaration_of_float() {
6665 assert_code_action!(
6666 EXTRACT_CONSTANT,
6667 r#"import gleam/io
6668
6669pub fn main() {
6670 let c = 3.1415
6671 io.debug(c)
6672}"#,
6673 find_position_of("l")
6674 .nth_occurrence(2)
6675 .select_until(find_position_of("c"))
6676 );
6677}
6678
6679#[test]
6680fn extract_constant_from_declaration_of_bit_array() {
6681 assert_code_action!(
6682 EXTRACT_CONSTANT,
6683 r#"pub fn main() {
6684 let b = <<"arr":utf32>>
6685}"#,
6686 find_position_of("u")
6687 .nth_occurrence(2)
6688 .select_until(find_position_of(">").nth_occurrence(2))
6689 );
6690}
6691
6692#[test]
6693fn extract_constant_from_whole_declaration_of_bit_array() {
6694 assert_code_action!(
6695 EXTRACT_CONSTANT,
6696 r#"import gleam/io
6697
6698const n = 24
6699
6700pub fn main() {
6701 let bits = <<8080:size(n)>>
6702 bits
6703}"#,
6704 find_position_of("l")
6705 .nth_occurrence(2)
6706 .select_until(find_position_of("s").nth_occurrence(2))
6707 );
6708}
6709
6710#[test]
6711fn extract_constant_from_declaration_of_bin_op() {
6712 assert_code_action!(
6713 EXTRACT_CONSTANT,
6714 r#"pub fn main() {
6715 let twelve = "1" <> "2"
6716}"#,
6717 find_position_of("<").to_selection()
6718 );
6719}
6720
6721#[test]
6722fn extract_constant_from_whole_declaration_of_bin_op() {
6723 assert_code_action!(
6724 EXTRACT_CONSTANT,
6725 r#"import gleam/io
6726
6727pub fn main() {
6728 let twelve = "1" <> "2"
6729 io.print(twelve)
6730}"#,
6731 find_position_of("l")
6732 .nth_occurrence(2)
6733 .select_until(find_position_of("e").nth_occurrence(4))
6734 );
6735}
6736
6737#[test]
6738fn extract_constant_from_declaration_of_int() {
6739 assert_code_action!(
6740 EXTRACT_CONSTANT,
6741 r#"pub fn main() {
6742 let c = 125
6743}"#,
6744 find_position_of("1").select_until(find_position_of("5"))
6745 );
6746}
6747
6748#[test]
6749fn extract_constant_from_whole_declaration_of_int() {
6750 assert_code_action!(
6751 EXTRACT_CONSTANT,
6752 r#"import gleam/io
6753
6754pub fn main() {
6755 let c = 125
6756 io.debug(c)
6757}"#,
6758 find_position_of("l")
6759 .nth_occurrence(2)
6760 .select_until(find_position_of("c"))
6761 );
6762}
6763
6764#[test]
6765fn extract_constant_from_declaration_of_list() {
6766 assert_code_action!(
6767 EXTRACT_CONSTANT,
6768 r#"pub fn main() {
6769 let c = [3.1415, 0.33333333]
6770}"#,
6771 find_position_of("[").to_selection()
6772 );
6773}
6774
6775#[test]
6776fn extract_constant_from_whole_declaration_of_list() {
6777 assert_code_action!(
6778 EXTRACT_CONSTANT,
6779 r#"import gleam/io
6780
6781pub fn main() {
6782 let c = [3.1415, 0.33333333]
6783 io.debug(c)
6784}"#,
6785 find_position_of("l")
6786 .nth_occurrence(2)
6787 .select_until(find_position_of("c"))
6788 );
6789}
6790
6791#[test]
6792fn extract_constant_from_declaration_of_nested_inside() {
6793 assert_code_action!(
6794 EXTRACT_CONSTANT,
6795 r#"pub fn main() {
6796 let c = #([1, 2, 3], [3, 2, 1])
6797}"#,
6798 find_position_of("[").to_selection()
6799 );
6800}
6801
6802#[test]
6803fn extract_constant_from_declaration_of_nested_outside() {
6804 assert_code_action!(
6805 EXTRACT_CONSTANT,
6806 r#"pub fn main() {
6807 let c = #([1, 2, 3], [3, 2, 1])
6808}"#,
6809 find_position_of("#").select_until(find_position_of("(").nth_occurrence(2))
6810 );
6811}
6812
6813#[test]
6814fn extract_constant_from_whole_declaration_of_nested() {
6815 assert_code_action!(
6816 EXTRACT_CONSTANT,
6817 r#"import gleam/io
6818
6819pub fn main() {
6820 let c = #([1, 2, 3], [3, 2, 1])
6821 io.debug(c)
6822}"#,
6823 find_position_of("l")
6824 .nth_occurrence(2)
6825 .select_until(find_position_of("c"))
6826 );
6827}
6828
6829#[test]
6830fn extract_constant_from_declaration_of_string() {
6831 assert_code_action!(
6832 EXTRACT_CONSTANT,
6833 r#"pub fn main() {
6834 let c = "constant"
6835}"#,
6836 find_position_of("\"").select_until(find_position_of("\""))
6837 );
6838}
6839
6840#[test]
6841fn extract_constant_from_whole_declaration_of_string() {
6842 assert_code_action!(
6843 EXTRACT_CONSTANT,
6844 r#"import gleam/io
6845
6846pub fn main() {
6847 let c = "constant"
6848 io.debug(c)
6849}"#,
6850 find_position_of("l")
6851 .nth_occurrence(2)
6852 .select_until(find_position_of("c"))
6853 );
6854}
6855
6856#[test]
6857fn extract_constant_from_declaration_of_tuple() {
6858 assert_code_action!(
6859 EXTRACT_CONSTANT,
6860 r#"pub fn main() {
6861 let #(one, two, three) = #("one", "two", "three")
6862}"#,
6863 find_position_of("#")
6864 .nth_occurrence(2)
6865 .select_until(find_position_of("(").nth_occurrence(3))
6866 );
6867}
6868
6869#[test]
6870fn extract_constant_from_whole_declaration_of_tuple() {
6871 assert_code_action!(
6872 EXTRACT_CONSTANT,
6873 r#"import gleam/io
6874
6875pub fn main() {
6876 let c = #("one", "two", "three")
6877 io.debug(c)
6878}"#,
6879 find_position_of("l")
6880 .nth_occurrence(2)
6881 .select_until(find_position_of("c"))
6882 );
6883}
6884
6885#[test]
6886fn extract_constant_from_literal_within_list() {
6887 assert_code_action!(
6888 EXTRACT_CONSTANT,
6889 r#"pub fn main() {
6890 let c = ["constant", todo]
6891}"#,
6892 find_position_of("\"").select_until(find_position_of("\""))
6893 );
6894}
6895
6896#[test]
6897fn extract_constant_from_list_containing_constant() {
6898 assert_code_action!(
6899 EXTRACT_CONSTANT,
6900 r#"const something = "something"
6901
6902pub fn main() {
6903 let c = ["constant", something]
6904}"#,
6905 find_position_of("[").to_selection()
6906 );
6907}
6908
6909#[test]
6910fn extract_constant_from_literal_within_tuple() {
6911 assert_code_action!(
6912 EXTRACT_CONSTANT,
6913 r#"pub fn main() {
6914 let c = #(0.333334, todo)
6915}"#,
6916 find_position_of("0").select_until(find_position_of("4"))
6917 );
6918}
6919
6920#[test]
6921fn extract_constant_from_tuple_containing_constant() {
6922 assert_code_action!(
6923 EXTRACT_CONSTANT,
6924 r#"const something = "something"
6925
6926pub fn main() {
6927 let c = #(0.333334, something)
6928}"#,
6929 find_position_of("#").select_until(find_position_of("(").nth_occurrence(2))
6930 );
6931}
6932
6933#[test]
6934fn extract_constant_from_nested_inside_in_expr() {
6935 assert_code_action!(
6936 EXTRACT_CONSTANT,
6937 r#"pub fn main() {
6938 [#("a", 0), #("b", 1), #("a", 2)]
6939 |> key_filter("a")
6940}"#,
6941 find_position_of("#").select_until(find_position_of("(").nth_occurrence(2))
6942 );
6943}
6944
6945#[test]
6946fn extract_constant_from_nested_outside_in_expr() {
6947 assert_code_action!(
6948 EXTRACT_CONSTANT,
6949 r#"pub fn main() {
6950 [#("a", 0), #("b", 1), #("a", 2)]
6951 |> key_filter("a")
6952}"#,
6953 find_position_of("[").to_selection()
6954 );
6955}
6956
6957#[test]
6958fn extract_constant_from_return_of_float() {
6959 assert_code_action!(
6960 EXTRACT_CONSTANT,
6961 r#"pub fn main() {
6962 0.25
6963}"#,
6964 find_position_of("0").select_until(find_position_of("5"))
6965 );
6966}
6967
6968#[test]
6969fn extract_constant_from_return_of_int() {
6970 assert_code_action!(
6971 EXTRACT_CONSTANT,
6972 r#"pub fn main() {
6973 100
6974}"#,
6975 find_position_of("1").select_until(find_position_of("0").nth_occurrence(2))
6976 );
6977}
6978
6979#[test]
6980fn extract_constant_from_return_of_list() {
6981 assert_code_action!(
6982 EXTRACT_CONSTANT,
6983 r#"pub fn main() {
6984 [1, 2, 3, 4]
6985}"#,
6986 find_position_of("[").to_selection()
6987 );
6988}
6989
6990#[test]
6991fn extract_constant_from_return_of_nested_outside() {
6992 assert_code_action!(
6993 EXTRACT_CONSTANT,
6994 r#"pub fn main() {
6995 [#(0.25, 0.75), #(0.5, 1.5)]
6996}"#,
6997 find_position_of("#").select_until(find_position_of("(").nth_occurrence(2))
6998 );
6999}
7000
7001#[test]
7002fn extract_constant_from_return_of_string() {
7003 assert_code_action!(
7004 EXTRACT_CONSTANT,
7005 r#"pub fn main() {
7006 "constant"
7007}"#,
7008 find_position_of("\"").select_until(find_position_of("\""))
7009 );
7010}
7011
7012#[test]
7013fn extract_constant_from_return_of_tuple() {
7014 assert_code_action!(
7015 EXTRACT_CONSTANT,
7016 r#"pub fn main() {
7017 #(0.25, 0.75)
7018}"#,
7019 find_position_of("#").select_until(find_position_of("(").nth_occurrence(2))
7020 );
7021}
7022
7023#[test]
7024fn extract_constant_from_taken_name_by_function() {
7025 assert_code_action!(
7026 EXTRACT_CONSTANT,
7027 r#"fn floats() {
7028 [1.0, 2.0]
7029}
7030
7031pub fn main() {
7032 [0.25, 0.75]
7033}"#,
7034 find_position_of("[").nth_occurrence(2).to_selection()
7035 );
7036}
7037
7038#[test]
7039fn extract_constant_from_taken_name_by_constant() {
7040 assert_code_action!(
7041 EXTRACT_CONSTANT,
7042 r#"const ints = [1, 2]
7043
7044pub fn main() {
7045 [5, 50]
7046}"#,
7047 find_position_of("[").nth_occurrence(2).to_selection()
7048 );
7049}
7050
7051#[test]
7052fn extract_constant_in_correct_position_1() {
7053 assert_code_action!(
7054 EXTRACT_CONSTANT,
7055 r#"
7056fn first() {
7057 1
7058}
7059
7060fn second() {
7061 2
7062}
7063
7064fn third() {
7065 3
7066}
7067"#,
7068 find_position_of("1").to_selection()
7069 );
7070}
7071
7072#[test]
7073fn extract_constant_in_correct_position_2() {
7074 assert_code_action!(
7075 EXTRACT_CONSTANT,
7076 r#"
7077fn first() {
7078 1
7079}
7080
7081fn second() {
7082 2
7083}
7084
7085fn third() {
7086 3
7087}
7088"#,
7089 find_position_of("2").to_selection()
7090 );
7091}
7092
7093#[test]
7094fn extract_constant_in_correct_position_3() {
7095 assert_code_action!(
7096 EXTRACT_CONSTANT,
7097 r#"
7098fn first() {
7099 1
7100}
7101
7102fn second() {
7103 2
7104}
7105
7106fn third() {
7107 3
7108}
7109"#,
7110 find_position_of("3").to_selection()
7111 );
7112}
7113
7114#[test]
7115fn extract_constant_declaration_with_proper_indentation() {
7116 assert_code_action!(
7117 EXTRACT_CONSTANT,
7118 r#"
7119pub fn main() {
7120 let fahrenheit = {
7121 let degrees = 64
7122 degrees
7123 }
7124 fahrenheit
7125}
7126"#,
7127 find_position_of("l")
7128 .nth_occurrence(2)
7129 .select_until(find_position_of("s"))
7130 );
7131}
7132
7133#[test]
7134fn extract_constant_from_nil() {
7135 assert_code_action!(
7136 EXTRACT_CONSTANT,
7137 r#"pub fn main() {
7138 let x = Nil
7139 x
7140}
7141"#,
7142 find_position_of("l").select_until(find_position_of("x"))
7143 );
7144}
7145
7146#[test]
7147fn extract_constant_from_non_record_variant_1() {
7148 assert_code_action!(
7149 EXTRACT_CONSTANT,
7150 r#"pub type Auth {
7151 Verified
7152 Unverified
7153}
7154
7155pub fn main() {
7156 let a = Unverified
7157 let a = verify(something, a)
7158
7159 a
7160}
7161"#,
7162 find_position_of("U")
7163 .nth_occurrence(2)
7164 .select_until(find_position_of("d").nth_occurrence(3))
7165 );
7166}
7167
7168#[test]
7169fn extract_constant_from_non_record_variant_2() {
7170 assert_code_action!(
7171 EXTRACT_CONSTANT,
7172 r#"pub type Auth {
7173 Verified
7174 Unverified
7175}
7176
7177pub fn main() {
7178 let a = verify(something, Unverified)
7179
7180 a
7181}
7182"#,
7183 find_position_of("U")
7184 .nth_occurrence(2)
7185 .select_until(find_position_of("d").nth_occurrence(3))
7186 );
7187}
7188
7189#[test]
7190fn extract_constant_from_record_variant_1() {
7191 assert_code_action!(
7192 EXTRACT_CONSTANT,
7193 r#"pub type Auth {
7194 Verified(String)
7195 Unverified
7196}
7197
7198pub fn main() {
7199 let u = Verified("User")
7200 let v = verify(something, u)
7201
7202 v
7203}"#,
7204 find_position_of("l").select_until(find_position_of("u").nth_occurrence(4))
7205 );
7206}
7207
7208#[test]
7209fn extract_constant_from_record_variant_2() {
7210 assert_code_action!(
7211 EXTRACT_CONSTANT,
7212 r#"pub type Auth {
7213 Verified(Int)
7214 Unverified
7215}
7216
7217const auth = True
7218
7219const id = 1234
7220
7221pub fn main() {
7222 let v = verify(auth, Verified(id))
7223
7224 v
7225}"#,
7226 find_position_of("V")
7227 .nth_occurrence(2)
7228 .select_until(find_position_of("(").nth_occurrence(4))
7229 );
7230}
7231
7232#[test]
7233fn extract_constant_from_inside_block() {
7234 assert_code_action!(
7235 EXTRACT_CONSTANT,
7236 r#"pub fn main() {
7237 let fahrenheit = {
7238 let degrees = 64 + 32
7239 degrees
7240 }
7241}"#,
7242 find_position_of("32").to_selection()
7243 );
7244}
7245
7246#[test]
7247fn extract_constant_from_inside_case() {
7248 assert_code_action!(
7249 EXTRACT_CONSTANT,
7250 r#"pub fn main(result) {
7251 case result {
7252 Ok(value) -> value + 1
7253 Error(_) -> panic
7254 }
7255}"#,
7256 find_position_of("1").to_selection()
7257 );
7258}
7259
7260#[test]
7261fn extract_constant_from_inside_use_1() {
7262 assert_code_action!(
7263 EXTRACT_CONSTANT,
7264 r#"pub fn main() {
7265 use x <- result.try(todo)
7266 Ok(123)
7267}"#,
7268 find_position_of("Ok").to_selection()
7269 );
7270}
7271
7272#[test]
7273fn extract_constant_from_inside_use_2() {
7274 assert_code_action!(
7275 EXTRACT_CONSTANT,
7276 r#"const number = 123
7277
7278pub fn main() {
7279 use x <- result.try(todo)
7280 Ok(number)
7281}"#,
7282 find_position_of("Ok").to_selection()
7283 );
7284}
7285
7286#[test]
7287fn do_not_extract_constant_from_pattern() {
7288 assert_no_code_actions!(
7289 EXTRACT_CONSTANT,
7290 r#"pub fn main() {
7291 let #(one, two) = #(1, 2)
7292 one
7293}"#,
7294 find_position_of("l").select_until(find_position_of("(").nth_occurrence(2))
7295 );
7296}
7297
7298#[test]
7299fn do_not_extract_constant_from_fn_call_1() {
7300 assert_no_code_actions!(
7301 EXTRACT_CONSTANT,
7302 r#"import gleam/io
7303
7304pub fn main() {
7305 io.print("constant")
7306}"#,
7307 find_position_of("p")
7308 .nth_occurrence(3)
7309 .select_until(find_position_of("t").nth_occurrence(2))
7310 );
7311}
7312
7313#[test]
7314fn do_not_extract_constant_from_fn_call_2() {
7315 assert_no_code_actions!(
7316 EXTRACT_CONSTANT,
7317 r#"import gleam/list
7318
7319pub fn main() {
7320 let first = list.first([1, 2, 3])
7321 first
7322}"#,
7323 find_position_of("l")
7324 .nth_occurrence(3)
7325 .select_until(find_position_of("t").nth_occurrence(4))
7326 );
7327}
7328
7329#[test]
7330fn do_not_extract_constant_from_bin_op() {
7331 assert_no_code_actions!(
7332 EXTRACT_CONSTANT,
7333 r#"pub fn main() {
7334 let res = 64 < 32
7335 res
7336}"#,
7337 find_position_of("<").to_selection()
7338 );
7339}
7340
7341#[test]
7342fn do_not_extract_constant_from_bit_array_1() {
7343 assert_no_code_actions!(
7344 EXTRACT_CONSTANT,
7345 r#"pub fn main() {
7346 let c = "constant"
7347 let res = <<c:utf16>>
7348 res
7349}"#,
7350 find_position_of("<").to_selection()
7351 );
7352}
7353
7354#[test]
7355fn do_not_extract_constant_from_bit_array_2() {
7356 assert_no_code_actions!(
7357 EXTRACT_CONSTANT,
7358 r#"import gleam/io
7359
7360pub fn main() {
7361 let n = 1234
7362 io.debug(<<8080:size(n)>>)
7363}"#,
7364 find_position_of("<").to_selection()
7365 );
7366}
7367
7368#[test]
7369fn do_not_extract_constant_from_bit_array_3() {
7370 assert_no_code_actions!(
7371 EXTRACT_CONSTANT,
7372 r#"import gleam/io
7373
7374pub fn main() {
7375 let l = 1234
7376 let r = 1234
7377 let result = <<l:size(r)>>
7378 result
7379}"#,
7380 find_position_of("l")
7381 .nth_occurrence(5)
7382 .select_until(find_position_of("t").nth_occurrence(5))
7383 );
7384}
7385
7386#[test]
7387fn do_not_extract_constant_from_list_1() {
7388 assert_no_code_actions!(
7389 EXTRACT_CONSTANT,
7390 r#"pub fn main() {
7391 let c = ["constant", todo]
7392 c
7393}"#,
7394 find_position_of("[").to_selection()
7395 );
7396}
7397
7398#[test]
7399fn do_not_extract_constant_from_list_2() {
7400 assert_no_code_actions!(
7401 EXTRACT_CONSTANT,
7402 r#"pub fn main() {
7403 [10, todo]
7404}"#,
7405 find_position_of("[").to_selection()
7406 );
7407}
7408
7409#[test]
7410fn do_not_extract_constant_from_list_3() {
7411 assert_no_code_actions!(
7412 EXTRACT_CONSTANT,
7413 r#"pub fn main() {
7414 let c = [0.25, todo]
7415 c
7416}"#,
7417 find_position_of("l").select_until(find_position_of("c"))
7418 );
7419}
7420
7421#[test]
7422fn do_not_extract_constant_from_tuple_1() {
7423 assert_no_code_actions!(
7424 EXTRACT_CONSTANT,
7425 r#"pub fn main() {
7426 let c = #("constant", todo)
7427 c
7428}"#,
7429 find_position_of("#").select_until(find_position_of("("))
7430 );
7431}
7432
7433#[test]
7434fn do_not_extract_constant_from_tuple_2() {
7435 assert_no_code_actions!(
7436 EXTRACT_CONSTANT,
7437 r#"pub fn main() {
7438 #(10, todo)
7439}"#,
7440 find_position_of("#").select_until(find_position_of("("))
7441 );
7442}
7443
7444#[test]
7445fn do_not_extract_constant_from_tuple_3() {
7446 assert_no_code_actions!(
7447 EXTRACT_CONSTANT,
7448 r#"pub fn main() {
7449 let c = #(0.25, todo)
7450 c
7451}"#,
7452 find_position_of("l").select_until(find_position_of("c"))
7453 );
7454}
7455
7456#[test]
7457fn do_not_extract_constant_from_nested_1() {
7458 assert_no_code_actions!(
7459 EXTRACT_CONSTANT,
7460 r#"pub fn main() {
7461 let c = list.unzip([#(1, 2), #(3, todo)])
7462 c
7463}"#,
7464 find_position_of("[").to_selection()
7465 );
7466}
7467
7468#[test]
7469fn do_not_extract_constant_from_nested_2() {
7470 assert_no_code_actions!(
7471 EXTRACT_CONSTANT,
7472 r#"pub fn main() {
7473 [[1.25, 1], [0.25, todo]]
7474}"#,
7475 find_position_of("[").to_selection()
7476 );
7477}
7478
7479#[test]
7480fn do_not_extract_constant_from_nested_3() {
7481 assert_no_code_actions!(
7482 EXTRACT_CONSTANT,
7483 r#"import gleam/list
7484
7485pub fn main() {
7486 let c = [#(1, 2), #(3, todo)]
7487 c
7488}"#,
7489 find_position_of("l")
7490 .nth_occurrence(3)
7491 .select_until(find_position_of("c"))
7492 );
7493}
7494
7495#[test]
7496fn do_not_extract_constant_from_record_1() {
7497 assert_no_code_actions!(
7498 EXTRACT_CONSTANT,
7499 r#"type Pair {
7500 Pair(Int, Int)
7501}
7502
7503pub fn main() {
7504 let c = list.unzip([Pair(1, 2), Pair(3, todo)])
7505 c
7506}"#,
7507 find_position_of("P").nth_occurrence(4).to_selection()
7508 );
7509}
7510
7511#[test]
7512fn do_not_extract_constant_from_record_2() {
7513 assert_no_code_actions!(
7514 EXTRACT_CONSTANT,
7515 r#"type Couple {
7516 Couple(l: Float, r: Float)
7517}
7518
7519pub fn main() {
7520 #(Couple(1.25, 1.0), Couple(0.25, todo))
7521}"#,
7522 find_position_of("C").nth_occurrence(4).to_selection()
7523 );
7524}
7525
7526#[test]
7527fn do_not_extract_constant_from_record_update() {
7528 assert_no_code_actions!(
7529 EXTRACT_CONSTANT,
7530 r#"type Couple {
7531 Couple(l: Int, r: Int)
7532}
7533
7534pub fn main() {
7535 let c = Couple(1, 2)
7536 let cc = Couple(..c, 2)
7537 cc
7538}"#,
7539 find_position_of("C")
7540 .nth_occurrence(4)
7541 .select_until(find_position_of("e").nth_occurrence(7))
7542 );
7543}
7544
7545#[test]
7546fn do_not_extract_constant_from_record_capture() {
7547 assert_no_code_actions!(
7548 EXTRACT_CONSTANT,
7549 r#"type Couple {
7550 Couple(l: Int, r: Int)
7551}
7552
7553pub fn main() {
7554 let c = Couple(1, _)
7555 c
7556}"#,
7557 find_position_of("C")
7558 .nth_occurrence(3)
7559 .select_until(find_position_of("e").nth_occurrence(5))
7560 );
7561}
7562
7563#[test]
7564fn do_not_extract_top_level_expression_statement() {
7565 assert_no_code_actions!(
7566 EXTRACT_VARIABLE,
7567 r#"pub fn main() {
7568 1
7569}
7570"#,
7571 find_position_of("1").to_selection()
7572 );
7573}
7574
7575#[test]
7576fn do_not_extract_top_level_expression_in_let_statement() {
7577 assert_no_code_actions!(
7578 EXTRACT_VARIABLE,
7579 r#"pub fn main() {
7580 let a = 1
7581}
7582"#,
7583 find_position_of("1").to_selection()
7584 );
7585}
7586
7587#[test]
7588fn allow_extracting_multiple_selects() {
7589 assert_code_action!(
7590 EXTRACT_VARIABLE,
7591 r#"
7592pub fn go(wibble: Wibble) {
7593 [
7594 wibble.wobble.wubble.woo,
7595 todo as "something else",
7596 ]
7597}
7598
7599pub type Wibble { Wibble(wobble: Wobble) }
7600pub type Wobble { Wobble(wubble: Wubble) }
7601pub type Wubble { Wubble(woo: Int) }
7602"#,
7603 find_position_of("wibble")
7604 .nth_occurrence(2)
7605 .select_until(find_position_of("woo"))
7606 );
7607}
7608
7609#[test]
7610fn allow_extracting_part_of_multiple_selects() {
7611 assert_code_action!(
7612 EXTRACT_VARIABLE,
7613 r#"
7614pub fn go(wibble: Wibble) {
7615 [
7616 wibble.wobble.wubble.woo,
7617 todo as "something else",
7618 ]
7619}
7620
7621pub type Wibble { Wibble(wobble: Wobble) }
7622pub type Wobble { Wobble(wubble: Wubble) }
7623pub type Wubble { Wubble(woo: Int) }
7624"#,
7625 find_position_of("wubble").to_selection()
7626 );
7627}
7628
7629#[test]
7630fn do_not_extract_top_level_module_call() {
7631 let src = r#"
7632import list
7633pub fn main() {
7634 list.map([1, 2, 3], todo)
7635}"#;
7636
7637 assert_no_code_actions!(
7638 EXTRACT_VARIABLE,
7639 TestProject::for_source(src).add_module("list", "pub fn map(l, f) { todo }"),
7640 find_position_of("map").to_selection()
7641 );
7642}
7643
7644#[test]
7645fn expand_function_capture() {
7646 assert_code_action!(
7647 EXPAND_FUNCTION_CAPTURE,
7648 r#"pub fn main() {
7649 wibble(_, 1)
7650}"#,
7651 find_position_of("_").to_selection()
7652 );
7653}
7654
7655#[test]
7656fn expand_function_capture_2() {
7657 assert_code_action!(
7658 EXPAND_FUNCTION_CAPTURE,
7659 r#"pub fn main() {
7660 wibble(1, _)
7661}"#,
7662 find_position_of("wibble").to_selection()
7663 );
7664}
7665
7666#[test]
7667fn expand_function_capture_does_not_shadow_variables() {
7668 assert_code_action!(
7669 EXPAND_FUNCTION_CAPTURE,
7670 r#"pub fn main() {
7671 let value = 1
7672 let value_2 = 2
7673 wibble(value, _, value_2)
7674}"#,
7675 find_position_of("wibble").to_selection()
7676 );
7677}
7678
7679#[test]
7680fn expand_function_capture_picks_a_name_based_on_the_type_of_the_hole() {
7681 assert_code_action!(
7682 EXPAND_FUNCTION_CAPTURE,
7683 r#"pub fn main() {
7684 [1, 2, 3]
7685 |> map(add(_, 1))
7686}
7687
7688pub fn map(l: List(a), f: fn(a) -> b) -> List(b) { todo }
7689pub fn add(n, m) { n + m }
7690"#,
7691 find_position_of("add").to_selection()
7692 );
7693}
7694
7695#[test]
7696fn expand_function_capture_in_pipeline() {
7697 assert_code_action!(
7698 EXPAND_FUNCTION_CAPTURE,
7699 "pub fn main() {
7700 1 |> wibble(2, _) |> wobble
7701}
7702
7703fn wibble(a, b) {
7704 todo
7705}
7706
7707fn wobble(i) {
7708 todo
7709}
7710
7711",
7712 find_position_of("wibble").to_selection()
7713 );
7714}
7715
7716#[test]
7717fn generate_dynamic_decoder() {
7718 assert_code_action!(
7719 GENERATE_DYNAMIC_DECODER,
7720 "
7721pub type Person {
7722 Person(name: String, age: Int, height: Float, is_cool: Bool, brain: BitArray)
7723}
7724",
7725 find_position_of("type").to_selection()
7726 );
7727}
7728
7729#[test]
7730fn generate_dynamic_decoder_only_works_if_withing_a_type() {
7731 assert_no_code_actions!(
7732 GENERATE_DYNAMIC_DECODER,
7733 "
7734pub type Person {
7735 Person(name: String, age: Int, height: Float, is_cool: Bool, brain: BitArray)
7736}
7737
7738pub fn main() {
7739 // unrelated
7740 todo
7741}
7742",
7743 find_position_of("pub type").select_until(find_position_of("todo"))
7744 );
7745}
7746
7747#[test]
7748fn generate_dynamic_decoder_complex_types() {
7749 let src = "
7750import gleam/option
7751import gleam/dynamic
7752import gleam/dict
7753
7754pub type Something
7755
7756pub type Wibble(value) {
7757 Wibble(
7758 maybe: option.Option(Something),
7759 map: dict.Dict(String, List(value)),
7760 unknown: List(dynamic.Dynamic),
7761 )
7762}
7763";
7764
7765 assert_code_action!(
7766 GENERATE_DYNAMIC_DECODER,
7767 TestProject::for_source(src)
7768 .add_module("gleam/option", "pub type Option(a)")
7769 .add_module("gleam/dynamic", "pub type Dynamic")
7770 .add_module("gleam/dict", "pub type Dict(k, v)"),
7771 find_position_of("type W").to_selection()
7772 );
7773}
7774
7775#[test]
7776fn generate_dynamic_decoder_already_imported_module() {
7777 let src = "
7778import gleam/dynamic/decode as dyn_dec
7779
7780pub type Wibble {
7781 Wibble(a: Int, b: Float, c: String)
7782}
7783";
7784
7785 assert_code_action!(
7786 GENERATE_DYNAMIC_DECODER,
7787 TestProject::for_source(src).add_module("gleam/dynamic/decode", "pub type Decoder(a)"),
7788 find_position_of("type W").to_selection()
7789 );
7790}
7791
7792#[test]
7793fn generate_dynamic_decoder_tuple() {
7794 assert_code_action!(
7795 GENERATE_DYNAMIC_DECODER,
7796 "
7797pub type Wibble {
7798 Wibble(tuple: #(Int, Float, #(String, Bool)))
7799}
7800",
7801 find_position_of("type W").to_selection()
7802 );
7803}
7804
7805#[test]
7806fn generate_dynamic_decoder_recursive_type() {
7807 let src = "
7808import gleam/option
7809
7810pub type LinkedList {
7811 LinkedList(value: Int, next: option.Option(LinkedList))
7812}
7813";
7814 assert_code_action!(
7815 GENERATE_DYNAMIC_DECODER,
7816 TestProject::for_source(src).add_module("gleam/option", "pub type Option(a)"),
7817 find_position_of("type").to_selection()
7818 );
7819}
7820
7821#[test]
7822fn generate_dynamic_decoder_for_multi_variant_type() {
7823 assert_code_action!(
7824 GENERATE_DYNAMIC_DECODER,
7825 "
7826pub type Wibble {
7827 Wibble(wibble: Int, next: Wibble)
7828 Wobble(wobble: Float, text: String, values: List(Bool))
7829}
7830",
7831 find_position_of("type").to_selection()
7832 );
7833}
7834
7835#[test]
7836fn generate_dynamic_decoder_for_multi_variant_type_multi_word_name() {
7837 assert_code_action!(
7838 GENERATE_DYNAMIC_DECODER,
7839 "
7840pub type Wibble {
7841 OneTwo(wibble: Int, next: Wibble)
7842 ThreeFour(wobble: Float, text: String, values: List(Bool))
7843 FiveSixSeven(one_two: Int)
7844}
7845",
7846 find_position_of("type").to_selection()
7847 );
7848}
7849
7850#[test]
7851fn generate_dynamic_decoder_for_variant_with_no_fields() {
7852 assert_code_action!(
7853 GENERATE_DYNAMIC_DECODER,
7854 "
7855pub type Wibble {
7856 Wibble
7857}
7858",
7859 find_position_of("type").to_selection()
7860 );
7861}
7862
7863#[test]
7864fn generate_dynamic_decoder_for_variants_with_no_fields() {
7865 assert_code_action!(
7866 GENERATE_DYNAMIC_DECODER,
7867 "
7868pub type Wibble {
7869 Wibble
7870 Wobble
7871 Woo
7872}
7873",
7874 find_position_of("type").to_selection()
7875 );
7876}
7877
7878#[test]
7879fn generate_dynamic_decoder_for_variants_with_mixed_fields() {
7880 assert_code_action!(
7881 GENERATE_DYNAMIC_DECODER,
7882 "
7883pub type Wibble {
7884 Wibble
7885 Wobble(field: String, field2: Int)
7886}
7887",
7888 find_position_of("type").to_selection()
7889 );
7890}
7891
7892#[test]
7893fn no_code_action_to_generate_dynamic_decoder_for_type_without_labels() {
7894 assert_no_code_actions!(
7895 GENERATE_DYNAMIC_DECODER,
7896 "
7897pub type Wibble {
7898 Wibble(Int, Int, String)
7899}
7900",
7901 find_position_of("type").to_selection()
7902 );
7903}
7904
7905#[test]
7906fn pattern_match_on_argument_empty_tuple() {
7907 assert_no_code_actions!(
7908 PATTERN_MATCH_ON_ARGUMENT,
7909 "
7910pub fn main(tuple: #()) {
7911 todo
7912}
7913",
7914 find_position_of("tuple").to_selection()
7915 );
7916}
7917
7918#[test]
7919fn pattern_match_on_argument_single_item_tuple() {
7920 assert_code_action!(
7921 PATTERN_MATCH_ON_ARGUMENT,
7922 "
7923pub fn main(tuple: #(Int)) {
7924 todo
7925}
7926",
7927 find_position_of(":").to_selection()
7928 );
7929}
7930
7931#[test]
7932fn pattern_match_on_argument_multi_item_tuple() {
7933 assert_code_action!(
7934 PATTERN_MATCH_ON_ARGUMENT,
7935 "
7936pub fn main(tuple: #(Int, String, Bool)) {
7937 todo
7938}
7939",
7940 find_position_of("tuple").select_until(find_position_of("Int"))
7941 );
7942}
7943
7944#[test]
7945fn pattern_match_on_argument_uses_case_with_multiple_constructors() {
7946 assert_code_action!(
7947 PATTERN_MATCH_ON_ARGUMENT,
7948 "
7949pub type CannotBeDestructured {
7950 One(one: String)
7951 Two(two: Int)
7952}
7953
7954pub fn main(arg: CannotBeDestructured) {
7955 todo
7956}
7957",
7958 find_position_of("arg").to_selection()
7959 );
7960}
7961
7962#[test]
7963fn pattern_match_on_argument_with_multiple_constructors_is_nicely_formatted_in_function_with_empty_body()
7964 {
7965 assert_code_action!(
7966 PATTERN_MATCH_ON_ARGUMENT,
7967 "
7968pub type CannotBeDestructured {
7969 One(one: String)
7970 Two(two: Int)
7971}
7972
7973pub fn main(arg: CannotBeDestructured) {}
7974",
7975 find_position_of("arg").to_selection()
7976 );
7977}
7978
7979#[test]
7980fn pattern_match_on_argument_uses_label_shorthand_syntax_for_labelled_arguments() {
7981 assert_code_action!(
7982 PATTERN_MATCH_ON_ARGUMENT,
7983 "
7984pub type Wibble {
7985 Wobble(Int, String, i_want_to_see_this: String, and_this: Bool)
7986}
7987
7988pub fn main(arg: Wibble) {
7989 todo
7990}
7991",
7992 find_position_of("arg").select_until(find_position_of("Wibble").nth_occurrence(2))
7993 );
7994}
7995
7996#[test]
7997fn pattern_match_on_argument_with_private_type_from_same_module() {
7998 assert_code_action!(
7999 PATTERN_MATCH_ON_ARGUMENT,
8000 "
8001type Wibble {
8002 Wobble(Int, String)
8003}
8004
8005pub fn main(arg: Wibble) {
8006 todo
8007}
8008",
8009 find_position_of("arg").select_until(find_position_of("Wibble").nth_occurrence(2))
8010 );
8011}
8012
8013#[test]
8014fn pattern_match_on_value_with_private_type_from_same_module() {
8015 assert_code_action!(
8016 PATTERN_MATCH_ON_VARIABLE,
8017 "
8018type Wibble {
8019 Wobble(Int, String)
8020}
8021
8022pub fn main() {
8023 let wibble = Wobble(1, \"Hello\")
8024 todo
8025}
8026",
8027 find_position_of("wibble").to_selection()
8028 );
8029}
8030
8031#[test]
8032fn pattern_match_on_clause_variable() {
8033 assert_code_action!(
8034 PATTERN_MATCH_ON_VARIABLE,
8035 "
8036pub fn main() {
8037 case maybe_wibble() {
8038 Ok(something) -> 1
8039 Error(_) -> 2
8040 }
8041}
8042
8043type Wibble {
8044 Wobble
8045 Woo
8046}
8047
8048fn maybe_wibble() { Ok(Wobble) }
8049
8050",
8051 find_position_of("something").to_selection()
8052 );
8053}
8054
8055#[test]
8056// https://github.com/gleam-lang/gleam/issues/5648
8057fn pattern_match_on_variable_defined_inside_anonymous_function() {
8058 assert_code_action!(
8059 PATTERN_MATCH_ON_VARIABLE,
8060 "
8061pub fn main() {
8062 let outcome = apply(#(Ok(1), Nil), fn(pair) {
8063 let #(result, nil) = pair
8064 })
8065}
8066
8067fn apply(a, f) { f(a) }
8068",
8069 find_position_of("result").to_selection()
8070 );
8071}
8072
8073#[test]
8074fn pattern_match_on_clause_variable_with_label() {
8075 assert_code_action!(
8076 PATTERN_MATCH_ON_VARIABLE,
8077 "
8078pub fn main() {
8079 case wibble() {
8080 Wobble(wibble: something) -> 1
8081 _ -> 2
8082 }
8083}
8084
8085type Wibble {
8086 Wobble(wibble: Wibble)
8087 Woo
8088}
8089
8090fn new() { Wobble }
8091
8092",
8093 find_position_of("something").to_selection()
8094 );
8095}
8096
8097#[test]
8098fn pattern_match_on_clause_variable_with_label_shorthand() {
8099 assert_code_action!(
8100 PATTERN_MATCH_ON_VARIABLE,
8101 "
8102pub fn main() {
8103 case new() {
8104 Wobble(wibble:) -> 1
8105 _ -> 2
8106 }
8107}
8108
8109type Wibble {
8110 Wobble(wibble: Wibble)
8111 Woo
8112}
8113
8114fn new() { Wobble }
8115
8116",
8117 find_position_of("wibble").to_selection()
8118 );
8119}
8120
8121#[test]
8122fn pattern_match_on_clause_variable_nested_pattern() {
8123 assert_code_action!(
8124 PATTERN_MATCH_ON_VARIABLE,
8125 "
8126pub fn main() {
8127 case maybe_wibble() {
8128 Ok(Wobble(something)) -> 1
8129 Error(_) -> 2
8130 }
8131}
8132
8133type Wibble {
8134 Wobble(Wibble)
8135 Woo
8136}
8137
8138fn maybe_wibble() { Ok(Woo) }
8139
8140",
8141 find_position_of("something").to_selection()
8142 );
8143}
8144
8145#[test]
8146fn pattern_match_on_clause_variable_with_block_body() {
8147 assert_code_action!(
8148 PATTERN_MATCH_ON_VARIABLE,
8149 "
8150pub fn main() {
8151 case maybe_wibble() {
8152 Ok(something) -> {
8153 1
8154 2
8155 }
8156 Error(_) -> 2
8157 }
8158}
8159
8160type Wibble {
8161 Wobble
8162 Woo
8163}
8164
8165fn maybe_wibble() { Ok(Wobble) }
8166
8167",
8168 find_position_of("something").to_selection()
8169 );
8170}
8171
8172#[test]
8173fn pattern_match_on_argument_will_use_qualified_name() {
8174 let src = "
8175import wibble
8176
8177pub fn main(arg: wibble.Wibble) {
8178 todo
8179}
8180";
8181
8182 let dep = "
8183pub type Wibble {
8184 ThisShouldBeQualified(label: Int)
8185}
8186";
8187
8188 assert_code_action!(
8189 PATTERN_MATCH_ON_ARGUMENT,
8190 TestProject::for_source(src).add_module("wibble", dep),
8191 find_position_of("wibble").nth_occurrence(2).to_selection()
8192 );
8193}
8194
8195#[test]
8196fn pattern_match_on_argument_will_use_unqualified_name() {
8197 let src = "
8198import wibble.{ThisShouldBeUnqualified}
8199
8200pub fn main(arg: wibble.Wibble) {
8201 todo
8202}
8203";
8204
8205 let dep = "
8206pub type Wibble {
8207 ThisShouldBeUnqualified(label: Int)
8208}
8209";
8210
8211 assert_code_action!(
8212 PATTERN_MATCH_ON_ARGUMENT,
8213 TestProject::for_source(src).add_module("wibble", dep),
8214 find_position_of("Wibble").to_selection()
8215 );
8216}
8217
8218#[test]
8219fn pattern_match_on_argument_will_use_aliased_constructor_name() {
8220 let src = "
8221import wibble.{Wobble as IWantToSeeThisName}
8222
8223pub fn main(arg: wibble.Wibble) {
8224 todo
8225}
8226";
8227
8228 let dep = "
8229pub type Wibble {
8230 Wobble(label: Int)
8231}
8232";
8233
8234 assert_code_action!(
8235 PATTERN_MATCH_ON_ARGUMENT,
8236 TestProject::for_source(src).add_module("wibble", dep),
8237 find_position_of("arg").to_selection()
8238 );
8239}
8240
8241#[test]
8242fn pattern_match_on_argument_will_use_aliased_module_name() {
8243 let src = "
8244import wibble as i_want_to_see_this_name
8245
8246pub fn main(arg: i_want_to_see_this_name.Wibble) {
8247 todo
8248}
8249";
8250
8251 let dep = "
8252pub type Wibble {
8253 Wobble(label: Int)
8254}
8255";
8256
8257 assert_code_action!(
8258 PATTERN_MATCH_ON_ARGUMENT,
8259 TestProject::for_source(src).add_dep_module("wibble", dep),
8260 find_position_of("arg").to_selection()
8261 );
8262}
8263
8264#[test]
8265fn pattern_match_on_argument_not_available_for_internal_type() {
8266 let src = "
8267import wibble
8268
8269pub fn main(arg: wobble.Wibble) {
8270 todo
8271}
8272";
8273
8274 let dep = "
8275@internal
8276pub type Wibble {
8277 Wobble(label: Int)
8278}
8279";
8280
8281 assert_no_code_actions!(
8282 PATTERN_MATCH_ON_ARGUMENT,
8283 TestProject::for_source(src).add_module("wibble", dep),
8284 find_position_of("arg").to_selection()
8285 );
8286}
8287
8288#[test]
8289fn pattern_match_on_argument_available_for_internal_type_defined_in_current_module() {
8290 assert_code_action!(
8291 PATTERN_MATCH_ON_ARGUMENT,
8292 "
8293@internal
8294pub type Wibble {
8295 Wobble(label: Int)
8296}
8297
8298pub fn main(arg: Wibble) {
8299 todo
8300}
8301",
8302 find_position_of("arg").select_until(find_position_of("Wibble").nth_occurrence(2))
8303 );
8304}
8305
8306#[test]
8307fn pattern_match_on_argument_preserves_indentation_of_statement_following_inserted_let() {
8308 assert_code_action!(
8309 PATTERN_MATCH_ON_ARGUMENT,
8310 "pub fn main(arg: #(Int, String)) {
8311 todo
8312//^^^^ This should still have two spaces of indentation!
8313}",
8314 find_position_of("arg").to_selection()
8315 );
8316}
8317
8318#[test]
8319fn pattern_match_on_argument_nicely_formats_code_when_used_on_function_with_empty_body() {
8320 assert_code_action!(
8321 PATTERN_MATCH_ON_ARGUMENT,
8322 "pub fn main(arg: #(Int, String)) {}",
8323 find_position_of("arg").to_selection()
8324 );
8325}
8326
8327#[test]
8328fn pattern_match_on_argument_single_unlabelled_field_is_not_numbered() {
8329 assert_code_action!(
8330 PATTERN_MATCH_ON_ARGUMENT,
8331 "
8332pub type Wibble {
8333 Wibble(Int)
8334}
8335
8336pub fn main(arg: Wibble) {}
8337",
8338 find_position_of(":").to_selection()
8339 );
8340}
8341
8342#[test]
8343fn pattern_match_on_let_assignment() {
8344 assert_code_action!(
8345 PATTERN_MATCH_ON_VARIABLE,
8346 "
8347pub fn main() {
8348 let var = #(1, 2)
8349}
8350",
8351 find_position_of("var").to_selection()
8352 );
8353}
8354
8355#[test]
8356fn pattern_match_on_let_assignment_with_multiple_constructors() {
8357 assert_code_action!(
8358 PATTERN_MATCH_ON_VARIABLE,
8359 "
8360pub type Wibble {
8361 Wobble
8362 Woo
8363}
8364
8365pub fn main() {
8366 let var = Woo
8367 todo
8368}
8369",
8370 find_position_of("var").to_selection()
8371 );
8372}
8373
8374#[test]
8375fn pattern_match_on_use_assignment() {
8376 assert_code_action!(
8377 PATTERN_MATCH_ON_VARIABLE,
8378 "
8379pub fn main() {
8380 use var <- f
8381}
8382
8383fn f(g) { g(#(1, 2)) }
8384",
8385 find_position_of("var").to_selection()
8386 );
8387}
8388
8389#[test]
8390fn pattern_match_on_use_assignment_with_multiple_constructors() {
8391 assert_code_action!(
8392 PATTERN_MATCH_ON_VARIABLE,
8393 "
8394pub type Wibble {
8395 Wobble
8396 Woo
8397}
8398
8399pub fn main() {
8400 use var <- f
8401}
8402
8403fn f(g) { g(Wobble) }
8404",
8405 find_position_of("var").to_selection()
8406 );
8407}
8408
8409#[test]
8410fn pattern_match_on_pattern_use_assignment() {
8411 assert_no_code_actions!(
8412 PATTERN_MATCH_ON_VARIABLE,
8413 "
8414pub fn main() {
8415 use #(a, b) <- f
8416}
8417
8418fn f(g) { g(#(1, 2)) }
8419",
8420 find_position_of("#").to_selection()
8421 );
8422}
8423
8424#[test]
8425fn pattern_match_on_argument_works_on_fn_arguments() {
8426 assert_code_action!(
8427 PATTERN_MATCH_ON_ARGUMENT,
8428 "
8429pub fn main() {
8430 [#(1, 2)]
8431 |> map(fn(tuple) {})
8432}
8433
8434fn map(list: List(a), fun: fn(a) -> b) { todo }
8435",
8436 find_position_of("tuple").to_selection()
8437 );
8438}
8439
8440#[test]
8441fn pattern_match_on_argument_works_on_nested_fn_arguments() {
8442 assert_code_action!(
8443 PATTERN_MATCH_ON_ARGUMENT,
8444 "
8445pub fn main() {
8446 map([[#(1, 2)]], fn(list) {
8447 map(list, fn(tuple) {
8448 todo
8449 })
8450 })
8451}
8452
8453fn map(list: List(a), fun: fn(a) -> b) { todo }
8454",
8455 find_position_of("tuple").to_selection()
8456 );
8457}
8458
8459#[test]
8460// https://github.com/gleam-lang/gleam/issues/5042
8461fn pattern_match_on_variable_crashes() {
8462 assert_code_action!(
8463 PATTERN_MATCH_ON_VARIABLE,
8464 r#"
8465pub type Wibble {
8466 Wibble(Wobble)
8467}
8468
8469pub type Wobble {
8470 Wobble
8471 Wubble
8472}
8473
8474pub fn main() {
8475 let Wibble(wobble) = todo
8476
8477 case todo {
8478 _ -> todo
8479 }
8480}
8481"#,
8482 find_position_of("wobble").to_selection()
8483 );
8484}
8485
8486#[test]
8487fn generate_function_works_with_invalid_call() {
8488 assert_code_action!(
8489 GENERATE_FUNCTION,
8490 "
8491pub fn main() -> Bool {
8492 wibble(1, True, 2.3)
8493}
8494",
8495 find_position_of("wibble").to_selection()
8496 );
8497}
8498
8499#[test]
8500fn generate_function_works_with_constants() {
8501 assert_code_action!(
8502 GENERATE_FUNCTION,
8503 "const wibble: fn(Int) -> String = wobble",
8504 find_position_of("wobble").to_selection()
8505 );
8506}
8507
8508#[test]
8509fn generate_function_works_with_constants_2() {
8510 assert_code_action!(
8511 GENERATE_FUNCTION,
8512 "
8513type Wibble(a) {
8514 Wibble(fun: fn(Int) -> a)
8515}
8516
8517const wibble: Wibble(Int) = Wibble(missing)
8518",
8519 find_position_of("missing").to_selection()
8520 );
8521}
8522
8523#[test]
8524fn generate_function_works_with_pipeline_steps() {
8525 assert_code_action!(
8526 GENERATE_FUNCTION,
8527 "
8528pub fn main() {
8529 [1, 2, 3]
8530 |> sum
8531 |> int_to_string
8532}
8533
8534fn int_to_string(n: Int) -> String {
8535 todo
8536}
8537",
8538 find_position_of("sum").to_selection()
8539 );
8540}
8541
8542#[test]
8543fn generate_function_works_with_pipeline_steps_1() {
8544 assert_code_action!(
8545 GENERATE_FUNCTION,
8546 "
8547pub fn main() {
8548 [1, 2, 3]
8549 |> map(int_to_string)
8550 |> join
8551}
8552
8553fn map(list: List(a), fun: fn(a) -> b) -> List(b) {
8554 todo
8555}
8556
8557fn join(n: List(String)) -> String {
8558 todo
8559}
8560",
8561 find_position_of("int_to_string").to_selection()
8562 );
8563}
8564
8565// https://github.com/gleam-lang/gleam/issues/4177#event-15968345230
8566#[test]
8567fn generate_function_picks_argument_name_based_on_type() {
8568 assert_code_action!(
8569 GENERATE_FUNCTION,
8570 "
8571pub fn main() {
8572 wibble(\"Hello\", 1)
8573}
8574",
8575 find_position_of("wibble").to_selection()
8576 );
8577}
8578
8579#[test]
8580fn generate_function_picks_argument_name_based_on_record_access() {
8581 assert_code_action!(
8582 GENERATE_FUNCTION,
8583 "
8584pub type User {
8585 User(id: Int, name: String)
8586}
8587
8588pub fn go(user: User) {
8589 authenticate(user.id, user.name)
8590}
8591",
8592 find_position_of("authenticate").to_selection()
8593 );
8594}
8595
8596#[test]
8597fn generate_function_wont_generate_two_arguments_with_the_same_name_if_they_have_the_same_type() {
8598 assert_code_action!(
8599 GENERATE_FUNCTION,
8600 "
8601pub fn main() {
8602 wibble(2, 1)
8603}
8604",
8605 find_position_of("wibble").to_selection()
8606 );
8607}
8608
8609#[test]
8610fn generate_function_takes_labels_into_account() {
8611 assert_code_action!(
8612 GENERATE_FUNCTION,
8613 "
8614pub fn main() {
8615 wibble(2, n: 1)
8616}
8617",
8618 find_position_of("wibble").to_selection()
8619 );
8620}
8621
8622#[test]
8623fn generate_function_does_not_trigger_if_labels_are_in_the_wrong_order() {
8624 assert_no_code_actions!(
8625 GENERATE_FUNCTION,
8626 "
8627pub fn main() {
8628 wibble(n: 2, 1)
8629}
8630",
8631 find_position_of("wibble").to_selection()
8632 );
8633}
8634
8635#[test]
8636fn generate_function_does_not_trigger_if_there_are_repeated_labels() {
8637 assert_no_code_actions!(
8638 GENERATE_FUNCTION,
8639 "
8640pub fn main() {
8641 wibble(n: 2, n: 1)
8642}
8643",
8644 find_position_of("wibble").to_selection()
8645 );
8646}
8647
8648#[test]
8649fn generate_function_generates_argument_names_from_labels() {
8650 assert_code_action!(
8651 GENERATE_FUNCTION,
8652 "
8653pub fn main() {
8654 add(1, addend: 10)
8655}
8656",
8657 find_position_of("add").to_selection()
8658 );
8659}
8660
8661#[test]
8662fn generate_function_generates_argument_names_from_variables() {
8663 assert_code_action!(
8664 GENERATE_FUNCTION,
8665 "
8666pub fn main() {
8667 let wibble = 10
8668 let wobble = 20
8669
8670 wubble(wibble, wobble, 14)
8671}
8672",
8673 find_position_of("wubble").to_selection()
8674 );
8675}
8676
8677#[test]
8678fn generate_function_labels_and_arguments_can_share_the_same_name() {
8679 assert_code_action!(
8680 GENERATE_FUNCTION,
8681 "
8682pub fn main() {
8683 let wibble = 10
8684 wubble(wibble, wibble: 14)
8685}
8686",
8687 find_position_of("wubble").to_selection()
8688 );
8689}
8690
8691#[test]
8692fn generate_function_arguments_with_same_name_get_renamed() {
8693 assert_code_action!(
8694 GENERATE_FUNCTION,
8695 "
8696pub fn main() {
8697 let wibble = 10
8698 wubble(wibble, wibble)
8699}
8700",
8701 find_position_of("wubble").to_selection()
8702 );
8703}
8704
8705#[test]
8706fn generate_function_arguments_with_labels_and_variables_uses_different_names() {
8707 assert_code_action!(
8708 GENERATE_FUNCTION,
8709 "
8710pub fn main() {
8711 let list = [2, 4, 5]
8712 let value = 1
8713 find(each: value, in: list)
8714}
8715",
8716 find_position_of("find").to_selection()
8717 );
8718}
8719
8720#[test]
8721fn pattern_match_on_argument_generates_unique_names_even_with_labels() {
8722 assert_code_action!(
8723 PATTERN_MATCH_ON_ARGUMENT,
8724 "
8725pub type Wibble {
8726 Wibble(String, string: String)
8727}
8728
8729pub fn main(wibble: Wibble) {
8730 todo
8731}
8732",
8733 find_position_of("wibble").to_selection()
8734 );
8735}
8736
8737#[test]
8738fn extract_variable_with_list_with_plural_name_does_not_add_another_s() {
8739 assert_code_action!(
8740 EXTRACT_VARIABLE,
8741 "
8742pub fn main() {
8743 wibble([Names, Names])
8744}
8745
8746pub type Names {
8747 Names
8748}
8749",
8750 find_position_of("[").to_selection()
8751 );
8752}
8753
8754#[test]
8755fn convert_to_function_call_works_with_argument_in_first_position() {
8756 assert_code_action!(
8757 CONVERT_TO_FUNCTION_CALL,
8758 "
8759pub fn main() {
8760 [1, 2, 3]
8761 |> map(todo)
8762}
8763
8764fn map(list: List(a), fun: fn(a) -> b) -> List(b) { todo }
8765",
8766 find_position_of("map").to_selection()
8767 );
8768}
8769
8770#[test]
8771fn generate_json_encoder() {
8772 let src = "
8773pub type Person {
8774 Person(name: String, age: Int, height: Float, is_cool: Bool)
8775}
8776";
8777
8778 assert_code_action!(
8779 GENERATE_TO_JSON_FUNCTION,
8780 TestProject::for_source(src).add_package_module(
8781 "gleam_json",
8782 "gleam/json",
8783 "pub type Json"
8784 ),
8785 find_position_of("type").to_selection()
8786 );
8787}
8788
8789#[test]
8790fn generate_json_only_triggers_within_a_type() {
8791 let src = "
8792pub type Person {
8793 Person(name: String, age: Int, height: Float, is_cool: Bool)
8794}
8795
8796pub fn main() {
8797 // unrelated
8798 todo
8799}
8800";
8801
8802 assert_no_code_actions!(
8803 GENERATE_TO_JSON_FUNCTION,
8804 TestProject::for_source(src).add_package_module(
8805 "gleam_json",
8806 "gleam/json",
8807 "pub type Json"
8808 ),
8809 find_position_of("type").select_until(find_position_of("todo"))
8810 );
8811}
8812
8813#[test]
8814fn convert_to_function_call_works_with_argument_in_first_position_2() {
8815 assert_code_action!(
8816 CONVERT_TO_FUNCTION_CALL,
8817 "
8818pub fn main() {
8819 [1, 2, 3] |> wibble
8820}
8821
8822fn wibble(a) { todo }
8823",
8824 find_position_of("wibble").to_selection()
8825 );
8826}
8827
8828#[test]
8829fn generate_json_encoder_complex_types() {
8830 let src = "
8831import gleam/option
8832import gleam/dict
8833
8834pub type Something
8835
8836pub type Wibble(value) {
8837 Wibble(
8838 maybe: option.Option(Int),
8839 something: Something,
8840 map: dict.Dict(String, List(Float)),
8841 unknown: List(value),
8842 )
8843}
8844";
8845
8846 assert_code_action!(
8847 GENERATE_TO_JSON_FUNCTION,
8848 TestProject::for_source(src)
8849 .add_module("gleam/option", "pub type Option(a)")
8850 .add_module("gleam/dict", "pub type Dict(k, v)")
8851 .add_package_module("gleam_json", "gleam/json", "pub type Json"),
8852 find_position_of("type W").to_selection()
8853 );
8854}
8855
8856#[test]
8857fn convert_to_function_call_works_with_argument_in_first_position_3() {
8858 assert_code_action!(
8859 CONVERT_TO_FUNCTION_CALL,
8860 "
8861pub fn main() {
8862 [1, 2, 3] |> wibble()
8863}
8864
8865fn wibble(a) { todo }
8866",
8867 find_position_of("wibble").to_selection()
8868 );
8869}
8870
8871#[test]
8872fn generate_json_encoder_already_imported_module() {
8873 let src = "
8874import gleam/json as json_encoding
8875
8876pub type Wibble {
8877 Wibble(a: Int, b: Float, c: String)
8878}
8879";
8880
8881 assert_code_action!(
8882 GENERATE_TO_JSON_FUNCTION,
8883 TestProject::for_source(src).add_package_module(
8884 "gleam_json",
8885 "gleam/json",
8886 "pub type Json"
8887 ),
8888 find_position_of("type W").to_selection()
8889 );
8890}
8891
8892#[test]
8893fn convert_to_function_call_works_with_argument_in_first_position_4() {
8894 assert_code_action!(
8895 CONVERT_TO_FUNCTION_CALL,
8896 "
8897pub fn main() {
8898 [1, 2, 3] |> wibble.wobble
8899}
8900",
8901 find_position_of("wibble").to_selection()
8902 );
8903}
8904
8905#[test]
8906fn generate_json_encoder_tuple() {
8907 let src = "
8908pub type Wibble {
8909 Wibble(tuple: #(Int, Float, #(String, Bool)))
8910}
8911";
8912
8913 assert_code_action!(
8914 GENERATE_TO_JSON_FUNCTION,
8915 TestProject::for_source(src).add_package_module(
8916 "gleam_json",
8917 "gleam/json",
8918 "pub type Json"
8919 ),
8920 find_position_of("type W").to_selection()
8921 );
8922}
8923
8924#[test]
8925fn generate_json_encoder_for_variant_with_no_fields() {
8926 let src = "
8927pub type Wibble {
8928 Wibble
8929}
8930";
8931
8932 assert_code_action!(
8933 GENERATE_TO_JSON_FUNCTION,
8934 TestProject::for_source(src).add_package_module(
8935 "gleam_json",
8936 "gleam/json",
8937 "pub type Json"
8938 ),
8939 find_position_of("type W").to_selection()
8940 );
8941}
8942
8943#[test]
8944fn generate_json_encoder_for_type_with_multiple_variants_with_no_fields() {
8945 let src = "
8946pub type Wibble {
8947 Wibble
8948 Wobble
8949 Woo
8950}
8951";
8952
8953 assert_code_action!(
8954 GENERATE_TO_JSON_FUNCTION,
8955 TestProject::for_source(src).add_package_module(
8956 "gleam_json",
8957 "gleam/json",
8958 "pub type Json"
8959 ),
8960 find_position_of("type W").to_selection()
8961 );
8962}
8963
8964#[test]
8965fn generate_json_encoder_for_variants_with_mixed_fields() {
8966 let src = "
8967pub type Wibble {
8968 Wibble
8969 Wobble(field: String, field1: Int)
8970}
8971";
8972
8973 assert_code_action!(
8974 GENERATE_TO_JSON_FUNCTION,
8975 TestProject::for_source(src).add_package_module(
8976 "gleam_json",
8977 "gleam/json",
8978 "pub type Json"
8979 ),
8980 find_position_of("type W").to_selection()
8981 );
8982}
8983
8984#[test]
8985fn convert_to_function_call_works_with_function_producing_another_function() {
8986 assert_code_action!(
8987 CONVERT_TO_FUNCTION_CALL,
8988 "
8989pub fn main() {
8990 1 |> wibble(2)
8991}
8992
8993fn wibble(c) -> fn(a) -> Nil {
8994 fn(_) { Nil }
8995}
8996",
8997 find_position_of("wibble").to_selection()
8998 );
8999}
9000
9001#[test]
9002fn generate_json_encoder_recursive_type() {
9003 let src = "
9004import gleam/option.{Some}
9005
9006pub type LinkedList {
9007 LinkedList(value: Int, next: option.Option(LinkedList))
9008}
9009";
9010 assert_code_action!(
9011 GENERATE_TO_JSON_FUNCTION,
9012 TestProject::for_source(src)
9013 .add_module("gleam/option", "pub type Option(a) { Some(a) None }")
9014 .add_package_module("gleam_json", "gleam/json", "pub type Json"),
9015 find_position_of("type").to_selection()
9016 );
9017}
9018
9019#[test]
9020fn convert_to_function_call_works_with_hole_in_first_position() {
9021 assert_code_action!(
9022 CONVERT_TO_FUNCTION_CALL,
9023 "
9024pub fn main() {
9025 [1, 2, 3]
9026 |> map(_, todo)
9027}
9028
9029fn map(list: List(a), fun: fn(a) -> b) -> List(b) { todo }
9030",
9031 find_position_of("[").to_selection()
9032 );
9033}
9034
9035#[test]
9036fn generate_json_encoder_list_of_tuples() {
9037 let src = "
9038pub type Wibble {
9039 Wibble(values: List(#(Int, String)))
9040}
9041";
9042
9043 assert_code_action!(
9044 GENERATE_TO_JSON_FUNCTION,
9045 TestProject::for_source(src).add_package_module(
9046 "gleam_json",
9047 "gleam/json",
9048 "pub type Json"
9049 ),
9050 find_position_of("type").to_selection()
9051 );
9052}
9053
9054#[test]
9055fn generate_json_encoder_for_multi_variant_type() {
9056 let src = "
9057pub type Wibble {
9058 Wibble(wibble: Int, next: Wibble)
9059 Wobble(wobble: Float, text: String, values: List(Bool))
9060}
9061";
9062
9063 assert_code_action!(
9064 GENERATE_TO_JSON_FUNCTION,
9065 TestProject::for_source(src).add_package_module(
9066 "gleam_json",
9067 "gleam/json",
9068 "pub type Json"
9069 ),
9070 find_position_of("type").to_selection()
9071 );
9072}
9073
9074#[test]
9075fn generate_json_encoder_for_multi_variant_type_multi_word_name() {
9076 let src = "
9077pub type Wibble {
9078 OneTwoThree(wibble: Int, next: Wibble)
9079 FourFive(wobble: Float, text: String, values: List(Bool))
9080 SixSevenEight(one_two: Float)
9081}
9082";
9083
9084 assert_code_action!(
9085 GENERATE_TO_JSON_FUNCTION,
9086 TestProject::for_source(src).add_package_module(
9087 "gleam_json",
9088 "gleam/json",
9089 "pub type Json"
9090 ),
9091 find_position_of("type").to_selection()
9092 );
9093}
9094
9095#[test]
9096fn convert_to_function_call_works_with_hole_not_in_first_position() {
9097 assert_code_action!(
9098 CONVERT_TO_FUNCTION_CALL,
9099 "
9100pub fn main() {
9101 fn(a) { todo }
9102 |> map([1, 2, 3], _)
9103}
9104
9105fn map(list: List(a), fun: fn(a) -> b) -> List(b) { todo }
9106",
9107 find_position_of("fn(a)").select_until(find_position_of("map"))
9108 );
9109}
9110
9111#[test]
9112fn convert_to_function_call_always_inlines_the_first_step() {
9113 assert_code_action!(
9114 CONVERT_TO_FUNCTION_CALL,
9115 "
9116pub fn main() {
9117 [1, 2, 3]
9118 |> map(todo)
9119 |> filter(todo)
9120}
9121
9122fn map(list: List(a), fun: fn(a) -> b) -> List(b) { todo }
9123fn filter(list: List(a), fun: fn(a) -> Bool) -> List(b) { todo }
9124",
9125 find_position_of("[1, 2, 3]").select_until(find_position_of("map"))
9126 );
9127}
9128
9129#[test]
9130fn convert_to_function_call_works_with_labelled_argument() {
9131 assert_code_action!(
9132 CONVERT_TO_FUNCTION_CALL,
9133 "
9134pub fn main() {
9135 [1, 2, 3] |> wibble(wobble: _, woo:)
9136}
9137",
9138 find_position_of("wibble").to_selection()
9139 );
9140}
9141
9142#[test]
9143fn convert_to_function_call_works_with_labelled_argument_2() {
9144 assert_code_action!(
9145 CONVERT_TO_FUNCTION_CALL,
9146 "
9147pub fn main() {
9148 [1, 2, 3] |> wibble(wobble:, woo: _)
9149}
9150",
9151 find_position_of("wibble").to_selection()
9152 );
9153}
9154
9155#[test]
9156fn convert_to_function_call_works_when_piping_an_invalid_module_select() {
9157 assert_code_action!(
9158 CONVERT_TO_FUNCTION_CALL,
9159 "
9160pub fn main() {
9161 wibble.wobble |> woo(_)
9162}
9163",
9164 find_position_of("woo").to_selection()
9165 );
9166}
9167
9168#[test]
9169fn convert_to_function_call_works_when_piping_a_module_select() {
9170 let src = "
9171import wibble
9172
9173pub fn main() {
9174 wibble.wobble |> woo(_)
9175}
9176
9177fn woo(n) { todo }
9178";
9179
9180 assert_code_action!(
9181 CONVERT_TO_FUNCTION_CALL,
9182 TestProject::for_source(src).add_module("wibble", "pub const wobble = 1"),
9183 find_position_of("woo").to_selection()
9184 );
9185}
9186
9187#[test]
9188fn convert_to_function_call_works_with_echo() {
9189 assert_code_action!(
9190 CONVERT_TO_FUNCTION_CALL,
9191 "
9192pub fn main() {
9193 wibble.wobble |> echo
9194}
9195",
9196 find_position_of("echo").to_selection()
9197 );
9198}
9199
9200#[test]
9201fn no_code_action_to_generate_json_encoder_for_type_without_labels() {
9202 let src = "
9203pub type Wibble {
9204 Wibble(Int, Int, String)
9205}
9206 ";
9207
9208 assert_no_code_actions!(
9209 GENERATE_TO_JSON_FUNCTION,
9210 TestProject::for_source(src).add_package_module(
9211 "gleam_json",
9212 "gleam/json",
9213 "pub type Json"
9214 ),
9215 find_position_of("type").to_selection()
9216 );
9217}
9218
9219#[test]
9220fn no_code_action_to_generate_json_encoder_without_gleam_json_dependency() {
9221 assert_no_code_actions!(
9222 GENERATE_TO_JSON_FUNCTION,
9223 "
9224pub type Wibble {
9225 Wibble(w: Int)
9226}
9227",
9228 find_position_of("type").to_selection()
9229 );
9230}
9231
9232#[test]
9233fn inline_variable() {
9234 let src = r#"
9235import gleam/io
9236
9237pub fn main() {
9238 let message = "Hello!"
9239 io.println(message)
9240}
9241"#;
9242 assert_code_action!(
9243 INLINE_VARIABLE,
9244 TestProject::for_source(src).add_module("gleam/io", "pub fn println(value) {}"),
9245 find_position_of("message)").to_selection()
9246 );
9247}
9248
9249#[test]
9250fn inline_variable_from_definition() {
9251 let src = r#"
9252import gleam/io
9253
9254pub fn main() {
9255 let message = "Hello!"
9256 io.println(message)
9257}
9258"#;
9259 assert_code_action!(
9260 INLINE_VARIABLE,
9261 TestProject::for_source(src).add_module("gleam/io", "pub fn println(value) {}"),
9262 find_position_of("message =").to_selection()
9263 );
9264}
9265
9266#[test]
9267fn inline_variable_in_nested_scope() {
9268 let src = r#"
9269import gleam/io
9270
9271pub fn main() {
9272 let _ = {
9273 let message = "Hello!"
9274 io.println(message)
9275 }
9276}
9277"#;
9278 assert_code_action!(
9279 INLINE_VARIABLE,
9280 TestProject::for_source(src).add_module("gleam/io", "pub fn println(value) {}"),
9281 find_position_of("message =").to_selection()
9282 );
9283}
9284
9285#[test]
9286fn inline_variable_in_case_scope() {
9287 let src = r#"
9288import gleam/io
9289
9290pub fn main(x) {
9291 case x {
9292 True -> {
9293 let message = "Hello!"
9294 io.println(message)
9295 }
9296 False -> Nil
9297 }
9298}
9299"#;
9300 assert_code_action!(
9301 INLINE_VARIABLE,
9302 TestProject::for_source(src).add_module("gleam/io", "pub fn println(value) {}"),
9303 find_position_of("message =").to_selection()
9304 );
9305}
9306
9307#[test]
9308fn inline_variable_when_over_let_keyword() {
9309 assert_code_action!(
9310 INLINE_VARIABLE,
9311 r#"
9312pub fn main() {
9313 let x = 123
9314 x + 1
9315}
9316"#,
9317 find_position_of("let").to_selection()
9318 );
9319}
9320
9321#[test]
9322fn no_code_action_to_inline_variable_used_multiple_times() {
9323 let src = r#"
9324import gleam/io
9325
9326pub fn main() {
9327 let message = "Hello!"
9328 io.println(message)
9329 io.debug(message)
9330}
9331"#;
9332 assert_no_code_actions!(
9333 INLINE_VARIABLE,
9334 TestProject::for_source(src).add_module(
9335 "gleam/io",
9336 "pub fn println(value) {} pub fn debug(value) {}"
9337 ),
9338 find_position_of("message =").to_selection()
9339 );
9340}
9341
9342#[test]
9343fn no_code_action_to_inline_variable_defined_in_complex_pattern() {
9344 let src = r#"
9345import gleam/io
9346
9347pub fn main() {
9348 let #(message, second, _) = todo
9349 io.println(message)
9350}
9351"#;
9352 assert_no_code_actions!(
9353 INLINE_VARIABLE,
9354 TestProject::for_source(src).add_module("gleam/io", "pub fn println(value) {}"),
9355 find_position_of("message)").to_selection()
9356 );
9357}
9358
9359#[test]
9360fn no_code_action_to_inline_variable_defined_in_case_clause() {
9361 let src = r#"
9362import gleam/io
9363
9364pub fn main(result) {
9365 case result {
9366 Ok(value) -> value
9367 Error(message) -> {
9368 io.println(message)
9369 panic
9370 }
9371 }
9372}
9373"#;
9374 assert_no_code_actions!(
9375 INLINE_VARIABLE,
9376 TestProject::for_source(src).add_module("gleam/io", "pub fn println(value) {}"),
9377 find_position_of("message").nth_occurrence(2).to_selection()
9378 );
9379}
9380
9381#[test]
9382fn convert_to_pipe_with_function_call_on_first_argument() {
9383 assert_code_action!(
9384 CONVERT_TO_PIPE,
9385 "
9386pub fn main() {
9387 wibble(wobble, woo)
9388}
9389",
9390 find_position_of("wobble").to_selection()
9391 );
9392}
9393
9394#[test]
9395fn convert_to_pipe_with_function_call_on_second_argument() {
9396 assert_code_action!(
9397 CONVERT_TO_PIPE,
9398 "
9399pub fn main() {
9400 wibble(wobble, woo)
9401}
9402",
9403 find_position_of("woo").to_selection()
9404 );
9405}
9406
9407#[test]
9408fn convert_to_pipe_with_function_call_on_function_name_extracts_first_argument() {
9409 assert_code_action!(
9410 CONVERT_TO_PIPE,
9411 "
9412pub fn main() {
9413 wibble(wobble, woo)
9414}
9415",
9416 find_position_of("wibble").to_selection()
9417 );
9418}
9419
9420#[test]
9421fn convert_to_pipe_works_in_anonymous_function_inside_a_pipeline() {
9422 assert_code_action!(
9423 CONVERT_TO_PIPE,
9424 "
9425pub fn main() {
9426 wibble |> wobble(fn() { woo(1) })
9427}
9428",
9429 find_position_of("woo").to_selection()
9430 );
9431}
9432
9433#[test]
9434fn convert_to_pipe_works_in_final_step_of_a_pipeline() {
9435 assert_code_action!(
9436 CONVERT_TO_PIPE,
9437 "
9438pub fn main() {
9439 wibble |> wobble(woo(1))
9440}
9441",
9442 find_position_of("woo").to_selection()
9443 );
9444}
9445
9446#[test]
9447fn convert_to_pipe_with_function_call_with_labelled_arguments_inserts_hole() {
9448 assert_code_action!(
9449 CONVERT_TO_PIPE,
9450 "
9451pub fn main() {
9452 wibble(wobble: 1, woo: 2)
9453}
9454",
9455 find_position_of("wobble").to_selection()
9456 );
9457}
9458
9459#[test]
9460fn convert_to_pipe_with_function_call_with_labelled_arguments_inserts_hole_2() {
9461 assert_code_action!(
9462 CONVERT_TO_PIPE,
9463 "
9464pub fn main() {
9465 wibble(wobble: 1, woo: 2)
9466}
9467",
9468 find_position_of("woo").to_selection()
9469 );
9470}
9471
9472#[test]
9473fn convert_to_pipe_with_function_call_with_shorthand_labelled_argument_inserts_hole() {
9474 assert_code_action!(
9475 CONVERT_TO_PIPE,
9476 "
9477pub fn main() {
9478 wibble(wobble:, woo:)
9479}
9480",
9481 find_position_of("wobble").to_selection()
9482 );
9483}
9484
9485#[test]
9486fn convert_to_pipe_with_function_call_with_shorthand_labelled_argument_inserts_hole_2() {
9487 assert_code_action!(
9488 CONVERT_TO_PIPE,
9489 "
9490pub fn main() {
9491 wibble(wobble:, woo:)
9492}
9493",
9494 find_position_of("woo").to_selection()
9495 );
9496}
9497
9498#[test]
9499fn convert_to_pipe_on_first_step_of_pipeline() {
9500 assert_code_action!(
9501 CONVERT_TO_PIPE,
9502 "
9503pub fn main() {
9504 wibble(wobble, woo) |> wobble
9505}
9506",
9507 find_position_of("wibble").to_selection()
9508 );
9509}
9510
9511#[test]
9512fn convert_to_pipe_not_allowed_on_other_pipeline_steps() {
9513 assert_no_code_actions!(
9514 CONVERT_TO_PIPE,
9515 "
9516pub fn main() {
9517 wibble(wobble) |> wobble(woo)
9518}
9519",
9520 find_position_of("woo").to_selection()
9521 );
9522}
9523
9524#[test]
9525fn convert_to_pipe_with_function_returning_other_function() {
9526 assert_code_action!(
9527 CONVERT_TO_PIPE,
9528 "
9529pub fn main() {
9530 wibble(wobble)(woo)
9531}
9532",
9533 find_position_of("woo").to_selection()
9534 );
9535}
9536
9537#[test]
9538fn convert_to_pipe_does_not_work_on_function_on_the_right_hand_side_of_use() {
9539 assert_no_code_actions!(
9540 CONVERT_TO_PIPE,
9541 "
9542pub fn main() {
9543 use <- wibble(wobble)
9544 todo
9545}
9546",
9547 find_position_of("wibble").to_selection()
9548 );
9549}
9550
9551#[test]
9552fn convert_to_pipe_does_not_work_on_function_on_the_right_hand_side_of_use_2() {
9553 assert_no_code_actions!(
9554 CONVERT_TO_PIPE,
9555 "
9556pub fn main() {
9557 use <- wibble(wobble)
9558 todo
9559}
9560",
9561 find_position_of("todo").to_selection()
9562 );
9563}
9564
9565#[test]
9566fn convert_to_pipe_does_not_work_on_function_with_capture() {
9567 assert_no_code_actions!(
9568 CONVERT_TO_PIPE,
9569 "import gleam/int
9570
9571pub fn main() {
9572 let sum = int.add(1, _)
9573 sum
9574}
9575",
9576 find_position_of("1").to_selection()
9577 );
9578}
9579
9580#[test]
9581fn convert_to_pipe_does_not_work_on_record_with_capture() {
9582 assert_no_code_actions!(
9583 CONVERT_TO_PIPE,
9584 "pub fn main() {
9585 Ok(_)
9586}
9587",
9588 find_position_of("O").to_selection()
9589 );
9590}
9591
9592#[test]
9593fn convert_to_pipe_works_inside_body_of_use() {
9594 assert_code_action!(
9595 CONVERT_TO_PIPE,
9596 "
9597pub fn main() {
9598 use <- wibble(wobble)
9599 woo(123)
9600}
9601",
9602 find_position_of("woo").to_selection()
9603 );
9604}
9605
9606#[test]
9607fn convert_to_pipe_pipes_the_outermost_argument() {
9608 assert_code_action!(
9609 CONVERT_TO_PIPE,
9610 "
9611pub fn main() {
9612 wibble(wobble(woo))
9613}
9614",
9615 find_position_of("wobble").to_selection()
9616 );
9617}
9618
9619#[test]
9620fn convert_to_pipe_when_first_arg_is_a_pipe_itself() {
9621 assert_code_action!(
9622 CONVERT_TO_PIPE,
9623 "
9624pub fn main() {
9625 wibble(wobble |> woo, waa)
9626}
9627",
9628 find_position_of("wibble").to_selection()
9629 );
9630}
9631
9632#[test]
9633fn convert_to_pipe_with_string_concat_adds_braces() {
9634 assert_code_action!(
9635 CONVERT_TO_PIPE,
9636 "
9637pub fn main() {
9638 wibble(wobble <> woo, waa)
9639}
9640",
9641 find_position_of("wibble").to_selection()
9642 );
9643}
9644
9645#[test]
9646fn convert_to_pipe_with_bool_operator_adds_braces() {
9647 assert_code_action!(
9648 CONVERT_TO_PIPE,
9649 "
9650pub fn main() {
9651 wibble(wobble != woo, waa)
9652}
9653",
9654 find_position_of("wibble").to_selection()
9655 );
9656}
9657
9658#[test]
9659fn convert_to_pipe_with_sum_adds_no_braces() {
9660 assert_code_action!(
9661 CONVERT_TO_PIPE,
9662 "
9663pub fn main() {
9664 wibble(1 + 1, waa)
9665}
9666",
9667 find_position_of("wibble").to_selection()
9668 );
9669}
9670
9671#[test]
9672fn convert_to_pipe_with_comparison_adds_braces() {
9673 assert_code_action!(
9674 CONVERT_TO_PIPE,
9675 "
9676pub fn main() {
9677 wibble(1.0 >=. 0.0, waa)
9678}
9679",
9680 find_position_of("wibble").to_selection()
9681 );
9682}
9683
9684#[test]
9685fn convert_to_pipe_with_complex_binop_adds_braces() {
9686 assert_code_action!(
9687 CONVERT_TO_PIPE,
9688 "
9689fn bug() {
9690 bool.guard(1 == 2 || 2 == 3, Nil, fn() { Nil })
9691}
9692",
9693 find_position_of("||").to_selection()
9694 );
9695}
9696
9697#[test]
9698fn convert_to_pipe_with_nested_calls_picks_the_innermost_one() {
9699 assert_code_action!(
9700 CONVERT_TO_PIPE,
9701 "
9702fn bug() {
9703 wibble(Wobble(
9704 field: woo(other_call(last))
9705 ))
9706}
9707",
9708 find_position_of("woo").to_selection()
9709 );
9710}
9711
9712// https://github.com/gleam-lang/gleam/issues/4342
9713#[test]
9714fn inline_variable_in_record_update() {
9715 assert_code_action!(
9716 INLINE_VARIABLE,
9717 "
9718type Couple {
9719 Couple(l: Int, r: Int)
9720}
9721
9722pub fn main() {
9723 let c1 = Couple(l: 1, r: 1)
9724 let c2 = Couple(..c1, r: 1)
9725}
9726",
9727 find_position_of("c1,").to_selection()
9728 );
9729}
9730
9731// https://github.com/gleam-lang/gleam/issues/4430
9732#[test]
9733fn inline_variable_with_record_field() {
9734 assert_code_action!(
9735 INLINE_VARIABLE,
9736 "
9737type Couple {
9738 Couple(l: Int, r: Int)
9739}
9740
9741pub fn main() {
9742 let c1 = Couple(l: 1, r: 1)
9743 let c2 = c1.l
9744 echo c2
9745}
9746",
9747 find_position_of("c2").nth_occurrence(2).to_selection()
9748 );
9749}
9750
9751#[test]
9752fn wrap_case_clause_in_block() {
9753 assert_code_action!(
9754 WRAP_IN_BLOCK,
9755 "
9756pub fn f(option) {
9757 case option {
9758 Some(content) -> content
9759 None -> panic
9760 }
9761}",
9762 find_position_of("content").nth_occurrence(2).to_selection()
9763 );
9764}
9765
9766#[test]
9767fn wrap_nested_case_clause_in_block() {
9768 assert_code_action!(
9769 WRAP_IN_BLOCK,
9770 "
9771pub fn f(result) {
9772 case result {
9773 Ok(reresult) -> {
9774 case reresult {
9775 Ok(w) -> w
9776 Error(_) -> panic
9777 }
9778 }
9779 Error(_) -> panic
9780 }
9781}",
9782 find_position_of("w").nth_occurrence(2).to_selection()
9783 );
9784}
9785
9786#[test]
9787fn wrap_case_clause_with_guard_in_block() {
9788 assert_code_action!(
9789 WRAP_IN_BLOCK,
9790 "
9791pub fn f(option) {
9792 case option {
9793 Some(integer) if integer > 0 -> integer
9794 Some(integer) -> 0
9795 None -> panic
9796 }
9797}",
9798 find_position_of("integer").nth_occurrence(3).to_selection()
9799 );
9800}
9801
9802#[test]
9803fn wrap_case_clause_with_multiple_patterns_in_block() {
9804 assert_code_action!(
9805 WRAP_IN_BLOCK,
9806 "pub type PokemonType {
9807 Air
9808 Water
9809 Fire
9810}
9811
9812 pub fn f(pokemon_type: PokemonType) {
9813 case pokemon_type {
9814 Water | Air -> soak()
9815 Fire -> burn()
9816 }
9817 }",
9818 find_position_of("soak").to_selection()
9819 );
9820}
9821
9822#[test]
9823fn wrap_case_clause_inside_assignment_in_block() {
9824 assert_code_action!(
9825 WRAP_IN_BLOCK,
9826 r#"pub type PokemonType {
9827 Air
9828 Water
9829 Fire
9830}
9831
9832 pub fn f(pokemon_type: PokemonType) {
9833 let damage = case pokemon_type {
9834 Water -> soak()
9835 Fire -> burn()
9836 }
9837
9838 "Pokemon did " <> damage
9839 }"#,
9840 find_position_of("burn").to_selection()
9841 );
9842}
9843
9844#[test]
9845fn wrap_case_assignment_of_record_access_in_block() {
9846 assert_code_action!(
9847 WRAP_IN_BLOCK,
9848 r#"
9849pub type Record {
9850 R(left: Int, right: Int)
9851}
9852
9853pub fn main() {
9854 let r = R(1, 2)
9855 let l = r.left
9856 l
9857}
9858"#,
9859 find_position_of("left").nth_occurrence(2).to_selection()
9860 );
9861}
9862
9863#[test]
9864fn do_not_wrap_case_clause_in_block_1() {
9865 assert_no_code_actions!(
9866 WRAP_IN_BLOCK,
9867 "
9868pub fn f(option) {
9869 case option {
9870 Some(content) -> {
9871 content
9872 }
9873 None -> panic
9874 }
9875}",
9876 find_position_of("content").nth_occurrence(2).to_selection()
9877 );
9878}
9879
9880#[test]
9881fn do_not_wrap_case_clause_in_block_2() {
9882 assert_no_code_actions!(
9883 WRAP_IN_BLOCK,
9884 "
9885pub fn f(result) {
9886 case result {
9887 Ok(reresult) -> {
9888 case reresult {
9889 Ok(w) -> {
9890 w
9891 }
9892 Error(_) -> panic
9893 }
9894 }
9895 Error(_) -> panic
9896 }
9897}",
9898 find_position_of("w").nth_occurrence(2).to_selection()
9899 );
9900}
9901
9902#[test]
9903fn do_not_wrap_case_clause_in_block_3() {
9904 assert_no_code_actions!(
9905 WRAP_IN_BLOCK,
9906 "
9907pub fn f(option) {
9908 case option {
9909 Some(content) -> content
9910 None -> panic
9911 }
9912}",
9913 find_position_of("Some(content)").to_selection()
9914 );
9915}
9916
9917#[test]
9918fn wrap_assignment_value_in_block() {
9919 assert_code_action!(
9920 WRAP_IN_BLOCK,
9921 r#"pub fn main() {
9922 let var = "value"
9923}"#,
9924 find_position_of("value").select_until(find_position_of("e").nth_occurrence(2))
9925 );
9926}
9927
9928#[test]
9929fn do_not_wrap_assignment_value_in_block() {
9930 assert_no_code_actions!(
9931 WRAP_IN_BLOCK,
9932 r#"pub fn main() {
9933 let var = "value"
9934}"#,
9935 find_position_of("var").to_selection()
9936 );
9937}
9938
9939// https://github.com/gleam-lang/gleam/issues/4427
9940#[test]
9941fn extract_constant_function() {
9942 assert_no_code_actions!(
9943 EXTRACT_CONSTANT,
9944 r#"
9945fn print(x) {
9946 Nil
9947}
9948
9949pub fn main() {
9950 print("Hello")
9951}
9952"#,
9953 find_position_of("print").nth_occurrence(2).to_selection()
9954 );
9955}
9956
9957#[test]
9958fn fix_float_operator_on_ints() {
9959 let name = "Use `>=`";
9960 assert_code_action!(
9961 name,
9962 r#"
9963pub fn main() {
9964 1 >=. 2
9965}
9966"#,
9967 find_position_of("1").to_selection()
9968 );
9969}
9970
9971#[test]
9972fn fix_float_operator_on_ints_2() {
9973 let name = "Use `-`";
9974 assert_code_action!(
9975 name,
9976 r#"
9977pub fn main() {
9978 1 -. 2
9979}
9980"#,
9981 find_position_of("1").select_until(find_position_of("2"))
9982 );
9983}
9984
9985#[test]
9986fn fix_float_operator_on_ints_3() {
9987 let name = "Use `*`";
9988 assert_code_action!(
9989 name,
9990 r#"
9991pub fn main() {
9992 1 *. wobble()
9993}
9994
9995fn wobble() { 3 }
9996"#,
9997 find_position_of("*.").to_selection()
9998 );
9999}
10000
10001#[test]
10002fn fix_int_operator_on_floats() {
10003 let name = "Use `>=.`";
10004 assert_code_action!(
10005 name,
10006 r#"
10007pub fn main() {
10008 1.0 >= 2.3
10009}
10010"#,
10011 find_position_of("1").to_selection()
10012 );
10013}
10014
10015#[test]
10016fn fix_int_operator_on_floats_2() {
10017 let name = "Use `-.`";
10018 assert_code_action!(
10019 name,
10020 r#"
10021pub fn main() {
10022 1.12 - 2.0
10023}
10024"#,
10025 find_position_of("1").select_until(find_position_of("2.0"))
10026 );
10027}
10028
10029#[test]
10030fn fix_int_operator_on_floats_3() {
10031 let name = "Use `*.`";
10032 assert_code_action!(
10033 name,
10034 r#"
10035pub fn main() {
10036 1.3 * wobble()
10037}
10038
10039fn wobble() { 3.2 }
10040"#,
10041 find_position_of("*").to_selection()
10042 );
10043}
10044
10045#[test]
10046fn fix_plus_operator_on_strings() {
10047 let name = "Use `<>`";
10048 assert_code_action!(
10049 name,
10050 r#"
10051pub fn main() {
10052 "hello, " + name()
10053}
10054
10055fn name() { "Jak" }
10056"#,
10057 find_position_of("hello").select_until(find_position_of("name()"))
10058 );
10059}
10060
10061// https://github.com/gleam-lang/gleam/issues/4454
10062#[test]
10063fn unqualify_already_imported_type() {
10064 let src = "
10065import wibble.{type Wibble}
10066
10067pub fn main() -> wibble.Wibble {
10068 todo
10069}
10070";
10071
10072 assert_code_action!(
10073 "Unqualify wibble.Wibble",
10074 TestProject::for_source(src).add_hex_module("wibble", "pub type Wibble"),
10075 find_position_of("wibble.Wibble").to_selection(),
10076 );
10077}
10078
10079#[test]
10080fn fill_labels_pattern_constructor() {
10081 assert_code_action!(
10082 FILL_LABELS,
10083 "
10084pub type Wibble {
10085 Wibble(a: Int, b: Float, c: String)
10086 Wobble(d: Bool, e: BitArray, f: List(Result(String, Nil)))
10087}
10088
10089pub fn main(w: Wibble) {
10090 case w {
10091 Wibble(..) -> todo
10092 Wobble() -> todo
10093 }
10094}
10095",
10096 find_position_of("Wobble()").to_selection(),
10097 );
10098}
10099
10100#[test]
10101fn fill_labels_pattern_constructor_let_assignment() {
10102 assert_code_action!(
10103 FILL_LABELS,
10104 "
10105pub type Wibble {
10106 Wibble(a: Int, b: Float, c: String)
10107}
10108
10109pub fn main() {
10110 let Wibble() = todo
10111}
10112",
10113 find_position_of("Wibble()").to_selection(),
10114 );
10115}
10116
10117#[test]
10118fn fill_labels_pattern_constructor_with_some_labels() {
10119 assert_code_action!(
10120 FILL_LABELS,
10121 "
10122pub type Wibble {
10123 Wibble(a: Int, b: Float, c: String)
10124 Wobble(d: Bool, e: BitArray, f: List(Result(String, Nil)))
10125}
10126
10127pub fn main(w: Wibble) {
10128 case w {
10129 Wobble(e: <<>>) -> todo
10130 _ -> todo
10131 }
10132}
10133",
10134 find_position_of("Wobble(e").to_selection(),
10135 );
10136}
10137
10138#[test]
10139fn fill_labels_nested_pattern_constructor() {
10140 assert_code_action!(
10141 FILL_LABELS,
10142 "
10143pub type Wibble {
10144 Wibble(a: Int, b: Float, c: String)
10145 Wobble(d: Bool, e: BitArray, f: List(Result(String, Nil)))
10146}
10147
10148pub fn main() {
10149 case todo {
10150 #([Wobble()], 2, 3) -> todo
10151 _ -> todo
10152 }
10153}
10154",
10155 find_position_of("Wobble()").to_selection(),
10156 );
10157}
10158
10159#[test]
10160// https://github.com/gleam-lang/gleam/issues/4499
10161fn fill_labels_with_function_with_unlabelled_arguments() {
10162 assert_no_code_actions!(
10163 FILL_LABELS,
10164 "
10165pub fn main() {
10166 fold(0, over: [], with: fn(acc, item) { acc + item })
10167}
10168
10169pub fn fold(over list, from initial, with fun) { todo }",
10170 find_position_of("fold").to_selection(),
10171 );
10172}
10173
10174#[test]
10175fn add_missing_patterns_with_labels() {
10176 assert_code_action!(
10177 ADD_MISSING_PATTERNS,
10178 "
10179pub type Wibble {
10180 Wibble(integer: Int, float: Float)
10181 Wobble(string: String, bool: Bool)
10182}
10183
10184pub fn main(w: Wibble) {
10185 case w {}
10186}
10187",
10188 find_position_of("case w").select_until(find_position_of("{}")),
10189 );
10190}
10191
10192// https://github.com/gleam-lang/gleam/issues/3628#issuecomment-2543342212
10193#[test]
10194fn add_missing_patterns_multibyte_grapheme() {
10195 assert_code_action!(
10196 ADD_MISSING_PATTERNS,
10197 r#"
10198// ä
10199fn wibble() {
10200 case True {}
10201}
10202"#,
10203 find_position_of("case").select_until(find_position_of("True {"))
10204 );
10205}
10206
10207#[test]
10208fn add_missing_patterns_needs_to_be_within_the_inexhaustive_case_expression() {
10209 assert_no_code_actions!(
10210 ADD_MISSING_PATTERNS,
10211 r#"
10212fn wibble() {
10213 case True {}
10214}
10215"#,
10216 find_position_of("fn").select_until(find_position_of("}"))
10217 );
10218}
10219
10220// https://github.com/gleam-lang/gleam/issues/4626
10221#[test]
10222fn add_missing_patterns_opaque_type() {
10223 let src = "
10224import mod
10225
10226pub fn main(w: mod.Wibble) {
10227 case w {}
10228}
10229";
10230
10231 assert_code_action!(
10232 ADD_MISSING_PATTERNS,
10233 TestProject::for_source(src).add_hex_module(
10234 "mod",
10235 "pub opaque type Wibble { Wibble(Int) Wobble(String) }"
10236 ),
10237 find_position_of("{}").to_selection(),
10238 );
10239}
10240
10241#[test]
10242fn pattern_match_on_variable_does_not_add_patterns_for_internal_type() {
10243 let src = "
10244import wibble
10245
10246pub type Type {
10247 Type(wibble: wibble.Wibble, list: List(Int))
10248}
10249
10250pub fn main(thing: Type) {
10251 case thing {
10252 Type(wibble:, ..) -> todo
10253 }
10254}
10255";
10256
10257 assert_no_code_actions!(
10258 PATTERN_MATCH_ON_VARIABLE,
10259 TestProject::for_source(src).add_module(
10260 "wibble",
10261 "@internal pub type Wibble { Wibble(Int) Wobble(String) }"
10262 ),
10263 find_position_of("wibble").nth_occurrence(3).to_selection(),
10264 );
10265}
10266
10267#[test]
10268fn pattern_match_on_argument_does_not_add_patterns_for_internal_type() {
10269 let src = "
10270import wibble
10271
10272pub fn main(thing: wibble.Wibble) {}
10273";
10274
10275 assert_no_code_actions!(
10276 PATTERN_MATCH_ON_ARGUMENT,
10277 TestProject::for_source(src).add_module(
10278 "wibble",
10279 "@internal pub type Wibble { Wibble(Int) Wobble(String) }"
10280 ),
10281 find_position_of("thing").to_selection(),
10282 );
10283}
10284
10285#[test]
10286fn pattern_match_on_argument_adds_patterns_for_internal_type_inside_module_where_it_is_defined() {
10287 let src = "
10288@internal
10289pub type Wibble {
10290 Wibble(Int)
10291 Wobble(String)
10292}
10293
10294pub fn main(thing: Wibble) {}
10295";
10296
10297 assert_code_action!(
10298 PATTERN_MATCH_ON_ARGUMENT,
10299 TestProject::for_source(src),
10300 find_position_of("thing").to_selection(),
10301 );
10302}
10303
10304#[test]
10305fn add_missing_patterns_adds_a_discard_for_opaque_type() {
10306 let src = "
10307import wibble
10308
10309pub fn main(w: wibble.Wibble) {
10310 case w {}
10311}
10312";
10313
10314 assert_code_action!(
10315 ADD_MISSING_PATTERNS,
10316 TestProject::for_source(src).add_module(
10317 "wibble",
10318 "@internal pub type Wibble { Wibble(Int) Wobble(String) }"
10319 ),
10320 find_position_of("{}").to_selection(),
10321 );
10322}
10323
10324#[test]
10325fn add_missing_patterns_adds_a_discard_for_opaque_type_1() {
10326 let src = "
10327import wibble
10328
10329pub type Type {
10330 Type(wibble: wibble.Wibble, list: List(Int))
10331}
10332
10333pub fn main(thing: Type) {
10334 case thing {}
10335}
10336";
10337
10338 assert_code_action!(
10339 ADD_MISSING_PATTERNS,
10340 TestProject::for_source(src).add_module(
10341 "wibble",
10342 "@internal pub type Wibble { Wibble(Int) Wobble(String) }"
10343 ),
10344 find_position_of("{}").to_selection(),
10345 );
10346}
10347
10348#[test]
10349fn add_missing_patterns_adds_a_discard_for_opaque_type_2() {
10350 let src = "
10351import wibble
10352
10353pub type Type {
10354 Type(wibble.Wibble)
10355}
10356
10357pub fn main(thing: Type) {
10358 case thing {}
10359}
10360";
10361
10362 assert_code_action!(
10363 ADD_MISSING_PATTERNS,
10364 TestProject::for_source(src).add_module(
10365 "wibble",
10366 "@internal pub type Wibble { Wibble(Int) Wobble(String) }"
10367 ),
10368 find_position_of("{}").to_selection(),
10369 );
10370}
10371
10372#[test]
10373fn add_missing_patterns_adds_patterns_for_internal_type_inside_same_module_where_it_is_defined() {
10374 let src = "
10375@internal
10376pub type Wibble {
10377 Wibble(Int)
10378 Wobble(String)
10379}
10380
10381pub fn main(thing: Wibble) {
10382 case thing {}
10383}
10384";
10385
10386 assert_code_action!(
10387 ADD_MISSING_PATTERNS,
10388 TestProject::for_source(src),
10389 find_position_of("thing").nth_occurrence(2).to_selection(),
10390 );
10391}
10392
10393// https://github.com/gleam-lang/gleam/issues/4653
10394#[test]
10395fn generate_function_capture() {
10396 assert_code_action!(
10397 GENERATE_FUNCTION,
10398 "
10399fn map(list: List(a), f: fn(a) -> b) -> List(b) {
10400 todo
10401}
10402
10403pub fn main() {
10404 map([1, 2, 3], add(_, 1))
10405}
10406",
10407 find_position_of("add").to_selection()
10408 );
10409}
10410
10411// https://github.com/gleam-lang/gleam/issues/4660#issuecomment-2932371619
10412#[test]
10413fn inline_variable_label_shorthand() {
10414 assert_code_action!(
10415 INLINE_VARIABLE,
10416 "
10417pub type Example {
10418 Example(sum: Int, nil: Nil)
10419}
10420
10421pub fn main() {
10422 let sum = 1 + 1
10423
10424 Example(Nil, sum:)
10425}
10426",
10427 find_position_of("sum = ").to_selection()
10428 );
10429}
10430
10431// https://github.com/gleam-lang/gleam/issues/4660
10432#[test]
10433fn no_inline_variable_action_for_parameter() {
10434 assert_no_code_actions!(
10435 INLINE_VARIABLE,
10436 "
10437pub fn main() {
10438 let x = fn(something) {
10439 something
10440 }
10441
10442 x
10443}
10444",
10445 find_position_of("something")
10446 .nth_occurrence(2)
10447 .to_selection()
10448 );
10449}
10450
10451#[test]
10452fn no_inline_variable_action_when_spanning_multiple_items() {
10453 assert_no_code_actions!(
10454 INLINE_VARIABLE,
10455 "
10456pub fn main(x: Int, y: Int) {
10457 let a = 1
10458 let b = 2
10459 main(a, b)
10460}
10461",
10462 find_position_of("main")
10463 .nth_occurrence(2)
10464 .select_until(find_position_of(")").nth_occurrence(2))
10465 );
10466}
10467
10468#[test]
10469fn no_inline_variable_action_for_use_pattern() {
10470 assert_no_code_actions!(
10471 INLINE_VARIABLE,
10472 "
10473pub fn main() {
10474 let x = {
10475 use something <- todo
10476 something
10477 }
10478
10479 x
10480}
10481",
10482 find_position_of("something").to_selection()
10483 );
10484}
10485
10486#[test]
10487fn no_inline_variable_action_for_case_pattern() {
10488 assert_no_code_actions!(
10489 INLINE_VARIABLE,
10490 "
10491pub fn main() {
10492 let x = case todo {
10493 something -> something
10494 }
10495
10496 x
10497}
10498",
10499 find_position_of("something")
10500 .nth_occurrence(2)
10501 .to_selection()
10502 );
10503}
10504
10505// https://github.com/gleam-lang/gleam/issues/4675
10506#[test]
10507fn extract_variable_use() {
10508 assert_no_code_actions!(
10509 EXTRACT_VARIABLE,
10510 "
10511pub fn main() {
10512 #({
10513 use <- todo
10514 todo
10515 })
10516}
10517 ",
10518 find_position_of("use").to_selection()
10519 );
10520}
10521
10522#[test]
10523fn extract_variable_in_anonymous_fn_in_argument() {
10524 assert_code_action!(
10525 EXTRACT_VARIABLE,
10526 "fn map(value, fn_over_value) { todo }
10527
10528pub fn main() {
10529 1
10530 |> Ok
10531 |> map(fn(value) { value + 2 })
10532}",
10533 find_position_of("2").to_selection()
10534 );
10535}
10536
10537#[test]
10538fn extract_variable_starting_pipeline_steps() {
10539 assert_code_action!(
10540 EXTRACT_VARIABLE,
10541 "fn map(value, fn_over_value) { todo }
10542
10543pub fn main() {
10544 1
10545 |> Ok
10546 |> map(fn(value) { value + 2 })
10547}",
10548 find_position_of("1").select_until(find_position_of("Ok"))
10549 );
10550}
10551
10552#[test]
10553fn do_not_extract_top_level_variable_in_anonymous_fn_in_argument() {
10554 assert_no_code_actions!(
10555 EXTRACT_VARIABLE,
10556 "fn map(value, fn_over_value) { todo }
10557
10558pub fn main() {
10559 1
10560 |> Ok
10561 |> map(fn(value) { value + 1 })
10562}",
10563 find_position_of("value").nth_occurrence(4).to_selection()
10564 );
10565}
10566
10567// https://github.com/gleam-lang/gleam/issues/4739
10568#[test]
10569fn do_not_import_internal_modules() {
10570 const IMPORT_MODULE: &str = "Import `package/internal`";
10571 let code = "
10572pub fn main() {
10573 internal.some_internal_function()
10574}
10575";
10576
10577 assert_no_code_actions!(
10578 IMPORT_MODULE,
10579 TestProject::for_source(code).add_package_module(
10580 "package",
10581 "package/internal",
10582 "pub fn some_internal_function() { todo }"
10583 ),
10584 find_position_of("internal").to_selection()
10585 );
10586}
10587
10588#[test]
10589fn import_internal_module_from_same_package() {
10590 let code = "
10591pub fn main() {
10592 internal.some_internal_function()
10593}
10594";
10595
10596 assert_code_action!(
10597 "Import `app/internal`",
10598 TestProject::for_source(code).add_package_module(
10599 "app",
10600 "app/internal",
10601 "pub fn some_internal_function() { todo }"
10602 ),
10603 find_position_of("internal").to_selection()
10604 );
10605}
10606
10607#[test]
10608fn remove_block_1() {
10609 assert_code_action!(
10610 REMOVE_BLOCK,
10611 "pub fn main() {
10612 { 1 }
10613}
10614",
10615 find_position_of("1").to_selection()
10616 );
10617}
10618
10619#[test]
10620fn remove_block_2() {
10621 assert_code_action!(
10622 REMOVE_BLOCK,
10623 "pub fn main() {
10624 { main() <> 2 }
10625}
10626",
10627 find_position_of("}").to_selection()
10628 );
10629}
10630
10631#[test]
10632fn remove_block_3() {
10633 assert_code_action!(
10634 REMOVE_BLOCK,
10635 "pub fn main() {
10636 case 1 {
10637 _ -> { main() <> 2 }
10638 }
10639}
10640",
10641 find_position_of("{").nth_occurrence(3).to_selection()
10642 );
10643}
10644
10645#[test]
10646fn remove_block_triggers_on_the_innermost_selected_block() {
10647 assert_code_action!(
10648 REMOVE_BLOCK,
10649 "pub fn main(x) {
10650 {
10651 main({
10652 1
10653 })
10654 }
10655}
10656",
10657 find_position_of("1").to_selection()
10658 );
10659}
10660
10661#[test]
10662fn remove_block_does_not_unwrap_a_let_assignment() {
10663 assert_no_code_actions!(
10664 REMOVE_BLOCK,
10665 "pub fn main(x) {
10666 {
10667 let a = 1
10668 }
10669}
10670",
10671 find_position_of("let").to_selection()
10672 );
10673}
10674
10675#[test]
10676fn remove_block_unwraps_a_single_expression_in_a_binop() {
10677 assert_code_action!(
10678 REMOVE_BLOCK,
10679 "pub fn main(x) {
10680 { main(1) } * 3
10681}
10682",
10683 find_position_of("main").nth_occurrence(2).to_selection()
10684 );
10685}
10686
10687#[test]
10688fn remove_block_does_not_unwrap_a_binop() {
10689 assert_no_code_actions!(
10690 REMOVE_BLOCK,
10691 "pub fn main(x) {
10692 { 1 * 2 } + 3
10693}
10694",
10695 find_position_of("1").to_selection()
10696 );
10697}
10698
10699#[test]
10700fn remove_block_does_not_unwrap_a_block_with_multiple_statements() {
10701 assert_no_code_actions!(
10702 REMOVE_BLOCK,
10703 "pub fn main(x) {
10704 {
10705 main(1)
10706 main(2)
10707 }
10708}
10709",
10710 find_position_of("1").to_selection()
10711 );
10712}
10713
10714#[test]
10715fn remove_opaque_from_private_type() {
10716 assert_code_action!(
10717 REMOVE_OPAQUE_FROM_PRIVATE_TYPE,
10718 "opaque type Wibble {
10719 Wobble
10720}
10721",
10722 find_position_of("Wibble").to_selection()
10723 );
10724}
10725
10726#[test]
10727fn allow_further_pattern_matching_on_let_tuple_destructuring() {
10728 assert_code_action!(
10729 PATTERN_MATCH_ON_VARIABLE,
10730 "pub fn main(x) {
10731 let #(one, other) = #(Ok(1), Error(Nil))
10732}
10733",
10734 find_position_of("one").to_selection()
10735 );
10736}
10737
10738#[test]
10739fn allow_further_pattern_matching_on_let_record_destructuring() {
10740 assert_code_action!(
10741 PATTERN_MATCH_ON_VARIABLE,
10742 "pub fn main(x) {
10743 let Wibble(field:) = Wibble(Ok(Nil))
10744}
10745
10746pub type Wibble { Wibble(field: Result(Nil, String)) }
10747",
10748 find_position_of("field").to_selection()
10749 );
10750}
10751
10752#[test]
10753fn allow_further_pattern_matching_on_asserted_result() {
10754 assert_code_action!(
10755 PATTERN_MATCH_ON_VARIABLE,
10756 "pub fn main(x) {
10757 let assert Ok(one) = Ok(Error(Nil))
10758}
10759",
10760 find_position_of("one").to_selection()
10761 );
10762}
10763
10764#[test]
10765fn allow_further_pattern_matching_on_asserted_list() {
10766 assert_code_action!(
10767 PATTERN_MATCH_ON_VARIABLE,
10768 "pub fn main(x) {
10769 let assert [first, ..] = [Ok(Nil), ..todo]
10770 todo
10771}
10772",
10773 find_position_of("first").to_selection()
10774 );
10775}
10776
10777#[test]
10778fn pattern_match_on_list_variable() {
10779 assert_code_action!(
10780 PATTERN_MATCH_ON_ARGUMENT,
10781 "pub fn main(a_list: List(a)) {
10782 todo
10783}",
10784 find_position_of("a_list").to_selection()
10785 );
10786}
10787
10788#[test]
10789fn pattern_match_on_list_tail() {
10790 assert_code_action!(
10791 PATTERN_MATCH_ON_VARIABLE,
10792 "pub fn main(a_list: List(a)) {
10793 case a_list {
10794 [] -> todo
10795 [first, ..rest] -> todo
10796 }
10797}",
10798 find_position_of("rest").to_selection()
10799 );
10800}
10801
10802#[test]
10803fn pattern_match_on_list_tail_with_strange_whitespace() {
10804 assert_code_action!(
10805 PATTERN_MATCH_ON_VARIABLE,
10806 "pub fn main(a_list: List(a)) {
10807 case a_list {
10808 [] -> todo
10809 [first, .. rest] -> todo
10810 }
10811}",
10812 find_position_of(" ").to_selection()
10813 );
10814}
10815
10816#[test]
10817fn pattern_match_on_list_tail_used_in_a_branch() {
10818 assert_code_action!(
10819 PATTERN_MATCH_ON_VARIABLE,
10820 "pub fn main(a_list: List(a)) {
10821 case a_list {
10822 [] -> todo
10823 [first, ..rest] -> rest
10824 }
10825}",
10826 find_position_of("rest").to_selection()
10827 );
10828}
10829
10830#[test]
10831fn pattern_match_on_list_tail_with_shadowed_name() {
10832 assert_code_action!(
10833 PATTERN_MATCH_ON_VARIABLE,
10834 "pub fn main(a_list: List(a)) {
10835 case a_list {
10836 [] -> todo
10837 [rest, ..else_] -> todo
10838 }
10839}",
10840 find_position_of("else_").to_selection()
10841 );
10842}
10843
10844#[test]
10845fn collapse_nested_case() {
10846 assert_code_action!(
10847 COLLAPSE_NESTED_CASE,
10848 "pub fn main(x) {
10849 case x {
10850 Ok(var) -> case var {
10851 1 -> 2
10852 2 -> 4
10853 _ -> -1
10854 }
10855 _ -> todo
10856 }
10857}",
10858 find_position_of("var").to_selection()
10859 );
10860}
10861
10862#[test]
10863fn collapse_nested_case_works_with_blocks() {
10864 assert_code_action!(
10865 COLLAPSE_NESTED_CASE,
10866 "pub fn main(x) {
10867 case x {
10868 Ok(var) -> {
10869 case var {
10870 1 -> 2
10871 2 -> 4
10872 _ -> -1
10873 }
10874 }
10875 _ -> todo
10876 }
10877}",
10878 find_position_of("var").to_selection()
10879 );
10880}
10881
10882#[test]
10883fn collapse_nested_case_works_with_patterns_defining_multiple_variables() {
10884 assert_code_action!(
10885 COLLAPSE_NESTED_CASE,
10886 "pub fn main(x) {
10887 case x {
10888 Wibble(var, var2) ->
10889 case var {
10890 1 -> 2
10891 2 -> 4
10892 _ -> -1
10893 }
10894
10895 Wobble -> todo
10896 }
10897}
10898
10899pub type Wibble {
10900 Wibble(Int, String)
10901 Wobble
10902}
10903",
10904 find_position_of("var").to_selection()
10905 );
10906}
10907
10908#[test]
10909fn collapse_nested_case_does_not_remove_labels() {
10910 assert_code_action!(
10911 COLLAPSE_NESTED_CASE,
10912 "pub fn main(x) {
10913 case x {
10914 Wibble(field2:, field: wibble) ->
10915 case wibble {
10916 1 -> 2
10917 2 -> 4
10918 _ -> -1
10919 }
10920
10921 Wobble -> todo
10922 }
10923}
10924
10925pub type Wibble {
10926 Wibble(field: Int, field2: String)
10927 Wobble
10928}
10929",
10930 find_position_of("field").to_selection()
10931 );
10932}
10933
10934#[test]
10935fn collapse_nested_case_does_not_remove_labels_with_shorthand_syntax() {
10936 assert_code_action!(
10937 COLLAPSE_NESTED_CASE,
10938 "pub fn main(x) {
10939 case x {
10940 Wibble(field2:, field:) ->
10941 case field {
10942 1 -> 2
10943 2 -> 4
10944 _ -> -1
10945 }
10946
10947 Wobble -> todo
10948 }
10949}
10950
10951pub type Wibble {
10952 Wibble(field: Int, field2: String)
10953 Wobble
10954}
10955",
10956 find_position_of("field").to_selection()
10957 );
10958}
10959
10960#[test]
10961fn collapse_nested_case_works_with_alternative_patterns() {
10962 assert_code_action!(
10963 COLLAPSE_NESTED_CASE,
10964 "pub fn main(x) {
10965 case x {
10966 [first, ..rest] ->
10967 case first {
10968 1 | 2 -> True
10969 3 | 4 | 5 -> False
10970 _ -> False
10971 }
10972
10973 [] -> True
10974 }
10975}
10976",
10977 find_position_of("first").to_selection()
10978 );
10979}
10980
10981#[test]
10982fn collapse_nested_case_aliases_variable_if_it_is_used() {
10983 assert_code_action!(
10984 COLLAPSE_NESTED_CASE,
10985 "pub fn main(x) {
10986 case x {
10987 [first, ..rest] ->
10988 case first {
10989 1 | 2 -> first
10990 3 | 4 | 5 -> 5
10991 _ -> 0
10992 }
10993
10994 [] -> -1
10995 }
10996}
10997",
10998 find_position_of("first").to_selection()
10999 );
11000}
11001
11002#[test]
11003fn collapse_nested_case_does_not_ignore_outer_guards() {
11004 assert_code_action!(
11005 COLLAPSE_NESTED_CASE,
11006 "pub fn main(x) {
11007 case x {
11008 [first, ..rest] if True ->
11009 case first {
11010 1 -> 1.1
11011 _ -> 0.0 *. 10.0
11012 }
11013
11014 [] -> 1.1
11015 }
11016}
11017",
11018 find_position_of("first").to_selection()
11019 );
11020}
11021
11022#[test]
11023fn collapse_nested_case_does_not_ignore_inner_guards() {
11024 assert_code_action!(
11025 COLLAPSE_NESTED_CASE,
11026 "pub fn main(x) {
11027 case x {
11028 [first, ..rest] ->
11029 case first {
11030 1 -> 1.1
11031 _ if True -> 0.0 *. 10.0
11032 _ -> 0.0
11033 }
11034
11035 [] -> 1.1
11036 }
11037}
11038",
11039 find_position_of("first").to_selection()
11040 );
11041}
11042
11043#[test]
11044fn collapse_nested_case_combines_inner_and_outer_guards() {
11045 assert_code_action!(
11046 COLLAPSE_NESTED_CASE,
11047 "pub fn main(x) {
11048 case x {
11049 [first, ..rest] if False ->
11050 case first {
11051 1 if False -> 1.1
11052 _ if True -> 0.0 *. 10.0
11053 _ -> 0.0
11054 }
11055
11056 [] -> 1.1
11057 }
11058}
11059",
11060 find_position_of("first").to_selection()
11061 );
11062}
11063
11064#[test]
11065fn collapse_nested_case_combines_inner_and_outer_guards_and_adds_parentheses_when_needed() {
11066 assert_code_action!(
11067 COLLAPSE_NESTED_CASE,
11068 "pub fn main(x) {
11069 case x {
11070 [first, ..rest] if False || True ->
11071 case first {
11072 1 if False && True -> 1.1
11073 _ if True || False -> 0.0 *. 10.0
11074 _ -> 0.0
11075 }
11076
11077 [] -> 1.1
11078 }
11079}
11080",
11081 find_position_of("first").to_selection()
11082 );
11083}
11084
11085#[test]
11086fn collapse_nested_case_combines_list_with_unformatted_tail() {
11087 assert_code_action!(
11088 COLLAPSE_NESTED_CASE,
11089 r#"pub fn main(elems: List(String)) {
11090 case elems {
11091 [first, .. rest_of_list] ->
11092 case rest_of_list {
11093 [] -> 0
11094 ["first"] -> 1
11095 [_, "second"] -> 2
11096 [_, "second", "third", _] -> 4
11097 _ -> -1
11098 }
11099 _ -> -1
11100 }
11101}"#,
11102 find_position_of("rest").to_selection()
11103 );
11104}
11105
11106#[test]
11107fn collapse_nested_case_combines_list_with_tail() {
11108 assert_code_action!(
11109 COLLAPSE_NESTED_CASE,
11110 r#"pub fn main(elems: List(List(Int))) {
11111 case elems {
11112 [] -> 0
11113 [_, ..tail] ->
11114 case tail {
11115 [] -> -1
11116 [[1]] -> 1
11117 [_, [3, 4]] -> 3
11118 [_, _, [5, 6, 7], _] -> 5
11119 tail_list -> {
11120 echo tail_list
11121 -1
11122 }
11123 }
11124 _ -> -1
11125 }
11126}"#,
11127 find_position_of("tail").to_selection()
11128 );
11129}
11130
11131// https://github.com/gleam-lang/gleam/issues/3786
11132#[test]
11133fn type_variables_from_other_functions_do_not_change_annotations() {
11134 assert_code_action!(
11135 ADD_ANNOTATIONS,
11136 "
11137fn wibble(a: a, b: b, c: c) -> d { todo }
11138
11139fn pair(a, b) {
11140 #(a, b)
11141}
11142",
11143 find_position_of("pair").to_selection()
11144 );
11145}
11146
11147#[test]
11148fn type_variables_from_other_functions_do_not_change_annotations_constant() {
11149 assert_code_action!(
11150 ADD_ANNOTATION,
11151 "
11152fn wibble(a: a, b: b, c: c) -> d { todo }
11153
11154const empty = []
11155",
11156 find_position_of("empty").to_selection()
11157 );
11158}
11159
11160#[test]
11161fn type_variables_are_not_duplicated_when_adding_annotations() {
11162 assert_code_action!(
11163 ADD_ANNOTATIONS,
11164 "
11165fn wibble(a: a, b: b, c: c) -> d { todo }
11166
11167fn many_args(a, b, c, d: d, e: a, f, g) {
11168 todo
11169}
11170",
11171 find_position_of("many_args").to_selection()
11172 );
11173}
11174
11175#[test]
11176fn type_variables_in_let_bindings_are_considered_when_adding_annotations() {
11177 assert_code_action!(
11178 ADD_ANNOTATIONS,
11179 "
11180fn wibble(a, b, c) {
11181 let x: a = todo
11182 fn(a: b, b: c) -> d {
11183 todo
11184 }
11185}
11186",
11187 find_position_of("wibble").to_selection()
11188 );
11189}
11190
11191#[test]
11192fn generated_function_annotations_are_not_affected_by_other_functions() {
11193 assert_code_action!(
11194 GENERATE_FUNCTION,
11195 "
11196fn wibble(a: a, b: b, c: c) -> d { todo }
11197
11198pub fn main() {
11199 let x = todo
11200 let y = todo
11201 let #(a, b) = something(x, y)
11202 b
11203}
11204",
11205 find_position_of("something").to_selection()
11206 );
11207}
11208
11209#[test]
11210fn generate_function_in_other_module() {
11211 let src = "
11212import wibble
11213
11214pub fn main() {
11215 wibble.wibble()
11216 wibble.wobble()
11217}
11218";
11219
11220 assert_code_action!(
11221 GENERATE_FUNCTION,
11222 TestProject::for_source(src).add_module("wibble", "pub fn wibble() {}"),
11223 find_position_of("wobble").to_selection()
11224 );
11225}
11226
11227#[test]
11228fn generate_function_is_not_offered_for_variants() {
11229 assert_no_code_actions!(
11230 GENERATE_FUNCTION,
11231 "
11232pub type Wibble
11233
11234pub fn main() -> Wibble {
11235 Wobble(1)
11236}
11237",
11238 find_position_of("Wobble").to_selection()
11239 );
11240}
11241
11242#[test]
11243fn generating_function_in_other_module_uses_local_names() {
11244 let src = r#"
11245import wibble
11246
11247pub fn main() -> List(Nil) {
11248 wibble.wibble(1, #(True, "Hello"))
11249}
11250"#;
11251
11252 assert_code_action!(
11253 GENERATE_FUNCTION,
11254 TestProject::for_source(src).add_module(
11255 "wibble",
11256 "import gleam.{type Int as Number, type Bool as Boolean, type String as Text, type Nil as Nothing}"
11257 ),
11258 find_position_of("wibble(").to_selection()
11259 );
11260}
11261
11262#[test]
11263fn generating_function_in_other_module_uses_labels() {
11264 let src = r#"
11265import wibble
11266
11267pub fn main() {
11268 wibble.wibble("Unlabelled", int: 1, bool: True)
11269}
11270"#;
11271
11272 assert_code_action!(
11273 GENERATE_FUNCTION,
11274 TestProject::for_source(src).add_module("wibble", ""),
11275 find_position_of("wibble(").to_selection()
11276 );
11277}
11278
11279#[test]
11280fn no_code_action_to_generate_existing_function_in_other_module() {
11281 let src = r#"
11282import wibble
11283
11284pub fn main() {
11285 wibble.wibble(1, 2, 3)
11286}
11287"#;
11288
11289 assert_no_code_actions!(
11290 GENERATE_FUNCTION,
11291 TestProject::for_source(src).add_module("wibble", "pub fn wibble(a, b, c) { a + b + c }"),
11292 find_position_of("wibble(").to_selection()
11293 );
11294}
11295
11296#[test]
11297fn do_not_generate_function_in_other_package() {
11298 let src = r#"
11299import maths
11300
11301pub fn main() {
11302 maths.add(1, 2)
11303 maths.subtract(1, 2)
11304}
11305"#;
11306
11307 assert_no_code_actions!(
11308 GENERATE_FUNCTION,
11309 TestProject::for_source(src).add_dep_module("maths", "pub fn add(a, b) { a + b }"),
11310 find_position_of("subtract").to_selection()
11311 );
11312}
11313
11314#[test]
11315fn remove_unreachable_clauses() {
11316 assert_code_action!(
11317 REMOVE_UNREACHABLE_CLAUSES,
11318 "pub fn main(x) {
11319 case x {
11320 Ok(n) -> 1
11321 Ok(_) -> 2
11322 Error(_) -> todo
11323 Ok(1) -> 3
11324 }
11325}
11326",
11327 find_position_of("Ok(1)").to_selection()
11328 );
11329}
11330
11331#[test]
11332fn remove_unreachable_branches_does_not_pop_up_if_all_branches_are_reachable() {
11333 assert_no_code_actions!(
11334 REMOVE_UNREACHABLE_CLAUSES,
11335 "pub fn main(x) {
11336 case x {
11337 Ok(n) -> 1
11338 Error(_) -> todo
11339 }
11340
11341 case x {
11342 Ok(n) -> todo
11343 Ok(_) -> todo
11344 _ -> todo
11345 }
11346}
11347",
11348 find_position_of("Ok(n)").to_selection()
11349 );
11350}
11351
11352#[test]
11353fn add_type_annotations_public_alias_to_internal_type_aliased_module() {
11354 let src = "
11355import package as pkg
11356
11357pub fn main() {
11358 pkg.make_wibble()
11359}
11360";
11361
11362 assert_code_action!(
11363 ADD_ANNOTATION,
11364 TestProject::for_source(src)
11365 .add_package_module(
11366 "package",
11367 "package",
11368 "
11369import package/internal
11370
11371pub type Wibble = internal.Wibble
11372
11373pub fn make_wibble() {
11374 internal.Wibble
11375}
11376"
11377 )
11378 .add_package_module("package", "package/internal", "pub type Wibble { Wibble }"),
11379 find_position_of("main").to_selection(),
11380 );
11381}
11382
11383// https://github.com/gleam-lang/gleam/issues/3898
11384#[test]
11385fn add_type_annotations_public_alias_to_internal_type() {
11386 let src = "
11387import package
11388
11389pub fn main() {
11390 package.make_wibble()
11391}
11392";
11393
11394 assert_code_action!(
11395 ADD_ANNOTATION,
11396 TestProject::for_source(src)
11397 .add_package_module(
11398 "package",
11399 "package",
11400 "
11401import package/internal
11402
11403pub type Wibble = internal.Wibble
11404
11405pub fn make_wibble() {
11406 internal.Wibble
11407}
11408"
11409 )
11410 .add_package_module("package", "package/internal", "pub type Wibble { Wibble }"),
11411 find_position_of("main").to_selection(),
11412 );
11413}
11414
11415#[test]
11416fn add_type_annotations_public_alias_to_internal_generic_type() {
11417 let src = "
11418import package
11419
11420pub fn main() {
11421 package.make_wibble(10)
11422}
11423";
11424
11425 assert_code_action!(
11426 ADD_ANNOTATION,
11427 TestProject::for_source(src)
11428 .add_package_module(
11429 "package",
11430 "package",
11431 "
11432import package/internal
11433
11434pub type Wibble(a, b) = internal.Wibble(a, b)
11435
11436pub fn make_wibble(x) {
11437 internal.Wibble(x)
11438}
11439"
11440 )
11441 .add_package_module(
11442 "package",
11443 "package/internal",
11444 "pub type Wibble(a, b) { Wibble(a) }"
11445 ),
11446 find_position_of("main").to_selection(),
11447 );
11448}
11449
11450#[test]
11451fn add_type_annotations_uses_internal_name_for_same_package() {
11452 let src = "
11453import thepackage/internal
11454
11455pub fn main() {
11456 internal.Constructor
11457}
11458";
11459
11460 assert_code_action!(
11461 ADD_ANNOTATION,
11462 TestProject::for_source(src)
11463 .add_module(
11464 "thepackage/internal",
11465 "
11466pub type Internal { Constructor }
11467"
11468 )
11469 .add_module(
11470 "thepackage/external",
11471 "
11472import thepackage/internal
11473
11474pub type External = internal.Internal
11475"
11476 ),
11477 find_position_of("main").to_selection(),
11478 );
11479}
11480
11481#[test]
11482fn add_omitted_labels_in_function_call() {
11483 assert_code_action!(
11484 ADD_OMITTED_LABELS,
11485 "
11486pub fn main() {
11487 labelled(1, 2)
11488}
11489
11490pub fn labelled(a a, b b) { todo }
11491 ",
11492 find_position_of("labelled").to_selection(),
11493 );
11494}
11495
11496#[test]
11497fn add_omitted_labels_in_function_call_with_some_labels() {
11498 assert_code_action!(
11499 ADD_OMITTED_LABELS,
11500 "
11501pub fn main() {
11502 labelled(1, 2)
11503}
11504
11505pub fn labelled(a, b b) { todo }
11506 ",
11507 find_position_of("labelled").to_selection(),
11508 );
11509}
11510
11511#[test]
11512fn add_omitted_labels_in_function_call_uses_shorthand_syntax() {
11513 assert_code_action!(
11514 ADD_OMITTED_LABELS,
11515 "
11516pub fn main() {
11517 let a = 1
11518 labelled(a, 2)
11519}
11520
11521pub fn labelled(a a, b b) { todo }
11522 ",
11523 find_position_of("labelled").to_selection(),
11524 );
11525}
11526
11527#[test]
11528fn add_omitted_labels_works_with_innermost_function_call() {
11529 assert_code_action!(
11530 ADD_OMITTED_LABELS,
11531 "
11532pub fn main() {
11533 let a = 1
11534 labelled(a, labelled(1, 2))
11535}
11536
11537pub fn labelled(a a, b b) { todo }
11538 ",
11539 find_position_of("labelled")
11540 .nth_occurrence(2)
11541 .to_selection(),
11542 );
11543}
11544
11545#[test]
11546fn add_omitted_labels_works_with_constructors_calls() {
11547 assert_code_action!(
11548 ADD_OMITTED_LABELS,
11549 "
11550pub fn main() {
11551 Labelled(1, 2)
11552}
11553
11554pub type Labelled {
11555 Labelled(Int, b: Int)
11556}
11557 ",
11558 find_position_of("Labelled").to_selection(),
11559 );
11560}
11561
11562#[test]
11563fn add_omitted_labels_works_with_constructors_calls_with_some_labels_1() {
11564 assert_code_action!(
11565 ADD_OMITTED_LABELS,
11566 "
11567pub fn main() {
11568 labelled(3, 1, b: 2)
11569}
11570
11571pub fn labelled(a a, b b, c c) { todo }
11572 ",
11573 find_position_of("labelled").to_selection(),
11574 );
11575}
11576
11577#[test]
11578fn add_omitted_labels_works_with_constructors_calls_with_some_labels() {
11579 assert_code_action!(
11580 ADD_OMITTED_LABELS,
11581 "
11582pub fn main() {
11583 let a = 1
11584 labelled(a, b: 2)
11585}
11586
11587pub fn labelled(a a, b b) { todo }
11588 ",
11589 find_position_of("labelled").to_selection(),
11590 );
11591}
11592
11593#[test]
11594fn add_omitted_labels_works_on_call_with_wrongly_placed_labels() {
11595 assert_code_action!(
11596 ADD_OMITTED_LABELS,
11597 "
11598pub fn main() {
11599 labelled(3, b: 2, 1)
11600}
11601
11602pub fn labelled(a a, b b, c c) { todo }
11603 ",
11604 find_position_of("labelled").to_selection(),
11605 );
11606}
11607
11608#[test]
11609fn add_omitted_labels_does_not_work_on_call_with_wrong_labels_2() {
11610 assert_no_code_actions!(
11611 ADD_OMITTED_LABELS,
11612 "
11613pub fn main() {
11614 labelled(3, 1, d: 2)
11615}
11616
11617pub fn labelled(a a, b b, c c) { todo }
11618 ",
11619 find_position_of("labelled").to_selection(),
11620 );
11621}
11622
11623#[test]
11624fn add_omitted_labels_does_not_pop_up_if_called_function_has_no_labels() {
11625 assert_no_code_actions!(
11626 ADD_OMITTED_LABELS,
11627 "
11628pub fn main() {
11629 let a = 1
11630 labelled(a, 2)
11631}
11632
11633pub fn labelled(a, b) { todo }
11634 ",
11635 find_position_of("labelled").to_selection(),
11636 );
11637}
11638
11639#[test]
11640fn add_omitted_labels_does_not_label_piped_argument() {
11641 assert_code_action!(
11642 ADD_OMITTED_LABELS,
11643 "
11644pub fn main() {
11645 1 |> labelled(2)
11646}
11647
11648pub fn labelled(a a, b b) { todo }
11649 ",
11650 find_position_of("labelled").to_selection(),
11651 );
11652}
11653
11654#[test]
11655fn add_omitted_labels_does_not_label_use() {
11656 assert_code_action!(
11657 ADD_OMITTED_LABELS,
11658 "
11659pub fn main() {
11660 use <- labelled(1)
11661 todo
11662}
11663
11664pub fn labelled(a a, b b) { todo }
11665 ",
11666 find_position_of("labelled").to_selection(),
11667 );
11668}
11669
11670#[test]
11671fn extract_function() {
11672 assert_code_action!(
11673 EXTRACT_FUNCTION,
11674 "
11675pub fn do_things(a, b) {
11676 let result = {
11677 let a = 10 + a
11678 let b = 10 + b
11679 a * b
11680 }
11681 result + 3
11682}
11683",
11684 find_position_of("{")
11685 .nth_occurrence(2)
11686 .select_until(find_position_of("}"))
11687 );
11688}
11689
11690#[test]
11691fn extract_function_with_selected_expression_in_statement() {
11692 assert_code_action!(
11693 EXTRACT_FUNCTION,
11694 "
11695pub fn do_things(a, b) {
11696 let result = call(a, [])
11697 result + 3
11698}
11699",
11700 find_position_of("call").select_until(find_position_of("[]"))
11701 );
11702}
11703
11704#[test]
11705fn extract_function_with_selected_pipeline_expression_in_statement() {
11706 assert_code_action!(
11707 EXTRACT_FUNCTION,
11708 "
11709pub fn do_things(a, b) {
11710 let result =
11711 wibble
11712 |> wobble
11713 |> woo
11714
11715 result + 3
11716}
11717",
11718 find_position_of("wibble").select_until(find_position_of("woo"))
11719 );
11720}
11721
11722#[test]
11723fn extract_function_with_assignment_expression_and_multiple_assignments() {
11724 assert_code_action!(
11725 EXTRACT_FUNCTION,
11726 "
11727pub fn do_things(a, b) {
11728 let result =
11729 wibble
11730 |> wobble
11731 |> woo
11732
11733 let a = result + 2
11734
11735 a + 3
11736}
11737",
11738 find_position_of("wibble").select_until(find_position_of("2"))
11739 );
11740}
11741
11742#[test]
11743fn extract_function_from_statements() {
11744 assert_code_action!(
11745 EXTRACT_FUNCTION,
11746 "
11747pub fn do_things(a, b) {
11748 let a = 10 + a
11749 let b = 10 + b
11750 let result = a * b
11751 result + 3
11752}
11753",
11754 find_position_of("let").select_until(find_position_of("* b"))
11755 );
11756}
11757
11758#[test]
11759fn extract_function_which_use_variables_defined_in_the_extracted_span() {
11760 assert_code_action!(
11761 EXTRACT_FUNCTION,
11762 "
11763pub fn do_things(a, b) {
11764 let new_a = 10 + a
11765 let new_b = 10 + b
11766 let result = new_a * new_b
11767 result + 3
11768}
11769",
11770 find_position_of("let").select_until(find_position_of("* new_b"))
11771 );
11772}
11773
11774#[test]
11775fn extract_function_which_use_variables_shadowed_in_an_inner_scope() {
11776 assert_code_action!(
11777 EXTRACT_FUNCTION,
11778 "
11779pub fn do_things(a, b) {
11780 let first_part = {
11781 let a = a + 10
11782 let b = b + 10
11783 a * b
11784 }
11785 let result = first_part + a * b
11786 result + 3
11787}
11788",
11789 find_position_of("let").select_until(find_position_of("+ a * b"))
11790 );
11791}
11792
11793#[test]
11794fn extract_function_which_uses_multiple_extracted_variables() {
11795 assert_code_action!(
11796 EXTRACT_FUNCTION,
11797 "
11798pub fn do_things(a, b) {
11799 let wibble = a + b
11800 let wobble = a * b
11801 wobble / wibble
11802}
11803",
11804 find_position_of("let").select_until(find_position_of("* b"))
11805 );
11806}
11807
11808#[test]
11809fn extract_function_which_uses_no_extracted_variables() {
11810 assert_code_action!(
11811 EXTRACT_FUNCTION,
11812 "
11813pub fn do_things(a, b) {
11814 let x = a + b
11815 echo x
11816 a
11817}
11818",
11819 find_position_of("let").select_until(find_position_of("echo x"))
11820 );
11821}
11822
11823#[test]
11824fn extract_function_which_uses_variable_in_guard() {
11825 assert_code_action!(
11826 EXTRACT_FUNCTION,
11827 "
11828pub fn do_things(a, b) {
11829 let result = case Nil {
11830 _ if a > b -> 17
11831 _ if a < b -> 12
11832 _ -> panic
11833 }
11834
11835 result % 4
11836}
11837",
11838 find_position_of("case").select_until(find_position_of("}"))
11839 );
11840}
11841
11842#[test]
11843fn extract_function_which_uses_variable_in_bit_array_pattern() {
11844 assert_code_action!(
11845 EXTRACT_FUNCTION,
11846 "
11847pub fn main() {
11848 let bits = todo
11849 let size = todo
11850
11851 let segment = case bits {
11852 <<x:size(size), _:bits>> -> Ok(x)
11853 _ -> Error(Nil)
11854 }
11855
11856 case segment {
11857 Ok(value) -> echo value
11858 Error(_) -> panic
11859 }
11860}
11861",
11862 find_position_of("case").select_until(find_position_of("}"))
11863 );
11864}
11865
11866#[test]
11867fn extract_function_which_uses_constant() {
11868 assert_code_action!(
11869 EXTRACT_FUNCTION,
11870 "
11871const pi = 3.14
11872
11873pub fn main() {
11874 let radius = 4.5
11875
11876 let circumference = radius *. pi *. 2.0
11877
11878 echo circumference
11879}
11880",
11881 find_position_of("radius *.").select_until(find_position_of("2.0"))
11882 );
11883}
11884
11885#[test]
11886fn extract_function_which_uses_constant_in_guard() {
11887 assert_code_action!(
11888 EXTRACT_FUNCTION,
11889 r#"
11890const pi = 3.14
11891
11892pub fn main() {
11893 let value = 3.15
11894
11895 let string = case value {
11896 0.0 -> "Zero"
11897 1.0 -> "One"
11898 _ if value == pi -> "PI"
11899 _ -> "Something else"
11900 }
11901
11902 echo string
11903}
11904"#,
11905 find_position_of("case").select_until(find_position_of("}"))
11906 );
11907}
11908
11909#[test]
11910fn no_extract_function_selecting_multiple_case_branches() {
11911 assert_no_code_actions!(
11912 EXTRACT_FUNCTION,
11913 r#"
11914const pi = 3.14
11915
11916pub fn main() {
11917 let value = 3.15
11918
11919 let string = case value {
11920 0.0 -> "Zero"
11921 1.0 -> "One"
11922 _ -> "Something else"
11923 }
11924
11925 echo string
11926}
11927"#,
11928 find_position_of("0.0").select_until(find_position_of("else"))
11929 );
11930}
11931
11932#[test]
11933fn no_extract_function_selecting_case_branch_pattern() {
11934 assert_no_code_actions!(
11935 EXTRACT_FUNCTION,
11936 r#"
11937const pi = 3.14
11938
11939pub fn main() {
11940 let value = 3.15
11941
11942 let string = case value {
11943 0.0 -> "Zero"
11944 1.0 -> "One"
11945 _ -> "Something else"
11946 }
11947
11948 echo string
11949}
11950"#,
11951 find_position_of("0.0").select_until(find_position_of("Zero").under_last_char())
11952 );
11953}
11954
11955#[test]
11956fn no_extract_function_selecting_case_branch_guard() {
11957 assert_no_code_actions!(
11958 EXTRACT_FUNCTION,
11959 r#"
11960const pi = 3.14
11961
11962pub fn main() {
11963 let value = 3.15
11964
11965 let string = case value {
11966 0.0 if True -> "Zero"
11967 1.0 -> "One"
11968 _ -> "Something else"
11969 }
11970
11971 echo string
11972}
11973"#,
11974 find_position_of("True")
11975 .under_char('u')
11976 .select_until(find_position_of("Zero").under_last_char())
11977 );
11978}
11979
11980#[test]
11981fn extract_use_inside_function() {
11982 assert_code_action!(
11983 EXTRACT_FUNCTION,
11984 r#"
11985fn wibble(f) { todo }
11986
11987pub fn main() {
11988 use a <- wibble()
11989 Ok(Nil)
11990}
11991"#,
11992 find_position_of("use").select_until(find_position_of("<-"))
11993 );
11994}
11995
11996#[test]
11997fn extract_use_inside_block() {
11998 assert_code_action!(
11999 EXTRACT_FUNCTION,
12000 r#"
12001fn wibble(f) -> Int { todo }
12002
12003pub fn main() {
12004 let wobble = {
12005 use a <- wibble()
12006 Ok(Nil)
12007 }
12008
12009 wobble
12010}
12011"#,
12012 find_position_of("use").select_until(find_position_of("Nil)"))
12013 );
12014}
12015
12016#[test]
12017fn extract_use_with_some_statements_before() {
12018 assert_code_action!(
12019 EXTRACT_FUNCTION,
12020 r#"
12021fn wibble(f) { todo }
12022
12023pub fn main() {
12024 let a = wibble(todo)
12025 use a <- wibble()
12026 Ok(Nil)
12027}
12028"#,
12029 find_position_of("let").select_until(find_position_of("use"))
12030 );
12031}
12032
12033#[test]
12034fn extract_use_including_some_statements_after() {
12035 assert_code_action!(
12036 EXTRACT_FUNCTION,
12037 r#"
12038fn wibble(f) { todo }
12039
12040pub fn main() {
12041 let wobble = {
12042 use a <- wibble()
12043 Ok(Nil)
12044 }
12045 wibble(panic)
12046
12047 wobble
12048}
12049"#,
12050 find_position_of("let").select_until(find_position_of("panic)"))
12051 );
12052}
12053
12054#[test]
12055fn extract_entire_pipeline() {
12056 assert_code_action!(
12057 EXTRACT_FUNCTION,
12058 r#"
12059pub fn main() {
12060 True
12061 |> wibble
12062 |> wobble
12063}
12064
12065fn wibble(_) { 1 }
12066fn wobble(_) { 1.0 }
12067"#,
12068 find_position_of("True").select_until(find_position_of("wobble"))
12069 );
12070}
12071
12072#[test]
12073fn extract_starting_steps_of_pipeline() {
12074 assert_code_action!(
12075 EXTRACT_FUNCTION,
12076 r#"
12077pub fn main() {
12078 True
12079 |> wibble
12080 |> wobble
12081}
12082
12083fn wibble(_) { 1 }
12084fn wobble(_) { 1.0 }
12085"#,
12086 find_position_of("True").select_until(find_position_of("wibble"))
12087 );
12088}
12089
12090#[test]
12091fn extract_starting_steps_of_pipeline_with_argument() {
12092 assert_code_action!(
12093 EXTRACT_FUNCTION,
12094 r#"
12095pub fn main() {
12096 let something = 1
12097
12098 True
12099 |> wibble(something)
12100 |> wobble
12101}
12102
12103fn wibble(_, _) { 1 }
12104fn wobble(_) { 1.0 }
12105"#,
12106 find_position_of("True").select_until(find_position_of("wibble"))
12107 );
12108}
12109
12110#[test]
12111fn extract_final_steps_of_pipeline() {
12112 assert_code_action!(
12113 EXTRACT_FUNCTION,
12114 r#"
12115pub fn main() {
12116 True
12117 |> wibble
12118 |> wobble
12119}
12120
12121fn wibble(_) { 1 }
12122fn wobble(_) { 1.0 }
12123"#,
12124 find_position_of("wibble").select_until(find_position_of("wobble"))
12125 );
12126}
12127
12128#[test]
12129fn extract_final_steps_of_pipeline_with_arguments() {
12130 assert_code_action!(
12131 EXTRACT_FUNCTION,
12132 r#"
12133pub fn main() {
12134 let something = 1
12135
12136 True
12137 |> wibble
12138 |> wobble(something)
12139}
12140
12141fn wibble(_) { 1 }
12142fn wobble(_, _) { 1.0 }
12143"#,
12144 find_position_of("wibble").select_until(find_position_of("wobble"))
12145 );
12146}
12147
12148#[test]
12149fn extract_middle_steps_of_pipeline() {
12150 assert_code_action!(
12151 EXTRACT_FUNCTION,
12152 r#"
12153pub fn main() {
12154 True
12155 |> wibble
12156 |> wobble
12157 |> woo
12158}
12159
12160fn wibble(_) { 1 }
12161fn wobble(_) { 1.0 }
12162fn woo(_) { [] }
12163"#,
12164 find_position_of("wibble").select_until(find_position_of("wobble"))
12165 );
12166}
12167
12168#[test]
12169fn extract_middle_steps_of_pipeline_with_arguments() {
12170 assert_code_action!(
12171 EXTRACT_FUNCTION,
12172 r#"
12173pub fn main() {
12174 let something = 1
12175
12176 True
12177 |> wibble(1)
12178 |> wobble(something)
12179 |> woo
12180}
12181
12182fn wibble(_, _) { 1 }
12183fn wobble(_, _) { 1.0 }
12184fn woo(_) { [] }
12185"#,
12186 find_position_of("wibble").select_until(find_position_of("wobble"))
12187 );
12188}
12189
12190#[test]
12191fn cannot_extract_a_single_starting_step_as_function() {
12192 assert_no_code_actions!(
12193 EXTRACT_FUNCTION,
12194 r#"
12195pub fn main() {
12196 True
12197 |> wibble
12198 |> wobble
12199 |> woo
12200}
12201
12202fn wibble(_, _) { 1 }
12203fn wobble(_, _) { 1.0 }
12204fn woo(_) { [] }
12205"#,
12206 find_position_of("True").to_selection()
12207 );
12208}
12209
12210#[test]
12211fn cannot_extract_a_single_middle_step_as_function() {
12212 assert_no_code_actions!(
12213 EXTRACT_FUNCTION,
12214 r#"
12215pub fn main() {
12216 True
12217 |> wibble
12218 |> wobble
12219 |> woo
12220}
12221
12222fn wibble(_, _) { 1 }
12223fn wobble(_, _) { 1.0 }
12224fn woo(_) { [] }
12225"#,
12226 find_position_of("wibble").to_selection()
12227 );
12228}
12229
12230#[test]
12231fn cannot_extract_a_single_final_step_as_function() {
12232 assert_no_code_actions!(
12233 EXTRACT_FUNCTION,
12234 r#"
12235pub fn main() {
12236 True
12237 |> wibble
12238 |> wobble
12239 |> woo
12240}
12241
12242fn wibble(_, _) { 1 }
12243fn wobble(_, _) { 1.0 }
12244fn woo(_) { [] }
12245"#,
12246 find_position_of("woo").to_selection()
12247 );
12248}
12249
12250#[test]
12251fn no_code_action_to_extract_function_when_expression_is_not_fully_selected() {
12252 assert_no_code_actions!(
12253 EXTRACT_FUNCTION,
12254 r#"
12255fn print(text: String) { todo }
12256
12257pub fn main() {
12258 let arguments = todo
12259
12260 case arguments {
12261 ["help"] -> print("USAGE TEXT HERE")
12262 _ -> panic as "Invalid args"
12263 }
12264}
12265"#,
12266 find_position_of("print")
12267 .under_char('i')
12268 .select_until(find_position_of("TEXT"))
12269 );
12270}
12271
12272#[test]
12273fn extract_function_when_name_already_in_scope() {
12274 assert_code_action!(
12275 EXTRACT_FUNCTION,
12276 r#"
12277fn function() { todo }
12278
12279pub fn do_things(a, b) {
12280 let result = {
12281 let a = 10 + a
12282 let b = 10 + b
12283 a * b
12284 }
12285 result + 3
12286}
12287"#,
12288 find_position_of("= {").select_until(find_position_of("}").nth_occurrence(2))
12289 );
12290}
12291
12292#[test]
12293fn extract_function_when_multiple_names_already_in_scope() {
12294 assert_code_action!(
12295 EXTRACT_FUNCTION,
12296 r#"
12297fn function() { todo }
12298fn function_2() { todo }
12299fn function_3() { todo }
12300fn function_4() { todo }
12301
12302pub fn do_things(a, b) {
12303 let result = {
12304 let a = 10 + a
12305 let b = 10 + b
12306 a * b
12307 }
12308 result + 3
12309}
12310"#,
12311 find_position_of("= {").select_until(find_position_of("}").nth_occurrence(5))
12312 );
12313}
12314
12315#[test]
12316fn extract_function_partially_selected() {
12317 assert_code_action!(
12318 EXTRACT_FUNCTION,
12319 r#"
12320pub fn main() {
12321 let a = 10
12322 let b = 20
12323 let c = a + b
12324
12325 echo c
12326}
12327"#,
12328 find_position_of("a =").select_until(find_position_of("c ="))
12329 );
12330}
12331
12332#[test]
12333fn selected_statements_do_not_select_outer_block() {
12334 // We want to make sure only the statements within the block are extracted,
12335 // and not the block itself.
12336 assert_code_action!(
12337 EXTRACT_FUNCTION,
12338 r#"
12339pub fn main() {
12340 let c = {
12341 let a = 10
12342 let b = 20
12343 a + b
12344 }
12345
12346 echo c
12347}
12348"#,
12349 find_position_of("let a").select_until(find_position_of("+ b"))
12350 );
12351}
12352
12353#[test]
12354fn no_code_action_to_extract_when_multiple_functions_are_selected() {
12355 assert_no_code_actions!(
12356 EXTRACT_FUNCTION,
12357 r#"
12358pub fn main() {
12359 let a = 10
12360 a + 1
12361}
12362
12363pub fn other() {
12364 let b = 20
12365 b * 2
12366}
12367"#,
12368 find_position_of("let a").select_until(find_position_of("let b"))
12369 );
12370}
12371
12372#[test]
12373fn extract_statements_in_tail_position() {
12374 assert_code_action!(
12375 EXTRACT_FUNCTION,
12376 r#"
12377pub fn main() {
12378 let a = 1
12379 let b = 2
12380 let c = 3
12381 let d = 4
12382 a * b + c * d
12383}
12384"#,
12385 find_position_of("let c").select_until(find_position_of("* d"))
12386 );
12387}
12388
12389#[test]
12390fn extract_use_in_tail_position() {
12391 assert_code_action!(
12392 EXTRACT_FUNCTION,
12393 r#"
12394pub fn main() {
12395 use <- wibble
12396 123
12397}
12398
12399fn wibble(f: fn() -> Int) -> Int { f() }
12400"#,
12401 find_position_of("use").select_until(find_position_of("wibble"))
12402 );
12403}
12404
12405#[test]
12406fn extract_use_in_tail_position_2() {
12407 assert_code_action!(
12408 EXTRACT_FUNCTION,
12409 r#"
12410pub fn main() {
12411 use <- wibble
12412 use <- wobble
12413 123
12414}
12415
12416fn wibble(f: fn() -> Float) -> Float { f() }
12417fn wobble(f: fn() -> Int) -> Float { 1.1 }
12418"#,
12419 find_position_of("use")
12420 .nth_occurrence(2)
12421 .select_until(find_position_of("wobble"))
12422 );
12423}
12424
12425#[test]
12426fn extract_block_tail_position_3() {
12427 assert_code_action!(
12428 EXTRACT_FUNCTION,
12429 r#"
12430pub fn main() {
12431 case 1 {
12432 _ -> {
12433 use <- wibble
12434 123
12435 }
12436 _ -> todo
12437 }
12438}
12439
12440fn wibble(f: fn() -> Float) -> Float { f() }
12441"#,
12442 find_position_of("{")
12443 .nth_occurrence(3)
12444 .select_until(find_position_of("}"))
12445 );
12446}
12447
12448#[test]
12449fn extract_block_tail_position_4() {
12450 assert_code_action!(
12451 EXTRACT_FUNCTION,
12452 r#"
12453pub fn main() {
12454 case 1 {
12455 _ -> {
12456 use <- wibble
12457 use <- wibble
12458 123
12459 }
12460 _ -> todo
12461 }
12462}
12463
12464fn wibble(f: fn() -> Float) -> Float { f() }
12465"#,
12466 find_position_of("{")
12467 .nth_occurrence(3)
12468 .select_until(find_position_of("}"))
12469 );
12470}
12471
12472// https://github.com/gleam-lang/gleam/issues/5036
12473#[test]
12474fn generate_function_in_other_module_correctly_appends() {
12475 let src = "import module_breaker/another
12476
12477pub fn main() -> Nil {
12478 another.function()
12479}
12480";
12481
12482 assert_code_action!(
12483 GENERATE_FUNCTION,
12484 TestProject::for_source(src).add_module(
12485 "module_breaker/another",
12486 "pub fn useless() {
12487 Nil
12488}
12489"
12490 ),
12491 find_position_of("function").to_selection()
12492 );
12493}
12494
12495// https://github.com/gleam-lang/gleam/issues/4904
12496#[test]
12497fn no_code_action_to_generate_function_on_unsupported_target() {
12498 assert_no_code_actions!(
12499 GENERATE_FUNCTION,
12500 r#"
12501pub fn main() {
12502 wibble()
12503}
12504
12505@external(javascript, "./ffi.mjs", "wibble")
12506fn wibble() -> Nil
12507"#,
12508 find_position_of("wibble").to_selection()
12509 );
12510}
12511
12512#[test]
12513fn merge_case_branch() {
12514 assert_code_action!(
12515 MERGE_CASE_BRANCHES,
12516 r#"pub fn go(n: Int) {
12517 case n {
12518 1 -> todo
12519 2 -> todo
12520 _ -> todo
12521 }
12522 }"#,
12523 find_position_of("1").select_until(find_position_of("2"))
12524 );
12525}
12526
12527#[test]
12528fn merge_case_branch_with_todo_keeps_the_non_todo_body() {
12529 assert_code_action!(
12530 MERGE_CASE_BRANCHES,
12531 r#"pub fn go(n: Int) {
12532 case n {
12533 1 -> todo
12534 2 -> n * 2
12535 3 -> todo
12536 _ -> todo
12537 }
12538 }"#,
12539 find_position_of("1").select_until(find_position_of("3"))
12540 );
12541}
12542
12543#[test]
12544fn merge_case_branch_with_todo_keeps_the_non_todo_body_1() {
12545 assert_code_action!(
12546 MERGE_CASE_BRANCHES,
12547 r#"pub fn go(n: Int) {
12548 case n {
12549 1 -> todo
12550 2 -> todo
12551 3 -> n * 2
12552 _ -> todo
12553 }
12554 }"#,
12555 find_position_of("1").select_until(find_position_of("3"))
12556 );
12557}
12558
12559#[test]
12560fn merge_case_branch_with_todo_keeps_the_non_todo_body_2() {
12561 assert_code_action!(
12562 MERGE_CASE_BRANCHES,
12563 r#"pub fn go(n: Int) {
12564 case n {
12565 1 -> n * 2
12566 2 -> todo
12567 3 -> todo
12568 _ -> todo
12569 }
12570 }"#,
12571 find_position_of("1").select_until(find_position_of("3"))
12572 );
12573}
12574
12575#[test]
12576fn merge_case_branch_with_complex_bodies_1() {
12577 assert_code_action!(
12578 MERGE_CASE_BRANCHES,
12579 r#"pub fn go(n: Int) {
12580 case n {
12581 1 -> Ok("one or two")
12582 2 -> Ok("one or two")
12583 _ -> Error("neither one or two")
12584 }
12585 }"#,
12586 find_position_of("1").select_until(find_position_of("2"))
12587 );
12588}
12589
12590#[test]
12591fn merge_case_branch_with_complex_bodies_2() {
12592 assert_code_action!(
12593 MERGE_CASE_BRANCHES,
12594 r#"pub fn go(n: Int) {
12595 case n {
12596 1 -> n
12597 2 -> n
12598 _ -> panic as "neither one nor two"
12599 }
12600 }"#,
12601 find_position_of("1").select_until(find_position_of("2"))
12602 );
12603}
12604
12605#[test]
12606fn merge_case_branch_with_complex_bodies_3() {
12607 assert_code_action!(
12608 MERGE_CASE_BRANCHES,
12609 r#"pub fn go(n: Int) {
12610 case n {
12611 1 -> go(n - 1)
12612 2 -> go(n - 1)
12613 _ -> 10
12614 }
12615}"#,
12616 find_position_of("1").select_until(find_position_of("2"))
12617 );
12618}
12619
12620#[test]
12621fn merge_case_branch_with_complex_bodies_4() {
12622 assert_code_action!(
12623 MERGE_CASE_BRANCHES,
12624 r#"pub fn go(n: Int) {
12625 case n {
12626 1 -> {
12627 let a = go(n - 1)
12628 a * 10
12629 }
12630 2 -> {
12631 let a = go(n - 1)
12632 a * 10
12633 }
12634 _ -> 10
12635 }
12636}"#,
12637 find_position_of("1").select_until(find_position_of("2"))
12638 );
12639}
12640
12641#[test]
12642fn merge_case_branch_will_not_merge_branches_with_guards() {
12643 assert_no_code_actions!(
12644 MERGE_CASE_BRANCHES,
12645 r#"pub fn go(n: Int) {
12646 case n {
12647 1 if True -> todo
12648 2 -> todo
12649 _ -> todo
12650 }
12651 }"#,
12652 find_position_of("1").select_until(find_position_of("2"))
12653 );
12654}
12655
12656#[test]
12657fn merge_case_branch_will_not_merge_branches_defining_different_variables() {
12658 assert_no_code_actions!(
12659 MERGE_CASE_BRANCHES,
12660 r#"pub fn go(result: Result(Int, Int)) {
12661 case result {
12662 Ok(value) -> todo
12663 Error(error) -> todo
12664 _ -> todo
12665 }
12666 }"#,
12667 find_position_of("Ok").select_until(find_position_of("error"))
12668 );
12669}
12670
12671#[test]
12672fn merge_case_branch_can_merge_branches_defining_the_same_variables() {
12673 assert_code_action!(
12674 MERGE_CASE_BRANCHES,
12675 r#"pub fn go(result) {
12676 case result {
12677 [Ok(value), ..] -> todo
12678 [_, Error(value)] -> todo
12679 _ -> todo
12680 }
12681}"#,
12682 find_position_of("Ok").select_until(find_position_of("todo").nth_occurrence(2))
12683 );
12684}
12685
12686#[test]
12687fn merge_case_branch_can_merge_multiple_branches() {
12688 assert_code_action!(
12689 MERGE_CASE_BRANCHES,
12690 r#"pub fn go(result) {
12691 case result {
12692 [_] -> 1
12693 [Ok(value), ..] -> todo
12694 [_, Error(value)] -> todo
12695 [_, _, Error(value)] -> todo
12696 [_, _] -> 1
12697 _ -> 2
12698 }
12699}"#,
12700 find_position_of("todo").select_until(find_position_of("todo").nth_occurrence(3))
12701 );
12702}
12703
12704#[test]
12705fn merge_case_branch_does_not_pop_up_with_a_single_selected_branch() {
12706 assert_no_code_actions!(
12707 MERGE_CASE_BRANCHES,
12708 r#"pub fn go(result) {
12709 case result {
12710 [] -> todo
12711 _ -> 2
12712 }
12713}"#,
12714 find_position_of("[]").to_selection()
12715 );
12716}
12717
12718#[test]
12719fn merge_case_branch_works_with_existing_alternative_patterns() {
12720 assert_code_action!(
12721 MERGE_CASE_BRANCHES,
12722 r#"pub fn go(result) {
12723 case result {
12724 [] | [_, _, ..]-> todo
12725 [_] -> todo
12726 _ -> 2
12727 }
12728}"#,
12729 find_position_of("[]").select_until(find_position_of("[_]"))
12730 );
12731}
12732
12733#[test]
12734fn merge_case_branch_does_not_merge_branches_with_variables_with_same_name_and_different_types() {
12735 assert_no_code_actions!(
12736 MERGE_CASE_BRANCHES,
12737 r#"pub fn go(result: Result(Int, String)) {
12738 case result {
12739 Ok(one) -> todo
12740 Error(one) -> todo
12741 }
12742}"#,
12743 find_position_of("Ok").select_until(find_position_of("Error"))
12744 );
12745}
12746
12747#[test]
12748fn annotate_all_top_level_definitions_dont_affect_local_vars() {
12749 assert_code_action!(
12750 ANNOTATE_TOP_LEVEL_DEFINITIONS,
12751 r#"
12752pub const answer = 42
12753
12754pub fn add_two(thing) {
12755 thing + 2
12756}
12757
12758pub fn add_one(thing) {
12759 let result = thing + 1
12760 result
12761}
12762"#,
12763 find_position_of("fn").select_until(find_position_of("("))
12764 );
12765}
12766
12767#[test]
12768fn annotate_all_top_level_definitions_constant() {
12769 assert_code_action!(
12770 ANNOTATE_TOP_LEVEL_DEFINITIONS,
12771 r#"
12772pub const answer = 42
12773
12774pub fn add_two(thing) {
12775 thing + 2
12776}
12777
12778pub fn add_one(thing) {
12779 thing + 1
12780}
12781"#,
12782 find_position_of("const").select_until(find_position_of("="))
12783 );
12784}
12785
12786#[test]
12787fn annotate_all_top_level_definitions_function() {
12788 assert_code_action!(
12789 ANNOTATE_TOP_LEVEL_DEFINITIONS,
12790 r#"
12791pub fn add_two(thing) {
12792 thing + 2
12793}
12794
12795pub fn add_one(thing) {
12796 thing + 1
12797}
12798"#,
12799 find_position_of("fn").select_until(find_position_of("("))
12800 );
12801}
12802
12803#[test]
12804fn annotate_all_top_level_definitions_already_annotated() {
12805 assert_no_code_actions!(
12806 ANNOTATE_TOP_LEVEL_DEFINITIONS,
12807 r#"
12808pub const answer: Int = 42
12809
12810pub fn add_two(thing: Int) -> Int {
12811 thing + 2
12812}
12813
12814pub fn add_one(thing: Int) -> Int {
12815 thing + 1
12816}
12817"#,
12818 find_position_of("fn").select_until(find_position_of("("))
12819 );
12820}
12821
12822#[test]
12823fn annotate_all_top_level_definitions_inside_body() {
12824 assert_no_code_actions!(
12825 ANNOTATE_TOP_LEVEL_DEFINITIONS,
12826 r#"
12827pub fn add_one(thing) {
12828 thing + 1
12829}
12830"#,
12831 find_position_of("thing + 1").to_selection()
12832 );
12833}
12834
12835#[test]
12836fn annotate_all_top_level_definitions_partially_annotated() {
12837 assert_code_action!(
12838 ANNOTATE_TOP_LEVEL_DEFINITIONS,
12839 r#"
12840pub const answer: Int = 42
12841pub const another_answer = 43
12842
12843pub fn add_two(thing) -> Int {
12844 thing + 2
12845}
12846
12847pub fn add_one(thing: Int) {
12848 thing + 1
12849}
12850"#,
12851 find_position_of("fn").select_until(find_position_of("("))
12852 );
12853}
12854
12855#[test]
12856fn annotate_all_top_level_definitions_with_partially_annotated_generic_function() {
12857 assert_code_action!(
12858 ANNOTATE_TOP_LEVEL_DEFINITIONS,
12859 r#"
12860pub fn wibble(a: a, b, c: c, d) {
12861 todo
12862}
12863"#,
12864 find_position_of("wibble").to_selection()
12865 );
12866}
12867
12868#[test]
12869fn annotate_all_top_level_definitions_with_two_generic_functions() {
12870 assert_code_action!(
12871 ANNOTATE_TOP_LEVEL_DEFINITIONS,
12872 r#"
12873fn wibble(one) { todo }
12874
12875fn wobble(other) { todo }
12876"#,
12877 find_position_of("wobble").to_selection()
12878 );
12879}
12880
12881#[test]
12882fn annotate_all_top_level_definitions_with_constant_and_generic_functions() {
12883 assert_code_action!(
12884 ANNOTATE_TOP_LEVEL_DEFINITIONS,
12885 r#"
12886const answer = 42
12887
12888fn wibble(one) { todo }
12889
12890fn wobble(other) { todo }
12891"#,
12892 find_position_of("wobble").to_selection()
12893 );
12894}
12895
12896#[test]
12897fn annotate_all_top_level_definitions_not_suggested_if_annotations_present() {
12898 assert_no_code_actions!(
12899 ANNOTATE_TOP_LEVEL_DEFINITIONS,
12900 r#"
12901fn wibble(one: Int) -> Int { one }
12902
12903fn wobble(one) { wibble(one) }
12904"#,
12905 find_position_of("wibble").to_selection()
12906 );
12907}
12908
12909// https://github.com/gleam-lang/gleam/issues/5273
12910#[test]
12911fn extract_constant_doesnt_place_constant_below_documentation() {
12912 assert_code_action!(
12913 EXTRACT_CONSTANT,
12914 r#"
12915/// Wibble does some wobbling
12916pub fn wibble() {
12917 let x = "wobble"
12918 x
12919}
12920"#,
12921 find_position_of("x").to_selection()
12922 );
12923}
12924
12925// https://github.com/gleam-lang/gleam/issues/5273
12926#[test]
12927fn extract_constant_doesnt_place_constant_below_large_documentation() {
12928 assert_code_action!(
12929 EXTRACT_CONSTANT,
12930 r#"
12931/// Wibble does some wobbling
12932/// Note that it doesn't perform wibbling
12933pub fn wibble() {
12934 let x = "wobble"
12935 x
12936}
12937"#,
12938 find_position_of("x").to_selection()
12939 );
12940}
12941
12942#[test]
12943fn add_missing_type_parameter_for_single_constructor() {
12944 assert_code_action!(
12945 ADD_MISSING_TYPE_PARAMETER,
12946 r#"
12947type Wibble {
12948 Wibble(field: t)
12949}
12950"#,
12951 find_position_of("t").nth_occurrence(2).to_selection()
12952 );
12953}
12954
12955#[test]
12956fn add_missing_type_parameter_can_only_trigger_if_within_type() {
12957 assert_no_code_actions!(
12958 ADD_MISSING_TYPE_PARAMETER,
12959 r#"
12960type Wibble {
12961 Wibble(field: t)
12962}
12963
12964pub fn main() {
12965 // unrelated
12966 todo
12967}
12968"#,
12969 find_position_of("type").select_until(find_position_of("todo"))
12970 );
12971}
12972
12973#[test]
12974fn add_missing_type_parameter_to_exising_parameter() {
12975 assert_code_action!(
12976 ADD_MISSING_TYPE_PARAMETER,
12977 r#"
12978type Wibble(t) {
12979 Wibble(field: t)
12980 Wobble(field: u)
12981}
12982"#,
12983 find_position_of("u").to_selection()
12984 );
12985}
12986
12987#[test]
12988fn add_missing_type_parameter_preserves_comments() {
12989 assert_code_action!(
12990 ADD_MISSING_TYPE_PARAMETER,
12991 r#"
12992type Wibble(
12993 // Comment 1
12994 b,
12995 // Comment 2
12996) {
12997 Wibble(a, b, c)
12998}
12999"#,
13000 find_position_of("c").to_selection()
13001 );
13002}
13003
13004#[test]
13005fn add_missing_type_parameter_sorted_alphabetically() {
13006 assert_code_action!(
13007 ADD_MISSING_TYPE_PARAMETER,
13008 r#"
13009type Wibble(b) {
13010 Wibble(c, b, a)
13011}
13012"#,
13013 find_position_of("a").to_selection()
13014 );
13015}
13016
13017#[test]
13018fn add_missing_type_parameter_not_suggested_when_nothing_missing() {
13019 assert_no_code_actions!(
13020 ADD_MISSING_TYPE_PARAMETER,
13021 r#"
13022type Wibble(t) {
13023 Wibble(field: t)
13024}
13025"#,
13026 find_position_of("t").nth_occurrence(2).to_selection()
13027 );
13028}
13029
13030#[test]
13031fn add_missing_type_parameter_not_suggested_when_no_parameters() {
13032 assert_no_code_actions!(
13033 ADD_MISSING_TYPE_PARAMETER,
13034 r#"
13035type Wibble {
13036 Wibble
13037}
13038"#,
13039 find_position_of("Wibble").nth_occurrence(2).to_selection()
13040 );
13041}
13042
13043#[test]
13044fn add_missing_type_parameter_does_not_add_types_that_do_not_exist() {
13045 assert_no_code_actions!(
13046 ADD_MISSING_TYPE_PARAMETER,
13047 r#"
13048type Wibble {
13049 Wibble(Wobble)
13050}
13051"#,
13052 find_position_of("Wibble").nth_occurrence(2).to_selection()
13053 );
13054}
13055
13056// https://github.com/gleam-lang/gleam/issues/5288
13057#[test]
13058fn extract_anonymous_function_without_variable_capture_1() {
13059 assert_code_action!(
13060 EXTRACT_FUNCTION,
13061 "
13062pub fn main() {
13063 let int_pow = fn(base, exp) {
13064 case exp {
13065 exp if exp < 0 -> 0
13066 0 -> base
13067 exp -> int_pow(base * exp, exp - 1)
13068 }
13069 }
13070}
13071 ",
13072 find_position_of("fn(").select_until(find_position_of("}").nth_occurrence(2))
13073 );
13074}
13075
13076#[test]
13077fn extract_anonymous_function_without_variable_capture_2() {
13078 let src = r#"
13079import gleam/io
13080import gleam/list
13081
13082pub fn main() {
13083 list.each(list.range(0, 10), fn(_) { io.println("wibble wobble") })
13084}
13085 "#;
13086
13087 assert_code_action!(
13088 EXTRACT_FUNCTION,
13089 TestProject::for_source(src)
13090 .add_hex_module(
13091 "gleam/io",
13092 "
13093 pub fn println(string: String) -> Nil { todo }
13094 "
13095 )
13096 .add_hex_module(
13097 "gleam/list",
13098 "
13099 pub fn each(list: List(a), f: fn(a) -> b) -> Nil { todo }
13100 pub fn range(from start: Int, to stop: Int) -> List(Int) { todo }
13101 "
13102 ),
13103 find_position_of("fn(").select_until(find_position_of("}"))
13104 );
13105}
13106
13107#[test]
13108fn extract_unary_anonymous_function_with_variable_capture_1() {
13109 let src = "
13110import gleam/list
13111
13112pub fn main() {
13113 let needle = 42
13114 let haystack = [25, 81, 74, 42, 33]
13115 list.filter(haystack, fn(x) { x == needle })
13116}
13117 ";
13118
13119 assert_code_action!(
13120 EXTRACT_FUNCTION,
13121 TestProject::for_source(src).add_hex_module(
13122 "gleam/list",
13123 "
13124 pub fn filter(
13125 list: List(a),
13126 keeping predicate: fn(a) -> Bool,
13127 ) -> List(a) { todo }
13128 "
13129 ),
13130 find_position_of("fn(").select_until(find_position_of("}"))
13131 );
13132}
13133
13134#[test]
13135fn extract_unary_anonymous_function_with_variable_capture_2() {
13136 let src = "
13137import gleam/list
13138
13139pub fn main() {
13140 let needle = 42
13141 let haystack = [25, 81, 74, 42, 33]
13142 list.filter(haystack, fn(_) { needle == 42 })
13143}
13144 ";
13145
13146 assert_code_action!(
13147 EXTRACT_FUNCTION,
13148 TestProject::for_source(src).add_hex_module(
13149 "gleam/list",
13150 "
13151 pub fn filter(
13152 list: List(a),
13153 keeping predicate: fn(a) -> Bool,
13154 ) -> List(a) { todo }
13155 "
13156 ),
13157 find_position_of("fn(").select_until(find_position_of("}"))
13158 );
13159}
13160
13161#[test]
13162fn extract_anonymous_function_with_variable_capture_1() {
13163 assert_code_action!(
13164 EXTRACT_FUNCTION,
13165 "
13166pub fn main() {
13167 let outer_scope = 3
13168 let wibble = fn(a, b) {
13169 a + b + outer_scope
13170 }
13171}
13172 ",
13173 find_position_of("fn(").select_until(find_position_of("}"))
13174 );
13175}
13176
13177#[test]
13178fn extract_anonymous_function_with_variable_capture_2() {
13179 let src = "
13180import gleam/list
13181
13182pub fn main() {
13183 let shorter = [1, 2, 3]
13184 let longer = [4, 5, 6, 7, 8]
13185 let offset = 5
13186 list.map2(shorter, longer, fn(_left, right) { right + offset })
13187}
13188 ";
13189
13190 assert_code_action!(
13191 EXTRACT_FUNCTION,
13192 TestProject::for_source(src).add_hex_module(
13193 "gleam/list",
13194 "
13195 pub fn map2(
13196 list1: List(a),
13197 list2: List(b),
13198 with fun: fn(a, b) -> c,
13199 ) -> List(c) { todo }
13200 "
13201 ),
13202 find_position_of("fn(").select_until(find_position_of("}"))
13203 );
13204}
13205
13206// https://github.com/gleam-lang/gleam/issues/5263
13207#[test]
13208fn interpolate_string_allows_extracting_record_access_syntax() {
13209 assert_code_action!(
13210 INTERPOLATE_STRING,
13211 r#"pub fn main() {
13212 "wibble wobble.some_field woo"
13213}"#,
13214 find_position_of("wobble").select_until(find_position_of("some_field ").under_last_char()),
13215 );
13216}
13217
13218// https://github.com/gleam-lang/gleam/issues/5299
13219#[test]
13220fn generate_dynamic_decoder_produces_zero_values_for_prelude_and_stdlib_types() {
13221 let src = r#"
13222import gleam/option
13223import gleam/dict
13224
13225pub type Wobble {
13226 Wobble(
13227 bit_array: BitArray,
13228 int: Int,
13229 float: Float,
13230 bool: Bool,
13231 list: List(Int),
13232 string: String,
13233 nil: Nil,
13234 option: option.Option(String),
13235 dict: dict.Dict(Int, Bool),
13236 )
13237 Dummy(
13238 a: Int,
13239 b: Int,
13240 c: Int,
13241 d: Int,
13242 e: Int,
13243 f: Int,
13244 g: Int,
13245 h: Int,
13246 i: Int,
13247 j: Int,
13248 )
13249}
13250 "#;
13251 assert_code_action!(
13252 GENERATE_DYNAMIC_DECODER,
13253 TestProject::for_source(src)
13254 .add_hex_module("gleam/option", "pub type Option(a) { Some(a) None }")
13255 .add_hex_module("gleam/dict", "pub type Dict(key, value)"),
13256 find_position_of("pub type Wobble {").select_until(find_position_of("}"))
13257 );
13258}
13259
13260#[test]
13261fn interpolate_string_does_not_add_empty_string_right_at_the_start() {
13262 assert_code_action!(
13263 INTERPOLATE_STRING,
13264 r#"pub fn main() {
13265 "wibble wobble woo"
13266}"#,
13267 find_position_of("wibble ").select_until(find_position_of("wibble ").under_last_char()),
13268 );
13269}
13270
13271#[test]
13272fn generate_dynamic_decoder_produces_zero_values_for_user_defined_type_in_the_same_package_1() {
13273 let src = r#"
13274import wibble.{type Wibble}
13275
13276pub type Wobble {
13277 Wobble(value: Wibble)
13278 Dummy(a: Int, b: Int)
13279}
13280 "#;
13281 assert_code_action!(
13282 GENERATE_DYNAMIC_DECODER,
13283 TestProject::for_source(src).add_module("wibble", "pub type Wibble { Wibble }"),
13284 find_position_of("pub type Wobble {").select_until(find_position_of("}").nth_occurrence(2))
13285 );
13286}
13287
13288#[test]
13289fn interpolate_string_does_not_add_empty_string_right_at_the_end() {
13290 assert_code_action!(
13291 INTERPOLATE_STRING,
13292 r#"pub fn main() {
13293 "wibble wobble woo"
13294}"#,
13295 find_position_of("woo\"").select_until(find_position_of("woo\"").under_last_char()),
13296 );
13297}
13298
13299#[test]
13300fn generate_dynamic_decoder_produces_zero_values_for_user_defined_type_in_the_same_package_2() {
13301 let src = r#"
13302import internal/wibble.{type Wibble}
13303
13304pub type Wobble {
13305 Wobble(value: Wibble)
13306 Dummy(a: Int, b: Int)
13307}
13308 "#;
13309 assert_code_action!(
13310 GENERATE_DYNAMIC_DECODER,
13311 TestProject::for_source(src)
13312 .add_module(
13313 "internal/wibble",
13314 r#"
13315import internal/nested_wibble.{type NestedWibble}
13316pub type Wibble { Wibble(value: NestedWibble) }
13317"#
13318 )
13319 .add_module(
13320 "internal/nested_wibble",
13321 r#"
13322pub type NestedWibble { NestedWibble }
13323"#
13324 ),
13325 find_position_of("pub type Wobble {").select_until(find_position_of("}").nth_occurrence(2))
13326 );
13327}
13328
13329#[test]
13330fn generate_dynamic_decoder_produces_zero_values_for_user_defined_type_in_the_same_package_3() {
13331 let src = r#"
13332import wibble.{type Wibble}
13333
13334pub type Wobble {
13335 Wobble(value: Wibble)
13336 Dummy(a: Int, b: Int)
13337}
13338 "#;
13339 assert_code_action!(
13340 GENERATE_DYNAMIC_DECODER,
13341 TestProject::for_source(src)
13342 .add_module(
13343 "wibble",
13344 r#"
13345import gleam/dict.{type Dict}
13346pub type Wibble { Wibble(map: Dict(Int, Bool)) }
13347"#
13348 )
13349 .add_hex_module(
13350 "gleam/dict",
13351 r#"
13352pub type Dict(key, value)
13353pub fn new() -> Dict(k, v) { todo }
13354"#
13355 ),
13356 find_position_of("pub type Wobble {").select_until(find_position_of("}").nth_occurrence(2))
13357 );
13358}
13359
13360#[test]
13361fn generate_dynamic_decoder_does_not_produce_zero_values_for_types_from_other_packages() {
13362 let src = r#"
13363import wibble.{type Wibble}
13364
13365pub type Wobble {
13366 Wobble(value: Wibble)
13367 Dummy(a: Int, b: Wibble)
13368}
13369 "#;
13370 assert_code_action!(
13371 GENERATE_DYNAMIC_DECODER,
13372 TestProject::for_source(src).add_hex_module("wibble", "pub type Wibble { Wibble }"),
13373 find_position_of("pub type Wobble {").select_until(find_position_of("}").nth_occurrence(2))
13374 );
13375}
13376
13377#[test]
13378fn generate_dynamic_decoder_skips_over_recursive_constructors_when_generating_zero_values() {
13379 assert_code_action!(
13380 GENERATE_DYNAMIC_DECODER,
13381 r#"
13382pub type Wobble {
13383 Wobble(inside: Wobble)
13384 Dummy(a: Int, b: Int)
13385}
13386 "#,
13387 find_position_of("pub type Wobble {").select_until(find_position_of("}"))
13388 );
13389}
13390
13391#[test]
13392fn generate_dynamic_decoder_skips_over_mutually_recursive_constructors_when_generating_zero_values()
13393{
13394 assert_code_action!(
13395 GENERATE_DYNAMIC_DECODER,
13396 r#"
13397pub type Wobble {
13398 Wobble(inside: Wibble)
13399 DummyWobble(a: Int, b: Int)
13400}
13401
13402pub type Wibble {
13403 Wibble(inside: Wobble)
13404 DummyWibble(a: Int, b: Int)
13405}
13406 "#,
13407 find_position_of("pub type Wobble {").select_until(find_position_of("}"))
13408 );
13409}
13410
13411#[test]
13412fn generate_dynamic_decoder_uses_smallest_possible_constructor_for_zero_value() {
13413 let src = r#"
13414import wibble.{type Wibble}
13415
13416pub type Wobble {
13417 Wobble(impossible: Wobble)
13418 WibbleWobble(nope: Wibble)
13419 Dummy(a: Int, b: #(Float, Float))
13420}
13421 "#;
13422 assert_code_action!(
13423 GENERATE_DYNAMIC_DECODER,
13424 TestProject::for_source(src).add_hex_module("wibble", "pub type Wibble { Wibble }"),
13425 find_position_of("pub type Wobble {").select_until(find_position_of("}").nth_occurrence(2))
13426 );
13427}
13428
13429#[test]
13430fn generate_dynamic_decoder_generates_todo_for_zero_value_when_all_constructors_fail() {
13431 assert_code_action!(
13432 GENERATE_DYNAMIC_DECODER,
13433 r#"
13434pub type Wobble {
13435 Wobble(nope: Wobble)
13436 Wibble(not: Int, again: Wobble)
13437}
13438 "#,
13439 find_position_of("pub type Wobble {").select_until(find_position_of("}"))
13440 );
13441}
13442
13443#[test]
13444fn generate_dynamic_decoder_uses_decode_success_for_nil() {
13445 assert_code_action!(
13446 GENERATE_DYNAMIC_DECODER,
13447 r#"
13448pub type Nothing {
13449 No(val: Nil)
13450 Nope(val: #(Nil, Nil))
13451 Nothing(val: List(Nil))
13452}
13453 "#,
13454 find_position_of("pub type Nothing {").select_until(find_position_of("}"))
13455 );
13456}
13457
13458#[test]
13459fn generate_to_json_function_uses_json_null_for_nil() {
13460 let src = "
13461import gleam/json
13462import gleam/dict.{type Dict}
13463
13464
13465pub type Nothing {
13466 Nope(val: Nil)
13467 Nothing(val1: List(Nil), val2: Dict(Int, Nil))
13468}
13469";
13470
13471 assert_code_action!(
13472 GENERATE_TO_JSON_FUNCTION,
13473 TestProject::for_source(src)
13474 .add_package_module("gleam_json", "gleam/json", "pub type Json")
13475 .add_hex_module("gleam/dict", "pub type Dict(key, value)"),
13476 find_position_of("pub type Nothing {")
13477 .select_until(find_position_of("}").nth_occurrence(2))
13478 );
13479}
13480
13481#[test]
13482fn generate_to_json_function_ignores_nil_and_nil_tuple_fields_with_underscore() {
13483 let src = "
13484import gleam/json
13485import gleam/dict.{type Dict}
13486
13487
13488pub type Nothing {
13489 No(val: #(Nil), another: #(Nil, #(Nil, Int)))
13490 Nope(val: List(#(Nil)))
13491}
13492";
13493
13494 assert_code_action!(
13495 GENERATE_TO_JSON_FUNCTION,
13496 TestProject::for_source(src)
13497 .add_package_module("gleam_json", "gleam/json", "pub type Json")
13498 .add_hex_module("gleam/dict", "pub type Dict(key, value)"),
13499 find_position_of("pub type Nothing {")
13500 .select_until(find_position_of("}").nth_occurrence(2))
13501 );
13502}
13503
13504#[test]
13505fn replace_underscore_with_function_return_type() {
13506 assert_code_action!(
13507 REPLACE_UNDERSCORE_WITH_TYPE,
13508 r#"
13509pub fn wibble() -> _ {
13510 12
13511}
13512 "#,
13513 find_position_of("_").to_selection()
13514 );
13515}
13516
13517#[test]
13518fn wrap_uncalled_function_in_anonymous_function() {
13519 assert_code_action!(
13520 WRAP_IN_ANONYMOUS_FUNCTION,
13521 "pub fn main() {
13522 op
13523}
13524
13525fn op(i) {
13526 todo
13527}
13528",
13529 find_position_of("op").to_selection()
13530 );
13531}
13532
13533#[test]
13534fn wrap_function_in_anonymous_function_does_not_trigger_if_selection_is_not_within_the_function() {
13535 assert_no_code_actions!(
13536 WRAP_IN_ANONYMOUS_FUNCTION,
13537 "pub fn main() {
13538 let a = 1
13539 some_function
13540 let b = 2
13541 Nil
13542}
13543
13544fn some_function(i) {
13545 todo
13546}
13547",
13548 find_position_of("1").select_until(find_position_of("2"))
13549 );
13550}
13551
13552#[test]
13553fn wrap_function_in_anonymous_function_does_not_trigger_on_record_update() {
13554 assert_no_code_actions!(
13555 WRAP_IN_ANONYMOUS_FUNCTION,
13556 "pub fn main() {
13557 Wibble(..todo, a: 1)
13558}
13559
13560pub type Wibble { Wibble(a: Int, b: Int) }
13561",
13562 find_position_of("Wibble").to_selection()
13563 );
13564}
13565
13566#[test]
13567fn wrap_function_in_anonymous_function_does_not_trigger_on_invalid_record_update() {
13568 assert_no_code_actions!(
13569 WRAP_IN_ANONYMOUS_FUNCTION,
13570 "pub fn main() {
13571 Wibble(..todo, a: 1)
13572}
13573",
13574 find_position_of("Wibble").to_selection()
13575 );
13576}
13577
13578#[test]
13579fn wrap_function_in_anonymous_function_does_not_trigger_on_record_call() {
13580 assert_no_code_actions!(
13581 WRAP_IN_ANONYMOUS_FUNCTION,
13582 "pub fn main() {
13583 Wibble(1, 2)
13584}
13585
13586pub type Wibble { Wibble(a: Int, b: Int) }
13587",
13588 find_position_of("Wibble").to_selection()
13589 );
13590}
13591
13592#[test]
13593fn wrap_function_in_anonymous_function_does_not_trigger_on_invalid_record_call() {
13594 assert_no_code_actions!(
13595 WRAP_IN_ANONYMOUS_FUNCTION,
13596 "pub fn main() {
13597 Wibble(1)
13598}
13599",
13600 find_position_of("Wibble").to_selection()
13601 );
13602}
13603
13604#[test]
13605fn replace_nested_underscore_with_generic_type() {
13606 assert_code_action!(
13607 REPLACE_UNDERSCORE_WITH_TYPE,
13608 r#"
13609pub fn wibble() -> Result(a, _) {
13610 Ok(todo)
13611}
13612 "#,
13613 find_position_of("_").to_selection()
13614 );
13615}
13616
13617#[test]
13618fn wrap_uncalled_constructor_in_anonymous_function() {
13619 assert_code_action!(
13620 WRAP_IN_ANONYMOUS_FUNCTION,
13621 "pub fn main() {
13622 Record
13623}
13624
13625type Record {
13626 Record(i: Int)
13627}
13628",
13629 find_position_of("Record").to_selection()
13630 );
13631}
13632
13633#[test]
13634fn replace_nested_underscore_with_function_return_type() {
13635 assert_code_action!(
13636 REPLACE_UNDERSCORE_WITH_TYPE,
13637 r#"
13638pub fn wibble() -> Result(Result(_, Nil), Int) {
13639 Ok(Ok("hello"))
13640}
13641 "#,
13642 find_position_of("_").to_selection()
13643 );
13644}
13645
13646#[test]
13647fn wrap_call_arg_in_anonymous_function() {
13648 assert_code_action!(
13649 WRAP_IN_ANONYMOUS_FUNCTION,
13650 "import gleam/list
13651
13652pub fn main() {
13653 list.map([1, 2, 3], op)
13654}
13655
13656fn op(i: Int) -> Int {
13657 todo
13658}
13659",
13660 find_position_of("op").to_selection()
13661 );
13662}
13663
13664#[test]
13665fn replace_nested_underscore_in_let_annotation() {
13666 assert_code_action!(
13667 REPLACE_UNDERSCORE_WITH_TYPE,
13668 r#"
13669pub fn wibble() {
13670 let a: Result(Result(_, Nil), Nil) = Ok(Ok("hello"))
13671}
13672 "#,
13673 find_position_of("_").to_selection()
13674 );
13675}
13676
13677#[test]
13678fn wrap_function_in_anonymous_function_without_shadowing() {
13679 assert_code_action!(
13680 WRAP_IN_ANONYMOUS_FUNCTION,
13681 "pub fn main() {
13682 int
13683}
13684
13685fn int(i: Int) {
13686 todo
13687}
13688",
13689 find_position_of("int").to_selection()
13690 );
13691}
13692
13693#[test]
13694fn replace_underscore_in_let_annotation() {
13695 assert_code_action!(
13696 REPLACE_UNDERSCORE_WITH_TYPE,
13697 r#"
13698pub fn wibble() {
13699 let a: _ = Ok(Ok("hello"))
13700}
13701 "#,
13702 find_position_of("_").to_selection()
13703 );
13704}
13705
13706#[test]
13707fn replace_nested_underscore_with_tuple_type() {
13708 assert_code_action!(
13709 REPLACE_UNDERSCORE_WITH_TYPE,
13710 r#"
13711pub fn wibble() {
13712 let a: #(Int, _, Int) = #(1, "hello", 2)
13713}
13714 "#,
13715 find_position_of("_").to_selection()
13716 );
13717}
13718
13719#[test]
13720fn replace_underscore_in_function_argument() {
13721 assert_code_action!(
13722 REPLACE_UNDERSCORE_WITH_TYPE,
13723 r#"
13724pub fn wibble(a: _) -> Int {
13725 a + 2
13726}
13727 "#,
13728 find_position_of("_").to_selection()
13729 );
13730}
13731
13732#[test]
13733fn replace_underscore_in_fn_expr_argument() {
13734 assert_code_action!(
13735 REPLACE_UNDERSCORE_WITH_TYPE,
13736 r#"
13737pub fn wibble() {
13738 fn(a: _) { a + 2 }
13739}
13740 "#,
13741 find_position_of("_").to_selection()
13742 );
13743}
13744
13745#[test]
13746fn wrap_assignment_in_anonymous_function() {
13747 assert_code_action!(
13748 WRAP_IN_ANONYMOUS_FUNCTION,
13749 "pub fn main() {
13750 let op = op_factory(1, 2, 3)
13751}
13752
13753fn op_factory(a: Int, b: Int, c: Int) -> fn(Int) -> Int {
13754 todo
13755}
13756",
13757 find_position_of("op_factory").to_selection()
13758 );
13759}
13760
13761#[test]
13762fn wrap_imported_function_in_anonymous_function() {
13763 let source = "import gleam/list
13764import gleam/int
13765
13766pub fn main() {
13767 list.map([1, 2, 3], int.is_even)
13768}
13769";
13770
13771 let int_source = "pub fn is_even(int) { int % 2 == 0 }";
13772
13773 assert_code_action!(
13774 WRAP_IN_ANONYMOUS_FUNCTION,
13775 TestProject::for_source(source).add_module("gleam/int", int_source),
13776 find_position_of("int.is_even").to_selection()
13777 );
13778}
13779
13780#[test]
13781fn dont_wrap_anonymous_function_in_anonymous_function() {
13782 assert_no_code_actions!(
13783 WRAP_IN_ANONYMOUS_FUNCTION,
13784 "pub fn main() {
13785 let f = fn(in) { ception(in) }
13786}
13787
13788",
13789 find_position_of("fn(in)").to_selection()
13790 );
13791}
13792
13793#[test]
13794fn wrap_pipeline_step_in_anonymous_function() {
13795 assert_code_action!(
13796 WRAP_IN_ANONYMOUS_FUNCTION,
13797 "pub fn main() {
13798 1 |> wibble |> wobble
13799}
13800
13801fn wibble(i) {
13802 todo
13803}
13804
13805fn wobble(i) {
13806 todo
13807}
13808
13809",
13810 find_position_of("wibble").to_selection()
13811 );
13812}
13813
13814#[test]
13815fn wrap_final_pipeline_step_in_anonymous_function() {
13816 assert_code_action!(
13817 WRAP_IN_ANONYMOUS_FUNCTION,
13818 "pub fn main() {
13819 1 |> wibble |> wobble
13820}
13821
13822fn wibble(i) {
13823 todo
13824}
13825
13826fn wobble(i) {
13827 todo
13828}
13829
13830",
13831 find_position_of("wobble").to_selection()
13832 );
13833}
13834
13835#[test]
13836fn wrap_record_field_in_anonymous_function() {
13837 assert_code_action!(
13838 WRAP_IN_ANONYMOUS_FUNCTION,
13839 "pub fn main() {
13840 let r = Record(wibble)
13841}
13842
13843type Record {
13844 Record(wibbler: fn(Int) -> Int)
13845}
13846
13847fn wibble(v) {
13848 todo
13849}
13850
13851",
13852 find_position_of("wibble").to_selection()
13853 );
13854}
13855
13856#[test]
13857fn dont_wrap_functions_that_are_already_being_called() {
13858 assert_no_code_actions!(
13859 WRAP_IN_ANONYMOUS_FUNCTION,
13860 "pub fn main() {
13861 wibble(1)
13862}
13863
13864fn wibble(i) {
13865 todo
13866}
13867
13868",
13869 find_position_of("wibble").to_selection()
13870 );
13871}
13872
13873#[test]
13874fn unwrap_trivial_anonymous_function() {
13875 assert_code_action!(
13876 UNWRAP_ANONYMOUS_FUNCTION,
13877 "import gleam/list
13878
13879pub fn main() {
13880 list.map([1, 2, 3], fn(int) { op(int) })
13881}
13882
13883fn op(i: Int) -> Int {
13884 todo
13885}
13886",
13887 find_position_of("fn(int)").to_selection()
13888 );
13889}
13890
13891#[test]
13892fn unwrap_anonymous_function_can_only_trigger_if_cursor_is_within_the_function() {
13893 assert_no_code_actions!(
13894 UNWRAP_ANONYMOUS_FUNCTION,
13895 "import gleam/list
13896
13897pub fn main() {
13898 list.map([1, 2, 3], fn(int) { op(int) })
13899}
13900
13901fn op(i: Int) -> Int {
13902 todo
13903}
13904",
13905 find_position_of("fn(int)").select_until(find_position_of("todo"))
13906 );
13907}
13908
13909#[test]
13910fn unwrap_trivial_anonymous_function_with_bad_spacing() {
13911 assert_code_action!(
13912 UNWRAP_ANONYMOUS_FUNCTION,
13913 "import gleam/list
13914
13915pub fn main() {
13916 list.map([1, 2, 3], fn (int) {
13917
13918 op(int)
13919
13920 })
13921}
13922
13923fn op(i: Int) -> Int {
13924 todo
13925}
13926",
13927 find_position_of("fn (int)").to_selection()
13928 );
13929}
13930
13931#[test]
13932fn unwrap_nested_anonymous_function() {
13933 assert_code_action!(
13934 UNWRAP_ANONYMOUS_FUNCTION,
13935 "pub fn main() {
13936 fn(do) { fn(re){ mi(re) }(do) }
13937}
13938
13939fn mi(v) {
13940 todo
13941}
13942",
13943 find_position_of("fn(re)").to_selection()
13944 );
13945}
13946
13947#[test]
13948fn unwrap_anonymous_function_with_labelled_args() {
13949 assert_code_action!(
13950 UNWRAP_ANONYMOUS_FUNCTION,
13951 "pub fn main() {
13952 fn(a, b) { op(first: a, second: b) }
13953}
13954
13955fn op(first a, second b) {
13956 todo
13957}
13958",
13959 find_position_of("fn(a, b)").to_selection()
13960 );
13961}
13962
13963#[test]
13964fn unwrap_anonymous_function_with_labelled_and_unlabelled_args() {
13965 assert_code_action!(
13966 UNWRAP_ANONYMOUS_FUNCTION,
13967 "pub fn main() {
13968 fn(a, b, c) { op(a, second: b, third: c) }
13969}
13970
13971fn op(a, second b, third c) {
13972 todo
13973}
13974",
13975 find_position_of("fn(a, b, c)").to_selection()
13976 );
13977}
13978
13979#[test]
13980fn unwrap_anonymous_function_with_labelled_args_out_of_order() {
13981 assert_code_action!(
13982 UNWRAP_ANONYMOUS_FUNCTION,
13983 "pub fn main() {
13984 fn(a, b) { op(second: b, first: a) }
13985}
13986
13987fn op(first a, second b) {
13988 todo
13989}
13990",
13991 find_position_of("fn(a, b)").to_selection()
13992 );
13993}
13994
13995#[test]
13996fn unwrap_anonymous_function_with_comment_after() {
13997 assert_code_action!(
13998 UNWRAP_ANONYMOUS_FUNCTION,
13999 "pub fn main() {
14000 fn(a) {
14001 op(a)
14002 // look out!
14003 }
14004}
14005
14006fn op(a) {
14007 todo
14008}
14009",
14010 find_position_of("fn(a)").to_selection()
14011 );
14012}
14013
14014#[test]
14015fn unwrap_anonymous_function_with_comment_on_line() {
14016 assert_code_action!(
14017 UNWRAP_ANONYMOUS_FUNCTION,
14018 "pub fn main() {
14019 fn(a) {
14020 op(a) // look out!
14021 }
14022}
14023
14024fn op(a) {
14025 todo
14026}
14027",
14028 find_position_of("fn(a)").to_selection()
14029 );
14030}
14031
14032#[test]
14033fn unwrap_anonymous_function_with_comment_on_head_line() {
14034 assert_code_action!(
14035 UNWRAP_ANONYMOUS_FUNCTION,
14036 "pub fn main() {
14037 fn(a) { // look out!
14038 op(a)
14039 }
14040}
14041
14042fn op(a) {
14043 todo
14044}
14045",
14046 find_position_of("fn(a)").to_selection()
14047 );
14048}
14049
14050#[test]
14051fn unwrap_anonymous_function_with_comments_before() {
14052 assert_code_action!(
14053 UNWRAP_ANONYMOUS_FUNCTION,
14054 "pub fn main() {
14055 fn(a) {
14056 // look out,
14057 // there's a comment!
14058
14059 // another comment!
14060 //here's one without a leading space
14061 // here's one indented wrong
14062 // here's one indented even wronger
14063 op(a)
14064 }
14065}
14066
14067fn op(a) {
14068 todo
14069}
14070",
14071 find_position_of("fn(a)").to_selection()
14072 );
14073}
14074
14075#[test]
14076fn unwrap_anonymous_function_unavailable_when_args_discarded() {
14077 assert_no_code_actions!(
14078 UNWRAP_ANONYMOUS_FUNCTION,
14079 "import gleam/list
14080
14081pub fn main() {
14082 list.index_map([1, 2, 3], fn(_, int) { op(int) })
14083}
14084
14085fn op(i: Int) -> Int {
14086 todo
14087}
14088",
14089 find_position_of("fn(_, int)").to_selection()
14090 );
14091}
14092
14093#[test]
14094fn unwrap_anonymous_function_unavailable_with_different_args() {
14095 assert_no_code_actions!(
14096 UNWRAP_ANONYMOUS_FUNCTION,
14097 "import gleam/list
14098
14099const another_int = 7
14100
14101pub fn main() {
14102 list.map([1, 2, 3], fn(int) { op(another_int) })
14103}
14104
14105fn op(i: Int) -> Int {
14106 todo
14107}
14108",
14109 find_position_of("fn(int)").to_selection()
14110 );
14111}
14112
14113#[test]
14114fn dont_wrap_use_in_anonymous_function() {
14115 assert_no_code_actions!(
14116 WRAP_IN_ANONYMOUS_FUNCTION,
14117 "fn apply(x, k) {
14118 k(x)
14119}
14120
14121pub fn main() {
14122 use a <- apply(5)
14123 a * 1
14124}
14125",
14126 find_position_of("a * 1").to_selection()
14127 );
14128}
14129
14130#[test]
14131fn dont_wrap_uses_in_anonymous_function() {
14132 assert_no_code_actions!(
14133 WRAP_IN_ANONYMOUS_FUNCTION,
14134 "pub fn main() {
14135 use a <- apply(1)
14136 use b <- apply(2)
14137 use c <- apply(3)
14138 a * b * c
14139}
14140
14141fn apply(x, k) {
14142 k(x)
14143}",
14144 find_position_of("a * b * c").to_selection()
14145 );
14146}
14147
14148#[test]
14149fn create_unknown_module_under_src() {
14150 assert_code_action!(
14151 "Create src/wibble/wobble.gleam",
14152 "
14153import wibble/wobble
14154
14155pub fn main() {
14156 Nil
14157}",
14158 find_position_of("wobble").to_selection()
14159 );
14160}
14161
14162#[test]
14163fn create_unknown_module_under_dev() {
14164 let project = TestProject::for_source(
14165 "
14166pub fn main() {
14167 Nil
14168}",
14169 )
14170 .add_dev_module(
14171 "wibble",
14172 "
14173import wobble/woo",
14174 );
14175
14176 assert_code_action!(
14177 "Create dev/wobble/woo.gleam",
14178 project,
14179 Origin::Dev,
14180 "wibble",
14181 find_position_of("woo").to_selection()
14182 );
14183}
14184
14185#[test]
14186fn create_unknown_module_under_test() {
14187 let project = TestProject::for_source(
14188 "
14189pub fn main() {
14190 Nil
14191}",
14192 )
14193 .add_test_module(
14194 "wibble",
14195 "
14196import wobble/woo",
14197 );
14198
14199 assert_code_action!(
14200 "Create test/wobble/woo.gleam",
14201 project,
14202 Origin::Test,
14203 "wibble",
14204 find_position_of("woo").to_selection()
14205 );
14206}
14207
14208#[test]
14209fn create_unknown_module_doesnt_trigger_when_module_exists() {
14210 let code = "
14211import wibble/wobble
14212
14213pub fn main() {
14214 Nil
14215}
14216";
14217
14218 assert_no_code_actions!(
14219 "Create src/wibble/wobble.gleam",
14220 TestProject::for_source(code)
14221 .add_module("wibble/wobble", "pub type Wibble { Wobble(String) }"),
14222 find_position_of("wobble").to_selection()
14223 );
14224}
14225
14226#[test]
14227fn create_unknown_module_doesnt_trigger_when_import_not_selected() {
14228 let code = "
14229import wibble/wobble
14230
14231pub fn main() {
14232 Nil
14233}
14234";
14235
14236 assert_no_code_actions!(
14237 "Create src/wibble/wobble.gleam",
14238 TestProject::for_source(code),
14239 find_position_of("main").to_selection()
14240 );
14241}
14242
14243#[test]
14244fn remove_redundant_record_update_triggered_on_the_record_spread() {
14245 assert_code_action!(
14246 REMOVE_REDUNDANT_RECORD_UPDATE,
14247 "
14248pub fn go(record: Wibble) {
14249 Wibble(..record, a: 1, b: 2)
14250}
14251
14252pub type Wibble { Wibble(a: Int, b: Int) }
14253",
14254 find_position_of("..").to_selection()
14255 );
14256}
14257
14258#[test]
14259fn remove_redundant_record_update_triggered_on_the_record() {
14260 assert_code_action!(
14261 REMOVE_REDUNDANT_RECORD_UPDATE,
14262 "
14263pub fn go(record: Wibble) {
14264 Wibble(..record, a: 1, b: 2)
14265}
14266
14267pub type Wibble { Wibble(a: Int, b: Int) }
14268",
14269 find_position_of("record").nth_occurrence(2).to_selection()
14270 );
14271}
14272
14273#[test]
14274fn remove_redundant_record_update_triggered_anywhere_on_the_expression() {
14275 assert_code_action!(
14276 REMOVE_REDUNDANT_RECORD_UPDATE,
14277 "
14278pub fn go(record: Wibble) {
14279 Wibble(..record, a: 1, b: 2)
14280}
14281
14282pub type Wibble { Wibble(a: Int, b: Int) }
14283",
14284 find_position_of("1").select_until(find_position_of("2"))
14285 );
14286}
14287
14288#[test]
14289fn remove_redundant_record_update_does_not_trigger_if_update_is_not_redundant() {
14290 assert_no_code_actions!(
14291 REMOVE_REDUNDANT_RECORD_UPDATE,
14292 "
14293pub fn go(record: Wibble) {
14294 Wibble(..record, a: 1)
14295}
14296
14297pub type Wibble { Wibble(a: Int, b: Int) }
14298",
14299 find_position_of("..record").to_selection()
14300 );
14301}
14302
14303#[test]
14304fn remove_redundant_constant_record_update_triggered_on_the_record_spread() {
14305 assert_code_action!(
14306 REMOVE_REDUNDANT_RECORD_UPDATE,
14307 "
14308pub const updated = Wibble(..base, a: 1, b: 3)
14309pub const base = Wibble(a: 1, b: 2)
14310pub type Wibble { Wibble(a: Int, b: Int) }
14311",
14312 find_position_of("..").to_selection()
14313 );
14314}
14315
14316#[test]
14317fn remove_redundant_constant_record_update_triggered_on_the_record() {
14318 assert_code_action!(
14319 REMOVE_REDUNDANT_RECORD_UPDATE,
14320 "
14321pub const updated = Wibble(..base, a: 1, b: 3)
14322pub const base = Wibble(a: 1, b: 2)
14323pub type Wibble { Wibble(a: Int, b: Int) }
14324",
14325 find_position_of("1").select_until(find_position_of("3"))
14326 );
14327}
14328
14329#[test]
14330fn remove_redundant_constant_record_update_does_not_trigger_if_update_i_not_redundant() {
14331 assert_no_code_actions!(
14332 REMOVE_REDUNDANT_RECORD_UPDATE,
14333 "
14334pub const updated = Wibble(..base, a: 1)
14335pub const base = Wibble(a: 1, b: 2)
14336pub type Wibble { Wibble(a: Int, b: Int) }
14337",
14338 find_position_of("base").to_selection()
14339 );
14340}