Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

gleam / language-server / src / tests / signature_help.rs
12 kB 499 lines
1// SPDX-License-Identifier: Apache-2.0 2// SPDX-FileCopyrightText: 2024 The Gleam contributors 3 4use super::*; 5use lsp_types::{ 6 ActiveParameter, ParameterInformation, ParameterInformationLabel, SignatureHelp, 7 SignatureHelpParams, SignatureInformation, 8}; 9 10fn signature_help(tester: TestProject<'_>, position: Position) -> Option<SignatureHelp> { 11 tester.at(position, |engine, param, _| { 12 let params = SignatureHelpParams { 13 context: None, 14 text_document_position_params: param, 15 work_done_progress_params: Default::default(), 16 }; 17 let response = engine.signature_help(params); 18 19 response.result.unwrap() 20 }) 21} 22 23fn pretty_signature_help(signature_help: SignatureHelp) -> String { 24 let SignatureHelp { 25 signatures, 26 active_signature, 27 active_parameter, 28 } = signature_help; 29 30 let SignatureInformation { 31 label, 32 documentation, 33 parameters, 34 active_parameter: _, 35 } = signatures 36 .get(active_signature.expect("an active signature") as usize) 37 .expect("an active signature"); 38 39 let parameters = parameters 40 .as_ref() 41 .expect("no signature help for function with no parameters"); 42 43 let documentation = match documentation { 44 Some(d) => format!("Documentation:\n{d:#?}"), 45 None => "No documentation".to_string(), 46 }; 47 48 let label = match active_parameter { 49 None | Some(ActiveParameter::Null) => label.to_string(), 50 Some(ActiveParameter::Int(i)) => match parameters.get(i as usize) { 51 None => label.to_string(), 52 Some(ParameterInformation { 53 label: ParameterInformationLabel::Tuple((start, end)), 54 .. 55 }) => { 56 let spaces = " ".repeat(*start as usize); 57 let underlined = "".repeat((end - start) as usize); 58 format!("{label}\n{spaces}{underlined}") 59 } 60 Some(_) => panic!("unexpected response"), 61 }, 62 }; 63 64 format!("{label}\n\n{documentation}") 65} 66 67#[macro_export] 68macro_rules! assert_signature_help { 69 ($code:literal, $position:expr $(,)?) => { 70 let project = TestProject::for_source($code); 71 assert_signature_help!(project, $position); 72 }; 73 74 ($project:expr, $position:expr $(,)?) => { 75 let src = $project.src; 76 let position = $position.find_position(src); 77 let result = signature_help($project, position).expect("no signature help produced"); 78 let pretty_hover = hover::show_hover( 79 src, 80 lsp_types::Range { 81 start: Position { 82 character: 1, 83 line: 1, 84 }, 85 end: Position { 86 character: 0, 87 line: 0, 88 }, 89 }, 90 position, 91 ); 92 let output = format!( 93 "{}\n\n----- Signature help -----\n{}", 94 pretty_hover, 95 pretty_signature_help(result) 96 ); 97 insta::assert_snapshot!(insta::internals::AutoName, output, src); 98 }; 99} 100 101#[macro_export] 102macro_rules! assert_no_signature_help { 103 ($code:literal, $position:expr $(,)?) => { 104 let project = TestProject::for_source($code); 105 assert_no_signature_help!(project, $position); 106 }; 107 108 ($project:expr, $position:expr $(,)?) => { 109 let src = $project.src; 110 let position = $position.find_position(src); 111 let result = signature_help($project, position); 112 match result { 113 Some(_) => panic!("Expected no signature help"), 114 None => (), 115 } 116 }; 117} 118 119#[test] 120pub fn help_for_calling_local_variable_first_arg() { 121 assert_signature_help!( 122 r#" 123pub fn main() { 124 let wibble = fn(a: Int, b: String) { 1.0 } 125 wibble() 126} 127"#, 128 find_position_of("wibble()").under_last_char() 129 ); 130} 131 132#[test] 133pub fn help_for_calling_local_variable_last_arg() { 134 assert_signature_help!( 135 r#" 136pub fn main() { 137 let wibble = fn(a: Int, b: String) { 1.0 } 138 wibble(1,) 139} 140"#, 141 find_position_of("wibble(1,)").under_last_char() 142 ); 143} 144 145#[test] 146pub fn help_for_calling_local_variable_with_module_function() { 147 assert_signature_help!( 148 r#" 149pub fn wibble(a: Int, b: String) { 1.0 } 150 151pub fn main() { 152 let wobble = fn(a: Int, b: String) { 1.0 } 153 wobble(1,) 154} 155"#, 156 find_position_of("wobble(1,)").under_last_char() 157 ); 158} 159 160#[test] 161pub fn help_for_calling_module_function() { 162 assert_signature_help!( 163 r#" 164pub fn wibble(a: Int, b: String) { 1.0 } 165 166pub fn main() { 167 wibble() 168} 169"#, 170 find_position_of("wibble()").under_last_char() 171 ); 172} 173 174#[test] 175pub fn help_for_calling_module_constant_referencing_function() { 176 assert_signature_help!( 177 r#" 178pub fn wibble(a: Int, b: String) { 1.0 } 179const wobble = wibble 180 181pub fn main() { 182 wobble() 183} 184"#, 185 find_position_of("wobble()").under_last_char() 186 ); 187} 188 189#[test] 190pub fn help_for_calling_local_variable_referencing_constant_referencing_function() { 191 assert_signature_help!( 192 r#" 193pub fn wibble(a: Int, b: String) { 1.0 } 194const wobble = wibble 195 196pub fn main() { 197 let woo = wobble 198 woo() 199} 200"#, 201 find_position_of("woo()").under_last_char() 202 ); 203} 204 205#[test] 206pub fn help_still_shows_up_even_if_an_argument_has_the_wrong_type() { 207 assert_signature_help!( 208 r#" 209pub fn wibble(a: Int, b: String) { 1.0 } 210 211pub fn main() { 212 wibble("wrong",) 213} 214"#, 215 find_position_of("wibble(\"wrong\",)").under_last_char() 216 ); 217} 218 219#[test] 220pub fn help_shows_documentation_for_local_function() { 221 assert_signature_help!( 222 r#" 223/// Some doc! 224pub fn wibble(a: Int, b: String) { 1.0 } 225 226pub fn main() { 227 wibble() 228} 229"#, 230 find_position_of("wibble()").under_last_char() 231 ); 232} 233 234#[test] 235pub fn help_shows_documentation_for_imported_function() { 236 let code = r#" 237import example 238pub fn main() { 239 example.example_fn() 240} 241"#; 242 assert_signature_help!( 243 TestProject::for_source(code).add_module( 244 "example", 245 "/// Some doc! 246pub fn example_fn(a: Int, b: String) { Nil }" 247 ), 248 find_position_of("example_fn()").under_last_char() 249 ); 250} 251 252#[test] 253pub fn help_for_unqualified_call() { 254 let code = r#" 255import example.{example_fn} 256pub fn main() { 257 example_fn() 258} 259"#; 260 261 assert_signature_help!( 262 TestProject::for_source(code) 263 .add_module("example", "pub fn example_fn(a: Int, b: String) { Nil }"), 264 find_position_of("example_fn()").under_last_char() 265 ); 266} 267 268#[test] 269pub fn help_for_aliased_unqualified_call() { 270 let code = r#" 271import example.{example_fn as wibble} 272pub fn main() { 273 wibble() 274} 275"#; 276 277 assert_signature_help!( 278 TestProject::for_source(code) 279 .add_module("example", "pub fn example_fn(a: Int, b: String) { Nil }"), 280 find_position_of("wibble()").under_last_char() 281 ); 282} 283 284#[test] 285pub fn help_for_qualified_call() { 286 let code = r#" 287import example 288pub fn main() { 289 example.example_fn() 290} 291"#; 292 293 assert_signature_help!( 294 TestProject::for_source(code) 295 .add_module("example", "pub fn example_fn(a: Int, b: String) { Nil }"), 296 find_position_of("example_fn()").under_last_char() 297 ); 298} 299 300#[test] 301pub fn help_for_aliased_qualified_call() { 302 let code = r#" 303import example as wibble 304pub fn main() { 305 wibble.example_fn() 306} 307"#; 308 309 assert_signature_help!( 310 TestProject::for_source(code) 311 .add_module("example", "pub fn example_fn(a: Int, b: String) { Nil }"), 312 find_position_of("example_fn()").under_last_char() 313 ); 314} 315 316#[test] 317pub fn help_shows_labels() { 318 assert_signature_help!( 319 r#" 320pub fn wibble(a: Int, b b: Int, c c: String) { 1.0 } 321 322pub fn main() { 323 wibble() 324} 325 "#, 326 find_position_of("wibble()").under_last_char() 327 ); 328} 329 330#[test] 331pub fn help_shows_labelled_argument_after_all_unlabelled() { 332 assert_signature_help!( 333 r#" 334pub fn wibble(a: Int, b b: Int, c c: String) { 1.0 } 335 336pub fn main() { 337 wibble(1,) 338} 339 "#, 340 find_position_of("wibble(1,)").under_last_char() 341 ); 342} 343 344#[test] 345pub fn help_shows_first_missing_labelled_argument_if_out_of_order() { 346 assert_signature_help!( 347 r#" 348pub fn wibble(a a: Int, b b: Int, c c: String) { 1.0 } 349 350pub fn main() { 351 wibble(c: "c",) 352} 353 "#, 354 find_position_of("wibble(c: \"c\",)").under_last_char() 355 ); 356} 357 358#[test] 359pub fn help_for_piped_imported_function_starts_from_second_argument() { 360 let code = r#" 361import example 362pub fn main() { 363 1 |> example.example_fn() 364} 365 "#; 366 367 assert_signature_help!( 368 TestProject::for_source(code) 369 .add_module("example", "pub fn example_fn(a: Int, b: String) { Nil }"), 370 find_position_of("example_fn()").under_last_char() 371 ); 372} 373 374#[test] 375pub fn help_for_piped_function_starts_from_second_argument() { 376 assert_signature_help!( 377 r#" 378pub fn wibble(a a: Int, b b: Int, c c: String) { 1.0 } 379 380pub fn main() { 381 1 |> wibble() 382} 383 "#, 384 find_position_of("wibble()").under_last_char() 385 ); 386} 387 388#[test] 389pub fn help_for_use_function_call_starts_from_first_argument() { 390 assert_signature_help!( 391 r#" 392pub fn wibble(a: Int, b: Int, c: fn() -> Int) { 1.0 } 393 394pub fn main() { 395 use <- wibble() 396} 397 "#, 398 find_position_of("wibble()").under_last_char() 399 ); 400} 401 402#[test] 403pub fn help_for_use_function_call_uses_precise_types_when_missing_some_arguments() { 404 assert_signature_help!( 405 r#" 406pub fn guard(a: Bool, b: a, c: fn() -> a) { 1.0 } 407 408pub fn main() { 409 use <- guard(True,) 410} 411 "#, 412 find_position_of("guard(True,)").under_last_char() 413 ); 414} 415 416#[test] 417pub fn help_for_use_function_shows_next_unlabelled_argument() { 418 assert_signature_help!( 419 r#" 420pub fn guard(a a: Bool, b b: a, c c: fn() -> a) { 1.0 } 421 422pub fn main() { 423 use <- guard(b: 1,) 424} 425 "#, 426 find_position_of("guard(b: 1,)").under_last_char() 427 ); 428} 429 430#[test] 431pub fn help_does_not_come_up_for_function_that_does_not_exist() { 432 assert_no_signature_help!( 433 r#" 434pub fn main() { 435 use <- to_be_or_not_to_be() 436} 437 "#, 438 find_position_of("to_be_or_not_to_be()").under_last_char() 439 ); 440} 441 442#[test] 443// Regression introduced by 4112682cdb5d5b0bb6d1defc6cde849b6a6f65ab. 444pub fn help_with_labelled_constructor() { 445 assert_signature_help!( 446 r#" 447pub type Pokemon { 448 Pokemon(name: String, types: List(String), moves: List(String)) 449} 450 451pub fn main() { 452 Pokemon(name: "Jirachi",) 453} 454 "#, 455 find_position_of(r#"Pokemon(name: "Jirachi",)"#).under_last_char() 456 ); 457} 458 459#[test] 460pub fn help_for_use_function_call_uses_generic_names_when_missing_all_arguments() { 461 assert_signature_help!( 462 r#" 463pub fn wibble(x: something, y: fn() -> something, z: anything) { Nil } 464pub fn main() { 465 wibble( ) 466} 467"#, 468 find_position_of("wibble( ").under_last_char() 469 ); 470} 471 472#[test] 473pub fn help_for_use_function_call_uses_concrete_types_when_possible_or_generic_names_when_unbound() 474{ 475 assert_signature_help!( 476 r#" 477pub fn wibble(x: something, y: fn() -> something, z: anything) { Nil } 478pub fn main() { 479 wibble(1, ) 480} 481"#, 482 find_position_of("wibble(1, )").under_last_char() 483 ); 484} 485 486#[test] 487pub fn help_for_use_function_call_uses_concrete_types_when_late_ubound() { 488 assert_signature_help!( 489 r#" 490fn identity(x: a) -> a { x } 491 492fn main() { 493 let a = Ok(10) 494 identity( a) 495} 496"#, 497 find_position_of("identity( ").under_last_char() 498 ); 499}