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