⭐️ A friendly language for building type-safe, scalable systems!
at main 1752 lines 33 kB view raw
1use lsp_types::{Hover, HoverParams, 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.le(&current_position) && current_position.lt(&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 52#[macro_export] 53macro_rules! assert_hover { 54 ($code:literal, $position:expr $(,)?) => { 55 let project = TestProject::for_source($code); 56 assert_hover!(project, $position); 57 }; 58 59 ($project:expr, $position:expr $(,)?) => { 60 let src = $project.src; 61 let position = $position.find_position(src); 62 let result = hover($project, position).expect("no hover produced"); 63 let pretty_hover = show_hover(src, result.range.expect("hover with no range"), position); 64 let output = format!( 65 "{}\n\n----- Hover content -----\n{:#?}", 66 pretty_hover, result.contents 67 ); 68 insta::assert_snapshot!(insta::internals::AutoName, output, src); 69 }; 70} 71 72#[test] 73fn hover_function_definition() { 74 assert_hover!( 75 " 76fn add_2(x) { 77 x + 2 78} 79", 80 find_position_of("add_2") 81 ); 82} 83 84#[test] 85fn hover_local_function() { 86 assert_hover!( 87 " 88fn my_fn() { 89 Nil 90} 91 92fn main() { 93 my_fn 94} 95", 96 find_position_of("my_fn").under_char('y').nth_occurrence(2) 97 ); 98} 99 100// https://github.com/gleam-lang/gleam/issues/2654 101#[test] 102fn hover_local_function_in_pipe() { 103 assert_hover!( 104 " 105fn add1(num: Int) -> Int { 106 num + 1 107} 108 109pub fn main() { 110 add1(1) 111 112 1 113 |> add1 114 |> add1 115 |> add1 116} 117", 118 find_position_of("add1") 119 .with_char_offset(1) 120 .nth_occurrence(2) 121 ); 122} 123 124// https://github.com/gleam-lang/gleam/issues/2654 125#[test] 126fn hover_local_function_in_pipe_1() { 127 assert_hover!( 128 " 129fn add1(num: Int) -> Int { 130 num + 1 131} 132 133pub fn main() { 134 add1(1) 135 136 1 137 |> add1 138 |> add1 139 |> add1 140} 141", 142 find_position_of("add1") 143 .with_char_offset(2) 144 .nth_occurrence(3) 145 ); 146} 147 148// https://github.com/gleam-lang/gleam/issues/2654 149#[test] 150fn hover_local_function_in_pipe_2() { 151 assert_hover!( 152 " 153fn add1(num: Int) -> Int { 154 num + 1 155} 156 157pub fn main() { 158 add1(1) 159 160 1 161 |> add1 162 |> add1 163 |> add1 164} 165", 166 find_position_of("add1") 167 .with_char_offset(2) 168 .nth_occurrence(4) 169 ); 170} 171 172// https://github.com/gleam-lang/gleam/issues/2654 173#[test] 174fn hover_local_function_in_pipe_3() { 175 assert_hover!( 176 " 177fn add1(num: Int) -> Int { 178 num + 1 179} 180 181pub fn main() { 182 add1(1) 183 184 1 185 |> add1 186 |> add1 187 |> add1 188} 189", 190 find_position_of("add1") 191 .with_char_offset(2) 192 .nth_occurrence(5) 193 ); 194} 195 196#[test] 197fn hover_imported_function() { 198 let code = " 199import example_module 200fn main() { 201 example_module.my_fn 202} 203"; 204 205 assert_hover!( 206 TestProject::for_source(code).add_module("example_module", "pub fn my_fn() { Nil }"), 207 find_position_of("my_fn").under_char('_'), 208 ); 209} 210 211#[test] 212fn hover_external_imported_function() { 213 let code = " 214import example_module 215fn main() { 216 example_module.my_fn 217} 218"; 219 220 assert_hover!( 221 TestProject::for_source(code).add_hex_module("example_module", "pub fn my_fn() { Nil }"), 222 find_position_of("my_fn").under_char('_'), 223 ); 224} 225 226#[test] 227fn hover_external_imported_unqualified_function() { 228 let code = " 229import example_module.{my_fn} 230fn main() { 231 my_fn 232} 233"; 234 235 assert_hover!( 236 TestProject::for_source(code).add_hex_module("example_module", "pub fn my_fn() { Nil }"), 237 find_position_of("my_fn").under_char('f').nth_occurrence(2), 238 ); 239} 240 241#[test] 242fn hover_external_imported_function_renamed_module() { 243 let code = " 244import example_module as renamed_module 245fn main() { 246 renamed_module.my_fn 247} 248"; 249 250 assert_hover!( 251 TestProject::for_source(code).add_hex_module("example_module", "pub fn my_fn() { Nil }"), 252 find_position_of("my_fn").under_char('f'), 253 ); 254} 255 256#[test] 257fn hover_external_unqualified_imported_function_renamed_module() { 258 let code = " 259import example_module.{my_fn} as renamed_module 260fn main() { 261 my_fn 262} 263"; 264 265 assert_hover!( 266 TestProject::for_source(code).add_hex_module("example_module", "pub fn my_fn() { Nil }"), 267 find_position_of("my_fn").under_char('_').nth_occurrence(2), 268 ); 269} 270 271#[test] 272fn hover_external_imported_function_nested_module() { 273 // Example of HexDocs link with nested modules: https://hexdocs.pm/lustre/lustre/element/svg.html 274 let code = " 275import my/nested/example_module 276fn main() { 277 example_module.my_fn 278} 279"; 280 281 assert_hover!( 282 TestProject::for_source(code) 283 .add_hex_module("my/nested/example_module", "pub fn my_fn() { Nil }"), 284 find_position_of("my_fn").under_char('f'), 285 ); 286} 287 288#[test] 289fn hover_external_imported_ffi_renamed_function() { 290 let code = r#" 291import example_module 292fn main() { 293 example_module.my_fn 294} 295"#; 296 297 let hex_module = r#" 298@external(erlang, "my_mod_ffi", "renamed_fn") 299pub fn my_fn() -> Nil 300"#; 301 302 assert_hover!( 303 TestProject::for_source(code).add_hex_module("example_module", hex_module,), 304 find_position_of("my_fn").under_char('f'), 305 ); 306} 307 308#[test] 309fn hover_external_imported_constants() { 310 let code = " 311import example_module 312fn main() { 313 example_module.my_const 314} 315"; 316 317 assert_hover!( 318 TestProject::for_source(code).add_hex_module("example_module", "pub const my_const = 42"), 319 find_position_of("my_const").under_char('_'), 320 ); 321} 322 323#[test] 324fn hover_external_imported_unqualified_constants() { 325 let code = " 326import example_module.{my_const} 327fn main() { 328 my_const 329} 330"; 331 332 assert_hover!( 333 TestProject::for_source(code).add_hex_module("example_module", "pub const my_const = 42"), 334 find_position_of("my_const") 335 .under_char('c') 336 .nth_occurrence(2), 337 ); 338} 339 340#[test] 341fn hover_external_value_with_two_modules_same_name() { 342 let code = " 343import a/example_module as _ 344import b/example_module 345fn main() { 346 example_module.my_const 347} 348"; 349 350 assert_hover!( 351 TestProject::for_source(code) 352 .add_hex_module("a/example_module", "pub const my_const = 42") 353 .add_hex_module("b/example_module", "pub const my_const = 42"), 354 find_position_of("my_const").under_char('c'), 355 ); 356} 357 358#[test] 359fn hover_external_function_with_another_value_same_name() { 360 let code = " 361import a/example_module.{my_const as discarded} 362import b/example_module.{my_const} as _ 363fn main() { 364 my_const 365} 366"; 367 368 assert_hover!( 369 TestProject::for_source(code) 370 .add_hex_module("a/example_module", "pub const my_const = 42") 371 .add_hex_module("b/example_module", "pub const my_const = 42"), 372 find_position_of("my_const") 373 .under_char('o') 374 .nth_occurrence(3), 375 ); 376} 377 378#[test] 379fn hover_function_definition_with_docs() { 380 assert_hover!( 381 " 382/// Exciting documentation 383/// Maybe even multiple lines 384fn append(x, y) { 385 x <> y 386} 387", 388 find_position_of("append") 389 ); 390} 391 392#[test] 393fn hover_function_argument() { 394 assert_hover!( 395 " 396/// Exciting documentation 397/// Maybe even multiple lines 398fn append(x, y) { 399 x <> y 400} 401", 402 find_position_of("append(x, y)").under_char('x') 403 ); 404} 405 406#[test] 407fn hover_function_body() { 408 let code = " 409/// Exciting documentation 410/// Maybe even multiple lines 411fn append(x, y) { 412 x <> y 413} 414"; 415 416 assert_eq!( 417 hover(TestProject::for_source(code), Position::new(4, 1)), 418 None 419 ); 420} 421 422#[test] 423fn hover_expressions_in_function_body() { 424 assert_hover!( 425 " 426fn append(x, y) { 427 x <> y 428} 429", 430 find_position_of("x").nth_occurrence(2) 431 ); 432} 433 434#[test] 435fn hover_module_constant() { 436 assert_hover!( 437 " 438/// Exciting documentation 439/// Maybe even multiple lines 440const one = 1 441", 442 find_position_of("one") 443 ); 444} 445 446#[test] 447fn hover_variable_in_use_expression() { 448 assert_hover!( 449 " 450fn b(fun: fn(Int) -> String) { 451 fun(42) 452} 453 454fn do_stuff() { 455 let c = \"done\" 456 457 use a <- b 458 c 459} 460", 461 find_position_of("use a").under_last_char() 462 ); 463} 464 465#[test] 466fn hover_variable_in_use_expression_1() { 467 assert_hover!( 468 " 469fn b(fun: fn(Int) -> String) { 470 fun(42) 471} 472 473fn do_stuff() { 474 let c = \"done\" 475 476 use a <- b 477 c 478} 479", 480 find_position_of("b").nth_occurrence(2) 481 ); 482} 483 484#[test] 485fn hover_variable_in_use_expression_2() { 486 assert_hover!( 487 " 488fn b(fun: fn(Int) -> String) { 489 fun(42) 490} 491 492fn do_stuff() { 493 let c = \"done\" 494 495 use a <- b 496 c 497} 498", 499 find_position_of("c").nth_occurrence(2) 500 ); 501} 502 503#[test] 504fn hover_function_arg_annotation_2() { 505 assert_hover!( 506 " 507/// Exciting documentation 508/// Maybe even multiple lines 509fn append(x: String, y: String) -> String { 510 x <> y 511} 512", 513 find_position_of("String").under_char('n') 514 ); 515} 516 517#[test] 518fn hover_function_return_annotation() { 519 assert_hover!( 520 " 521/// Exciting documentation 522/// Maybe even multiple lines 523fn append(x: String, y: String) -> String { 524 x <> y 525} 526", 527 find_position_of("String").under_char('n').nth_occurrence(3) 528 ); 529} 530 531#[test] 532fn hover_function_return_annotation_with_tuple() { 533 assert_hover!( 534 " 535/// Exciting documentation 536/// Maybe even multiple lines 537fn append(x: String, y: String) -> #(String, String) { 538 #(x, y) 539} 540", 541 find_position_of("String").under_char('r').nth_occurrence(3) 542 ); 543} 544 545#[test] 546fn hover_module_constant_annotation() { 547 assert_hover!( 548 " 549/// Exciting documentation 550/// Maybe even multiple lines 551const one: Int = 1 552", 553 find_position_of("Int").under_last_char() 554 ); 555} 556 557#[test] 558fn hover_type_constructor_annotation() { 559 assert_hover!( 560 " 561type Wibble { 562 Wibble(arg: String) 563} 564", 565 find_position_of("String").under_char('n') 566 ); 567} 568 569#[test] 570fn hover_type_alias_annotation() { 571 assert_hover!("type Wibble = Int", find_position_of("Int").under_char('n')); 572} 573 574#[test] 575fn hover_assignment_annotation() { 576 assert_hover!( 577 " 578fn wibble() { 579 let wobble: Int = 7 580 wobble 581} 582", 583 find_position_of("Int").under_last_char() 584 ); 585} 586 587#[test] 588fn hover_function_arg_annotation_with_documentation() { 589 assert_hover!( 590 " 591/// Exciting documentation 592/// Maybe even multiple lines 593type Wibble { 594 Wibble(arg: String) 595} 596 597fn identity(x: Wibble) -> Wibble { 598 x 599} 600", 601 find_position_of("Wibble") 602 .under_last_char() 603 .nth_occurrence(3) 604 ); 605} 606 607#[test] 608fn hover_import_unqualified_value() { 609 let code = " 610import example_module.{my_num} 611fn main() { 612 my_num 613} 614"; 615 616 assert_hover!( 617 TestProject::for_source(code).add_module( 618 "example_module", 619 " 620/// Exciting documentation 621/// Maybe even multiple lines 622pub const my_num = 1" 623 ), 624 find_position_of("my_num").under_char('n') 625 ); 626} 627 628#[test] 629fn hover_import_unqualified_value_from_hex() { 630 let code = " 631import example_module.{my_num} 632fn main() { 633 my_num 634} 635"; 636 637 assert_hover!( 638 TestProject::for_source(code).add_hex_module( 639 "example_module", 640 " 641/// Exciting documentation 642/// Maybe even multiple lines 643pub const my_num = 1" 644 ), 645 find_position_of("my_num").under_char('n') 646 ); 647} 648 649#[test] 650fn hover_import_unqualified_type() { 651 let code = " 652import example_module.{type MyType, MyType} 653fn main() -> MyType { 654 MyType 655} 656"; 657 658 assert_hover!( 659 TestProject::for_source(code).add_module( 660 "example_module", 661 " 662/// Exciting documentation 663/// Maybe even multiple lines 664pub type MyType { 665 MyType 666}" 667 ), 668 find_position_of("MyType").under_last_char() 669 ); 670} 671 672#[test] 673fn hover_works_even_for_invalid_code() { 674 assert_hover!( 675 " 676fn invalid() { 1 + Nil } 677fn valid() { Nil } 678", 679 find_position_of("fn valid").under_char('v') 680 ); 681} 682 683#[test] 684fn hover_for_pattern_spread_ignoring_all_fields() { 685 assert_hover!( 686 " 687pub type Model { 688 Model( 689 Int, 690 Float, 691 label1: Int, 692 label2: String, 693 ) 694} 695 696pub fn main() { 697 case todo { 698 Model(..) -> todo 699 } 700} 701", 702 find_position_of("..") 703 ); 704} 705 706#[test] 707fn hover_for_pattern_spread_ignoring_some_fields() { 708 assert_hover!( 709 " 710pub type Model { 711 Model( 712 Int, 713 Float, 714 label1: Int, 715 label2: String, 716 ) 717} 718 719pub fn main() { 720 case todo { 721 Model(_, label1: _, ..) -> todo 722 } 723} 724", 725 find_position_of("..").under_last_char() 726 ); 727} 728 729#[test] 730fn hover_for_pattern_spread_ignoring_all_positional_fields() { 731 assert_hover!( 732 " 733pub type Model { 734 Model( 735 Int, 736 Float, 737 label1: Int, 738 label2: String, 739 ) 740} 741 742pub fn main() { 743 case todo { 744 Model(_, _, _, ..) -> todo 745 } 746} 747", 748 find_position_of("..") 749 ); 750} 751 752#[test] 753fn hover_label_shorthand_in_call_arg() { 754 assert_hover!( 755 " 756fn wibble(arg1 arg1: Int, arg2 arg2: Bool) { Nil } 757 758fn main() { 759 let arg1 = 1 760 let arg2 = True 761 wibble(arg2:, arg1:) 762} 763", 764 find_position_of("arg2:").nth_occurrence(2) 765 ); 766} 767 768#[test] 769fn hover_label_shorthand_in_pattern_call_arg() { 770 assert_hover!( 771 " 772pub type Wibble { Wibble(arg1: Int, arg2: Bool) } 773 774pub fn main() { 775 case todo { 776 Wibble(arg2:, ..) -> todo 777 } 778} 779", 780 find_position_of("arg2:") 781 .nth_occurrence(2) 782 .under_last_char() 783 ); 784} 785 786#[test] 787fn hover_label_shorthand_in_pattern_call_arg_2() { 788 assert_hover!( 789 " 790pub type Wibble { Wibble(arg1: Int, arg2: Bool) } 791 792pub fn main() { 793 let Wibble(arg2:, ..) = todo 794} 795", 796 find_position_of("arg2:").nth_occurrence(2).under_char('r') 797 ); 798} 799 800#[test] 801fn hover_contextual_type() { 802 let code = " 803import wibble/wobble 804const value = wobble.Wobble 805"; 806 807 assert_hover!( 808 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wobble }"), 809 find_position_of("value").under_char('v') 810 ); 811} 812 813#[test] 814fn hover_contextual_type_aliased_module() { 815 let code = " 816import wibble/wobble as wubble 817const value = wubble.Wobble 818"; 819 820 assert_hover!( 821 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wobble }"), 822 find_position_of("value").under_char('v') 823 ); 824} 825 826#[test] 827fn hover_contextual_type_unqualified() { 828 let code = " 829import wibble/wobble.{type Wibble} 830const value = wobble.Wobble 831"; 832 833 assert_hover!( 834 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wobble }"), 835 find_position_of("value").under_char('v') 836 ); 837} 838 839#[test] 840fn hover_contextual_type_unqualified_aliased() { 841 let code = " 842import wibble/wobble.{type Wibble as Wobble} 843const value = wobble.Wobble 844"; 845 846 assert_hover!( 847 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wobble }"), 848 find_position_of("value").under_char('v') 849 ); 850} 851 852#[test] 853fn hover_contextual_type_aliased() { 854 let code = " 855import wibble/wobble 856type Local = wobble.Wibble 857const value = wobble.Wobble 858"; 859 860 assert_hover!( 861 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wobble }"), 862 find_position_of("value").under_char('v') 863 ); 864} 865 866#[test] 867fn hover_contextual_type_function() { 868 let code = " 869import wibble/wobble 870type MyInt = Int 871fn func(value: wobble.Wibble) -> MyInt { 1 } 872"; 873 874 assert_hover!( 875 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wobble }"), 876 find_position_of("func").under_char('f') 877 ); 878} 879 880#[test] 881fn hover_contextual_type_unqualified_import() { 882 let code = " 883import wibble/wobble.{type Wibble as Wobble, Wobble} 884"; 885 886 assert_hover!( 887 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wobble }"), 888 find_position_of("Wobble}").under_char('W') 889 ); 890} 891 892#[test] 893fn hover_contextual_type_pattern() { 894 let code = " 895import wibble/wobble.{Wibble, Wobble, Wubble} 896 897pub fn cycle(wibble: wobble.Wibble) { 898 case wibble { 899 Wibble -> Wobble 900 Wobble -> Wubble 901 Wubble -> Wibble 902 } 903} 904"; 905 906 assert_hover!( 907 TestProject::for_source(code) 908 .add_hex_module("wibble/wobble", "pub type Wibble { Wibble Wobble Wubble }"), 909 find_position_of("Wubble ->").under_char('u') 910 ); 911} 912 913#[test] 914fn hover_contextual_type_pattern_spread() { 915 let code = " 916import wibble/wobble.{type Wibble as Wobble} 917 918type Thing { 919 Thing(id: Int, value: Wobble) 920} 921 922pub fn main(thing: Thing) { 923 case thing { 924 Thing(id: 0, ..) -> 12 925 _ -> 14 926 } 927} 928"; 929 930 assert_hover!( 931 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wibble }"), 932 find_position_of("..").under_char('.') 933 ); 934} 935 936#[test] 937fn hover_contextual_type_expression() { 938 let code = " 939import wibble/wobble 940 941pub fn main() { 942 let wibble = wobble.Wibble 943} 944"; 945 946 assert_hover!( 947 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wibble }"), 948 find_position_of(".Wibble").under_char('l') 949 ); 950} 951 952#[test] 953fn hover_contextual_type_arg() { 954 let code = " 955import wibble/wobble 956 957fn do_things(wibble: wobble.Wibble) { wibble } 958"; 959 960 assert_hover!( 961 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wibble }"), 962 find_position_of("wibble:").under_char('w') 963 ); 964} 965 966#[test] 967fn hover_print_type_variable_names() { 968 let code = " 969fn main(value: Result(ok, error)) { 970 let v = value 971 v 972} 973"; 974 975 assert_hover!( 976 TestProject::for_source(code), 977 find_position_of("let v").under_char('v') 978 ); 979} 980 981#[test] 982fn hover_print_unbound_type_variable_names() { 983 let code = " 984fn make_ok(value: some_type) { 985 let result = Ok(value) 986 result 987} 988"; 989 990 assert_hover!( 991 TestProject::for_source(code), 992 find_position_of("result =").under_char('s') 993 ); 994} 995 996#[test] 997fn hover_print_unbound_type_variable_name_without_conflicts() { 998 let code = " 999fn make_ok(value: a) { 1000 let result = Ok(value) 1001 result 1002} 1003"; 1004 1005 assert_hover!( 1006 TestProject::for_source(code), 1007 find_position_of("result =").under_char('s') 1008 ); 1009} 1010 1011#[test] 1012fn hover_print_imported_alias() { 1013 let code = " 1014import aliases.{type Aliased} 1015const thing: Aliased = 10 1016"; 1017 1018 assert_hover!( 1019 TestProject::for_source(code).add_hex_module("aliases", "pub type Aliased = Int"), 1020 find_position_of("thing").under_char('g') 1021 ); 1022} 1023 1024#[test] 1025fn hover_prelude_type() { 1026 let code = " 1027const number = 100 1028"; 1029 1030 assert_hover!( 1031 TestProject::for_source(code), 1032 find_position_of("number").under_char('b') 1033 ); 1034} 1035 1036#[test] 1037fn hover_shadowed_prelude_type() { 1038 let code = " 1039type Int { Int } 1040const number = 100 1041"; 1042 1043 assert_hover!( 1044 TestProject::for_source(code), 1045 find_position_of("number").under_char('b') 1046 ); 1047} 1048 1049#[test] 1050fn hover_shadowed_prelude_type_imported() { 1051 let code = " 1052import numbers.{type Int} 1053const number = 100 1054"; 1055 1056 assert_hover!( 1057 TestProject::for_source(code).add_hex_module("numbers", "pub type Int"), 1058 find_position_of("number =").under_char('b') 1059 ); 1060} 1061 1062#[test] 1063fn hover_contextual_type_annotation() { 1064 let code = " 1065import wibble/wobble 1066 1067fn make_wibble() -> wobble.Wibble { wobble.Wibble } 1068"; 1069 1070 assert_hover!( 1071 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wibble }"), 1072 find_position_of("-> wobble.Wibble").under_char('i') 1073 ); 1074} 1075 1076#[test] 1077fn hover_contextual_type_annotation_prelude() { 1078 let code = " 1079fn add_one(a: Int) -> Int { 1080 a + 1 1081} 1082"; 1083 1084 assert_hover!( 1085 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wibble }"), 1086 find_position_of("-> Int").under_char('I') 1087 ); 1088} 1089 1090#[test] 1091fn hover_contextual_type_annotation_unqualified() { 1092 let code = " 1093import wibble/wobble.{type Wibble} 1094 1095fn main(wibble: Wibble) { 1096 wibble 1097} 1098"; 1099 1100 assert_hover!( 1101 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wibble }"), 1102 find_position_of(": Wibble").under_char('W') 1103 ); 1104} 1105 1106#[test] 1107fn hover_contextual_type_annotation_unqualified_aliased() { 1108 let code = " 1109import wibble/wobble.{type Wibble as Wubble} 1110 1111fn main(wibble: Wubble) { 1112 wibble 1113} 1114"; 1115 1116 assert_hover!( 1117 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wibble }"), 1118 find_position_of(": Wubble").under_char('W') 1119 ); 1120} 1121 1122#[test] 1123fn hover_contextual_type_annotation_aliased_module() { 1124 let code = " 1125import wibble/wobble as wubble 1126 1127fn main(wibble: wubble.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(": wubble.Wibble").under_char('W') 1135 ); 1136} 1137 1138#[test] 1139fn hover_contextual_type_annotation_aliased() { 1140 let code = " 1141import wibble/wobble 1142 1143type Wubble = wobble.Wibble 1144 1145fn main(wibble: Wubble) { 1146 wibble 1147} 1148"; 1149 1150 assert_hover!( 1151 TestProject::for_source(code).add_hex_module("wibble/wobble", "pub type Wibble { Wibble }"), 1152 find_position_of(": Wubble").under_char('e') 1153 ); 1154} 1155 1156#[test] 1157fn hover_print_underlying_for_alias_with_parameters() { 1158 let code = " 1159type LocalResult = Result(String, Int) 1160 1161fn do_thing() -> LocalResult { 1162 Error(1) 1163} 1164"; 1165 1166 assert_hover!( 1167 TestProject::for_source(code), 1168 find_position_of("do_thing").under_char('d') 1169 ); 1170} 1171 1172#[test] 1173fn hover_print_alias_when_parameters_match() { 1174 let code = " 1175type MyResult(a, b) = Result(a, b) 1176 1177fn do_thing() -> MyResult(Int, Int) { 1178 Error(1) 1179} 1180"; 1181 1182 assert_hover!( 1183 TestProject::for_source(code), 1184 find_position_of("do_thing").under_char('d') 1185 ); 1186} 1187 1188#[test] 1189fn hover_print_underlying_for_imported_alias() { 1190 let code = " 1191import alias.{type A} 1192 1193fn wibble() -> Result(Int, String) { 1194 todo 1195} 1196"; 1197 1198 assert_hover!( 1199 TestProject::for_source(code).add_hex_module("alias", "pub type A = Result(Int, String)"), 1200 find_position_of("wibble").under_char('l') 1201 ); 1202} 1203 1204#[test] 1205fn hover_print_aliased_imported_generic_type() { 1206 let code = " 1207import gleam/option.{type Option as Maybe} 1208 1209const none: Maybe(Int) = option.None 1210"; 1211 1212 assert_hover!( 1213 TestProject::for_source(code) 1214 .add_hex_module("gleam/option", "pub type Option(a) { None Some(a) }"), 1215 find_position_of("none").under_char('e') 1216 ); 1217} 1218 1219#[test] 1220fn hover_print_qualified_prelude_type_when_shadowed_by_alias() { 1221 let code = " 1222type Result = #(Bool, String) 1223const ok = Ok(10) 1224"; 1225 1226 assert_hover!( 1227 TestProject::for_source(code), 1228 find_position_of("ok").under_char('k') 1229 ); 1230} 1231 1232#[test] 1233fn hover_print_qualified_prelude_type_when_shadowed_by_imported_alias() { 1234 let code = " 1235import alias.{type Bool} 1236const value = True 1237"; 1238 1239 assert_hover!( 1240 TestProject::for_source(code).add_hex_module("alias", "pub type Bool = #(Int, Int)"), 1241 find_position_of("value").under_char('v') 1242 ); 1243} 1244 1245// https://github.com/gleam-lang/gleam/issues/3761 1246#[test] 1247fn hover_over_block_in_list_spread() { 1248 let code = " 1249pub fn main() { 1250 [1, 2, ..{ 1251 let x = 1 1252 [x] 1253 }] 1254} 1255"; 1256 1257 assert_hover!(TestProject::for_source(code), find_position_of("x")); 1258} 1259 1260// https://github.com/gleam-lang/gleam/issues/3758 1261#[test] 1262fn hover_for_anonymous_function_annotation() { 1263 let code = " 1264/// An example type. 1265pub type Wibble 1266 1267pub fn main() { 1268 fn(w: Wibble) { todo } 1269} 1270"; 1271 1272 assert_hover!( 1273 TestProject::for_source(code), 1274 find_position_of("w: Wibble").under_char('b') 1275 ); 1276} 1277 1278#[test] 1279fn hover_for_label_in_pattern() { 1280 let code = " 1281type Wibble { 1282 Wibble(wibble: Int, wobble: Int) 1283} 1284 1285pub fn main() { 1286 let Wibble(wibble: _, wobble: _) = todo 1287 todo 1288} 1289"; 1290 1291 assert_hover!( 1292 TestProject::for_source(code), 1293 find_position_of("wibble: _").under_char('l') 1294 ); 1295} 1296 1297#[test] 1298fn hover_for_label_in_expression() { 1299 let code = " 1300fn add(wibble a, wobble b) { 1301 a + b 1302} 1303 1304pub fn main() { 1305 add(wibble: 1, wobble: 2) 1306} 1307"; 1308 1309 assert_hover!( 1310 TestProject::for_source(code), 1311 find_position_of("wibble:").under_char('i') 1312 ); 1313} 1314 1315#[test] 1316fn hover_for_pattern_in_use() { 1317 let code = " 1318type Wibble { 1319 Wibble(Int, Float) 1320} 1321 1322pub fn main() { 1323 use Wibble(int, float) <- todo 1324 todo 1325} 1326"; 1327 1328 assert_hover!( 1329 TestProject::for_source(code), 1330 find_position_of("int").under_char('i') 1331 ); 1332} 1333 1334#[test] 1335fn hover_for_annotation_in_use() { 1336 let code = " 1337pub fn main() { 1338 use something: Int <- todo 1339 todo 1340} 1341"; 1342 1343 assert_hover!( 1344 TestProject::for_source(code), 1345 find_position_of("Int").under_char('n') 1346 ); 1347} 1348 1349#[test] 1350fn hover_on_pipe_with_invalid_step() { 1351 assert_hover!( 1352 " 1353pub fn main() { 1354 [1, 2, 3] 1355 |> map(wibble) 1356 |> filter(fn(value) { value }) 1357} 1358 1359fn map(list: List(a), fun: fn(a) -> b) -> List(b) {} 1360fn filter(list: List(a), fun: fn(a) -> Bool) -> List(a) {} 1361", 1362 find_position_of("[") 1363 ); 1364} 1365 1366#[test] 1367fn hover_on_pipe_with_invalid_step_1() { 1368 assert_hover!( 1369 " 1370pub fn main() { 1371 [1, 2, 3] 1372 |> map(wibble) 1373 |> filter(fn(value) { value }) 1374} 1375 1376fn map(list: List(a), fun: fn(a) -> b) -> List(b) {} 1377fn filter(list: List(a), fun: fn(a) -> Bool) -> List(a) {} 1378", 1379 find_position_of("1") 1380 ); 1381} 1382 1383#[test] 1384fn hover_on_pipe_with_invalid_step_2() { 1385 assert_hover!( 1386 " 1387pub fn main() { 1388 [1, 2, 3] 1389 |> map(wibble) 1390 |> filter(fn(value) { value }) 1391} 1392 1393fn map(list: List(a), fun: fn(a) -> b) -> List(b) {} 1394fn filter(list: List(a), fun: fn(a) -> Bool) -> List(a) {} 1395", 1396 find_position_of("map") 1397 ); 1398} 1399 1400#[test] 1401fn hover_on_pipe_with_invalid_step_3() { 1402 assert_hover!( 1403 " 1404pub fn main() { 1405 [1, 2, 3] 1406 |> map(wibble) 1407 |> filter(fn(value) { value }) 1408} 1409 1410fn map(list: List(a), fun: fn(a) -> b) -> List(b) {} 1411fn filter(list: List(a), fun: fn(a) -> Bool) -> List(a) {} 1412", 1413 find_position_of("wibble") 1414 ); 1415} 1416 1417#[test] 1418fn hover_on_pipe_with_invalid_step_4() { 1419 assert_hover!( 1420 " 1421pub fn main() { 1422 [1, 2, 3] 1423 |> map(wibble) 1424 |> filter(fn(value) { value }) 1425} 1426 1427fn map(list: List(a), fun: fn(a) -> b) -> List(b) {} 1428fn filter(list: List(a), fun: fn(a) -> Bool) -> List(a) {} 1429", 1430 find_position_of("filter") 1431 ); 1432} 1433 1434#[test] 1435fn hover_on_pipe_with_invalid_step_5() { 1436 assert_hover!( 1437 " 1438pub fn main() { 1439 [1, 2, 3] 1440 |> map(wibble) 1441 |> filter(fn(value) { value }) 1442} 1443 1444fn map(list: List(a), fun: fn(a) -> b) -> List(b) { todo } 1445fn filter(list: List(a), fun: fn(a) -> Bool) -> List(a) { todo } 1446", 1447 find_position_of("fn(value)") 1448 ); 1449} 1450 1451#[test] 1452fn hover_on_pipe_with_invalid_step_6() { 1453 assert_hover!( 1454 " 1455pub fn main() { 1456 [1, 2, 3] 1457 |> wibble 1458 |> filter(fn(value) { value }) 1459} 1460 1461fn filter(list: List(a), fun: fn(a) -> Bool) -> List(a) { todo } 1462", 1463 find_position_of("wibble") 1464 ); 1465} 1466 1467#[test] 1468fn hover_on_pipe_with_invalid_step_8() { 1469 assert_hover!( 1470 " 1471pub fn main() { 1472 [1, 2, 3] 1473 |> wibble 1474 |> filter(fn(value) { value }) 1475} 1476 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_over_module_name() { 1485 let src = " 1486import wibble 1487 1488pub fn main() { 1489 wibble.wibble() 1490} 1491"; 1492 assert_hover!( 1493 TestProject::for_source(src).add_hex_module( 1494 "wibble", 1495 " 1496//// This is the wibble module. 1497//// Here is some documentation about it. 1498//// This module does stuff 1499 1500pub fn wibble() { 1501 todo 1502} 1503" 1504 ), 1505 find_position_of("wibble.") 1506 ); 1507} 1508 1509#[test] 1510fn hover_over_module_with_path() { 1511 let src = " 1512import wibble/wobble 1513 1514pub fn main() { 1515 wobble.wibble() 1516} 1517"; 1518 assert_hover!( 1519 TestProject::for_source(src).add_hex_module( 1520 "wibble/wobble", 1521 " 1522//// The module documentation 1523 1524pub fn wibble() { 1525 todo 1526} 1527" 1528 ), 1529 find_position_of("wobble.") 1530 ); 1531} 1532 1533#[test] 1534fn hover_over_module_name_in_annotation() { 1535 let src = " 1536import wibble 1537 1538pub fn main(w: wibble.Wibble) { 1539 todo 1540} 1541"; 1542 assert_hover!( 1543 TestProject::for_source(src).add_hex_module( 1544 "wibble", 1545 " 1546//// This is the wibble module. 1547//// Here is some documentation about it. 1548//// This module does stuff 1549 1550pub type Wibble 1551" 1552 ), 1553 find_position_of("wibble.") 1554 ); 1555} 1556 1557#[test] 1558fn hover_over_imported_module() { 1559 let src = " 1560import wibble 1561"; 1562 assert_hover!( 1563 TestProject::for_source(src).add_hex_module( 1564 "wibble", 1565 " 1566//// This is the wibble module. 1567//// Here is some documentation about it. 1568//// This module does stuff 1569" 1570 ), 1571 find_position_of("wibble") 1572 ); 1573} 1574 1575#[test] 1576fn no_hexdocs_link_when_hovering_over_local_module() { 1577 let src = " 1578import wibble 1579"; 1580 assert_hover!( 1581 TestProject::for_source(src).add_module( 1582 "wibble", 1583 " 1584//// This is the wibble module. 1585//// Here is some documentation about it. 1586//// This module does stuff 1587" 1588 ), 1589 find_position_of("wibble") 1590 ); 1591} 1592 1593#[test] 1594fn hover_for_constant_int() { 1595 assert_hover!( 1596 " 1597const ten = 10 1598", 1599 find_position_of("10") 1600 ); 1601} 1602 1603#[test] 1604fn hover_for_constant_float() { 1605 assert_hover!( 1606 " 1607const pi = 3.14 1608", 1609 find_position_of("3.14") 1610 ); 1611} 1612#[test] 1613fn hover_for_constant_string() { 1614 assert_hover!( 1615 r#" 1616const message = "Hello!" 1617"#, 1618 find_position_of("!") 1619 ); 1620} 1621 1622#[test] 1623fn hover_for_constant_other_constant() { 1624 assert_hover!( 1625 " 1626const constant1 = 10 1627const constant2 = constant1 1628", 1629 find_position_of("= constant1").under_char('s') 1630 ); 1631} 1632 1633#[test] 1634fn hover_for_constant_record() { 1635 assert_hover!( 1636 " 1637type Wibble { 1638 Wibble(Int) 1639} 1640 1641const w = Wibble(10) 1642", 1643 find_position_of("Wibble(10)").under_char('i') 1644 ); 1645} 1646 1647#[test] 1648fn hover_for_constant_tuple() { 1649 assert_hover!( 1650 " 1651const tuple = #(1, 3.5, False) 1652", 1653 find_position_of("#(") 1654 ); 1655} 1656 1657#[test] 1658fn hover_for_constant_tuple_element() { 1659 assert_hover!( 1660 " 1661const tuple = #(1, 3.5, False) 1662", 1663 find_position_of("False") 1664 ); 1665} 1666 1667#[test] 1668fn hover_for_constant_list() { 1669 assert_hover!( 1670 " 1671const numbers = [2, 4, 6, 8] 1672", 1673 find_position_of("[") 1674 ); 1675} 1676 1677#[test] 1678fn hover_for_constant_list_element() { 1679 assert_hover!( 1680 " 1681const numbers = [2, 4, 6, 8] 1682", 1683 find_position_of("4") 1684 ); 1685} 1686 1687#[test] 1688fn hover_for_constant_string_concatenation() { 1689 assert_hover!( 1690 r#" 1691const name = "Bob" 1692const message = "Hello " <> name 1693"#, 1694 find_position_of("<>") 1695 ); 1696} 1697 1698#[test] 1699fn hover_for_constant_string_concatenation_side() { 1700 assert_hover!( 1701 r#" 1702const name = "Bob" 1703const message = "Hello " <> name 1704"#, 1705 find_position_of("<> name").under_char('n') 1706 ); 1707} 1708 1709#[test] 1710fn hover_for_constant_bit_array() { 1711 assert_hover!( 1712 " 1713const bits = <<1:2, 3:4>> 1714", 1715 find_position_of(",") 1716 ); 1717} 1718 1719#[test] 1720fn hover_for_constant_bit_array_segment() { 1721 assert_hover!( 1722 " 1723const bits = <<1:2, 3:4>> 1724", 1725 find_position_of("1") 1726 ); 1727} 1728 1729#[test] 1730fn hover_for_constant_bit_array_segment_option() { 1731 assert_hover!( 1732 " 1733const bits = <<1:size(2), 3:4>> 1734", 1735 find_position_of("2") 1736 ); 1737} 1738 1739#[test] 1740fn hover_for_nested_constant() { 1741 assert_hover!( 1742 " 1743type Wibble { 1744 Wibble 1745 Wobble(BitArray) 1746} 1747 1748const value = #(1, 2, [Wibble, Wobble(<<1, 2, 3>>), Wibble]) 1749", 1750 find_position_of("3") 1751 ); 1752}