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