Fork of daniellemaywood.uk/gleam — Wasm codegen work
43 kB
2308 lines
1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2023 The Gleam contributors
3
4use lsp_types::{Contents, Hover, HoverParams, MarkedString, Position, Range};
5
6use super::*;
7
8fn hover(tester: TestProject<'_>, position: Position) -> Option<Hover> {
9 tester.at(position, |engine, param, _| {
10 let params = HoverParams {
11 text_document_position_params: param,
12 work_done_progress_params: Default::default(),
13 };
14 let response = engine.hover(params);
15
16 response.result.unwrap()
17 })
18}
19
20pub fn show_hover(code: &str, range: Range, position: Position) -> String {
21 let Range { start, end } = range;
22
23 // When we display the over range the end character is always excluded!
24 let end = Position::new(end.line, end.character);
25
26 let mut buffer: String = "".into();
27 for (line_number, line) in code.lines().enumerate() {
28 let mut underline: String = "".into();
29 let mut underline_empty = true;
30
31 for (column_number, _) in line.chars().enumerate() {
32 let current_position = Position::new(line_number as u32, column_number as u32);
33 if current_position == position {
34 underline_empty = false;
35 underline.push('↑');
36 } else if start <= current_position && current_position < end {
37 underline_empty = false;
38 underline.push('▔');
39 } else {
40 underline.push(' ');
41 }
42 }
43
44 buffer.push_str(line);
45 if !underline_empty {
46 buffer.push('\n');
47 buffer.push_str(&underline);
48 }
49 buffer.push('\n');
50 }
51
52 buffer
53}
54
55fn pretty_hover_contents(contents: Contents) -> String {
56 let (kind, content) = match contents {
57 Contents::MarkedString(marked_string) => ("markdown", pretty_marked_string(marked_string)),
58 Contents::MarkedStringList(marked_strings) => (
59 "markdown array",
60 marked_strings
61 .into_iter()
62 .map(pretty_marked_string)
63 .join("\n\n"),
64 ),
65 Contents::MarkupContent(lsp_types::MarkupContent { kind, value }) => match kind {
66 lsp_types::MarkupKind::PlainText => ("plaintext", value),
67 lsp_types::MarkupKind::Markdown => ("markdown", value),
68 },
69 };
70 format!("----- Hover content ({kind}) -----\n{content}")
71}
72
73fn pretty_marked_string(marked_string: MarkedString) -> String {
74 match marked_string {
75 MarkedString::String(string) => string,
76 #[allow(deprecated)]
77 MarkedString::MarkedStringWithLanguage(lsp_types::MarkedStringWithLanguage {
78 language,
79 value,
80 }) => {
81 format!("```{language}\n{value}\n```")
82 }
83 }
84}
85
86#[macro_export]
87macro_rules! assert_hover {
88 ($code:literal, $position:expr $(,)?) => {
89 let project = TestProject::for_source($code);
90 assert_hover!(project, $position);
91 };
92
93 ($project:expr, $position:expr $(,)?) => {
94 let src = $project.src;
95 let position = $position.find_position(src);
96 let result = hover($project, position).expect("no hover produced");
97 let pretty_hover = show_hover(src, result.range.expect("hover with no range"), position);
98 let output = format!(
99 "{}\n\n{}",
100 pretty_hover,
101 pretty_hover_contents(result.contents)
102 );
103 insta::assert_snapshot!(insta::internals::AutoName, output, src);
104 };
105}
106
107#[test]
108fn hover_function_definition() {
109 assert_hover!(
110 "
111fn add_2(x) {
112 x + 2
113}
114",
115 find_position_of("add_2")
116 );
117}
118
119#[test]
120fn hover_local_function() {
121 assert_hover!(
122 "
123fn my_fn() {
124 Nil
125}
126
127fn main() {
128 my_fn
129}
130",
131 find_position_of("my_fn").under_char('y').nth_occurrence(2)
132 );
133}
134
135// https://github.com/gleam-lang/gleam/issues/2654
136#[test]
137fn hover_local_function_in_pipe() {
138 assert_hover!(
139 "
140fn add1(num: Int) -> Int {
141 num + 1
142}
143
144pub fn main() {
145 add1(1)
146
147 1
148 |> add1
149 |> add1
150 |> add1
151}
152",
153 find_position_of("add1")
154 .with_char_offset(1)
155 .nth_occurrence(2)
156 );
157}
158
159// https://github.com/gleam-lang/gleam/issues/2654
160#[test]
161fn hover_local_function_in_pipe_1() {
162 assert_hover!(
163 "
164fn add1(num: Int) -> Int {
165 num + 1
166}
167
168pub fn main() {
169 add1(1)
170
171 1
172 |> add1
173 |> add1
174 |> add1
175}
176",
177 find_position_of("add1")
178 .with_char_offset(2)
179 .nth_occurrence(3)
180 );
181}
182
183// https://github.com/gleam-lang/gleam/issues/2654
184#[test]
185fn hover_local_function_in_pipe_2() {
186 assert_hover!(
187 "
188fn add1(num: Int) -> Int {
189 num + 1
190}
191
192pub fn main() {
193 add1(1)
194
195 1
196 |> add1
197 |> add1
198 |> add1
199}
200",
201 find_position_of("add1")
202 .with_char_offset(2)
203 .nth_occurrence(4)
204 );
205}
206
207// https://github.com/gleam-lang/gleam/issues/2654
208#[test]
209fn hover_local_function_in_pipe_3() {
210 assert_hover!(
211 "
212fn add1(num: Int) -> Int {
213 num + 1
214}
215
216pub fn main() {
217 add1(1)
218
219 1
220 |> add1
221 |> add1
222 |> add1
223}
224",
225 find_position_of("add1")
226 .with_char_offset(2)
227 .nth_occurrence(5)
228 );
229}
230
231#[test]
232fn hover_imported_function() {
233 let code = "
234import example_module
235fn main() {
236 example_module.my_fn
237}
238";
239
240 assert_hover!(
241 TestProject::for_source(code).add_module("example_module", "pub fn my_fn() { Nil }"),
242 find_position_of("my_fn").under_char('_'),
243 );
244}
245
246#[test]
247fn hover_external_imported_function() {
248 let code = "
249import example_module
250fn main() {
251 example_module.my_fn
252}
253";
254
255 assert_hover!(
256 TestProject::for_source(code).add_hex_module("example_module", "pub fn my_fn() { Nil }"),
257 find_position_of("my_fn").under_char('_'),
258 );
259}
260
261#[test]
262fn hover_external_imported_unqualified_function() {
263 let code = "
264import example_module.{my_fn}
265fn main() {
266 my_fn
267}
268";
269
270 assert_hover!(
271 TestProject::for_source(code).add_hex_module("example_module", "pub fn my_fn() { Nil }"),
272 find_position_of("my_fn").under_char('f').nth_occurrence(2),
273 );
274}
275
276#[test]
277fn hover_external_imported_function_renamed_module() {
278 let code = "
279import example_module as renamed_module
280fn main() {
281 renamed_module.my_fn
282}
283";
284
285 assert_hover!(
286 TestProject::for_source(code).add_hex_module("example_module", "pub fn my_fn() { Nil }"),
287 find_position_of("my_fn").under_char('f'),
288 );
289}
290
291#[test]
292fn hover_external_unqualified_imported_function_renamed_module() {
293 let code = "
294import example_module.{my_fn} as renamed_module
295fn main() {
296 my_fn
297}
298";
299
300 assert_hover!(
301 TestProject::for_source(code).add_hex_module("example_module", "pub fn my_fn() { Nil }"),
302 find_position_of("my_fn").under_char('_').nth_occurrence(2),
303 );
304}
305
306#[test]
307fn hover_external_imported_function_nested_module() {
308 // Example of HexDocs link with nested modules: https://lustre.hexdocs.pm/lustre/element/svg.html
309 let code = "
310import my/nested/example_module
311fn main() {
312 example_module.my_fn
313}
314";
315
316 assert_hover!(
317 TestProject::for_source(code)
318 .add_hex_module("my/nested/example_module", "pub fn my_fn() { Nil }"),
319 find_position_of("my_fn").under_char('f'),
320 );
321}
322
323#[test]
324fn hover_external_imported_ffi_renamed_function() {
325 let code = r#"
326import example_module
327fn main() {
328 example_module.my_fn
329}
330"#;
331
332 let hex_module = r#"
333@external(erlang, "my_mod_ffi", "renamed_fn")
334pub fn my_fn() -> Nil
335"#;
336
337 assert_hover!(
338 TestProject::for_source(code).add_hex_module("example_module", hex_module,),
339 find_position_of("my_fn").under_char('f'),
340 );
341}
342
343#[test]
344fn hover_external_imported_constants() {
345 let code = "
346import example_module
347fn main() {
348 example_module.my_const
349}
350";
351
352 assert_hover!(
353 TestProject::for_source(code).add_hex_module("example_module", "pub const my_const = 42"),
354 find_position_of("my_const").under_char('_'),
355 );
356}
357
358#[test]
359fn hover_external_imported_unqualified_constants() {
360 let code = "
361import example_module.{my_const}
362fn main() {
363 my_const
364}
365";
366
367 assert_hover!(
368 TestProject::for_source(code).add_hex_module("example_module", "pub const my_const = 42"),
369 find_position_of("my_const")
370 .under_char('c')
371 .nth_occurrence(2),
372 );
373}
374
375#[test]
376fn hover_external_value_with_two_modules_same_name() {
377 let code = "
378import a/example_module as _
379import b/example_module
380fn main() {
381 example_module.my_const
382}
383";
384
385 assert_hover!(
386 TestProject::for_source(code)
387 .add_hex_module("a/example_module", "pub const my_const = 42")
388 .add_hex_module("b/example_module", "pub const my_const = 42"),
389 find_position_of("my_const").under_char('c'),
390 );
391}
392
393#[test]
394fn hover_external_function_with_another_value_same_name() {
395 let code = "
396import a/example_module.{my_const as discarded}
397import b/example_module.{my_const} as _
398fn main() {
399 my_const
400}
401";
402
403 assert_hover!(
404 TestProject::for_source(code)
405 .add_hex_module("a/example_module", "pub const my_const = 42")
406 .add_hex_module("b/example_module", "pub const my_const = 42"),
407 find_position_of("my_const")
408 .under_char('o')
409 .nth_occurrence(3),
410 );
411}
412
413#[test]
414fn hover_function_definition_with_docs() {
415 assert_hover!(
416 "
417/// Exciting documentation
418/// Maybe even multiple lines
419fn append(x, y) {
420 x <> y
421}
422",
423 find_position_of("append")
424 );
425}
426
427#[test]
428fn hover_function_argument() {
429 assert_hover!(
430 "
431/// Exciting documentation
432/// Maybe even multiple lines
433fn append(x, y) {
434 x <> y
435}
436",
437 find_position_of("append(x, y)").under_char('x')
438 );
439}
440
441#[test]
442fn hover_function_body() {
443 let code = "
444/// Exciting documentation
445/// Maybe even multiple lines
446fn append(x, y) {
447 x <> y
448}
449";
450
451 assert_eq!(
452 hover(TestProject::for_source(code), Position::new(4, 1)),
453 None
454 );
455}
456
457#[test]
458fn hover_expressions_in_function_body() {
459 assert_hover!(
460 "
461fn append(x, y) {
462 x <> y
463}
464",
465 find_position_of("x").nth_occurrence(2)
466 );
467}
468
469#[test]
470fn hover_module_constant() {
471 assert_hover!(
472 "
473/// Exciting documentation
474/// Maybe even multiple lines
475const one = 1
476",
477 find_position_of("one")
478 );
479}
480
481#[test]
482fn hover_variable_in_use_expression() {
483 assert_hover!(
484 "
485fn b(fun: fn(Int) -> String) {
486 fun(42)
487}
488
489fn do_stuff() {
490 let c = \"done\"
491
492 use a <- b
493 c
494}
495",
496 find_position_of("use a").under_last_char()
497 );
498}
499
500#[test]
501fn hover_variable_in_use_expression_1() {
502 assert_hover!(
503 "
504fn b(fun: fn(Int) -> String) {
505 fun(42)
506}
507
508fn do_stuff() {
509 let c = \"done\"
510
511 use a <- b
512 c
513}
514",
515 find_position_of("b").nth_occurrence(2)
516 );
517}
518
519#[test]
520fn hover_variable_in_use_expression_2() {
521 assert_hover!(
522 "
523fn b(fun: fn(Int) -> String) {
524 fun(42)
525}
526
527fn do_stuff() {
528 let c = \"done\"
529
530 use a <- b
531 c
532}
533",
534 find_position_of("c").nth_occurrence(2)
535 );
536}
537
538#[test]
539fn hover_function_arg_annotation_2() {
540 assert_hover!(
541 "
542/// Exciting documentation
543/// Maybe even multiple lines
544fn append(x: String, y: String) -> String {
545 x <> y
546}
547",
548 find_position_of("String").under_char('n')
549 );
550}
551
552#[test]
553fn hover_function_return_annotation() {
554 assert_hover!(
555 "
556/// Exciting documentation
557/// Maybe even multiple lines
558fn append(x: String, y: String) -> String {
559 x <> y
560}
561",
562 find_position_of("String").under_char('n').nth_occurrence(3)
563 );
564}
565
566#[test]
567fn hover_function_return_annotation_with_tuple() {
568 assert_hover!(
569 "
570/// Exciting documentation
571/// Maybe even multiple lines
572fn append(x: String, y: String) -> #(String, String) {
573 #(x, y)
574}
575",
576 find_position_of("String").under_char('r').nth_occurrence(3)
577 );
578}
579
580#[test]
581fn hover_module_constant_annotation() {
582 assert_hover!(
583 "
584/// Exciting documentation
585/// Maybe even multiple lines
586const one: Int = 1
587",
588 find_position_of("Int").under_last_char()
589 );
590}
591
592#[test]
593fn hover_type_constructor_annotation() {
594 assert_hover!(
595 "
596type Wibble {
597 Wibble(arg: String)
598}
599",
600 find_position_of("String").under_char('n')
601 );
602}
603
604#[test]
605fn hover_type_alias_annotation() {
606 assert_hover!("type Wibble = Int", find_position_of("Int").under_char('n'));
607}
608
609#[test]
610fn hover_assignment_annotation() {
611 assert_hover!(
612 "
613fn wibble() {
614 let wobble: Int = 7
615 wobble
616}
617",
618 find_position_of("Int").under_last_char()
619 );
620}
621
622#[test]
623fn hover_function_arg_annotation_with_documentation() {
624 assert_hover!(
625 "
626/// Exciting documentation
627/// Maybe even multiple lines
628type Wibble {
629 Wibble(arg: String)
630}
631
632fn identity(x: Wibble) -> Wibble {
633 x
634}
635",
636 find_position_of("Wibble")
637 .under_last_char()
638 .nth_occurrence(3)
639 );
640}
641
642#[test]
643fn hover_import_unqualified_value() {
644 let code = "
645import example_module.{my_num}
646fn main() {
647 my_num
648}
649";
650
651 assert_hover!(
652 TestProject::for_source(code).add_module(
653 "example_module",
654 "
655/// Exciting documentation
656/// Maybe even multiple lines
657pub const my_num = 1"
658 ),
659 find_position_of("my_num").under_char('n')
660 );
661}
662
663#[test]
664fn hover_import_unqualified_value_from_hex() {
665 let code = "
666import example_module.{my_num}
667fn main() {
668 my_num
669}
670";
671
672 assert_hover!(
673 TestProject::for_source(code).add_hex_module(
674 "example_module",
675 "
676/// Exciting documentation
677/// Maybe even multiple lines
678pub const my_num = 1"
679 ),
680 find_position_of("my_num").under_char('n')
681 );
682}
683
684#[test]
685fn hover_import_unqualified_type() {
686 let code = "
687import example_module.{type MyType, MyType}
688fn main() -> MyType {
689 MyType
690}
691";
692
693 assert_hover!(
694 TestProject::for_source(code).add_module(
695 "example_module",
696 "
697/// Exciting documentation
698/// Maybe even multiple lines
699pub type MyType {
700 MyType
701}"
702 ),
703 find_position_of("MyType").under_last_char()
704 );
705}
706
707#[test]
708fn hover_works_even_for_invalid_code() {
709 assert_hover!(
710 "
711fn invalid() { 1 + Nil }
712fn valid() { Nil }
713",
714 find_position_of("fn valid").under_char('v')
715 );
716}
717
718#[test]
719fn hover_for_pattern_spread_ignoring_all_fields() {
720 assert_hover!(
721 "
722pub type Model {
723 Model(
724 Int,
725 Float,
726 label1: Int,
727 label2: String,
728 )
729}
730
731pub fn main() {
732 case todo {
733 Model(..) -> todo
734 }
735}
736",
737 find_position_of("..")
738 );
739}
740
741#[test]
742fn hover_for_pattern_spread_ignoring_some_fields() {
743 assert_hover!(
744 "
745pub type Model {
746 Model(
747 Int,
748 Float,
749 label1: Int,
750 label2: String,
751 )
752}
753
754pub fn main() {
755 case todo {
756 Model(_, label1: _, ..) -> todo
757 }
758}
759",
760 find_position_of("..").under_last_char()
761 );
762}
763
764#[test]
765fn hover_for_pattern_spread_ignoring_all_positional_fields() {
766 assert_hover!(
767 "
768pub type Model {
769 Model(
770 Int,
771 Float,
772 label1: Int,
773 label2: String,
774 )
775}
776
777pub fn main() {
778 case todo {
779 Model(_, _, _, ..) -> todo
780 }
781}
782",
783 find_position_of("..")
784 );
785}
786
787#[test]
788fn hover_label_shorthand_in_call_arg() {
789 assert_hover!(
790 "
791fn wibble(arg1 arg1: Int, arg2 arg2: Bool) { Nil }
792
793fn main() {
794 let arg1 = 1
795 let arg2 = True
796 wibble(arg2:, arg1:)
797}
798",
799 find_position_of("arg2:").nth_occurrence(2)
800 );
801}
802
803#[test]
804fn hover_label_shorthand_in_pattern_call_arg() {
805 assert_hover!(
806 "
807pub type Wibble { Wibble(arg1: Int, arg2: Bool) }
808
809pub fn main() {
810 case todo {
811 Wibble(arg2:, ..) -> todo
812 }
813}
814",
815 find_position_of("arg2:")
816 .nth_occurrence(2)
817 .under_last_char()
818 );
819}
820
821#[test]
822fn hover_label_shorthand_in_pattern_call_arg_2() {
823 assert_hover!(
824 "
825pub type Wibble { Wibble(arg1: Int, arg2: Bool) }
826
827pub fn main() {
828 let Wibble(arg2:, ..) = todo
829}
830",
831 find_position_of("arg2:").nth_occurrence(2).under_char('r')
832 );
833}
834
835#[test]
836fn hover_contextual_type() {
837 let code = "
838import wibble/wobble
839const value = wobble.Wobble
840";
841
842 assert_hover!(
843 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wobble }"),
844 find_position_of("value").under_char('v')
845 );
846}
847
848#[test]
849fn hover_contextual_type_aliased_module() {
850 let code = "
851import wibble/wobble as wubble
852const value = wubble.Wobble
853";
854
855 assert_hover!(
856 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wobble }"),
857 find_position_of("value").under_char('v')
858 );
859}
860
861#[test]
862fn hover_contextual_type_unqualified() {
863 let code = "
864import wibble/wobble.{type Wibble}
865const value = wobble.Wobble
866";
867
868 assert_hover!(
869 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wobble }"),
870 find_position_of("value").under_char('v')
871 );
872}
873
874#[test]
875fn hover_contextual_type_unqualified_aliased() {
876 let code = "
877import wibble/wobble.{type Wibble as Wobble}
878const value = wobble.Wobble
879";
880
881 assert_hover!(
882 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wobble }"),
883 find_position_of("value").under_char('v')
884 );
885}
886
887#[test]
888fn hover_contextual_type_aliased() {
889 let code = "
890import wibble/wobble
891type Local = wobble.Wibble
892const value = wobble.Wobble
893";
894
895 assert_hover!(
896 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wobble }"),
897 find_position_of("value").under_char('v')
898 );
899}
900
901#[test]
902fn hover_contextual_type_function() {
903 let code = "
904import wibble/wobble
905type MyInt = Int
906fn func(value: wobble.Wibble) -> MyInt { 1 }
907";
908
909 assert_hover!(
910 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wobble }"),
911 find_position_of("func").under_char('f')
912 );
913}
914
915#[test]
916fn hover_contextual_type_unqualified_import() {
917 let code = "
918import wibble/wobble.{type Wibble as Wobble, Wobble}
919";
920
921 assert_hover!(
922 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wobble }"),
923 find_position_of("Wobble}").under_char('W')
924 );
925}
926
927#[test]
928fn hover_contextual_type_pattern() {
929 let code = "
930import wibble/wobble.{Wibble, Wobble, Wubble}
931
932pub fn cycle(wibble: wobble.Wibble) {
933 case wibble {
934 Wibble -> Wobble
935 Wobble -> Wubble
936 Wubble -> Wibble
937 }
938}
939";
940
941 assert_hover!(
942 TestProject::for_source(code)
943 .add_hex_module("wibble/wobble", "pub type Wibble { Wibble Wobble Wubble }"),
944 find_position_of("Wubble ->").under_char('u')
945 );
946}
947
948#[test]
949fn hover_contextual_type_pattern_spread() {
950 let code = "
951import wibble/wobble.{type Wibble as Wobble}
952
953type Thing {
954 Thing(id: Int, value: Wobble)
955}
956
957pub fn main(thing: Thing) {
958 case thing {
959 Thing(id: 0, ..) -> 12
960 _ -> 14
961 }
962}
963";
964
965 assert_hover!(
966 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wibble }"),
967 find_position_of("..").under_char('.')
968 );
969}
970
971#[test]
972fn hover_contextual_type_expression() {
973 let code = "
974import wibble/wobble
975
976pub fn main() {
977 let wibble = wobble.Wibble
978}
979";
980
981 assert_hover!(
982 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wibble }"),
983 find_position_of(".Wibble").under_char('l')
984 );
985}
986
987#[test]
988fn hover_contextual_type_arg() {
989 let code = "
990import wibble/wobble
991
992fn do_things(wibble: wobble.Wibble) { wibble }
993";
994
995 assert_hover!(
996 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wibble }"),
997 find_position_of("wibble:").under_char('w')
998 );
999}
1000
1001#[test]
1002fn hover_print_type_variable_names() {
1003 let code = "
1004fn main(value: Result(ok, error)) {
1005 let v = value
1006 v
1007}
1008";
1009
1010 assert_hover!(
1011 TestProject::for_source(code),
1012 find_position_of("let v").under_char('v')
1013 );
1014}
1015
1016#[test]
1017fn hover_print_unbound_type_variable_names() {
1018 let code = "
1019fn make_ok(value: some_type) {
1020 let result = Ok(value)
1021 result
1022}
1023";
1024
1025 assert_hover!(
1026 TestProject::for_source(code),
1027 find_position_of("result =").under_char('s')
1028 );
1029}
1030
1031#[test]
1032fn hover_print_unbound_type_variable_name_without_conflicts() {
1033 let code = "
1034fn make_ok(value: a) {
1035 let result = Ok(value)
1036 result
1037}
1038";
1039
1040 assert_hover!(
1041 TestProject::for_source(code),
1042 find_position_of("result =").under_char('s')
1043 );
1044}
1045
1046#[test]
1047fn hover_print_imported_alias() {
1048 let code = "
1049import aliases.{type Aliased}
1050const thing: Aliased = 10
1051";
1052
1053 assert_hover!(
1054 TestProject::for_source(code).add_hex_module("aliases", "pub type Aliased = Int"),
1055 find_position_of("thing").under_char('g')
1056 );
1057}
1058
1059#[test]
1060fn hover_prelude_type() {
1061 let code = "
1062const number = 100
1063";
1064
1065 assert_hover!(
1066 TestProject::for_source(code),
1067 find_position_of("number").under_char('b')
1068 );
1069}
1070
1071#[test]
1072fn hover_shadowed_prelude_type() {
1073 let code = "
1074type Int { Int }
1075const number = 100
1076";
1077
1078 assert_hover!(
1079 TestProject::for_source(code),
1080 find_position_of("number").under_char('b')
1081 );
1082}
1083
1084#[test]
1085fn hover_shadowed_prelude_type_imported() {
1086 let code = "
1087import numbers.{type Int}
1088const number = 100
1089";
1090
1091 assert_hover!(
1092 TestProject::for_source(code).add_hex_module("numbers", "pub type Int"),
1093 find_position_of("number =").under_char('b')
1094 );
1095}
1096
1097#[test]
1098fn hover_contextual_type_annotation() {
1099 let code = "
1100import wibble/wobble
1101
1102fn make_wibble() -> wobble.Wibble { wobble.Wibble }
1103";
1104
1105 assert_hover!(
1106 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wibble }"),
1107 find_position_of("-> wobble.Wibble").under_char('i')
1108 );
1109}
1110
1111#[test]
1112fn hover_contextual_type_annotation_prelude() {
1113 let code = "
1114fn add_one(a: Int) -> Int {
1115 a + 1
1116}
1117";
1118
1119 assert_hover!(
1120 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wibble }"),
1121 find_position_of("-> Int").under_char('I')
1122 );
1123}
1124
1125#[test]
1126fn hover_contextual_type_annotation_unqualified() {
1127 let code = "
1128import wibble/wobble.{type Wibble}
1129
1130fn main(wibble: Wibble) {
1131 wibble
1132}
1133";
1134
1135 assert_hover!(
1136 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wibble }"),
1137 find_position_of(": Wibble").under_char('W')
1138 );
1139}
1140
1141#[test]
1142fn hover_contextual_type_annotation_unqualified_aliased() {
1143 let code = "
1144import wibble/wobble.{type Wibble as Wubble}
1145
1146fn main(wibble: Wubble) {
1147 wibble
1148}
1149";
1150
1151 assert_hover!(
1152 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wibble }"),
1153 find_position_of(": Wubble").under_char('W')
1154 );
1155}
1156
1157#[test]
1158fn hover_contextual_type_annotation_aliased_module() {
1159 let code = "
1160import wibble/wobble as wubble
1161
1162fn main(wibble: wubble.Wibble) {
1163 wibble
1164}
1165";
1166
1167 assert_hover!(
1168 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wibble }"),
1169 find_position_of(": wubble.Wibble").under_char('W')
1170 );
1171}
1172
1173#[test]
1174fn hover_contextual_type_annotation_aliased() {
1175 let code = "
1176import wibble/wobble
1177
1178type Wubble = wobble.Wibble
1179
1180fn main(wibble: Wubble) {
1181 wibble
1182}
1183";
1184
1185 assert_hover!(
1186 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wibble }"),
1187 find_position_of(": Wubble").under_char('e')
1188 );
1189}
1190
1191#[test]
1192fn hover_print_underlying_for_alias_with_parameters() {
1193 let code = "
1194type LocalResult = Result(String, Int)
1195
1196fn do_thing() -> LocalResult {
1197 Error(1)
1198}
1199";
1200
1201 assert_hover!(
1202 TestProject::for_source(code),
1203 find_position_of("do_thing").under_char('d')
1204 );
1205}
1206
1207#[test]
1208fn hover_print_alias_when_parameters_match() {
1209 let code = "
1210type MyResult(a, b) = Result(a, b)
1211
1212fn do_thing() -> MyResult(Int, Int) {
1213 Error(1)
1214}
1215";
1216
1217 assert_hover!(
1218 TestProject::for_source(code),
1219 find_position_of("do_thing").under_char('d')
1220 );
1221}
1222
1223#[test]
1224fn hover_print_underlying_for_imported_alias() {
1225 let code = "
1226import alias.{type A}
1227
1228fn wibble() -> Result(Int, String) {
1229 todo
1230}
1231";
1232
1233 assert_hover!(
1234 TestProject::for_source(code).add_hex_module("alias", "pub type A = Result(Int, String)"),
1235 find_position_of("wibble").under_char('l')
1236 );
1237}
1238
1239#[test]
1240fn hover_print_aliased_imported_generic_type() {
1241 let code = "
1242import gleam/option.{type Option as Maybe}
1243
1244const none: Maybe(Int) = option.None
1245";
1246
1247 assert_hover!(
1248 TestProject::for_source(code)
1249 .add_hex_module("gleam/option", "pub type Option(a) { None Some(a) }"),
1250 find_position_of("none").under_char('e')
1251 );
1252}
1253
1254#[test]
1255fn hover_print_qualified_prelude_type_when_shadowed_by_alias() {
1256 let code = "
1257type Result = #(Bool, String)
1258const ok = Ok(10)
1259";
1260
1261 assert_hover!(
1262 TestProject::for_source(code),
1263 find_position_of("ok").under_char('k')
1264 );
1265}
1266
1267#[test]
1268fn hover_print_qualified_prelude_type_when_shadowed_by_imported_alias() {
1269 let code = "
1270import alias.{type Bool}
1271const value = True
1272";
1273
1274 assert_hover!(
1275 TestProject::for_source(code).add_hex_module("alias", "pub type Bool = #(Int, Int)"),
1276 find_position_of("value").under_char('v')
1277 );
1278}
1279
1280// https://github.com/gleam-lang/gleam/issues/3761
1281#[test]
1282fn hover_over_block_in_list_spread() {
1283 let code = "
1284pub fn main() {
1285 [1, 2, ..{
1286 let x = 1
1287 [x]
1288 }]
1289}
1290";
1291
1292 assert_hover!(TestProject::for_source(code), find_position_of("x"));
1293}
1294
1295// https://github.com/gleam-lang/gleam/issues/3758
1296#[test]
1297fn hover_for_anonymous_function_annotation() {
1298 let code = "
1299/// An example type.
1300pub type Wibble
1301
1302pub fn main() {
1303 fn(w: Wibble) { todo }
1304}
1305";
1306
1307 assert_hover!(
1308 TestProject::for_source(code),
1309 find_position_of("w: Wibble").under_char('b')
1310 );
1311}
1312
1313#[test]
1314fn hover_for_label_in_pattern() {
1315 let code = "
1316type Wibble {
1317 Wibble(wibble: Int, wobble: Int)
1318}
1319
1320pub fn main() {
1321 let Wibble(wibble: _, wobble: _) = todo
1322 todo
1323}
1324";
1325
1326 assert_hover!(
1327 TestProject::for_source(code),
1328 find_position_of("wibble: _").under_char('l')
1329 );
1330}
1331
1332#[test]
1333fn hover_for_label_in_expression() {
1334 let code = "
1335fn add(wibble a, wobble b) {
1336 a + b
1337}
1338
1339pub fn main() {
1340 add(wibble: 1, wobble: 2)
1341}
1342";
1343
1344 assert_hover!(
1345 TestProject::for_source(code),
1346 find_position_of("wibble:").under_char('i')
1347 );
1348}
1349
1350#[test]
1351fn hover_for_pattern_in_use() {
1352 let code = "
1353type Wibble {
1354 Wibble(Int, Float)
1355}
1356
1357pub fn main() {
1358 use Wibble(int, float) <- todo
1359 todo
1360}
1361";
1362
1363 assert_hover!(
1364 TestProject::for_source(code),
1365 find_position_of("int").under_char('i')
1366 );
1367}
1368
1369#[test]
1370fn hover_for_annotation_in_use() {
1371 let code = "
1372pub fn main() {
1373 use something: Int <- todo
1374 todo
1375}
1376";
1377
1378 assert_hover!(
1379 TestProject::for_source(code),
1380 find_position_of("Int").under_char('n')
1381 );
1382}
1383
1384#[test]
1385fn hover_on_pipe_with_invalid_step() {
1386 assert_hover!(
1387 "
1388pub fn main() {
1389 [1, 2, 3]
1390 |> map(wibble)
1391 |> filter(fn(value) { value })
1392}
1393
1394fn map(list: List(a), fun: fn(a) -> b) -> List(b) {}
1395fn filter(list: List(a), fun: fn(a) -> Bool) -> List(a) {}
1396",
1397 find_position_of("[")
1398 );
1399}
1400
1401#[test]
1402fn hover_on_pipe_with_invalid_step_1() {
1403 assert_hover!(
1404 "
1405pub fn main() {
1406 [1, 2, 3]
1407 |> map(wibble)
1408 |> filter(fn(value) { value })
1409}
1410
1411fn map(list: List(a), fun: fn(a) -> b) -> List(b) {}
1412fn filter(list: List(a), fun: fn(a) -> Bool) -> List(a) {}
1413",
1414 find_position_of("1")
1415 );
1416}
1417
1418#[test]
1419fn hover_on_pipe_with_invalid_step_2() {
1420 assert_hover!(
1421 "
1422pub fn main() {
1423 [1, 2, 3]
1424 |> map(wibble)
1425 |> filter(fn(value) { value })
1426}
1427
1428fn map(list: List(a), fun: fn(a) -> b) -> List(b) {}
1429fn filter(list: List(a), fun: fn(a) -> Bool) -> List(a) {}
1430",
1431 find_position_of("map")
1432 );
1433}
1434
1435#[test]
1436fn hover_on_pipe_with_invalid_step_3() {
1437 assert_hover!(
1438 "
1439pub fn main() {
1440 [1, 2, 3]
1441 |> map(wibble)
1442 |> filter(fn(value) { value })
1443}
1444
1445fn map(list: List(a), fun: fn(a) -> b) -> List(b) {}
1446fn filter(list: List(a), fun: fn(a) -> Bool) -> List(a) {}
1447",
1448 find_position_of("wibble")
1449 );
1450}
1451
1452#[test]
1453fn hover_on_pipe_with_invalid_step_4() {
1454 assert_hover!(
1455 "
1456pub fn main() {
1457 [1, 2, 3]
1458 |> map(wibble)
1459 |> filter(fn(value) { value })
1460}
1461
1462fn map(list: List(a), fun: fn(a) -> b) -> List(b) {}
1463fn filter(list: List(a), fun: fn(a) -> Bool) -> List(a) {}
1464",
1465 find_position_of("filter")
1466 );
1467}
1468
1469#[test]
1470fn hover_on_pipe_with_invalid_step_5() {
1471 assert_hover!(
1472 "
1473pub fn main() {
1474 [1, 2, 3]
1475 |> map(wibble)
1476 |> filter(fn(value) { value })
1477}
1478
1479fn map(list: List(a), fun: fn(a) -> b) -> List(b) { todo }
1480fn filter(list: List(a), fun: fn(a) -> Bool) -> List(a) { todo }
1481",
1482 find_position_of("fn(value)")
1483 );
1484}
1485
1486#[test]
1487fn hover_on_pipe_with_invalid_step_6() {
1488 assert_hover!(
1489 "
1490pub fn main() {
1491 [1, 2, 3]
1492 |> wibble
1493 |> filter(fn(value) { value })
1494}
1495
1496fn filter(list: List(a), fun: fn(a) -> Bool) -> List(a) { todo }
1497",
1498 find_position_of("wibble")
1499 );
1500}
1501
1502#[test]
1503fn hover_on_pipe_with_invalid_step_8() {
1504 assert_hover!(
1505 "
1506pub fn main() {
1507 [1, 2, 3]
1508 |> wibble
1509 |> filter(fn(value) { value })
1510}
1511
1512fn filter(list: List(a), fun: fn(a) -> Bool) -> List(a) { todo }
1513",
1514 find_position_of("fn(value)")
1515 );
1516}
1517
1518#[test]
1519fn hover_over_module_name() {
1520 let src = "
1521import wibble
1522
1523pub fn main() {
1524 wibble.wibble()
1525}
1526";
1527 assert_hover!(
1528 TestProject::for_source(src).add_hex_module(
1529 "wibble",
1530 "
1531//// This is the wibble module.
1532//// Here is some documentation about it.
1533//// This module does stuff
1534
1535pub fn wibble() {
1536 todo
1537}
1538"
1539 ),
1540 find_position_of("wibble.")
1541 );
1542}
1543
1544#[test]
1545fn hover_over_module_with_path() {
1546 let src = "
1547import wibble/wobble
1548
1549pub fn main() {
1550 wobble.wibble()
1551}
1552";
1553 assert_hover!(
1554 TestProject::for_source(src).add_hex_module(
1555 "wibble/wobble",
1556 "
1557//// The module documentation
1558
1559pub fn wibble() {
1560 todo
1561}
1562"
1563 ),
1564 find_position_of("wobble.")
1565 );
1566}
1567
1568#[test]
1569fn hover_over_module_name_in_annotation() {
1570 let src = "
1571import wibble
1572
1573pub fn main(w: wibble.Wibble) {
1574 todo
1575}
1576";
1577 assert_hover!(
1578 TestProject::for_source(src).add_hex_module(
1579 "wibble",
1580 "
1581//// This is the wibble module.
1582//// Here is some documentation about it.
1583//// This module does stuff
1584
1585pub type Wibble
1586"
1587 ),
1588 find_position_of("wibble.")
1589 );
1590}
1591
1592#[test]
1593fn hover_over_imported_module() {
1594 let src = "
1595import wibble
1596";
1597 assert_hover!(
1598 TestProject::for_source(src).add_hex_module(
1599 "wibble",
1600 "
1601//// This is the wibble module.
1602//// Here is some documentation about it.
1603//// This module does stuff
1604"
1605 ),
1606 find_position_of("wibble")
1607 );
1608}
1609
1610#[test]
1611fn no_hexdocs_link_when_hovering_over_local_module() {
1612 let src = "
1613import wibble
1614";
1615 assert_hover!(
1616 TestProject::for_source(src).add_module(
1617 "wibble",
1618 "
1619//// This is the wibble module.
1620//// Here is some documentation about it.
1621//// This module does stuff
1622"
1623 ),
1624 find_position_of("wibble")
1625 );
1626}
1627
1628#[test]
1629fn hover_for_constant_int() {
1630 assert_hover!(
1631 "
1632const ten = 10
1633",
1634 find_position_of("10")
1635 );
1636}
1637
1638#[test]
1639fn hover_for_constant_float() {
1640 assert_hover!(
1641 "
1642const pi = 3.14
1643",
1644 find_position_of("3.14")
1645 );
1646}
1647#[test]
1648fn hover_for_constant_string() {
1649 assert_hover!(
1650 r#"
1651const message = "Hello!"
1652"#,
1653 find_position_of("!")
1654 );
1655}
1656
1657#[test]
1658fn hover_for_constant_other_constant() {
1659 assert_hover!(
1660 "
1661const constant1 = 10
1662const constant2 = constant1
1663",
1664 find_position_of("= constant1").under_char('s')
1665 );
1666}
1667
1668#[test]
1669fn hover_for_constant_record() {
1670 assert_hover!(
1671 "
1672type Wibble {
1673 Wibble(Int)
1674}
1675
1676const w = Wibble(10)
1677",
1678 find_position_of("Wibble(10)").under_char('i')
1679 );
1680}
1681
1682#[test]
1683fn hover_for_constant_tuple() {
1684 assert_hover!(
1685 "
1686const tuple = #(1, 3.5, False)
1687",
1688 find_position_of("#(")
1689 );
1690}
1691
1692#[test]
1693fn hover_for_constant_tuple_element() {
1694 assert_hover!(
1695 "
1696const tuple = #(1, 3.5, False)
1697",
1698 find_position_of("False")
1699 );
1700}
1701
1702#[test]
1703fn hover_for_constant_list() {
1704 assert_hover!(
1705 "
1706const numbers = [2, 4, 6, 8]
1707",
1708 find_position_of("[")
1709 );
1710}
1711
1712#[test]
1713fn hover_for_constant_list_element() {
1714 assert_hover!(
1715 "
1716const numbers = [2, 4, 6, 8]
1717",
1718 find_position_of("4")
1719 );
1720}
1721
1722#[test]
1723fn hover_for_constant_string_concatenation() {
1724 assert_hover!(
1725 r#"
1726const name = "Bob"
1727const message = "Hello " <> name
1728"#,
1729 find_position_of("<>")
1730 );
1731}
1732
1733#[test]
1734fn hover_for_constant_string_concatenation_side() {
1735 assert_hover!(
1736 r#"
1737const name = "Bob"
1738const message = "Hello " <> name
1739"#,
1740 find_position_of("<> name").under_char('n')
1741 );
1742}
1743
1744#[test]
1745fn hover_for_constant_bit_array() {
1746 assert_hover!(
1747 "
1748const bits = <<1:2, 3:4>>
1749",
1750 find_position_of(",")
1751 );
1752}
1753
1754#[test]
1755fn hover_for_constant_bit_array_segment() {
1756 assert_hover!(
1757 "
1758const bits = <<1:2, 3:4>>
1759",
1760 find_position_of("1")
1761 );
1762}
1763
1764#[test]
1765fn hover_for_constant_bit_array_segment_option() {
1766 assert_hover!(
1767 "
1768const bits = <<1:size(2), 3:4>>
1769",
1770 find_position_of("2")
1771 );
1772}
1773
1774#[test]
1775fn hover_for_nested_constant() {
1776 assert_hover!(
1777 "
1778type Wibble {
1779 Wibble
1780 Wobble(BitArray)
1781}
1782
1783const value = #(1, 2, [Wibble, Wobble(<<1, 2, 3>>), Wibble])
1784",
1785 find_position_of("3")
1786 );
1787}
1788
1789#[test]
1790fn record_field_documentation() {
1791 assert_hover!(
1792 "
1793pub type Wibble {
1794 Wibble(
1795 /// This is some documentation about the wibble field.
1796 wibble: Int
1797 )
1798}
1799
1800pub fn wibble(w: Wibble) {
1801 w.wibble
1802}
1803",
1804 find_position_of("w.wibble").under_char('l')
1805 );
1806}
1807
1808#[test]
1809fn no_documentation_for_shared_record_field() {
1810 assert_hover!(
1811 "
1812pub type Wibble {
1813 Wibble(
1814 /// This is some documentation about the wibble field.
1815 wibble: Int
1816 )
1817 Wobble(
1818 /// Here's some documentation explaining a field of Wobble
1819 wibble: Int
1820 )
1821}
1822
1823pub fn wibble(w: Wibble) {
1824 w.wibble
1825}
1826",
1827 find_position_of("w.wibble").under_char('l')
1828 );
1829}
1830
1831#[test]
1832fn documentation_for_shared_record_field_when_variant_is_known() {
1833 assert_hover!(
1834 "
1835pub type Wibble {
1836 Wibble(
1837 /// This is some documentation about the wibble field.
1838 wibble: Int
1839 )
1840 Wobble(
1841 /// This won't show up because it's a Wibble variant
1842 wibble: Int
1843 )
1844}
1845
1846pub fn wibble(w: Wibble) {
1847 let assert Wibble(..) = w
1848 w.wibble
1849}
1850",
1851 find_position_of("w.wibble").under_char('l')
1852 );
1853}
1854
1855#[test]
1856fn hover_for_string_prefix_pattern() {
1857 assert_hover!(
1858 "
1859pub fn main() {
1860 case \"wibble\" {
1861 \"wib\" as alias <> suffix -> alias <> suffix
1862 other -> other
1863 }
1864}
1865",
1866 find_position_of("<>")
1867 );
1868}
1869
1870#[test]
1871fn hover_for_string_prefix_pattern_prefix_alias() {
1872 assert_hover!(
1873 "
1874pub fn main() {
1875 case \"wibble\" {
1876 \"wib\" as alias <> suffix -> alias <> suffix
1877 other -> other
1878 }
1879}
1880",
1881 find_position_of("alias").nth_occurrence(1)
1882 );
1883}
1884
1885#[test]
1886fn hover_for_string_prefix_pattern_suffix_variable() {
1887 assert_hover!(
1888 "
1889pub fn main() {
1890 case \"wibble\" {
1891 \"wib\" as alias <> suffix -> alias <> suffix
1892 other -> other
1893 }
1894}
1895",
1896 find_position_of("suffix").nth_occurrence(1)
1897 );
1898}
1899
1900#[test]
1901fn hover_for_string_prefix_pattern_prefix_alias_alternative_definition() {
1902 assert_hover!(
1903 "
1904pub fn main() {
1905 case \"wibble\" {
1906 \"wib\" as prefix <> rest | \"wob\" as prefix <> rest -> prefix <> rest
1907 other -> other
1908 }
1909}
1910",
1911 find_position_of("prefix").nth_occurrence(2)
1912 );
1913}
1914
1915#[test]
1916fn hover_for_string_prefix_pattern_suffix_variable_alternative_definition() {
1917 assert_hover!(
1918 "
1919pub fn main() {
1920 case \"wibble\" {
1921 \"wib\" <> rest | \"wob\" <> rest -> rest
1922 other -> other
1923 }
1924}
1925",
1926 find_position_of("rest").nth_occurrence(2)
1927 );
1928}
1929
1930#[test]
1931fn hover_for_string_prefix_pattern_suffix_variable_discard() {
1932 assert_hover!(
1933 "
1934pub fn main() {
1935 case \"wibble\" {
1936 \"wib\" <> _discard -> Nil
1937 other -> Nil
1938 }
1939}
1940",
1941 find_position_of("_discard")
1942 );
1943}
1944
1945#[test]
1946fn hover_for_custom_type() {
1947 assert_hover!(
1948 "/// Exciting documentation
1949/// Maybe even multiple lines
1950type Wibble {
1951 /// Some more exciting documentation
1952 Wibble(String)
1953 /// The most exciting documentation
1954 Wobble(arg: Int)
1955}",
1956 find_position_of("Wibble")
1957 );
1958}
1959
1960#[test]
1961fn hover_type_constructor_with_no_fields() {
1962 assert_hover!(
1963 "
1964/// Exciting documentation
1965/// Maybe even multiple lines
1966type Wibble {
1967 /// Some more exciting documentation
1968 Wibble
1969}
1970",
1971 find_position_of("Wibble").nth_occurrence(2)
1972 );
1973}
1974
1975#[test]
1976fn hover_type_constructor_with_label() {
1977 assert_hover!(
1978 "
1979/// Exciting documentation
1980/// Maybe even multiple lines
1981type Wibble {
1982 /// Some more exciting documentation
1983 Wibble(arg: String)
1984 /// The most exciting documentation
1985 Wobble(Int)
1986}
1987",
1988 find_position_of("Wibble").nth_occurrence(2)
1989 );
1990}
1991
1992#[test]
1993fn hover_type_constructor_with_no_label() {
1994 assert_hover!(
1995 "
1996/// Exciting documentation
1997/// Maybe even multiple lines
1998type Wibble {
1999 /// Some more exciting documentation
2000 Wibble(arg: String)
2001 /// The most exciting documentation
2002 Wobble(Int)
2003}
2004",
2005 find_position_of("Wobble")
2006 );
2007}
2008
2009#[test]
2010fn hover_for_local_variable_from_guard() {
2011 assert_hover!(
2012 "
2013pub fn main() {
2014 let wibble = True
2015 let wobble = False
2016 case wibble {
2017 True if wobble -> !wibble
2018 False if !wobble -> wibble
2019 _ -> wobble
2020 }
2021}
2022",
2023 find_position_of("wobble").nth_occurrence(2).under_char('o')
2024 );
2025}
2026
2027#[test]
2028fn hover_for_record_from_guard() {
2029 assert_hover!(
2030 "
2031type Wibble {
2032 Wibble
2033 Wobble
2034}
2035
2036pub fn main() {
2037 let wibble = True
2038 let wobble = Wibble
2039 case wibble {
2040 True if wobble == Wibble -> !wibble
2041 False if wobble == Wobble -> wibble
2042 _ -> Wibble
2043 }
2044}
2045",
2046 find_position_of("== Wibble").under_char('l')
2047 );
2048}
2049
2050#[test]
2051fn hover_for_module_select_from_guard() {
2052 let src = "
2053import mod
2054
2055pub fn main() {
2056 let wibble = True
2057 case wibble {
2058 True if mod.wibble < 5 -> !wibble
2059 False if mod.wibble != 10 -> wibble
2060 _ -> mod.wibble + 1
2061 }
2062}
2063";
2064 assert_hover!(
2065 TestProject::for_source(src).add_module("mod", "pub const wibble = 10"),
2066 find_position_of("mod.wibble").under_char('w')
2067 );
2068}
2069
2070#[test]
2071fn hover_for_record_module_select_from_guard() {
2072 let src = "
2073import mod
2074
2075pub fn main() {
2076 let wibble = True
2077 let wobble = mod.Wibble
2078 case wibble {
2079 True if wobble == mod.Wobble -> !wibble
2080 False if wobble == mod.Wibble -> wibble
2081 _ -> mod.Wibble
2082 }
2083}
2084";
2085 assert_hover!(
2086 TestProject::for_source(src).add_module("mod", "pub type Wobble { Wibble Wobble }"),
2087 find_position_of("== mod.Wibble").under_char('i')
2088 );
2089}
2090
2091#[test]
2092fn hover_for_module_select_pattern() {
2093 let src = "
2094import mod
2095pub fn go(x: mod.Wibble) {
2096 case x {
2097 mod.Wibble -> 1
2098 }
2099}
2100";
2101 assert_hover!(
2102 TestProject::for_source(src).add_module("mod", "pub type Wibble { Wibble }"),
2103 find_position_of("mod.Wibble")
2104 .under_char('W')
2105 .nth_occurrence(2)
2106 );
2107}
2108
2109#[test]
2110fn hover_on_record_dot_of_record_update_shows_ignored_fields() {
2111 assert_hover!(
2112 "
2113type Wibble {
2114 Wibble(a: Int, b: Bool)
2115}
2116
2117pub fn go(wibble: Wibble) {
2118 Wibble(..wibble, a: 1)
2119}
2120",
2121 find_position_of("..")
2122 );
2123}
2124
2125#[test]
2126fn hover_on_record_being_updated_shows_ignored_fields() {
2127 assert_hover!(
2128 "
2129type Wibble {
2130 Wibble(a: Int, b: Bool)
2131}
2132
2133pub fn go(wibble: Wibble) {
2134 Wibble(..wibble, a: 1)
2135}
2136",
2137 find_position_of("..wibble").under_last_char()
2138 );
2139}
2140
2141#[test]
2142fn hover_for_invalid_record_update_1() {
2143 assert_hover!(
2144 "
2145type Wibble {
2146 Wibble(a: Int, b: Bool)
2147}
2148
2149pub fn go(wibble: Wibble) {
2150 Wibble(..wibble, a: True, b: 1)
2151}
2152",
2153 find_position_of("True")
2154 );
2155}
2156
2157#[test]
2158fn hover_for_invalid_record_update_2() {
2159 assert_hover!(
2160 "
2161type Wibble {
2162 Wibble(a: Int, b: Bool)
2163}
2164
2165pub fn go(wibble: Wibble) {
2166 Wibble(..wibble, a: True, b: 1)
2167}
2168",
2169 find_position_of("1")
2170 );
2171}
2172
2173#[test]
2174fn hover_for_invalid_record_update_3() {
2175 assert_hover!(
2176 "
2177type Wibble {
2178 Wibble(a: Int, b: Bool)
2179}
2180
2181pub fn go(wibble: Wibble) {
2182 Wibble(..wibble, a: True, b: 1)
2183}
2184",
2185 find_position_of("Wibble").nth_occurrence(4)
2186 );
2187}
2188
2189#[test]
2190fn hover_on_constant_todo() {
2191 assert_hover!(
2192 "
2193const wibble = todo
2194",
2195 find_position_of("wibble")
2196 );
2197}
2198
2199#[test]
2200fn hover_on_constant_todo_1() {
2201 assert_hover!(
2202 "
2203const wibble = todo
2204",
2205 find_position_of("todo")
2206 );
2207}
2208
2209#[test]
2210fn hover_on_constant_todo_in_list() {
2211 assert_hover!(
2212 "
2213const wibble = [todo, 1]
2214",
2215 find_position_of("wibble")
2216 );
2217}
2218
2219#[test]
2220fn hover_on_constant_todo_in_list_2() {
2221 assert_hover!(
2222 "
2223const wibble = [todo, 1]
2224",
2225 find_position_of("todo")
2226 );
2227}
2228
2229#[test]
2230fn hover_on_constant_todo_in_bit_array() {
2231 assert_hover!(
2232 "
2233const wibble = <<todo:utf8>>
2234",
2235 find_position_of("todo")
2236 );
2237}
2238
2239#[test]
2240fn hover_on_constant_todo_in_bit_array_2() {
2241 assert_hover!(
2242 "
2243const wibble = <<todo>>
2244",
2245 find_position_of("todo")
2246 );
2247}
2248
2249#[test]
2250fn hover_on_constant_todo_in_constructor() {
2251 assert_hover!(
2252 "
2253const wibble = Ok(todo)
2254",
2255 find_position_of("wibble")
2256 );
2257}
2258
2259#[test]
2260fn hover_on_constant_todo_in_annotated_constructor() {
2261 assert_hover!(
2262 "
2263const wibble: Result(String, Int) = Ok(todo)
2264",
2265 find_position_of("todo")
2266 );
2267}
2268
2269#[test]
2270fn hover_on_constant_todo_in_constructor_2() {
2271 assert_hover!(
2272 "
2273const wibble = Ok(todo)
2274",
2275 find_position_of("todo")
2276 );
2277}
2278
2279#[test]
2280fn hover_on_constant_annotated_todo() {
2281 assert_hover!(
2282 "
2283const wibble: String = todo
2284",
2285 find_position_of("todo")
2286 );
2287}
2288
2289#[test]
2290fn hover_on_constant_todo_message() {
2291 assert_hover!(
2292 r#"
2293const wibble = todo as "wobble"
2294"#,
2295 find_position_of("wobble")
2296 );
2297}
2298
2299#[test]
2300fn hover_on_constant_todo_invalid_message_still_works() {
2301 assert_hover!(
2302 r#"
2303const wobble = 1
2304const wibble = todo as [wobble]
2305"#,
2306 find_position_of("wobble").nth_occurrence(2)
2307 );
2308}