Fork of daniellemaywood.uk/gleam — Wasm codegen work
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: ¶ms.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}