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 / completer.rs
60 kB 1624 lines
1// SPDX-License-Identifier: Apache-2.0 2// SPDX-FileCopyrightText: 2024 The Gleam contributors 3 4use std::{collections::HashMap, sync::Arc}; 5 6use ecow::{EcoString, eco_format}; 7use itertools::Itertools; 8use lsp_types::{ 9 CompletionItem, CompletionItemKind, CompletionItemLabelDetails, CompletionItemTextEdit, 10 Documentation, MarkupContent, MarkupKind, Position, Range, TextDocumentPositionParams, 11 TextEdit, 12}; 13use strum::IntoEnumIterator; 14use vec1::Vec1; 15 16use gleam_core::{ 17 Result, 18 ast::{ 19 self, Arg, CallArg, Function, FunctionLiteralKind, Pattern, Publicity, TypedExpr, 20 visit::Visit, 21 }, 22 build::{Module, Origin}, 23 line_numbers::LineNumbers, 24 parse::{LiteralFloatValue, parse_int_value}, 25 type_::{ 26 self, Deprecation, FieldMap, ModuleInterface, PRELUDE_MODULE_NAME, PreludeType, 27 RecordAccessor, Type, TypeConstructor, ValueConstructorVariant, collapse_links, 28 error::VariableOrigin, pretty::Printer, 29 }, 30}; 31 32use super::{ 33 compiler::LspProjectCompiler, 34 edits::{ 35 Newlines, add_newlines_after_import, get_import_edit, 36 position_of_first_definition_if_import, 37 }, 38 files::FileSystemProxy, 39}; 40 41// Represents the kind/specificity of completion that is being requested. 42#[derive(Copy, Clone)] 43enum CompletionKind { 44 // A language keyword that we can offer completions for, like `todo`, 45 // `panic`, or `echo` 46 Keyword, 47 // A label for a function or type definition 48 Label, 49 // A field of a record 50 FieldAccessor, 51 // Values or types defined in the current module 52 LocallyDefined, 53 // Values or types defined in an already imported module 54 ImportedModule, 55 // Types or values defined in the prelude 56 Prelude, 57 // Types defined in a module that has not been imported 58 ImportableModule, 59} 60 61#[derive(Copy, Clone)] 62enum TypeMatch { 63 Matching, 64 Incompatible, 65 Unknown, 66} 67 68// Gives the sort text for a completion item based on the kind and label. 69// This ensures that more specific kinds of completions are placed before 70// less specific ones.. 71fn sort_text(kind: CompletionKind, label: &str, type_match: TypeMatch) -> String { 72 let priority: u8 = match kind { 73 CompletionKind::Keyword => 0, 74 CompletionKind::Label => 1, 75 CompletionKind::FieldAccessor => 2, 76 CompletionKind::LocallyDefined => 3, 77 CompletionKind::ImportedModule => 4, 78 CompletionKind::Prelude => 5, 79 CompletionKind::ImportableModule => 6, 80 }; 81 match type_match { 82 // We want to prioritise type which match what is expected in the completion 83 // as those are more likely to be what the user wants. 84 TypeMatch::Matching => format!("0{priority}_{label}"), 85 TypeMatch::Incompatible | TypeMatch::Unknown => format!("{priority}_{label}"), 86 } 87} 88 89/// The form in which a type completion is needed in context. 90#[derive(Debug)] 91enum TypeCompletionContext { 92 /// The type completion is for an unqualified import that doesn't have an 93 /// import list yet. So adding the type will also require adding braces: 94 /// 95 /// ```gleam 96 /// import wibble.Wib| 97 /// // ^^^^^ We're typing this... 98 /// import wibble.{type Wibble} 99 /// // ^^^^^^^^^^^^^^ ...so this will be the completion 100 /// ``` 101 /// 102 UnqualifiedImport, 103 104 /// The type completion is for an unqualified import within already existing 105 /// braces. 106 /// 107 /// ```gleam 108 /// import wibble.{type AlreadyImported, Wibb|} 109 /// // ^^^^^ We're typing this... 110 /// import wibble.{type AlreadyImported, type Wibble} 111 /// // ^^^^^^^^^^^ ...so this will be the completion 112 /// ``` 113 /// 114 UnqualifiedImportWithinBraces, 115 116 /// The type completion is for an unqualified type (for example something 117 /// coming from the prelude, or a type that was already imported in a 118 /// unqualified way). 119 /// 120 /// ```gleam 121 /// import wobble.{type Wobble} 122 /// 123 /// pub fn new_wobble() -> Wob| 124 /// // ^^^^ We're typing this... 125 /// pub fn new_wobble() -> Wobble 126 /// // ^^^^^^ ... so this will be the completion 127 /// ``` 128 /// 129 UnqualifiedType, 130 131 /// The type completion is for a qualified type. 132 /// 133 /// ```gleam 134 /// import wobble 135 /// 136 /// pub fn new_wobble() -> wobble.W| 137 /// // ^^^^^^^^ We're typing this... 138 /// pub fn new_wobble() -> wobble.Wobble 139 /// // ^^^^^^^^^^^^^ ...so this will be the completion 140 /// ``` 141 /// 142 QualifiedType, 143} 144 145/// Represents the surroundings of the cursor when trying to figure out a 146/// completion. 147/// 148/// ```gleam 149/// wibble.w|ob 150/// // ^ cursor here 151/// ``` 152#[derive(Debug)] 153struct CursorSurroundings { 154 /// The text surrounding the cursor. For example: 155 /// 156 /// ```gleam 157 /// wibble.w|ob 158 /// // ^ cursor here 159 /// // The surrounding text is: "wibble.wob" 160 /// ``` 161 /// 162 surrounding_text: EcoString, 163 surrounding_text_range: Range, 164 165 /// The text that comes immediately before the cursor. 166 /// For example: 167 /// 168 /// ```gleam 169 /// wibble.w|ob 170 /// // ^ cursor here 171 /// // The text before is: "wibble.w" 172 /// ``` 173 /// 174 text_before_cursor: EcoString, 175 176 /// The range of the text that comes immediately before the cursor. 177 /// For example: 178 /// 179 /// ```gleam 180 /// wibble.w|ob 181 /// // ^ cursor here 182 /// // ^^^^^^^^ This is the range 183 /// ``` 184 /// 185 /// This is what is usually replaced with a completion. 186 /// 187 text_before_cursor_range: Range, 188 189 /// The text that comes immediately after the cursor. 190 /// For example: 191 /// 192 /// ```gleam 193 /// wibble.w|ob 194 /// // ^ cursor here 195 /// // The text before is: "ob" 196 /// ``` 197 /// 198 text_after_cursor: EcoString, 199} 200 201impl CursorSurroundings { 202 fn selected_module(&self) -> Option<EcoString> { 203 self.surrounding_text 204 .split_once('.') 205 .map(|(selected_module, _)| EcoString::from(selected_module)) 206 } 207 208 /// Given a proposed completion, this returns the text edit to obtain that 209 /// completion. 210 /// 211 /// > This could also return `None` as sometimes no further edit is actually 212 /// > needed! 213 /// 214 fn to_text_edit(&self, new_text: String) -> Option<CompletionItemTextEdit> { 215 // We need to check if the new text we're adding could actually be 216 // a simple addition in the middle of something that is already being 217 // typed. 218 // Say we're editing our code to actually make the `Json` type 219 // qualified: 220 // 221 // ```gleam 222 // jso|Json 223 // ^ cursor here 224 // ``` 225 // 226 // Halfway through writing the module name we might just want to accept 227 // the completion for: `json.Json`. 228 // What one would normally expect is for the final code to be: 229 // 230 // ```gleam 231 // json.Json| 232 // // after accepting completion 233 // 234 // // and not something like this! 235 // // json.JsonJson 236 // ``` 237 // 238 // This only makes sense if the completion we're adding has a prefix and 239 // suffix in common with the surrounding text: 240 let remaining_label = new_text 241 .strip_prefix(self.text_before_cursor.as_str()) 242 .and_then(|rest| rest.strip_suffix(self.text_after_cursor.as_str())); 243 match remaining_label { 244 // The entire existing label is already the same as the text we want 245 // to add or (like in the example above) a more complete version of 246 // the text surrounding the cursor. 247 // So we replace the entire range with the new suggestion. 248 Some(_) => Some(CompletionItemTextEdit::TextEdit(TextEdit { 249 range: self.surrounding_text_range, 250 new_text, 251 })), 252 // In all other cases the completion is never meant to replace text 253 // that comes after the cursor. 254 // We only replace what comes before it with the new text. 255 None => Some(CompletionItemTextEdit::TextEdit(TextEdit { 256 range: self.text_before_cursor_range, 257 new_text, 258 })), 259 } 260 } 261} 262 263pub struct Completer<'a, IO> { 264 /// The direct buffer source code 265 src: &'a EcoString, 266 /// The line number information of the buffer source code 267 pub src_line_numbers: LineNumbers, 268 /// The current cursor position within the buffer source code 269 cursor_position: &'a Position, 270 /// A reference to the lsp compiler for getting module information 271 compiler: &'a LspProjectCompiler<FileSystemProxy<IO>>, 272 /// A reference to the current module the completion is for 273 module: &'a Module, 274 /// The line number information of the latest compiled module. 275 /// This is not necessarily the same as src_line_numbers if the module 276 /// is in a non-compiling state 277 pub module_line_numbers: LineNumbers, 278 279 /// The expected type of the value we are completing. `None` if we are 280 /// completing a type annotation or label, where this information is not 281 /// applicable. 282 pub expected_type: Option<Arc<Type>>, 283} 284 285impl<'a, IO> Completer<'a, IO> { 286 pub fn new( 287 src: &'a EcoString, 288 params: &'a TextDocumentPositionParams, 289 compiler: &'a LspProjectCompiler<FileSystemProxy<IO>>, 290 module: &'a Module, 291 ) -> Self { 292 Completer { 293 src, 294 src_line_numbers: LineNumbers::new(src.as_str()), 295 cursor_position: &params.position, 296 compiler, 297 module, 298 module_line_numbers: LineNumbers::new(&module.code), 299 expected_type: None, 300 } 301 } 302 303 /// Gets the current range around the cursor to place a completion 304 /// and the phrase surrounding the cursor to use for completion. 305 /// The `valid_phrase_char` is a function that returns true if a character 306 /// should be included in the phrase surrounding the cursor. 307 fn get_phrase_surrounding_for_completion( 308 &'a self, 309 valid_phrase_char: &impl Fn(char) -> bool, 310 ) -> CursorSurroundings { 311 let cursor = self.src_line_numbers.byte_index(*self.cursor_position); 312 313 // Get part of phrase prior to cursor 314 let before = self 315 .src 316 .get(..cursor as usize) 317 .and_then(|line| { 318 line.rsplit_once(|char| !valid_phrase_char(char)) 319 .map(|(_, suffix)| suffix) 320 }) 321 .unwrap_or(""); 322 323 // Get part of phrase following cursor 324 let after = self 325 .src 326 .get(cursor as usize..) 327 .and_then(|line| { 328 line.split_once(|char| !valid_phrase_char(char)) 329 .map(|(prefix, _)| prefix) 330 }) 331 .unwrap_or(""); 332 333 let text_before_cursor_range = Range { 334 start: Position { 335 line: self.cursor_position.line, 336 character: self.cursor_position.character - before.len() as u32, 337 }, 338 end: Position { 339 line: self.cursor_position.line, 340 character: self.cursor_position.character, 341 }, 342 }; 343 344 let surrounding_text_range = Range { 345 start: text_before_cursor_range.start, 346 end: Position { 347 line: self.cursor_position.line, 348 character: self.cursor_position.character + after.len() as u32, 349 }, 350 }; 351 352 CursorSurroundings { 353 surrounding_text: eco_format!("{before}{after}"), 354 surrounding_text_range, 355 text_before_cursor: EcoString::from(before), 356 text_before_cursor_range, 357 text_after_cursor: EcoString::from(after), 358 } 359 } 360 361 // Gets the current range around the cursor to place a completion 362 // and any part of the phrase preceeding a dot if a module is being selected from. 363 // A continuous phrase in this case is a name or typename that may have a dot in it. 364 // This is used to match the exact location to fill in the completion. 365 fn get_phrase_surrounding_completion(&'a self) -> CursorSurroundings { 366 let cursor_surroundings = self.get_phrase_surrounding_for_completion(&|c: char| { 367 // Checks if a character is not a valid name/upname character or a 368 // dot. 369 c.is_ascii_alphanumeric() || c == '.' || c == '_' 370 }); 371 372 // While a single `.` is ok to be in the cursor sentence, there's a 373 // special case where always accepting `.` is not ok: in list tails and 374 // record updates! 375 // In those case accepting a `.` means we would end up with a phrase 376 // surrounding the cursor that looks like this: `..wibble` and so we 377 // won't be able to show completions for that because it doesn't look 378 // like a module access or a name! 379 // 380 // So we need to do some final massaging of the cursor surroundings if 381 // we realise we have captures a `..` at the beginning of the sentence. 382 // This will allow the language server to provide good completions for 383 // list tails and record updates as well. 384 if cursor_surroundings.text_before_cursor.starts_with("..") { 385 let CursorSurroundings { 386 surrounding_text, 387 surrounding_text_range, 388 text_before_cursor, 389 text_before_cursor_range, 390 text_after_cursor, 391 } = cursor_surroundings; 392 let (_, text_before_cursor) = text_before_cursor.split_at(2); 393 let text_before_cursor_range = Range { 394 start: Position { 395 character: text_before_cursor_range.start.character + 2, 396 ..text_before_cursor_range.start 397 }, 398 ..text_before_cursor_range 399 }; 400 401 let (_, surrounding_text) = surrounding_text.split_at(2); 402 let surrounding_text_range = Range { 403 start: Position { 404 character: surrounding_text_range.start.character + 2, 405 ..surrounding_text_range.start 406 }, 407 ..surrounding_text_range 408 }; 409 410 CursorSurroundings { 411 surrounding_text: EcoString::from(surrounding_text), 412 surrounding_text_range, 413 text_before_cursor: EcoString::from(text_before_cursor), 414 text_before_cursor_range, 415 text_after_cursor, 416 } 417 } else { 418 cursor_surroundings 419 } 420 } 421 422 // Gets the current range around the cursor to place a completion. 423 // For unqualified imports we special case the word being completed to allow for whitespace but not dots. 424 // This is to allow `type MyType` to be treated as 1 "phrase" for the sake of completion. 425 fn get_phrase_surrounding_completion_for_import(&'a self) -> CursorSurroundings { 426 self.get_phrase_surrounding_for_completion(&|c: char| { 427 // Checks if a character is not a valid name/upname character or whitespace. 428 // The newline character is not included as well. 429 c.is_ascii_alphanumeric() || c == '_' || c == ' ' || c == '\t' 430 }) 431 } 432 433 /// Checks if the line being edited is an import line and provides completions if it is. 434 /// If the line includes a dot then it provides unqualified import completions. 435 /// Otherwise it provides direct module import completions. 436 pub fn import_completions(&'a self) -> Option<Result<Option<Vec<CompletionItem>>>> { 437 let start_of_line = self.src_line_numbers.byte_index(Position { 438 line: self.cursor_position.line, 439 character: 0, 440 }); 441 let end_of_line = self.src_line_numbers.byte_index(Position { 442 line: self.cursor_position.line + 1, 443 character: 0, 444 }); 445 446 // Drop all lines except the line the cursor is on 447 let src = self.src.get(start_of_line as usize..end_of_line as usize)?; 448 449 // If this isn't an import line then we don't offer import completions 450 if !src.trim_start().starts_with("import") { 451 return None; 452 } 453 454 // Check if we are completing an unqualified import 455 if let Some(dot_index) = src.find('.') { 456 // Find the module that is being imported from 457 let importing_module_name = src.get(6..dot_index)?.trim(); 458 let importing_module: &ModuleInterface = 459 self.compiler.get_module_interface(importing_module_name)?; 460 let within_braces = match src.get(dot_index + 1..) { 461 Some(x) => x.trim_start().starts_with('{'), 462 None => false, 463 }; 464 465 Some(Ok(Some(self.unqualified_completions_from_module( 466 importing_module, 467 within_braces, 468 )))) 469 } else { 470 // Find where to start and end the import completion 471 let start = self.src_line_numbers.line_and_column_number(start_of_line); 472 let end = self 473 .src_line_numbers 474 .line_and_column_number(end_of_line - 1); 475 let start = Position::new(start.line - 1, start.column + 6); 476 let end = Position::new(end.line - 1, end.column - 1); 477 let completions = self.complete_modules_for_import(start, end); 478 479 Some(Ok(Some(completions))) 480 } 481 } 482 483 /// Gets the completes for unqualified imports from a module. 484 pub fn unqualified_completions_from_module( 485 &'a self, 486 module_being_imported_from: &'a ModuleInterface, 487 within_braces: bool, 488 ) -> Vec<CompletionItem> { 489 let cursor_surroundings = self.get_phrase_surrounding_completion_for_import(); 490 let mut completions = vec![]; 491 492 // Find values and type that have already previously been imported 493 let mut already_imported_types = std::collections::HashSet::new(); 494 let mut already_imported_values = std::collections::HashSet::new(); 495 496 // Search the ast for import statements 497 for import in &self.module.ast.definitions.imports { 498 // Find the import that matches the module being imported from 499 if import.module == module_being_imported_from.name { 500 // Add the values and types that have already been imported 501 for unqualified in &import.unqualified_types { 502 let _ = already_imported_types.insert(&unqualified.name); 503 } 504 505 for unqualified in &import.unqualified_values { 506 let _ = already_imported_values.insert(&unqualified.name); 507 } 508 } 509 } 510 511 // Get completable types 512 for (name, type_) in &module_being_imported_from.types { 513 // Skip types that should not be suggested 514 if !self.is_suggestable_import( 515 &type_.publicity, 516 &type_.deprecation, 517 module_being_imported_from.package.as_str(), 518 ) { 519 continue; 520 } 521 522 // Skip type that are already imported 523 if already_imported_types.contains(name) { 524 continue; 525 } 526 527 completions.push(type_completion( 528 None, 529 name, 530 type_, 531 &cursor_surroundings, 532 if within_braces { 533 TypeCompletionContext::UnqualifiedImportWithinBraces 534 } else { 535 TypeCompletionContext::UnqualifiedImport 536 }, 537 CompletionKind::ImportedModule, 538 )); 539 } 540 541 // Get completable values 542 for (name, value) in &module_being_imported_from.values { 543 // Skip values that should not be suggested 544 if !self.is_suggestable_import( 545 &value.publicity, 546 &value.deprecation, 547 module_being_imported_from.package.as_str(), 548 ) { 549 continue; 550 } 551 552 // Skip values that are already imported 553 if already_imported_values.contains(name) { 554 continue; 555 } 556 completions.push(self.value_completion( 557 None, 558 &module_being_imported_from.name, 559 name, 560 value, 561 &cursor_surroundings, 562 CompletionKind::ImportedModule, 563 )); 564 } 565 566 completions 567 } 568 569 // Get all the modules that can be imported that have not already been imported. 570 fn completable_modules_for_import(&self) -> Vec<(&EcoString, &ModuleInterface)> { 571 let mut direct_dep_packages: std::collections::HashSet<&EcoString> = 572 std::collections::HashSet::from_iter( 573 self.compiler.project_compiler.config.dependencies.keys(), 574 ); 575 if !self.module.origin.is_src() { 576 // In tests we can import direct dev dependencies 577 direct_dep_packages.extend( 578 self.compiler 579 .project_compiler 580 .config 581 .dev_dependencies 582 .keys(), 583 ) 584 } 585 586 let already_imported: std::collections::HashSet<EcoString> = 587 std::collections::HashSet::from_iter( 588 self.module.dependencies.iter().map(|d| d.0.clone()), 589 ); 590 self.compiler 591 .project_compiler 592 .get_importable_modules() 593 .iter() 594 // 595 // You cannot import yourself 596 .filter(|(name, _)| *name != &self.module.name) 597 // 598 // Different origin directories will get different import completions 599 .filter(|(_, module)| match self.module.origin { 600 // src/ can import from src/ 601 Origin::Src => module.origin.is_src(), 602 // dev/ can import from src/ or dev/ 603 Origin::Dev => !module.origin.is_test(), 604 // Test can import from anywhere 605 Origin::Test => true, 606 }) 607 // 608 // It is possible to import internal modules from other packages, 609 // but it's not recommended so we don't include them in completions 610 .filter(|(_, module)| module.package == self.root_package_name() || !module.is_internal) 611 // 612 // You cannot import a module twice 613 .filter(|(name, _)| !already_imported.contains(*name)) 614 // 615 // It is possible to import modules from dependencies of dependencies 616 // but it's not recommended so we don't include them in completions 617 .filter(|(_, module)| { 618 let is_root_or_prelude = 619 module.package == self.root_package_name() || module.package.is_empty(); 620 is_root_or_prelude || direct_dep_packages.contains(&module.package) 621 }) 622 .collect() 623 } 624 625 // Get all the completions for modules that can be imported 626 fn complete_modules_for_import( 627 &'a self, 628 start: Position, 629 end: Position, 630 ) -> Vec<CompletionItem> { 631 self.completable_modules_for_import() 632 .iter() 633 .map(|(name, _)| CompletionItem { 634 label: name.to_string(), 635 kind: Some(CompletionItemKind::Module), 636 text_edit: Some(CompletionItemTextEdit::TextEdit(TextEdit { 637 range: Range { start, end }, 638 new_text: name.to_string(), 639 })), 640 ..CompletionItem::default() 641 }) 642 .collect() 643 } 644 645 // NOTE: completion_types and completion_values are really similar 646 // but just different enough that an abstraction would 647 // be really hard to understand or use a lot of trait magic. 648 // For now I've left it as is but might be worth revisiting. 649 650 /// Provides completions for when the context being edited is a type. 651 pub fn completion_types(&'a self) -> Vec<CompletionItem> { 652 let cursor_surroundings = self.get_phrase_surrounding_completion(); 653 let selected_module = cursor_surroundings.selected_module(); 654 let mut completions = vec![]; 655 656 // Module and prelude types 657 // Do not complete direct module types if the user has already started 658 // typing a module select. That is, when the user has already typed 659 // `mymodule.|` we know local module types and prelude types are no 660 // longer relevant and needed for completions. 661 if selected_module.is_none() { 662 for (name, type_) in &self.module.ast.type_info.types { 663 completions.push(type_completion( 664 None, 665 name, 666 type_, 667 &cursor_surroundings, 668 TypeCompletionContext::UnqualifiedType, 669 CompletionKind::LocallyDefined, 670 )); 671 } 672 673 for type_ in PreludeType::iter() { 674 let label: String = type_.name().into(); 675 let sort_text = Some(sort_text( 676 CompletionKind::Prelude, 677 &label, 678 TypeMatch::Unknown, 679 )); 680 completions.push(CompletionItem { 681 label, 682 detail: Some("Type".into()), 683 kind: Some(CompletionItemKind::Class), 684 sort_text, 685 ..CompletionItem::default() 686 }); 687 } 688 } 689 690 // Qualified types 691 for import in &self.module.ast.definitions.imports { 692 // The module may not be known of yet if it has not previously 693 // compiled yet in this editor session. 694 let Some(module) = self.compiler.get_module_interface(&import.module) else { 695 continue; 696 }; 697 let Some(module_name) = &import.used_name() else { 698 continue; 699 }; 700 701 for (name, type_) in &module.types { 702 if !self.is_suggestable_import( 703 &type_.publicity, 704 &type_.deprecation, 705 module.package.as_str(), 706 ) { 707 continue; 708 } 709 710 // If the user has already started typing a module select 711 // then don't show irrelevant modules. 712 // For example: when the user has typed `mymodule.|` we 713 // should only show items from `mymodule`. 714 if let Some(typed_module) = &selected_module 715 && module_name != typed_module 716 { 717 continue; 718 } 719 720 completions.push(type_completion( 721 Some(module_name), 722 name, 723 type_, 724 &cursor_surroundings, 725 TypeCompletionContext::QualifiedType, 726 CompletionKind::ImportedModule, 727 )); 728 } 729 730 // Unqualified types 731 // Do not complete unqualified types if the user has already started 732 // typing a module select. 733 // For example, when the user has already typed `mymodule.|` we know 734 // unqualified module types are no longer relevant. 735 if selected_module.is_none() { 736 for unqualified in &import.unqualified_types { 737 if let Some(type_) = module.get_importable_type(&unqualified.name) { 738 completions.push(type_completion( 739 None, 740 unqualified.used_name(), 741 type_, 742 &cursor_surroundings, 743 TypeCompletionContext::UnqualifiedType, 744 CompletionKind::ImportedModule, 745 )) 746 } 747 } 748 } 749 } 750 751 // Importable modules 752 let first_import_pos = 753 position_of_first_definition_if_import(self.module, &self.src_line_numbers); 754 let first_is_import = first_import_pos.is_some(); 755 let import_location = first_import_pos.unwrap_or_default(); 756 757 let after_import_newlines = add_newlines_after_import( 758 import_location, 759 first_is_import, 760 &self.src_line_numbers, 761 self.src, 762 ); 763 for (module_full_name, module) in self.completable_modules_for_import() { 764 // Do not try to import the prelude. 765 if module_full_name == "gleam" { 766 continue; 767 } 768 769 let qualifier = module_full_name 770 .split('/') 771 .next_back() 772 .unwrap_or(module_full_name); 773 774 // If the user has already started a module select then don't show irrelevant modules. 775 // e.x. when the user has typed mymodule.| we should only show items from mymodule. 776 if let Some(selected_module) = &selected_module 777 && qualifier != selected_module 778 { 779 continue; 780 } 781 782 // Qualified types 783 for (name, type_) in &module.types { 784 if !self.is_suggestable_import( 785 &type_.publicity, 786 &type_.deprecation, 787 module.package.as_str(), 788 ) { 789 continue; 790 } 791 792 let mut completion = type_completion( 793 Some(qualifier), 794 name, 795 type_, 796 &cursor_surroundings, 797 TypeCompletionContext::QualifiedType, 798 CompletionKind::ImportableModule, 799 ); 800 add_import_to_completion( 801 &mut completion, 802 import_location, 803 module_full_name, 804 &after_import_newlines, 805 ); 806 completions.push(completion); 807 } 808 } 809 810 completions 811 } 812 813 /// Provides completions for when the context being edited is a value. 814 pub fn completion_values(&'a self) -> Vec<CompletionItem> { 815 let cursor_surroundings = self.get_phrase_surrounding_completion(); 816 let selected_module = cursor_surroundings.selected_module(); 817 let mut completions = vec![]; 818 let mod_name = self.module.name.as_str(); 819 let cursor = self.src_line_numbers.byte_index(*self.cursor_position); 820 821 // If the value for which we've been asked to give completions is a regular 822 // number it doesn't make sense to provide any completion! 823 // 824 // ```gleam 825 // // imagine you're typing a number... 826 // 2 827 // // ^ it would be quite annoying if suggestions popped up: 828 // // [list.window_by_2] 829 // // [int.to_base32] 830 // // ... 831 // ``` 832 // 833 // This usually happens in IDEs like Zed that still ask for completions 834 // even if the programmer is typing in a number. We can't control when 835 // an IDE asks for completions, but we can avoid replying nonsense in 836 // this context. 837 if parse_int_value(&cursor_surroundings.surrounding_text).is_some() 838 || LiteralFloatValue::parse(&cursor_surroundings.surrounding_text).is_some() 839 { 840 return vec![]; 841 } 842 843 // Keyword completions 844 if !cursor_surroundings.surrounding_text.is_empty() { 845 for keyword in ["panic", "todo", "echo"] { 846 if keyword.starts_with(cursor_surroundings.surrounding_text.as_str()) { 847 completions.push(self.keyword_completion(keyword, &cursor_surroundings)) 848 } 849 } 850 } 851 852 // Module and prelude values 853 // Do not complete direct module values if the user has already started 854 // typing a module select. 855 // e.x. when the user has typed mymodule.| we know local module and 856 // prelude values are no longer relevant. 857 if selected_module.is_none() { 858 // Find the function that the cursor is in and push completions for 859 // its arguments and local variables. 860 if let Some(function) = self 861 .module 862 .ast 863 .definitions 864 .functions 865 .iter() 866 .filter(|function| function.full_location().contains(cursor)) 867 .peekable() 868 .peek() 869 { 870 completions.extend( 871 LocalCompletion::new( 872 mod_name, 873 cursor_surroundings.surrounding_text_range, 874 cursor, 875 self.expected_type.clone(), 876 ) 877 .fn_completions(function), 878 ); 879 } 880 881 for (name, value) in &self.module.ast.type_info.values { 882 // Here we do not check for the internal attribute: we always want 883 // to show autocompletions for values defined in the same module, 884 // even if those are internal. 885 completions.push(self.value_completion( 886 None, 887 mod_name, 888 name, 889 value, 890 &cursor_surroundings, 891 CompletionKind::LocallyDefined, 892 )); 893 } 894 895 let mut push_prelude_completion = |label: &str, kind, type_: Arc<Type>| { 896 match match_type(&self.expected_type, &type_) { 897 TypeMatch::Incompatible => return, 898 TypeMatch::Matching | TypeMatch::Unknown => (), 899 }; 900 901 let label = label.to_string(); 902 let sort_text = Some(sort_text( 903 CompletionKind::Prelude, 904 &label, 905 match_type(&self.expected_type, &type_), 906 )); 907 completions.push(CompletionItem { 908 label, 909 detail: Some(PRELUDE_MODULE_NAME.into()), 910 kind: Some(kind), 911 sort_text, 912 ..CompletionItem::default() 913 }); 914 }; 915 916 for type_ in PreludeType::iter() { 917 match type_ { 918 PreludeType::Bool => { 919 push_prelude_completion( 920 "True", 921 CompletionItemKind::EnumMember, 922 type_::bool(), 923 ); 924 push_prelude_completion( 925 "False", 926 CompletionItemKind::EnumMember, 927 type_::bool(), 928 ); 929 } 930 PreludeType::Nil => { 931 push_prelude_completion( 932 "Nil", 933 CompletionItemKind::EnumMember, 934 type_::nil(), 935 ); 936 } 937 PreludeType::Result => { 938 push_prelude_completion( 939 "Ok", 940 CompletionItemKind::Constructor, 941 type_::result(type_::unbound_var(0), type_::unbound_var(0)), 942 ); 943 push_prelude_completion( 944 "Error", 945 CompletionItemKind::Constructor, 946 type_::result(type_::unbound_var(0), type_::unbound_var(0)), 947 ); 948 } 949 PreludeType::BitArray 950 | PreludeType::Float 951 | PreludeType::Int 952 | PreludeType::List 953 | PreludeType::String 954 | PreludeType::UtfCodepoint => {} 955 } 956 } 957 } 958 959 // Imported modules 960 for import in &self.module.ast.definitions.imports { 961 // The module may not be known of yet if it has not previously 962 // compiled yet in this editor session. 963 let Some(module) = self.compiler.get_module_interface(&import.module) else { 964 continue; 965 }; 966 967 // Qualified values 968 for (name, value) in &module.values { 969 if !self.is_suggestable_import( 970 &value.publicity, 971 &value.deprecation, 972 module.package.as_str(), 973 ) { 974 continue; 975 } 976 977 if let Some(module) = import.used_name() { 978 // If the user has already started a module select then 979 // don't show irrelevant modules. 980 // e.x. when the user has typed mymodule.| we should only 981 // show items from mymodule. 982 if let Some(input_mod_name) = &selected_module 983 && &module != input_mod_name 984 { 985 continue; 986 } 987 completions.push(self.value_completion( 988 Some(&module), 989 mod_name, 990 name, 991 value, 992 &cursor_surroundings, 993 CompletionKind::ImportedModule, 994 )); 995 } 996 } 997 998 // Unqualified values 999 // Do not complete unqualified values if the user has already 1000 // started typing a module select. 1001 // e.x. when the user has typed mymodule.| we know unqualified 1002 // module values are no longer relevant. 1003 if selected_module.is_none() { 1004 for unqualified in &import.unqualified_values { 1005 if let Some(value) = module.get_importable_value(&unqualified.name) { 1006 let name = unqualified.used_name(); 1007 completions.push(self.value_completion( 1008 None, 1009 mod_name, 1010 name, 1011 value, 1012 &cursor_surroundings, 1013 CompletionKind::ImportedModule, 1014 )) 1015 } 1016 } 1017 } 1018 } 1019 1020 // Importable modules 1021 let first_import_pos = 1022 position_of_first_definition_if_import(self.module, &self.src_line_numbers); 1023 let first_is_import = first_import_pos.is_some(); 1024 let import_location = first_import_pos.unwrap_or_default(); 1025 let after_import_newlines = add_newlines_after_import( 1026 import_location, 1027 first_is_import, 1028 &self.src_line_numbers, 1029 self.src, 1030 ); 1031 for (module_full_name, module) in self.completable_modules_for_import() { 1032 // Do not try to import the prelude. 1033 if module_full_name == "gleam" { 1034 continue; 1035 } 1036 let qualifier = module_full_name 1037 .split('/') 1038 .next_back() 1039 .unwrap_or(module_full_name); 1040 1041 // If the user has already started a module select then don't show 1042 // irrelevant modules. 1043 // e.x. when the user has typed mymodule.| we should only show items 1044 // from mymodule. 1045 if let Some(selected_module) = &selected_module 1046 && qualifier != selected_module 1047 { 1048 continue; 1049 } 1050 1051 // Qualified values 1052 for (name, value) in &module.values { 1053 if !self.is_suggestable_import( 1054 &value.publicity, 1055 &value.deprecation, 1056 module.package.as_str(), 1057 ) { 1058 continue; 1059 } 1060 1061 let mut completion = self.value_completion( 1062 Some(qualifier), 1063 module_full_name, 1064 name, 1065 value, 1066 &cursor_surroundings, 1067 CompletionKind::ImportableModule, 1068 ); 1069 1070 add_import_to_completion( 1071 &mut completion, 1072 import_location, 1073 module_full_name, 1074 &after_import_newlines, 1075 ); 1076 completions.push(completion); 1077 } 1078 } 1079 1080 completions 1081 } 1082 1083 // Looks up the type accessors for the given type 1084 fn type_accessors_from_modules( 1085 &'a self, 1086 importable_modules: &'a im::HashMap<EcoString, ModuleInterface>, 1087 type_: Arc<Type>, 1088 ) -> Option<&'a HashMap<EcoString, RecordAccessor>> { 1089 let type_ = collapse_links(type_); 1090 match type_.as_ref() { 1091 Type::Named { 1092 name, 1093 module, 1094 inferred_variant, 1095 .. 1096 } => importable_modules 1097 .get(module) 1098 .and_then(|interface| interface.accessors.get(name)) 1099 .filter(|accessor| { 1100 accessor.publicity.is_importable() || module == &self.module.name 1101 }) 1102 .map(|accessor| accessor.accessors_for_variant(*inferred_variant)), 1103 1104 Type::Fn { .. } | Type::Var { .. } | Type::Tuple { .. } => None, 1105 } 1106 } 1107 1108 /// Provides completions for field accessors when the context being edited 1109 /// is a custom type instance 1110 pub fn completion_field_accessors(&'a self, type_: Arc<Type>) -> Vec<CompletionItem> { 1111 if let Type::Named { 1112 publicity, 1113 package: type_package, 1114 .. 1115 } = type_.as_ref() 1116 && publicity.is_internal() 1117 && *type_package != self.module.ast.type_info.package 1118 { 1119 // If we're asking for field completions for an internal type that 1120 // is not defined in the current package, we don't want to show 1121 // anything. This makes it a lot harder to inadvertently rely on 1122 // internal implementation details without noticing. 1123 return vec![]; 1124 } 1125 1126 self.type_accessors_from_modules( 1127 self.compiler.project_compiler.get_importable_modules(), 1128 type_, 1129 ) 1130 .map(|accessors| { 1131 accessors 1132 .values() 1133 .map(|accessor| self.field_completion(&accessor.label, accessor.type_.clone())) 1134 .collect_vec() 1135 }) 1136 .unwrap_or_default() 1137 } 1138 1139 fn callable_field_map( 1140 &'a self, 1141 expr: &'a TypedExpr, 1142 importable_modules: &'a im::HashMap<EcoString, ModuleInterface>, 1143 ) -> Option<&'a FieldMap> { 1144 match expr { 1145 TypedExpr::Var { constructor, .. } => constructor.field_map(), 1146 TypedExpr::ModuleSelect { 1147 module_name, label, .. 1148 } => importable_modules 1149 .get(module_name) 1150 .and_then(|i| i.values.get(label)) 1151 .and_then(|a| a.field_map()), 1152 TypedExpr::Int { .. } 1153 | TypedExpr::Float { .. } 1154 | TypedExpr::String { .. } 1155 | TypedExpr::Block { .. } 1156 | TypedExpr::Pipeline { .. } 1157 | TypedExpr::Fn { .. } 1158 | TypedExpr::List { .. } 1159 | TypedExpr::Call { .. } 1160 | TypedExpr::BinOp { .. } 1161 | TypedExpr::Case { .. } 1162 | TypedExpr::RecordAccess { .. } 1163 | TypedExpr::PositionalAccess { .. } 1164 | TypedExpr::Tuple { .. } 1165 | TypedExpr::TupleIndex { .. } 1166 | TypedExpr::Todo { .. } 1167 | TypedExpr::Panic { .. } 1168 | TypedExpr::Echo { .. } 1169 | TypedExpr::BitArray { .. } 1170 | TypedExpr::RecordUpdate { .. } 1171 | TypedExpr::NegateBool { .. } 1172 | TypedExpr::NegateInt { .. } 1173 | TypedExpr::Invalid { .. } => None, 1174 } 1175 } 1176 1177 /// Provides completions for labels when the context being edited is a call 1178 /// that has labelled arguments that can be passed 1179 pub fn completion_labels( 1180 &'a self, 1181 fun: &TypedExpr, 1182 existing_arguments: &[CallArg<TypedExpr>], 1183 ) -> Vec<CompletionItem> { 1184 let fun_type = fun.type_().fn_types().map(|(arguments, _)| arguments); 1185 let already_included_labels = existing_arguments 1186 .iter() 1187 .filter_map(|argument| { 1188 // Record updates can have implicit arguments added as placeholders 1189 // by the compiler. Those are still arguments that could be typed 1190 // by the developer and used, so we don't want to include those 1191 // in the ones that have already been included and won't be recommended. 1192 if argument.is_implicit() && !argument.is_use_implicit_callback() { 1193 None 1194 } else { 1195 argument.label.clone() 1196 } 1197 }) 1198 .collect_vec(); 1199 let Some(field_map) = 1200 self.callable_field_map(fun, self.compiler.project_compiler.get_importable_modules()) 1201 else { 1202 return vec![]; 1203 }; 1204 1205 field_map 1206 .fields 1207 .iter() 1208 .filter(|field| !already_included_labels.contains(field.0)) 1209 .map(|(label, arg_index)| { 1210 let detail = fun_type.as_ref().and_then(|arguments| { 1211 arguments 1212 .get(*arg_index as usize) 1213 .map(|argument| Printer::new().pretty_print(argument, 0)) 1214 }); 1215 let label = format!("{label}:"); 1216 let sort_text = Some(sort_text(CompletionKind::Label, &label, TypeMatch::Unknown)); 1217 CompletionItem { 1218 label, 1219 detail, 1220 kind: Some(CompletionItemKind::Field), 1221 sort_text, 1222 ..CompletionItem::default() 1223 } 1224 }) 1225 .collect() 1226 } 1227 1228 fn root_package_name(&self) -> &str { 1229 self.compiler.project_compiler.config.name.as_str() 1230 } 1231 1232 // Checks based on the publicity and deprecation if something should be 1233 // suggested for import from root package 1234 fn is_suggestable_import( 1235 &self, 1236 publicity: &Publicity, 1237 deprecation: &Deprecation, 1238 package: &str, 1239 ) -> bool { 1240 // We always skip deprecated values 1241 if deprecation.is_deprecated() { 1242 return false; 1243 } 1244 1245 match publicity { 1246 // We skip private types as we never want those to appear in 1247 // completions. 1248 Publicity::Private => false, 1249 // We only skip internal types if those are not defined in 1250 // the root package. 1251 Publicity::Internal { .. } if package != self.root_package_name() => false, 1252 Publicity::Internal { .. } => true, 1253 // We never skip public types. 1254 Publicity::Public => true, 1255 } 1256 } 1257 1258 fn keyword_completion( 1259 &self, 1260 keyword: &str, 1261 cursor_surrounding: &CursorSurroundings, 1262 ) -> CompletionItem { 1263 let label = keyword.to_string(); 1264 1265 CompletionItem { 1266 label: label.clone(), 1267 kind: Some(CompletionItemKind::Keyword), 1268 detail: None, 1269 label_details: None, 1270 documentation: None, 1271 sort_text: Some(sort_text( 1272 CompletionKind::Keyword, 1273 &label, 1274 TypeMatch::Matching, 1275 )), 1276 text_edit: cursor_surrounding.to_text_edit(label), 1277 ..CompletionItem::default() 1278 } 1279 } 1280 1281 fn value_completion( 1282 &self, 1283 module_qualifier: Option<&str>, 1284 module_name: &str, 1285 name: &str, 1286 value: &type_::ValueConstructor, 1287 cursor_surrounding: &CursorSurroundings, 1288 priority: CompletionKind, 1289 ) -> CompletionItem { 1290 let type_match = match_type(&self.expected_type, &value.type_); 1291 let label = match module_qualifier { 1292 Some(module) => format!("{module}.{name}"), 1293 None => name.to_string(), 1294 }; 1295 1296 let type_ = Printer::new().pretty_print(&value.type_, 0); 1297 1298 let kind = Some(match value.variant { 1299 ValueConstructorVariant::LocalVariable { .. } => CompletionItemKind::Variable, 1300 ValueConstructorVariant::ModuleConstant { .. } => CompletionItemKind::Constant, 1301 ValueConstructorVariant::ModuleFn { .. } => CompletionItemKind::Function, 1302 ValueConstructorVariant::Record { arity: 0, .. } => CompletionItemKind::EnumMember, 1303 ValueConstructorVariant::Record { .. } => CompletionItemKind::Constructor, 1304 }); 1305 1306 let documentation = value.get_documentation().map(|documentation| { 1307 Documentation::MarkupContent(MarkupContent { 1308 kind: MarkupKind::Markdown, 1309 value: documentation.into(), 1310 }) 1311 }); 1312 1313 CompletionItem { 1314 label: label.clone(), 1315 kind, 1316 detail: Some(type_), 1317 label_details: Some(CompletionItemLabelDetails { 1318 detail: None, 1319 description: Some(module_name.into()), 1320 }), 1321 documentation, 1322 sort_text: Some(sort_text(priority, &label, type_match)), 1323 text_edit: cursor_surrounding.to_text_edit(label), 1324 ..CompletionItem::default() 1325 } 1326 } 1327 1328 fn field_completion(&self, label: &str, type_: Arc<Type>) -> CompletionItem { 1329 let type_match = match_type(&self.expected_type, &type_); 1330 let type_ = Printer::new().pretty_print(&type_, 0); 1331 1332 CompletionItem { 1333 label: label.into(), 1334 kind: Some(CompletionItemKind::Field), 1335 detail: Some(type_), 1336 sort_text: Some(sort_text(CompletionKind::FieldAccessor, label, type_match)), 1337 ..CompletionItem::default() 1338 } 1339 } 1340} 1341 1342fn add_import_to_completion( 1343 item: &mut CompletionItem, 1344 import_location: Position, 1345 module_full_name: &EcoString, 1346 insert_newlines: &Newlines, 1347) { 1348 item.additional_text_edits = Some(vec![get_import_edit( 1349 import_location, 1350 module_full_name, 1351 insert_newlines, 1352 )]); 1353} 1354 1355fn type_completion( 1356 module: Option<&str>, 1357 name: &str, 1358 type_: &TypeConstructor, 1359 cursor_surrounding: &CursorSurroundings, 1360 type_completion_context: TypeCompletionContext, 1361 priority: CompletionKind, 1362) -> CompletionItem { 1363 let label = match module { 1364 Some(module) => format!("{module}.{name}"), 1365 None => name.to_string(), 1366 }; 1367 1368 let kind = Some(if type_.type_.is_variable() { 1369 CompletionItemKind::Variable 1370 } else { 1371 CompletionItemKind::Class 1372 }); 1373 1374 let completion_text = match type_completion_context { 1375 TypeCompletionContext::UnqualifiedImport => format!("{{type {label}}}"), 1376 TypeCompletionContext::UnqualifiedImportWithinBraces => format!("type {label}"), 1377 TypeCompletionContext::QualifiedType | TypeCompletionContext::UnqualifiedType => { 1378 label.clone() 1379 } 1380 }; 1381 1382 CompletionItem { 1383 label: label.clone(), 1384 kind, 1385 detail: Some("Type".into()), 1386 sort_text: Some(sort_text(priority, &label, TypeMatch::Unknown)), 1387 text_edit: cursor_surrounding.to_text_edit(completion_text), 1388 ..CompletionItem::default() 1389 } 1390} 1391 1392fn match_type(expected_type: &Option<Arc<Type>>, type_: &Type) -> TypeMatch { 1393 if let Some(expected_type) = expected_type { 1394 // If the type of the value we are completing is unbound, that 1395 // technically means that all types match, which doesn't give us 1396 // any useful information so we treat it as not knowing what the 1397 // type is, which generally is the case. 1398 if expected_type.is_unbound() { 1399 TypeMatch::Unknown 1400 } 1401 // We also want to prioritise functions which return the desired type, 1402 // as often the user's intention will be to write a function call. 1403 else if let Some((_, return_)) = type_.fn_types() 1404 && expected_type.same_as(&return_) 1405 { 1406 TypeMatch::Matching 1407 } else if expected_type.same_as(type_) { 1408 TypeMatch::Matching 1409 } else { 1410 TypeMatch::Incompatible 1411 } 1412 } else { 1413 TypeMatch::Unknown 1414 } 1415} 1416 1417pub struct LocalCompletion<'a> { 1418 mod_name: &'a str, 1419 insert_range: Range, 1420 cursor: u32, 1421 completions: HashMap<EcoString, CompletionItem>, 1422 expected_type: Option<Arc<Type>>, 1423} 1424 1425impl<'a> LocalCompletion<'a> { 1426 pub fn new( 1427 mod_name: &'a str, 1428 insert_range: Range, 1429 cursor: u32, 1430 expected_type: Option<Arc<Type>>, 1431 ) -> Self { 1432 Self { 1433 mod_name, 1434 insert_range, 1435 cursor, 1436 completions: HashMap::new(), 1437 expected_type, 1438 } 1439 } 1440 1441 /// Generates completion items for a given function, including its arguments 1442 /// and local variables. 1443 pub fn fn_completions( 1444 mut self, 1445 fun: &'a Function<Arc<Type>, TypedExpr>, 1446 ) -> Vec<CompletionItem> { 1447 // Add function arguments to completions 1448 self.visit_fn_arguments(&fun.arguments); 1449 1450 // Visit the function body statements 1451 for statement in &fun.body { 1452 // Visit the statement to find local variables 1453 self.visit_typed_statement(statement); 1454 } 1455 1456 self.completions.into_values().collect_vec() 1457 } 1458 1459 fn visit_fn_arguments(&mut self, arguments: &[Arg<Arc<Type>>]) { 1460 for argument in arguments { 1461 if let Some(name) = argument.get_variable_name() { 1462 self.push_completion(name, argument.type_.clone()); 1463 } 1464 } 1465 } 1466 1467 fn push_completion(&mut self, name: &EcoString, type_: Arc<Type>) { 1468 if name.is_empty() || name.starts_with('_') { 1469 return; 1470 } 1471 1472 _ = self.completions.insert( 1473 name.clone(), 1474 self.local_value_completion(self.mod_name, name, type_, self.insert_range), 1475 ); 1476 } 1477 1478 fn local_value_completion( 1479 &self, 1480 module_name: &str, 1481 name: &str, 1482 type_: Arc<Type>, 1483 insert_range: Range, 1484 ) -> CompletionItem { 1485 let type_match = match_type(&self.expected_type, &type_); 1486 1487 let label = name.to_string(); 1488 let type_ = Printer::new().pretty_print(&type_, 0); 1489 1490 let documentation = Documentation::MarkupContent(MarkupContent { 1491 kind: MarkupKind::Markdown, 1492 value: String::from("A locally defined variable."), 1493 }); 1494 1495 CompletionItem { 1496 label: label.clone(), 1497 kind: Some(CompletionItemKind::Variable), 1498 detail: Some(type_), 1499 label_details: Some(CompletionItemLabelDetails { 1500 detail: None, 1501 description: Some(module_name.into()), 1502 }), 1503 documentation: Some(documentation), 1504 sort_text: Some(sort_text( 1505 CompletionKind::LocallyDefined, 1506 &label, 1507 type_match, 1508 )), 1509 text_edit: Some(CompletionItemTextEdit::TextEdit(TextEdit { 1510 range: insert_range, 1511 new_text: label.clone(), 1512 })), 1513 ..CompletionItem::default() 1514 } 1515 } 1516} 1517 1518impl<'ast> Visit<'ast> for LocalCompletion<'_> { 1519 fn visit_typed_statement(&mut self, statement: &'ast ast::TypedStatement) { 1520 // We only want to suggest local variables that are defined before 1521 // the cursor 1522 if statement.location().start >= self.cursor { 1523 return; 1524 } 1525 ast::visit::visit_typed_statement(self, statement); 1526 } 1527 1528 /// Visits a typed assignment, selectively processing either the value or the pattern 1529 /// based on the cursor position. 1530 /// - If the cursor is within the assignment It visits only the value expression. 1531 /// This avoids suggesting variables that are being defined in the assignment itself. 1532 /// - If the cursor is outside the assignment It visits only the pattern. 1533 /// This prevents suggesting variables that might be out of scope. 1534 fn visit_typed_assignment(&mut self, assignment: &'ast ast::TypedAssignment) { 1535 if assignment.location.contains(self.cursor) { 1536 self.visit_typed_expr(&assignment.value); 1537 } else { 1538 self.visit_typed_pattern(&assignment.pattern); 1539 } 1540 } 1541 1542 fn visit_typed_expr_fn( 1543 &mut self, 1544 location: &'ast ast::SrcSpan, 1545 _: &'ast Arc<Type>, 1546 _: &'ast FunctionLiteralKind, 1547 arguments: &'ast [ast::TypedArg], 1548 body: &'ast Vec1<ast::TypedStatement>, 1549 _: &'ast Option<ast::TypeAst>, 1550 ) { 1551 // If we are completing after the function body, any locally defined 1552 // variables are now out of scope so we don't register any. 1553 if self.cursor >= location.end { 1554 return; 1555 } 1556 self.visit_fn_arguments(arguments); 1557 for statement in body { 1558 self.visit_typed_statement(statement); 1559 } 1560 } 1561 1562 fn visit_typed_expr_block( 1563 &mut self, 1564 location: &'ast ast::SrcSpan, 1565 statements: &'ast [ast::TypedStatement], 1566 ) { 1567 // If we are completing after the block, any locally defined variables 1568 // are now out of scope so we don't register any. 1569 if self.cursor >= location.end { 1570 return; 1571 } 1572 ast::visit::visit_typed_expr_block(self, location, statements); 1573 } 1574 1575 fn visit_typed_clause(&mut self, clause: &'ast ast::TypedClause) { 1576 // Any code which comes before or after a case clause cannot access any 1577 // of the variables defined within it, so we ignore this clause if so. 1578 if self.cursor < clause.location.start || self.cursor > clause.location.end { 1579 return; 1580 } 1581 ast::visit::visit_typed_clause(self, clause); 1582 } 1583 1584 fn visit_typed_pattern_variable( 1585 &mut self, 1586 _: &'ast ast::SrcSpan, 1587 name: &'ast EcoString, 1588 type_: &'ast Arc<Type>, 1589 _origin: &'ast VariableOrigin, 1590 ) { 1591 self.push_completion(name, type_.clone()); 1592 } 1593 1594 fn visit_typed_pattern_discard( 1595 &mut self, 1596 _: &'ast ast::SrcSpan, 1597 name: &'ast EcoString, 1598 type_: &'ast Arc<Type>, 1599 ) { 1600 self.push_completion(name, type_.clone()); 1601 } 1602 1603 fn visit_typed_pattern_string_prefix( 1604 &mut self, 1605 _: &'ast ast::SrcSpan, 1606 _: &'ast ast::SrcSpan, 1607 _: &'ast Option<(EcoString, ast::SrcSpan)>, 1608 _: &'ast ast::SrcSpan, 1609 _: &'ast EcoString, 1610 right_side_assignment: &'ast ast::AssignName, 1611 ) { 1612 self.push_completion(right_side_assignment.name(), type_::string()); 1613 } 1614 1615 fn visit_typed_pattern_assign( 1616 &mut self, 1617 _: &'ast ast::SrcSpan, 1618 name: &'ast EcoString, 1619 pattern: &'ast Pattern<Arc<Type>>, 1620 ) { 1621 self.visit_typed_pattern(pattern); 1622 self.push_completion(name, pattern.type_()); 1623 } 1624}