1use std::collections::{BTreeSet, HashMap, HashSet};
2use std::fs;
3use std::hash::{Hash, Hasher};
4use std::path::PathBuf;
5use std::sync::{Mutex, OnceLock};
6
7use lsp_types::{
8 CodeAction, CodeActionKind, CodeActionOrCommand, CompletionItem, CompletionItemKind,
9 Diagnostic, DiagnosticSeverity, DocumentSymbol, GotoDefinitionResponse, Hover, HoverContents,
10 Location, MarkupContent, MarkupKind, Position, Range, SymbolKind, TextEdit, Url, WorkspaceEdit,
11};
12use rex_ast::{
13 ClassDecl, ClassMethodSig, CompilationUnit, Decl, DeclareFnDecl, Expr, FnDecl, ImportDecl,
14 ImportPath, InstanceDecl, InstanceMethodImpl, NameRef, Pattern, Symbol, TypeConstraint,
15 TypeDecl, TypeExpr, TypeVariant, Var,
16};
17use rex_ast::{Position as RexPosition, Span, Spanned};
18use rex_engine::{Engine, EngineError, ModuleError};
19use rex_parser::{
20 error::ParseError,
21 lexer::{LexicalError, Token, Tokens},
22 parse_with_tokens,
23};
24use rex_typesystem::{
25 error::TypeError as TsTypeError,
26 inference::infer_typed,
27 types::{BuiltinTypeId, Scheme, Type, TypeKind, TypedExpr, TypedExprKind, Types},
28 typesystem::{PreparedInstanceDecl, TypeSystem, instantiate},
29 unification::unify,
30};
31use rex_util::{resolve_local_import_path, sha256_hex, stdlib_source};
32use serde_json::{Value, json, to_value};
33
34const MAX_DIAGNOSTICS: usize = 50;
35pub const CMD_EXPECTED_TYPE_AT: &str = "rex.expectedTypeAt";
36pub const CMD_FUNCTIONS_PRODUCING_EXPECTED_TYPE_AT: &str = "rex.functionsProducingExpectedTypeAt";
37pub const CMD_FUNCTIONS_ACCEPTING_INFERRED_TYPE_AT: &str = "rex.functionsAcceptingInferredTypeAt";
38pub const CMD_ADAPTERS_FROM_INFERRED_TO_EXPECTED_AT: &str = "rex.adaptersFromInferredToExpectedAt";
39pub const CMD_FUNCTIONS_COMPATIBLE_WITH_IN_SCOPE_VALUES_AT: &str =
40 "rex.functionsCompatibleWithInScopeValuesAt";
41pub const CMD_HOLES_EXPECTED_TYPES: &str = "rex.holesExpectedTypes";
42pub const CMD_SEMANTIC_LOOP_STEP: &str = "rex.semanticLoopStep";
43pub const CMD_SEMANTIC_LOOP_APPLY_QUICK_FIX_AT: &str = "rex.semanticLoopApplyQuickFixAt";
44pub const CMD_SEMANTIC_LOOP_APPLY_BEST_QUICK_FIXES_AT: &str =
45 "rex.semanticLoopApplyBestQuickFixesAt";
46const NO_IMPROVEMENT_STREAK_LIMIT: usize = 2;
47pub const MAX_SEMANTIC_ENV_SCHEMES_SCAN: usize = 1024;
48pub const MAX_SEMANTIC_IN_SCOPE_VALUES: usize = 128;
49pub const MAX_SEMANTIC_CANDIDATES: usize = 64;
50pub const MAX_SEMANTIC_HOLE_FILL_ARITY: usize = 8;
51pub const MAX_SEMANTIC_HOLES: usize = 128;
52const BUILTIN_TYPES: &[&str] = &[
53 "u8", "u16", "u32", "u64", "i8", "i16", "i32", "i64", "f32", "f64", "bool", "string", "uuid",
54 "datetime", "Dict", "List", "Array", "Option", "Result",
55];
56const BUILTIN_VALUES: &[&str] = &["true", "false", "null", "Some", "None", "Ok", "Err"];
57
58#[derive(Debug)]
59enum TokenizeOrParseError {
60 Lex(LexicalError),
61 Parse(Vec<ParseError>),
62}
63
64#[derive(Clone, Copy, Debug, Eq, PartialEq)]
65pub enum BulkQuickFixStrategy {
66 Conservative,
67 Aggressive,
68}
69
70impl BulkQuickFixStrategy {
71 pub fn parse(s: &str) -> Self {
72 if s.eq_ignore_ascii_case("aggressive") {
73 Self::Aggressive
74 } else {
75 Self::Conservative
76 }
77 }
78
79 pub fn as_str(self) -> &'static str {
80 match self {
81 Self::Conservative => "conservative",
82 Self::Aggressive => "aggressive",
83 }
84 }
85}
86
87#[derive(Clone)]
88struct CachedParse {
89 hash: u64,
90 tokens: Tokens,
91 program: CompilationUnit,
92}
93
94fn text_hash(text: &str) -> u64 {
95 let mut hasher = std::collections::hash_map::DefaultHasher::new();
96 text.hash(&mut hasher);
97 hasher.finish()
98}
99
100fn parse_cache() -> &'static Mutex<HashMap<Url, CachedParse>> {
101 static CACHE: OnceLock<Mutex<HashMap<Url, CachedParse>>> = OnceLock::new();
102 CACHE.get_or_init(|| Mutex::new(HashMap::new()))
103}
104
105fn semantic_candidate_values(ts: &TypeSystem) -> Vec<(Symbol, Vec<Scheme>)> {
106 let mut out = Vec::new();
107 let mut scanned = 0usize;
108 for (name, schemes) in &ts.env.values {
109 if scanned >= MAX_SEMANTIC_ENV_SCHEMES_SCAN {
110 break;
111 }
112 let remaining = MAX_SEMANTIC_ENV_SCHEMES_SCAN - scanned;
113 let kept = schemes.iter().take(remaining).cloned().collect::<Vec<_>>();
114 if kept.is_empty() {
115 continue;
116 }
117 scanned += kept.len();
118 out.push((name.clone(), kept));
119 }
120 out
121}
122
123pub fn clear_parse_cache(uri: &Url) {
124 let Ok(mut cache) = parse_cache().lock() else {
125 return;
126 };
127 cache.remove(uri);
128}
129
130#[cfg(not(target_arch = "wasm32"))]
131fn uri_to_file_path(uri: &Url) -> Option<PathBuf> {
132 uri.to_file_path().ok()
133}
134
135#[cfg(target_arch = "wasm32")]
136fn uri_to_file_path(_uri: &Url) -> Option<PathBuf> {
137 None
138}
139
140#[cfg(not(target_arch = "wasm32"))]
141fn url_from_file_path(path: &std::path::Path) -> Option<Url> {
142 Url::from_file_path(path).ok()
143}
144
145#[cfg(target_arch = "wasm32")]
146fn url_from_file_path(_path: &std::path::Path) -> Option<Url> {
147 None
148}
149
150fn tokenize_and_parse(
151 text: &str,
152) -> std::result::Result<(Tokens, CompilationUnit), TokenizeOrParseError> {
153 let tokens = Token::tokenize(text).map_err(TokenizeOrParseError::Lex)?;
154 let program = parse_with_tokens(tokens.clone()).map_err(TokenizeOrParseError::Parse)?;
155 Ok((tokens, program))
156}
157
158fn tokenize_and_parse_cached(
159 uri: &Url,
160 text: &str,
161) -> std::result::Result<(Tokens, CompilationUnit), TokenizeOrParseError> {
162 let hash = text_hash(text);
163 if let Ok(cache) = parse_cache().lock()
164 && let Some(cached) = cache.get(uri)
165 && cached.hash == hash
166 {
167 return Ok((cached.tokens.clone(), cached.program.clone()));
168 }
169
170 let (tokens, program) = tokenize_and_parse(text)?;
171 if let Ok(mut cache) = parse_cache().lock() {
172 cache.insert(
173 uri.clone(),
174 CachedParse {
175 hash,
176 tokens: tokens.clone(),
177 program: program.clone(),
178 },
179 );
180 }
181 Ok((tokens, program))
182}
183
184#[derive(Clone)]
185pub struct ImportModuleInfo {
186 #[cfg_attr(target_arch = "wasm32", allow(dead_code))]
187 path: Option<PathBuf>,
188 value_map: HashMap<Symbol, Symbol>, type_map: HashMap<Symbol, Symbol>,
190 class_map: HashMap<Symbol, Symbol>,
191 #[cfg_attr(target_arch = "wasm32", allow(dead_code))]
192 export_defs: HashMap<String, Span>,
193}
194
195fn is_ident_like(name: &str) -> bool {
196 let mut chars = name.chars();
197 let Some(first) = chars.next() else {
198 return false;
199 };
200 if !(first.is_ascii_alphabetic() || first == '_') {
201 return false;
202 }
203 chars.all(|c| c.is_ascii_alphanumeric() || c == '_')
204}
205
206fn prelude_completion_values() -> &'static Vec<(String, CompletionItemKind)> {
207 static PRELUDE_VALUES: OnceLock<Vec<(String, CompletionItemKind)>> = OnceLock::new();
208 PRELUDE_VALUES.get_or_init(|| {
209 let ts = match TypeSystem::new_with_prelude() {
210 Ok(ts) => ts,
211 Err(e) => {
212 eprintln!("rex-lsp: failed to build prelude for completions: {e}");
213 return Vec::new();
214 }
215 };
216 let mut out = Vec::new();
217 for (name, schemes) in ts.env.values.iter() {
218 let name = name.as_ref().to_string();
219 if !is_ident_like(&name) {
220 continue;
221 }
222 let is_fun = schemes
223 .iter()
224 .any(|scheme| matches!(scheme.typ.as_ref(), TypeKind::Fun(..)));
225 let kind = if is_fun {
226 CompletionItemKind::FUNCTION
227 } else {
228 CompletionItemKind::VARIABLE
229 };
230 out.push((name, kind));
231 }
232 out.sort_by(|(a, _), (b, _)| a.cmp(b));
233 out
234 })
235}
236
237fn module_prefix(hash: &str) -> String {
238 let short = if hash.len() >= 16 { &hash[..16] } else { hash };
239 format!("@m{short}")
240}
241
242fn inject_program_decls(
243 ts: &mut TypeSystem,
244 compilation_unit: &CompilationUnit,
245 want_prepared_instance: Option<usize>,
246) -> std::result::Result<InjectedDecls, TsTypeError> {
247 let mut instances = Vec::new();
248 let mut prepared_target = None;
249 let mut pending_non_instances: Vec<Decl> = Vec::new();
250
251 let flush_non_instances =
252 |ts: &mut TypeSystem, pending: &mut Vec<Decl>| -> std::result::Result<(), TsTypeError> {
253 if pending.is_empty() {
254 return Ok(());
255 }
256 ts.register_decls(pending)?;
257 pending.clear();
258 Ok(())
259 };
260
261 for (idx, decl) in compilation_unit.decls.iter().enumerate() {
262 match decl {
263 Decl::Instance(inst_decl) => {
264 flush_non_instances(ts, &mut pending_non_instances)?;
265 let prepared = ts.register_instance_decl(inst_decl)?;
266 if want_prepared_instance.is_some_and(|want| want == idx) {
267 prepared_target = Some(prepared.clone());
268 }
269 instances.push((idx, prepared));
270 }
271 _ => pending_non_instances.push(decl.clone()),
272 }
273 }
274 flush_non_instances(ts, &mut pending_non_instances)?;
275
276 Ok((instances, prepared_target))
277}
278
279type PreparedInstance = (usize, PreparedInstanceDecl);
280type InjectedDecls = (Vec<PreparedInstance>, Option<PreparedInstanceDecl>);
281
282fn rewrite_type_expr(ty: &TypeExpr, type_map: &HashMap<Symbol, Symbol>) -> TypeExpr {
283 match ty {
284 TypeExpr::Name(span, name) => {
285 if let Some(new) = type_map.get(&name.to_dotted_symbol()) {
286 TypeExpr::Name(*span, NameRef::Unqualified(new.clone()))
287 } else {
288 TypeExpr::Name(*span, name.clone())
289 }
290 }
291 TypeExpr::App(span, f, x) => TypeExpr::App(
292 *span,
293 Box::new(rewrite_type_expr(f, type_map)),
294 Box::new(rewrite_type_expr(x, type_map)),
295 ),
296 TypeExpr::Fun(span, a, b) => TypeExpr::Fun(
297 *span,
298 Box::new(rewrite_type_expr(a, type_map)),
299 Box::new(rewrite_type_expr(b, type_map)),
300 ),
301 TypeExpr::Tuple(span, elems) => TypeExpr::Tuple(
302 *span,
303 elems
304 .iter()
305 .map(|e| rewrite_type_expr(e, type_map))
306 .collect(),
307 ),
308 TypeExpr::Record(span, fields) => TypeExpr::Record(
309 *span,
310 fields
311 .iter()
312 .map(|(name, ty)| (name.clone(), rewrite_type_expr(ty, type_map)))
313 .collect(),
314 ),
315 }
316}
317
318fn collect_pattern_bindings(pat: &Pattern, out: &mut Vec<Symbol>) {
319 match pat {
320 Pattern::Wildcard(..) => {}
321 Pattern::Var(v) => out.push(v.name.clone()),
322 Pattern::Named(_, _, args) => {
323 for arg in args {
324 collect_pattern_bindings(arg, out);
325 }
326 }
327 Pattern::Tuple(_, elems) | Pattern::List(_, elems) => {
328 for elem in elems {
329 collect_pattern_bindings(elem, out);
330 }
331 }
332 Pattern::Cons(_, head, tail) => {
333 collect_pattern_bindings(head, out);
334 collect_pattern_bindings(tail, out);
335 }
336 Pattern::Dict(_, fields) => {
337 for (_, pat) in fields {
338 collect_pattern_bindings(pat, out);
339 }
340 }
341 }
342}
343
344fn rewrite_import_projections_expr(
345 expr: &Expr,
346 bound: &mut BTreeSet<Symbol>,
347 imports: &HashMap<Symbol, ImportModuleInfo>,
348 diagnostics: &mut Vec<Diagnostic>,
349) -> Expr {
350 match expr {
351 Expr::Project(span, base, field) => {
352 if let Expr::Var(v) = base.as_ref()
353 && !bound.contains(&v.name)
354 && let Some(info) = imports.get(&v.name)
355 {
356 if let Some(internal) = info.value_map.get(field) {
357 return Expr::Var(Var {
358 span: *span,
359 name: internal.clone(),
360 });
361 }
362 diagnostics.push(diagnostic_for_span(
363 *span,
364 format!("module `{}` does not export `{}`", v.name, field),
365 ));
366 }
367 Expr::Project(
368 *span,
369 std::sync::Arc::new(rewrite_import_projections_expr(
370 base,
371 bound,
372 imports,
373 diagnostics,
374 )),
375 field.clone(),
376 )
377 }
378 Expr::Var(v) => Expr::Var(v.clone()),
379 Expr::Bool(span, v) => Expr::Bool(*span, *v),
380 Expr::Uint(span, v) => Expr::Uint(*span, *v),
381 Expr::Int(span, v) => Expr::Int(*span, *v),
382 Expr::Float(span, v) => Expr::Float(*span, *v),
383 Expr::String(span, v) => Expr::String(*span, v.clone()),
384 Expr::Uuid(span, v) => Expr::Uuid(*span, *v),
385 Expr::DateTime(span, v) => Expr::DateTime(*span, *v),
386 Expr::Hole(span) => Expr::Hole(*span),
387 Expr::Tuple(span, elems) => Expr::Tuple(
388 *span,
389 elems
390 .iter()
391 .map(|e| {
392 std::sync::Arc::new(rewrite_import_projections_expr(
393 e,
394 bound,
395 imports,
396 diagnostics,
397 ))
398 })
399 .collect(),
400 ),
401 Expr::List(span, elems) => Expr::List(
402 *span,
403 elems
404 .iter()
405 .map(|e| {
406 std::sync::Arc::new(rewrite_import_projections_expr(
407 e,
408 bound,
409 imports,
410 diagnostics,
411 ))
412 })
413 .collect(),
414 ),
415 Expr::Dict(span, kvs) => Expr::Dict(
416 *span,
417 kvs.iter()
418 .map(|(k, v)| {
419 (
420 k.clone(),
421 std::sync::Arc::new(rewrite_import_projections_expr(
422 v,
423 bound,
424 imports,
425 diagnostics,
426 )),
427 )
428 })
429 .collect(),
430 ),
431 Expr::RecordUpdate(span, base, updates) => Expr::RecordUpdate(
432 *span,
433 std::sync::Arc::new(rewrite_import_projections_expr(
434 base,
435 bound,
436 imports,
437 diagnostics,
438 )),
439 updates
440 .iter()
441 .map(|(k, v)| {
442 (
443 k.clone(),
444 std::sync::Arc::new(rewrite_import_projections_expr(
445 v,
446 bound,
447 imports,
448 diagnostics,
449 )),
450 )
451 })
452 .collect(),
453 ),
454 Expr::App(span, f, x) => Expr::App(
455 *span,
456 std::sync::Arc::new(rewrite_import_projections_expr(
457 f,
458 bound,
459 imports,
460 diagnostics,
461 )),
462 std::sync::Arc::new(rewrite_import_projections_expr(
463 x,
464 bound,
465 imports,
466 diagnostics,
467 )),
468 ),
469 Expr::Lam(span, scope, param, ann, constraints, body) => {
470 let ann = ann
471 .as_ref()
472 .map(|t| rewrite_import_projections_type_expr(t, bound, imports));
473 let constraints = constraints
474 .iter()
475 .map(|c| TypeConstraint {
476 class: rewrite_import_projections_class_name(&c.class, bound, imports),
477 typ: rewrite_import_projections_type_expr(&c.typ, bound, imports),
478 })
479 .collect();
480 bound.insert(param.name.clone());
481 let out = Expr::Lam(
482 *span,
483 scope.clone(),
484 param.clone(),
485 ann,
486 constraints,
487 std::sync::Arc::new(rewrite_import_projections_expr(
488 body,
489 bound,
490 imports,
491 diagnostics,
492 )),
493 );
494 bound.remove(¶m.name);
495 out
496 }
497 Expr::Let(span, var, ann, val, body) => {
498 let val = std::sync::Arc::new(rewrite_import_projections_expr(
499 val,
500 bound,
501 imports,
502 diagnostics,
503 ));
504 bound.insert(var.name.clone());
505 let body = std::sync::Arc::new(rewrite_import_projections_expr(
506 body,
507 bound,
508 imports,
509 diagnostics,
510 ));
511 bound.remove(&var.name);
512 Expr::Let(
513 *span,
514 var.clone(),
515 ann.as_ref()
516 .map(|t| rewrite_import_projections_type_expr(t, bound, imports)),
517 val,
518 body,
519 )
520 }
521 Expr::LetRec(span, bindings, body) => {
522 let anns: Vec<Option<TypeExpr>> = bindings
523 .iter()
524 .map(|(_, ann, _)| {
525 ann.as_ref()
526 .map(|t| rewrite_import_projections_type_expr(t, bound, imports))
527 })
528 .collect();
529 let names: Vec<Symbol> = bindings
530 .iter()
531 .map(|(var, _, _)| var.name.clone())
532 .collect();
533 for name in &names {
534 bound.insert(name.clone());
535 }
536 let bindings = bindings
537 .iter()
538 .zip(anns)
539 .map(|((var, _ann, def), ann)| {
540 (
541 var.clone(),
542 ann,
543 std::sync::Arc::new(rewrite_import_projections_expr(
544 def,
545 bound,
546 imports,
547 diagnostics,
548 )),
549 )
550 })
551 .collect();
552 let body = std::sync::Arc::new(rewrite_import_projections_expr(
553 body,
554 bound,
555 imports,
556 diagnostics,
557 ));
558 for name in &names {
559 bound.remove(name);
560 }
561 Expr::LetRec(*span, bindings, body)
562 }
563 Expr::Ite(span, c, t, e) => Expr::Ite(
564 *span,
565 std::sync::Arc::new(rewrite_import_projections_expr(
566 c,
567 bound,
568 imports,
569 diagnostics,
570 )),
571 std::sync::Arc::new(rewrite_import_projections_expr(
572 t,
573 bound,
574 imports,
575 diagnostics,
576 )),
577 std::sync::Arc::new(rewrite_import_projections_expr(
578 e,
579 bound,
580 imports,
581 diagnostics,
582 )),
583 ),
584 Expr::Match(span, scrutinee, arms) => {
585 let scrutinee = std::sync::Arc::new(rewrite_import_projections_expr(
586 scrutinee,
587 bound,
588 imports,
589 diagnostics,
590 ));
591 let mut out_arms = Vec::new();
592 for (pat, arm_expr) in arms {
593 let mut binds = Vec::new();
594 collect_pattern_bindings(pat, &mut binds);
595 for b in &binds {
596 bound.insert(b.clone());
597 }
598 let arm_expr = std::sync::Arc::new(rewrite_import_projections_expr(
599 arm_expr,
600 bound,
601 imports,
602 diagnostics,
603 ));
604 for b in &binds {
605 bound.remove(b);
606 }
607 out_arms.push((pat.clone(), arm_expr));
608 }
609 Expr::Match(*span, scrutinee, out_arms)
610 }
611 Expr::Ann(span, e, t) => Expr::Ann(
612 *span,
613 std::sync::Arc::new(rewrite_import_projections_expr(
614 e,
615 bound,
616 imports,
617 diagnostics,
618 )),
619 rewrite_import_projections_type_expr(t, bound, imports),
620 ),
621 }
622}
623
624fn qualified_alias_member(name: &NameRef) -> Option<(&Symbol, &Symbol)> {
625 match name {
626 NameRef::Qualified(_, segments) if segments.len() == 2 => {
627 Some((&segments[0], &segments[1]))
628 }
629 _ => None,
630 }
631}
632
633fn rewrite_import_projections_class_name(
634 class: &NameRef,
635 bound: &BTreeSet<Symbol>,
636 imports: &HashMap<Symbol, ImportModuleInfo>,
637) -> NameRef {
638 let Some((alias, member)) = qualified_alias_member(class) else {
639 return class.clone();
640 };
641 if bound.contains(alias) {
642 return class.clone();
643 }
644 let Some(info) = imports.get(alias) else {
645 return class.clone();
646 };
647 info.class_map
648 .get(member)
649 .map(|s| NameRef::Unqualified(s.clone()))
650 .unwrap_or_else(|| class.clone())
651}
652
653fn rewrite_import_projections_type_expr(
654 ty: &TypeExpr,
655 bound: &BTreeSet<Symbol>,
656 imports: &HashMap<Symbol, ImportModuleInfo>,
657) -> TypeExpr {
658 match ty {
659 TypeExpr::Name(span, name) => {
660 let Some((alias, member)) = qualified_alias_member(name) else {
661 return TypeExpr::Name(*span, name.clone());
662 };
663 if bound.contains(alias) {
664 return TypeExpr::Name(*span, name.clone());
665 }
666 let Some(info) = imports.get(alias) else {
667 return TypeExpr::Name(*span, name.clone());
668 };
669 if let Some(new) = info.type_map.get(member) {
670 TypeExpr::Name(*span, NameRef::Unqualified(new.clone()))
671 } else if let Some(new) = info.class_map.get(member) {
672 TypeExpr::Name(*span, NameRef::Unqualified(new.clone()))
673 } else {
674 TypeExpr::Name(*span, name.clone())
675 }
676 }
677 TypeExpr::App(span, f, x) => TypeExpr::App(
678 *span,
679 Box::new(rewrite_import_projections_type_expr(f, bound, imports)),
680 Box::new(rewrite_import_projections_type_expr(x, bound, imports)),
681 ),
682 TypeExpr::Fun(span, a, b) => TypeExpr::Fun(
683 *span,
684 Box::new(rewrite_import_projections_type_expr(a, bound, imports)),
685 Box::new(rewrite_import_projections_type_expr(b, bound, imports)),
686 ),
687 TypeExpr::Tuple(span, elems) => TypeExpr::Tuple(
688 *span,
689 elems
690 .iter()
691 .map(|e| rewrite_import_projections_type_expr(e, bound, imports))
692 .collect(),
693 ),
694 TypeExpr::Record(span, fields) => TypeExpr::Record(
695 *span,
696 fields
697 .iter()
698 .map(|(name, t)| {
699 (
700 name.clone(),
701 rewrite_import_projections_type_expr(t, bound, imports),
702 )
703 })
704 .collect(),
705 ),
706 }
707}
708
709fn rewrite_program_import_projections(
710 compilation_unit: &CompilationUnit,
711 imports: &HashMap<Symbol, ImportModuleInfo>,
712 diagnostics: &mut Vec<Diagnostic>,
713) -> CompilationUnit {
714 let decl_bound = BTreeSet::new();
715 let decls = compilation_unit
716 .decls
717 .iter()
718 .map(|decl| match decl {
719 Decl::Fn(fd) => {
720 let mut bound: BTreeSet<Symbol> =
721 fd.params.iter().map(|(v, _)| v.name.clone()).collect();
722 let body = std::sync::Arc::new(rewrite_import_projections_expr(
723 fd.body.as_ref(),
724 &mut bound,
725 imports,
726 diagnostics,
727 ));
728 Decl::Fn(FnDecl {
729 span: fd.span,
730 is_pub: fd.is_pub,
731 name: fd.name.clone(),
732 params: fd
733 .params
734 .iter()
735 .map(|(v, t)| {
736 (
737 v.clone(),
738 rewrite_import_projections_type_expr(t, &decl_bound, imports),
739 )
740 })
741 .collect(),
742 ret: rewrite_import_projections_type_expr(&fd.ret, &decl_bound, imports),
743 constraints: fd
744 .constraints
745 .iter()
746 .map(|c| TypeConstraint {
747 class: rewrite_import_projections_class_name(
748 &c.class,
749 &decl_bound,
750 imports,
751 ),
752 typ: rewrite_import_projections_type_expr(&c.typ, &decl_bound, imports),
753 })
754 .collect(),
755 body,
756 })
757 }
758 Decl::DeclareFn(df) => Decl::DeclareFn(DeclareFnDecl {
759 span: df.span,
760 is_pub: df.is_pub,
761 name: df.name.clone(),
762 params: df
763 .params
764 .iter()
765 .map(|(v, t)| {
766 (
767 v.clone(),
768 rewrite_import_projections_type_expr(t, &decl_bound, imports),
769 )
770 })
771 .collect(),
772 ret: rewrite_import_projections_type_expr(&df.ret, &decl_bound, imports),
773 constraints: df
774 .constraints
775 .iter()
776 .map(|c| TypeConstraint {
777 class: rewrite_import_projections_class_name(
778 &c.class,
779 &decl_bound,
780 imports,
781 ),
782 typ: rewrite_import_projections_type_expr(&c.typ, &decl_bound, imports),
783 })
784 .collect(),
785 }),
786 Decl::Type(td) => Decl::Type(TypeDecl {
787 span: td.span,
788 is_pub: td.is_pub,
789 name: td.name.clone(),
790 params: td.params.clone(),
791 variants: td
792 .variants
793 .iter()
794 .map(|v| TypeVariant {
795 name: v.name.clone(),
796 args: v
797 .args
798 .iter()
799 .map(|t| rewrite_import_projections_type_expr(t, &decl_bound, imports))
800 .collect(),
801 })
802 .collect(),
803 }),
804 Decl::Class(cd) => Decl::Class(ClassDecl {
805 span: cd.span,
806 is_pub: cd.is_pub,
807 name: cd.name.clone(),
808 params: cd.params.clone(),
809 supers: cd
810 .supers
811 .iter()
812 .map(|c| TypeConstraint {
813 class: rewrite_import_projections_class_name(
814 &c.class,
815 &decl_bound,
816 imports,
817 ),
818 typ: rewrite_import_projections_type_expr(&c.typ, &decl_bound, imports),
819 })
820 .collect(),
821 methods: cd
822 .methods
823 .iter()
824 .map(|m| ClassMethodSig {
825 name: m.name.clone(),
826 typ: rewrite_import_projections_type_expr(&m.typ, &decl_bound, imports),
827 })
828 .collect(),
829 }),
830 Decl::Instance(inst) => {
831 let methods = inst
832 .methods
833 .iter()
834 .map(|m| {
835 let mut bound = BTreeSet::new();
836 let body = std::sync::Arc::new(rewrite_import_projections_expr(
837 m.body.as_ref(),
838 &mut bound,
839 imports,
840 diagnostics,
841 ));
842 InstanceMethodImpl {
843 name: m.name.clone(),
844 body,
845 }
846 })
847 .collect();
848 Decl::Instance(InstanceDecl {
849 span: inst.span,
850 is_pub: inst.is_pub,
851 class: rewrite_import_projections_class_name(
852 &NameRef::from_dotted(inst.class.as_ref()),
853 &decl_bound,
854 imports,
855 )
856 .to_dotted_symbol(),
857 head: rewrite_import_projections_type_expr(&inst.head, &decl_bound, imports),
858 context: inst
859 .context
860 .iter()
861 .map(|c| TypeConstraint {
862 class: rewrite_import_projections_class_name(
863 &c.class,
864 &decl_bound,
865 imports,
866 ),
867 typ: rewrite_import_projections_type_expr(&c.typ, &decl_bound, imports),
868 })
869 .collect(),
870 methods,
871 })
872 }
873 other => other.clone(),
874 })
875 .collect();
876
877 let body = compilation_unit.body.as_ref().map(|body| {
878 let mut bound = BTreeSet::new();
879 std::sync::Arc::new(rewrite_import_projections_expr(
880 body.as_ref(),
881 &mut bound,
882 imports,
883 diagnostics,
884 ))
885 });
886
887 CompilationUnit { decls, body }
888}
889
890fn validate_import_projection_class_name(
891 class: &NameRef,
892 span: Span,
893 bound: &BTreeSet<Symbol>,
894 imports: &HashMap<Symbol, ImportModuleInfo>,
895 diagnostics: &mut Vec<Diagnostic>,
896) {
897 let Some((alias, member)) = qualified_alias_member(class) else {
898 return;
899 };
900 if bound.contains(alias) {
901 return;
902 }
903 let Some(info) = imports.get(alias) else {
904 return;
905 };
906 if info.class_map.contains_key(member) {
907 return;
908 }
909 diagnostics.push(diagnostic_for_span(
910 span,
911 format!("module `{alias}` does not export `{member}`"),
912 ));
913}
914
915fn validate_import_projection_type_expr(
916 ty: &TypeExpr,
917 bound: &BTreeSet<Symbol>,
918 imports: &HashMap<Symbol, ImportModuleInfo>,
919 diagnostics: &mut Vec<Diagnostic>,
920) {
921 match ty {
922 TypeExpr::Name(span, name) => {
923 let Some((alias, member)) = qualified_alias_member(name) else {
924 return;
925 };
926 if bound.contains(alias) {
927 return;
928 }
929 let Some(info) = imports.get(alias) else {
930 return;
931 };
932 if info.type_map.contains_key(member) || info.class_map.contains_key(member) {
933 return;
934 }
935 diagnostics.push(diagnostic_for_span(
936 *span,
937 format!("module `{alias}` does not export `{member}`"),
938 ));
939 }
940 TypeExpr::App(_, f, x) => {
941 validate_import_projection_type_expr(f, bound, imports, diagnostics);
942 validate_import_projection_type_expr(x, bound, imports, diagnostics);
943 }
944 TypeExpr::Fun(_, a, b) => {
945 validate_import_projection_type_expr(a, bound, imports, diagnostics);
946 validate_import_projection_type_expr(b, bound, imports, diagnostics);
947 }
948 TypeExpr::Tuple(_, elems) => {
949 for e in elems {
950 validate_import_projection_type_expr(e, bound, imports, diagnostics);
951 }
952 }
953 TypeExpr::Record(_, fields) => {
954 for (_, t) in fields {
955 validate_import_projection_type_expr(t, bound, imports, diagnostics);
956 }
957 }
958 }
959}
960
961fn validate_import_projection_expr(
962 expr: &Expr,
963 bound: &mut BTreeSet<Symbol>,
964 imports: &HashMap<Symbol, ImportModuleInfo>,
965 diagnostics: &mut Vec<Diagnostic>,
966) {
967 match expr {
968 Expr::Lam(_, _, param, ann, constraints, body) => {
969 if let Some(ann) = ann {
970 validate_import_projection_type_expr(ann, bound, imports, diagnostics);
971 }
972 for c in constraints {
973 validate_import_projection_class_name(
974 &c.class,
975 *c.typ.span(),
976 bound,
977 imports,
978 diagnostics,
979 );
980 validate_import_projection_type_expr(&c.typ, bound, imports, diagnostics);
981 }
982 bound.insert(param.name.clone());
983 validate_import_projection_expr(body, bound, imports, diagnostics);
984 bound.remove(¶m.name);
985 }
986 Expr::Let(_, var, ann, val, body) => {
987 if let Some(ann) = ann {
988 validate_import_projection_type_expr(ann, bound, imports, diagnostics);
989 }
990 validate_import_projection_expr(val, bound, imports, diagnostics);
991 bound.insert(var.name.clone());
992 validate_import_projection_expr(body, bound, imports, diagnostics);
993 bound.remove(&var.name);
994 }
995 Expr::LetRec(_, bindings, body) => {
996 for (_, ann, _) in bindings {
997 if let Some(ann) = ann {
998 validate_import_projection_type_expr(ann, bound, imports, diagnostics);
999 }
1000 }
1001 let names: Vec<_> = bindings
1002 .iter()
1003 .map(|(var, _, _)| var.name.clone())
1004 .collect();
1005 for name in &names {
1006 bound.insert(name.clone());
1007 }
1008 for (_, _ann, def) in bindings {
1009 validate_import_projection_expr(def, bound, imports, diagnostics);
1010 }
1011 validate_import_projection_expr(body, bound, imports, diagnostics);
1012 for name in &names {
1013 bound.remove(name);
1014 }
1015 }
1016 Expr::Match(_, scrutinee, arms) => {
1017 validate_import_projection_expr(scrutinee, bound, imports, diagnostics);
1018 for (pat, arm_expr) in arms {
1019 let mut binds = Vec::new();
1020 collect_pattern_bindings(pat, &mut binds);
1021 for b in &binds {
1022 bound.insert(b.clone());
1023 }
1024 validate_import_projection_expr(arm_expr, bound, imports, diagnostics);
1025 for b in &binds {
1026 bound.remove(b);
1027 }
1028 }
1029 }
1030 Expr::Tuple(_, elems) | Expr::List(_, elems) => {
1031 for e in elems {
1032 validate_import_projection_expr(e, bound, imports, diagnostics);
1033 }
1034 }
1035 Expr::Dict(_, kvs) => {
1036 for v in kvs.values() {
1037 validate_import_projection_expr(v, bound, imports, diagnostics);
1038 }
1039 }
1040 Expr::RecordUpdate(_, base, updates) => {
1041 validate_import_projection_expr(base, bound, imports, diagnostics);
1042 for v in updates.values() {
1043 validate_import_projection_expr(v, bound, imports, diagnostics);
1044 }
1045 }
1046 Expr::App(_, f, x) => {
1047 validate_import_projection_expr(f, bound, imports, diagnostics);
1048 validate_import_projection_expr(x, bound, imports, diagnostics);
1049 }
1050 Expr::Ite(_, c, t, e) => {
1051 validate_import_projection_expr(c, bound, imports, diagnostics);
1052 validate_import_projection_expr(t, bound, imports, diagnostics);
1053 validate_import_projection_expr(e, bound, imports, diagnostics);
1054 }
1055 Expr::Ann(_, e, t) => {
1056 validate_import_projection_expr(e, bound, imports, diagnostics);
1057 validate_import_projection_type_expr(t, bound, imports, diagnostics);
1058 }
1059 Expr::Project(_, base, _) => {
1060 validate_import_projection_expr(base, bound, imports, diagnostics);
1061 }
1062 Expr::Var(..)
1063 | Expr::Bool(..)
1064 | Expr::Uint(..)
1065 | Expr::Int(..)
1066 | Expr::Float(..)
1067 | Expr::String(..)
1068 | Expr::Uuid(..)
1069 | Expr::DateTime(..)
1070 | Expr::Hole(..) => {}
1071 }
1072}
1073
1074fn validate_import_projection_uses(
1075 compilation_unit: &CompilationUnit,
1076 imports: &HashMap<Symbol, ImportModuleInfo>,
1077 diagnostics: &mut Vec<Diagnostic>,
1078) {
1079 let decl_bound = BTreeSet::new();
1080 for decl in &compilation_unit.decls {
1081 match decl {
1082 Decl::Fn(fd) => {
1083 for (_, t) in &fd.params {
1084 validate_import_projection_type_expr(t, &decl_bound, imports, diagnostics);
1085 }
1086 validate_import_projection_type_expr(&fd.ret, &decl_bound, imports, diagnostics);
1087 for c in &fd.constraints {
1088 validate_import_projection_class_name(
1089 &c.class,
1090 *c.typ.span(),
1091 &decl_bound,
1092 imports,
1093 diagnostics,
1094 );
1095 validate_import_projection_type_expr(&c.typ, &decl_bound, imports, diagnostics);
1096 }
1097 let mut bound: BTreeSet<Symbol> =
1098 fd.params.iter().map(|(v, _)| v.name.clone()).collect();
1099 validate_import_projection_expr(fd.body.as_ref(), &mut bound, imports, diagnostics);
1100 }
1101 Decl::DeclareFn(df) => {
1102 for (_, t) in &df.params {
1103 validate_import_projection_type_expr(t, &decl_bound, imports, diagnostics);
1104 }
1105 validate_import_projection_type_expr(&df.ret, &decl_bound, imports, diagnostics);
1106 for c in &df.constraints {
1107 validate_import_projection_class_name(
1108 &c.class,
1109 *c.typ.span(),
1110 &decl_bound,
1111 imports,
1112 diagnostics,
1113 );
1114 validate_import_projection_type_expr(&c.typ, &decl_bound, imports, diagnostics);
1115 }
1116 }
1117 Decl::Type(td) => {
1118 for v in &td.variants {
1119 for t in &v.args {
1120 validate_import_projection_type_expr(t, &decl_bound, imports, diagnostics);
1121 }
1122 }
1123 }
1124 Decl::Class(cd) => {
1125 for c in &cd.supers {
1126 validate_import_projection_class_name(
1127 &c.class,
1128 *c.typ.span(),
1129 &decl_bound,
1130 imports,
1131 diagnostics,
1132 );
1133 validate_import_projection_type_expr(&c.typ, &decl_bound, imports, diagnostics);
1134 }
1135 for m in &cd.methods {
1136 validate_import_projection_type_expr(&m.typ, &decl_bound, imports, diagnostics);
1137 }
1138 }
1139 Decl::Instance(inst) => {
1140 validate_import_projection_class_name(
1141 &NameRef::from_dotted(inst.class.as_ref()),
1142 inst.span,
1143 &decl_bound,
1144 imports,
1145 diagnostics,
1146 );
1147 validate_import_projection_type_expr(&inst.head, &decl_bound, imports, diagnostics);
1148 for c in &inst.context {
1149 validate_import_projection_class_name(
1150 &c.class,
1151 *c.typ.span(),
1152 &decl_bound,
1153 imports,
1154 diagnostics,
1155 );
1156 validate_import_projection_type_expr(&c.typ, &decl_bound, imports, diagnostics);
1157 }
1158 for m in &inst.methods {
1159 let mut bound = BTreeSet::new();
1160 validate_import_projection_expr(
1161 m.body.as_ref(),
1162 &mut bound,
1163 imports,
1164 diagnostics,
1165 );
1166 }
1167 }
1168 Decl::Import(..) => {}
1169 }
1170 }
1171 if let Some(body) = &compilation_unit.body {
1172 let mut bound = BTreeSet::new();
1173 validate_import_projection_expr(body.as_ref(), &mut bound, imports, diagnostics);
1174 }
1175}
1176
1177pub type PreparedProgram = (
1178 CompilationUnit,
1179 TypeSystem,
1180 HashMap<Symbol, ImportModuleInfo>,
1181 Vec<Diagnostic>,
1182);
1183
1184pub fn prepare_program_with_imports(
1185 uri: &Url,
1186 compilation_unit: &CompilationUnit,
1187) -> std::result::Result<PreparedProgram, String> {
1188 let mut ts =
1189 TypeSystem::new_with_prelude().map_err(|e| format!("failed to build prelude: {e}"))?;
1190 let mut diagnostics = Vec::new();
1191
1192 let importer = uri_to_file_path(uri);
1193
1194 let mut imports: HashMap<Symbol, ImportModuleInfo> = HashMap::new();
1195
1196 for decl in &compilation_unit.decls {
1197 let Decl::Import(ImportDecl {
1198 span, path, alias, ..
1199 }) = decl
1200 else {
1201 continue;
1202 };
1203 let import_span = *span;
1204
1205 let (segments, expected_sha) = match path {
1206 ImportPath::Local { segments, sha } => (segments.as_slice(), sha.as_deref()),
1207 ImportPath::Remote { .. } => {
1208 continue;
1210 }
1211 };
1212
1213 let module_name = segments
1214 .iter()
1215 .map(|s| s.as_ref())
1216 .collect::<Vec<_>>()
1217 .join(".");
1218
1219 let (module_path, hash, source, module_label, keep_constraints) =
1220 if let Some(source) = stdlib_source(&module_name) {
1221 let hash = sha256_hex(source.as_bytes());
1222 if let Some(expected) = expected_sha {
1223 let expected = expected.to_ascii_lowercase();
1224 if !hash.starts_with(&expected) {
1225 diagnostics.push(diagnostic_for_span(
1226 import_span,
1227 format!(
1228 "sha mismatch for `{module_name}`: expected #{expected}, got #{hash}",
1229 ),
1230 ));
1231 }
1232 }
1233 (None, hash, source.to_string(), module_name, true)
1234 } else {
1235 let Some(importer) = importer.as_ref() else {
1236 continue;
1239 };
1240 let Some(base_dir) = importer.parent() else {
1241 diagnostics.push(diagnostic_for_span(
1242 import_span,
1243 "cannot resolve local import without a base directory".to_string(),
1244 ));
1245 continue;
1246 };
1247 let module_path = match resolve_local_import_path(base_dir, segments) {
1248 Ok(Some(p)) => p,
1249 Ok(None) => {
1250 diagnostics.push(diagnostic_for_span(
1251 import_span,
1252 format!("module not found for import `{module_name}`"),
1253 ));
1254 continue;
1255 }
1256 Err(err) => {
1257 diagnostics.push(diagnostic_for_span(import_span, err.to_string()));
1258 continue;
1259 }
1260 };
1261 let Ok(module_path) = module_path.canonicalize() else {
1262 diagnostics.push(diagnostic_for_span(
1263 import_span,
1264 format!("module not found for import `{module_name}`"),
1265 ));
1266 continue;
1267 };
1268
1269 let bytes = match fs::read(&module_path) {
1270 Ok(b) => b,
1271 Err(e) => {
1272 diagnostics.push(diagnostic_for_span(
1273 import_span,
1274 format!("failed to read module `{}`: {e}", module_path.display()),
1275 ));
1276 continue;
1277 }
1278 };
1279 let hash = sha256_hex(&bytes);
1280 if let Some(expected) = expected_sha {
1281 let expected = expected.to_ascii_lowercase();
1282 if !hash.starts_with(&expected) {
1283 diagnostics.push(diagnostic_for_span(
1284 import_span,
1285 format!(
1286 "sha mismatch for `{}`: expected #{expected}, got #{hash}",
1287 module_path.display()
1288 ),
1289 ));
1290 }
1291 }
1292
1293 let source = match String::from_utf8(bytes) {
1294 Ok(s) => s,
1295 Err(e) => {
1296 diagnostics.push(diagnostic_for_span(
1297 import_span,
1298 format!("module `{}` is not utf-8: {e}", module_path.display()),
1299 ));
1300 continue;
1301 }
1302 };
1303 (
1304 Some(module_path.clone()),
1305 hash,
1306 source,
1307 module_path.display().to_string(),
1308 false,
1309 )
1310 };
1311
1312 let (tokens, module_program) = match tokenize_and_parse(&source) {
1313 Ok(v) => v,
1314 Err(TokenizeOrParseError::Lex(err)) => {
1315 let msg = match err {
1316 LexicalError::UnexpectedToken(span) => format!(
1317 "lex error in module `{}` at {}:{}",
1318 module_label, span.begin.line, span.begin.column
1319 ),
1320 LexicalError::InvalidLiteral {
1321 kind,
1322 text,
1323 error,
1324 span,
1325 } => format!(
1326 "lex error in module `{}` at {}:{}: invalid {kind} literal `{text}`: {error}",
1327 module_label, span.begin.line, span.begin.column
1328 ),
1329 LexicalError::Internal(msg) => {
1330 format!("internal lexer error in module `{module_label}`: {msg}")
1331 }
1332 };
1333 diagnostics.push(diagnostic_for_span(import_span, msg));
1334 continue;
1335 }
1336 Err(TokenizeOrParseError::Parse(errs)) => {
1337 for err in errs {
1338 diagnostics.push(diagnostic_for_span(
1339 import_span,
1340 format!(
1341 "parse error in module `{}` at {}:{}: {}",
1342 module_label, err.span.begin.line, err.span.begin.column, err.message
1343 ),
1344 ));
1345 if diagnostics.len() >= MAX_DIAGNOSTICS {
1346 break;
1347 }
1348 }
1349 continue;
1350 }
1351 };
1352
1353 let index = index_decl_spans(&module_program, &tokens);
1354 let prefix = module_prefix(&hash);
1355
1356 let mut type_map: HashMap<Symbol, Symbol> = HashMap::new();
1357 let mut class_map: HashMap<Symbol, Symbol> = HashMap::new();
1358 for decl in &module_program.decls {
1359 match decl {
1360 Decl::Type(td) => {
1361 type_map.insert(
1362 td.name.clone(),
1363 Symbol::intern(&format!("{prefix}.{}", td.name.as_ref())),
1364 );
1365 }
1366 Decl::Class(cd) => {
1367 class_map.insert(
1368 cd.name.clone(),
1369 Symbol::intern(&format!("{prefix}.{}", cd.name.as_ref())),
1370 );
1371 }
1372 _ => {}
1373 }
1374 }
1375
1376 for decl in &module_program.decls {
1378 let Decl::Type(td) = decl else { continue };
1379 let name = type_map
1380 .get(&td.name)
1381 .cloned()
1382 .unwrap_or_else(|| td.name.clone());
1383 let variants = td
1384 .variants
1385 .iter()
1386 .map(|v| TypeVariant {
1387 name: Symbol::intern(&format!("{prefix}.{}", v.name.as_ref())),
1388 args: v
1389 .args
1390 .iter()
1391 .map(|t| rewrite_type_expr(t, &type_map))
1392 .collect(),
1393 })
1394 .collect();
1395 let td2 = TypeDecl {
1396 span: td.span,
1397 is_pub: td.is_pub,
1398 name,
1399 params: td.params.clone(),
1400 variants,
1401 };
1402 let _ = ts.register_type_decl(&td2);
1403 }
1404
1405 let mut value_map: HashMap<Symbol, Symbol> = HashMap::new();
1406 let mut export_names: BTreeSet<String> = BTreeSet::new();
1407
1408 for decl in &module_program.decls {
1410 match decl {
1411 Decl::Fn(fd) if fd.is_pub => {
1412 let internal = Symbol::intern(&format!("{prefix}.{}", fd.name.name.as_ref()));
1413 value_map.insert(Symbol::intern(fd.name.name.as_ref()), internal.clone());
1414 export_names.insert(fd.name.name.as_ref().to_string());
1415
1416 let params = fd
1417 .params
1418 .iter()
1419 .map(|(v, ty)| (v.clone(), rewrite_type_expr(ty, &type_map)))
1420 .collect();
1421 let ret = rewrite_type_expr(&fd.ret, &type_map);
1422 let decl = DeclareFnDecl {
1423 span: fd.span,
1424 is_pub: true,
1425 name: Var {
1426 span: fd.name.span,
1427 name: internal,
1428 },
1429 params,
1430 ret,
1431 constraints: if keep_constraints {
1432 fd.constraints.clone()
1433 } else {
1434 Default::default()
1435 },
1436 };
1437 let _ = ts.inject_declare_fn_decl(&decl);
1438 }
1439 Decl::DeclareFn(df) if df.is_pub => {
1440 let internal = Symbol::intern(&format!("{prefix}.{}", df.name.name.as_ref()));
1441 value_map.insert(Symbol::intern(df.name.name.as_ref()), internal.clone());
1442 export_names.insert(df.name.name.as_ref().to_string());
1443
1444 let params = df
1445 .params
1446 .iter()
1447 .map(|(v, ty)| (v.clone(), rewrite_type_expr(ty, &type_map)))
1448 .collect();
1449 let ret = rewrite_type_expr(&df.ret, &type_map);
1450 let decl = DeclareFnDecl {
1451 span: df.span,
1452 is_pub: true,
1453 name: Var {
1454 span: df.name.span,
1455 name: internal,
1456 },
1457 params,
1458 ret,
1459 constraints: if keep_constraints {
1460 df.constraints.clone()
1461 } else {
1462 Default::default()
1463 },
1464 };
1465 let _ = ts.inject_declare_fn_decl(&decl);
1466 }
1467 Decl::Type(td) if td.is_pub => {
1468 for variant in &td.variants {
1470 let internal =
1471 Symbol::intern(&format!("{prefix}.{}", variant.name.as_ref()));
1472 value_map.insert(variant.name.clone(), internal);
1473 export_names.insert(variant.name.as_ref().to_string());
1474 }
1475 }
1476 _ => {}
1477 }
1478 }
1479
1480 let mut export_defs = HashMap::new();
1481 for name in &export_names {
1482 if let Some(span) = index
1483 .fn_defs
1484 .get(name)
1485 .copied()
1486 .or_else(|| index.ctor_defs.get(name).copied())
1487 {
1488 export_defs.insert(name.clone(), span);
1489 }
1490 }
1491
1492 imports.insert(
1493 alias.clone(),
1494 ImportModuleInfo {
1495 path: module_path,
1496 value_map,
1497 type_map,
1498 class_map,
1499 export_defs,
1500 },
1501 );
1502 }
1503
1504 validate_import_projection_uses(compilation_unit, &imports, &mut diagnostics);
1505 let rewritten =
1506 rewrite_program_import_projections(compilation_unit, &imports, &mut diagnostics);
1507 Ok((rewritten, ts, imports, diagnostics))
1508}
1509
1510fn completion_exports_for_module_alias(
1511 uri: &Url,
1512 compilation_unit: &CompilationUnit,
1513 alias: &str,
1514) -> std::result::Result<Vec<String>, String> {
1515 let alias_sym = Symbol::intern(alias);
1516 let Some(import_decl) = compilation_unit.decls.iter().find_map(|d| {
1517 let Decl::Import(id) = d else { return None };
1518 if id.alias == alias_sym {
1519 Some(id)
1520 } else {
1521 None
1522 }
1523 }) else {
1524 return Ok(Vec::new());
1525 };
1526
1527 let ImportPath::Local { segments, sha: _ } = &import_decl.path else {
1528 return Ok(Vec::new());
1529 };
1530
1531 let module_name = segments
1532 .iter()
1533 .map(|s| s.as_ref())
1534 .collect::<Vec<_>>()
1535 .join(".");
1536
1537 let source = if let Some(source) = stdlib_source(&module_name) {
1538 source.to_string()
1539 } else {
1540 let importer = uri_to_file_path(uri).ok_or_else(|| "not a file uri".to_string())?;
1541 let Some(base_dir) = importer.parent() else {
1542 return Ok(Vec::new());
1543 };
1544 let Some(module_path) = resolve_local_import_path(base_dir, segments)
1545 .ok()
1546 .flatten()
1547 .and_then(|p| p.canonicalize().ok())
1548 else {
1549 return Ok(Vec::new());
1550 };
1551 fs::read_to_string(&module_path).map_err(|e| e.to_string())?
1552 };
1553 let (_tokens, module_program) =
1554 tokenize_and_parse(&source).map_err(|_| "parse error".to_string())?;
1555
1556 let mut exports = BTreeSet::new();
1557 for decl in &module_program.decls {
1558 match decl {
1559 Decl::Fn(fd) if fd.is_pub => {
1560 exports.insert(fd.name.name.as_ref().to_string());
1561 }
1562 Decl::DeclareFn(df) if df.is_pub => {
1563 exports.insert(df.name.name.as_ref().to_string());
1564 }
1565 Decl::Type(td) if td.is_pub => {
1566 for variant in &td.variants {
1567 exports.insert(variant.name.as_ref().to_string());
1568 }
1569 }
1570 _ => {}
1571 }
1572 }
1573 Ok(exports.into_iter().collect())
1574}
1575
1576pub(crate) fn goto_definition_response(
1577 uri: &Url,
1578 text: &str,
1579 position: Position,
1580) -> Option<GotoDefinitionResponse> {
1581 let Ok((tokens, program)) = tokenize_and_parse_cached(uri, text) else {
1584 return None;
1585 };
1586
1587 let imported_projection = imported_projection_at_position(&tokens, position);
1588
1589 let (ident, _token_span) = ident_token_at_position(&tokens, position)?;
1590
1591 if let Some((alias, field)) = imported_projection
1594 && let Ok((_rewritten, _ts, imports, _diags)) = prepare_program_with_imports(uri, &program)
1595 {
1596 let alias_sym = Symbol::intern(&alias);
1597 if let Some(info) = imports.get(&alias_sym)
1598 && let Some(span) = info.export_defs.get(&field)
1599 && let Some(path) = info.path.as_ref()
1600 && let Some(module_uri) = url_from_file_path(path)
1601 {
1602 return Some(GotoDefinitionResponse::Scalar(Location {
1603 uri: module_uri,
1604 range: span_to_range(*span),
1605 }));
1606 }
1607 }
1608
1609 let index = index_decl_spans(&program, &tokens);
1610 let pos = lsp_to_rex_position(position);
1611
1612 let body_with_fns = program.body_with_fns();
1616 let mut root_expr = body_with_fns.as_deref();
1617 for decl in &program.decls {
1618 let Decl::Instance(inst) = decl else {
1619 continue;
1620 };
1621 for method in &inst.methods {
1622 if position_in_span(pos, *method.body.span()) {
1623 root_expr = Some(method.body.as_ref());
1624 break;
1625 }
1626 }
1627 }
1628
1629 let value_def = root_expr.and_then(|expr| {
1630 definition_span_for_value_ident(expr, pos, &ident, &mut Vec::new(), &tokens)
1631 });
1632
1633 let instance_method_def = index
1634 .instance_method_defs
1635 .iter()
1636 .find_map(|(span, methods)| {
1637 if position_in_span(pos, *span) {
1638 methods.get(&ident).copied()
1639 } else {
1640 None
1641 }
1642 });
1643
1644 let target_span = value_def
1645 .or(instance_method_def)
1646 .or(index.class_method_defs.get(&ident).copied())
1647 .or(index.fn_defs.get(&ident).copied())
1648 .or(index.ctor_defs.get(&ident).copied())
1649 .or(index.type_defs.get(&ident).copied())
1650 .or(index.class_defs.get(&ident).copied())?;
1651
1652 Some(GotoDefinitionResponse::Scalar(Location {
1653 uri: uri.clone(),
1654 range: span_to_range(target_span),
1655 }))
1656}
1657
1658fn range_to_span(range: Range) -> Span {
1659 Span::new(
1660 (range.start.line + 1) as usize,
1661 (range.start.character + 1) as usize,
1662 (range.end.line + 1) as usize,
1663 (range.end.character + 1) as usize,
1664 )
1665}
1666
1667fn pattern_bindings_with_spans(pat: &Pattern, out: &mut Vec<(String, Span)>) {
1668 match pat {
1669 Pattern::Var(var) => out.push((var.name.to_string(), var.span)),
1670 Pattern::Named(_, _, args) => {
1671 for arg in args {
1672 pattern_bindings_with_spans(arg, out);
1673 }
1674 }
1675 Pattern::Tuple(_, elems) | Pattern::List(_, elems) => {
1676 for elem in elems {
1677 pattern_bindings_with_spans(elem, out);
1678 }
1679 }
1680 Pattern::Cons(_, head, tail) => {
1681 pattern_bindings_with_spans(head, out);
1682 pattern_bindings_with_spans(tail, out);
1683 }
1684 Pattern::Dict(_, fields) => {
1685 for (_, pat) in fields {
1686 pattern_bindings_with_spans(pat, out);
1687 }
1688 }
1689 Pattern::Wildcard(..) => {}
1690 }
1691}
1692
1693fn collect_references_in_expr(
1694 expr: &Expr,
1695 ident: &str,
1696 target_span: Span,
1697 uri: &Url,
1698 top_level_defs: &HashMap<String, Span>,
1699 scope: &mut Vec<(String, Span)>,
1700 out: &mut Vec<Location>,
1701) {
1702 match expr {
1703 Expr::Var(var) => {
1704 if var.name.as_ref() != ident {
1705 return;
1706 }
1707 let resolved = scope
1708 .iter()
1709 .rev()
1710 .find_map(|(name, span)| (name == ident).then_some(*span))
1711 .or_else(|| top_level_defs.get(ident).copied());
1712 if resolved.is_some_and(|span| span == target_span) {
1713 out.push(Location {
1714 uri: uri.clone(),
1715 range: span_to_range(var.span),
1716 });
1717 }
1718 }
1719 Expr::Let(_, var, _ann, def, body) => {
1720 collect_references_in_expr(def, ident, target_span, uri, top_level_defs, scope, out);
1721 scope.push((var.name.to_string(), var.span));
1722 collect_references_in_expr(body, ident, target_span, uri, top_level_defs, scope, out);
1723 scope.pop();
1724 }
1725 Expr::LetRec(_, bindings, body) => {
1726 let base_len = scope.len();
1727 for (var, _ann, _def) in bindings {
1728 scope.push((var.name.to_string(), var.span));
1729 }
1730 for (_var, _ann, def) in bindings {
1731 collect_references_in_expr(
1732 def,
1733 ident,
1734 target_span,
1735 uri,
1736 top_level_defs,
1737 scope,
1738 out,
1739 );
1740 }
1741 collect_references_in_expr(body, ident, target_span, uri, top_level_defs, scope, out);
1742 scope.truncate(base_len);
1743 }
1744 Expr::Lam(_, _scope, param, _ann, _constraints, body) => {
1745 scope.push((param.name.to_string(), param.span));
1746 collect_references_in_expr(body, ident, target_span, uri, top_level_defs, scope, out);
1747 scope.pop();
1748 }
1749 Expr::Match(_, scrutinee, arms) => {
1750 collect_references_in_expr(
1751 scrutinee,
1752 ident,
1753 target_span,
1754 uri,
1755 top_level_defs,
1756 scope,
1757 out,
1758 );
1759 for (pat, arm) in arms {
1760 let base_len = scope.len();
1761 let mut binds = Vec::new();
1762 pattern_bindings_with_spans(pat, &mut binds);
1763 scope.extend(binds);
1764 collect_references_in_expr(
1765 arm,
1766 ident,
1767 target_span,
1768 uri,
1769 top_level_defs,
1770 scope,
1771 out,
1772 );
1773 scope.truncate(base_len);
1774 }
1775 }
1776 Expr::App(_, fun, arg) => {
1777 collect_references_in_expr(fun, ident, target_span, uri, top_level_defs, scope, out);
1778 collect_references_in_expr(arg, ident, target_span, uri, top_level_defs, scope, out);
1779 }
1780 Expr::Project(_, base, _) => {
1781 collect_references_in_expr(base, ident, target_span, uri, top_level_defs, scope, out);
1782 }
1783 Expr::Tuple(_, elems) | Expr::List(_, elems) => {
1784 for elem in elems {
1785 collect_references_in_expr(
1786 elem,
1787 ident,
1788 target_span,
1789 uri,
1790 top_level_defs,
1791 scope,
1792 out,
1793 );
1794 }
1795 }
1796 Expr::Dict(_, entries) => {
1797 for value in entries.values() {
1798 collect_references_in_expr(
1799 value,
1800 ident,
1801 target_span,
1802 uri,
1803 top_level_defs,
1804 scope,
1805 out,
1806 );
1807 }
1808 }
1809 Expr::RecordUpdate(_, base, updates) => {
1810 collect_references_in_expr(base, ident, target_span, uri, top_level_defs, scope, out);
1811 for value in updates.values() {
1812 collect_references_in_expr(
1813 value,
1814 ident,
1815 target_span,
1816 uri,
1817 top_level_defs,
1818 scope,
1819 out,
1820 );
1821 }
1822 }
1823 Expr::Ite(_, cond, then_expr, else_expr) => {
1824 collect_references_in_expr(cond, ident, target_span, uri, top_level_defs, scope, out);
1825 collect_references_in_expr(
1826 then_expr,
1827 ident,
1828 target_span,
1829 uri,
1830 top_level_defs,
1831 scope,
1832 out,
1833 );
1834 collect_references_in_expr(
1835 else_expr,
1836 ident,
1837 target_span,
1838 uri,
1839 top_level_defs,
1840 scope,
1841 out,
1842 );
1843 }
1844 Expr::Ann(_, inner, _) => {
1845 collect_references_in_expr(inner, ident, target_span, uri, top_level_defs, scope, out);
1846 }
1847 Expr::Bool(..)
1848 | Expr::Uint(..)
1849 | Expr::Int(..)
1850 | Expr::Float(..)
1851 | Expr::String(..)
1852 | Expr::Uuid(..)
1853 | Expr::DateTime(..)
1854 | Expr::Hole(..) => {}
1855 }
1856}
1857
1858pub(crate) fn references_for_source(
1859 uri: &Url,
1860 text: &str,
1861 position: Position,
1862 include_declaration: bool,
1863) -> Vec<Location> {
1864 let Ok((tokens, program)) = tokenize_and_parse_cached(uri, text) else {
1865 return Vec::new();
1866 };
1867 let Some((ident, _token_span)) = ident_token_at_position(&tokens, position) else {
1868 return Vec::new();
1869 };
1870
1871 let Some(def_response) = goto_definition_response(uri, text, position) else {
1872 return Vec::new();
1873 };
1874 let GotoDefinitionResponse::Scalar(def_location) = def_response else {
1875 return Vec::new();
1876 };
1877 if def_location.uri != *uri {
1878 return Vec::new();
1879 }
1880 let target_span = range_to_span(def_location.range);
1881
1882 let index = index_decl_spans(&program, &tokens);
1883 let mut top_level_defs = index.fn_defs;
1884 top_level_defs.extend(index.ctor_defs);
1885
1886 let mut refs = Vec::new();
1887 if include_declaration {
1888 refs.push(def_location);
1889 }
1890 if let Some(expr) = program.body_with_fns() {
1891 collect_references_in_expr(
1892 expr.as_ref(),
1893 &ident,
1894 target_span,
1895 uri,
1896 &top_level_defs,
1897 &mut Vec::new(),
1898 &mut refs,
1899 );
1900 }
1901 refs.sort_by_key(|location| {
1902 (
1903 location.range.start.line,
1904 location.range.start.character,
1905 location.range.end.line,
1906 location.range.end.character,
1907 )
1908 });
1909 refs.dedup_by(|a, b| a.range == b.range && a.uri == b.uri);
1910 refs
1911}
1912
1913pub(crate) fn rename_for_source(
1914 uri: &Url,
1915 text: &str,
1916 position: Position,
1917 new_name: &str,
1918) -> Option<WorkspaceEdit> {
1919 if !is_ident_like(new_name) {
1920 return None;
1921 }
1922 let refs = references_for_source(uri, text, position, true);
1923 if refs.is_empty() {
1924 return None;
1925 }
1926 let edits: Vec<TextEdit> = refs
1927 .into_iter()
1928 .map(|location| TextEdit {
1929 range: location.range,
1930 new_text: new_name.to_string(),
1931 })
1932 .collect();
1933 let mut changes = HashMap::new();
1934 changes.insert(uri.clone(), edits);
1935 Some(WorkspaceEdit {
1936 changes: Some(changes),
1937 document_changes: None,
1938 change_annotations: None,
1939 })
1940}
1941
1942pub fn code_actions_for_source(
1943 uri: &Url,
1944 text: &str,
1945 request_range: Range,
1946 diagnostics: &[Diagnostic],
1947) -> Vec<CodeActionOrCommand> {
1948 let parsed = tokenize_and_parse_cached(uri, text)
1949 .ok()
1950 .map(|(_tokens, program)| program);
1951 let mut actions = Vec::new();
1952
1953 actions.extend(code_actions_for_hole_fill(
1955 uri,
1956 text,
1957 parsed.as_ref(),
1958 request_range,
1959 ));
1960
1961 for diag in diagnostics {
1962 let usable_diag_range = range_is_usable_for_text(text, diag.range);
1963 if usable_diag_range
1964 && !range_is_empty(diag.range)
1965 && !ranges_overlap(diag.range, request_range)
1966 && !range_contains_position(diag.range, request_range.start)
1967 && !range_contains_position(diag.range, request_range.end)
1968 {
1969 continue;
1970 }
1971 actions.extend(code_actions_for_diagnostic(
1972 uri,
1973 text,
1974 parsed.as_ref(),
1975 request_range,
1976 diag,
1977 ));
1978 }
1979
1980 actions
1981}
1982
1983fn code_actions_for_hole_fill(
1984 uri: &Url,
1985 text: &str,
1986 compilation_unit: Option<&CompilationUnit>,
1987 request_range: Range,
1988) -> Vec<CodeActionOrCommand> {
1989 let Some(compilation_unit) = compilation_unit else {
1990 return Vec::new();
1991 };
1992 let Some(body) = compilation_unit.body_with_fns() else {
1993 return Vec::new();
1994 };
1995 let mut hole_spans = Vec::new();
1996 collect_hole_spans(body.as_ref(), &mut hole_spans);
1997 let Some(hole_span) = hole_spans
1998 .into_iter()
1999 .find(|span| ranges_overlap(span_to_range(*span), request_range))
2000 else {
2001 return Vec::new();
2002 };
2003 let hole_range = span_to_range(hole_span);
2004 let pos = hole_range.start;
2005 let candidates = hole_fill_candidates_at_position(uri, text, pos);
2006 let mut actions = Vec::new();
2007 for (name, replacement) in candidates.into_iter().take(8) {
2008 let diagnostic = Diagnostic {
2009 range: hole_range,
2010 severity: Some(DiagnosticSeverity::HINT),
2011 message: "hole".to_string(),
2012 source: Some("rex-lsp".to_string()),
2013 ..Diagnostic::default()
2014 };
2015 actions.push(code_action_replace(
2016 format!("Fill hole with `{name}`"),
2017 uri,
2018 hole_range,
2019 replacement,
2020 diagnostic,
2021 ));
2022 }
2023 actions
2024}
2025
2026fn code_actions_for_diagnostic(
2027 uri: &Url,
2028 text: &str,
2029 compilation_unit: Option<&CompilationUnit>,
2030 request_range: Range,
2031 diagnostic: &Diagnostic,
2032) -> Vec<CodeActionOrCommand> {
2033 let mut actions = Vec::new();
2034 let target_range = if range_is_usable_for_text(text, diagnostic.range) {
2035 diagnostic.range
2036 } else {
2037 request_range
2038 };
2039
2040 if diagnostic
2041 .message
2042 .contains("typed hole `?` must be filled before evaluation")
2043 {
2044 actions.extend(code_actions_for_hole_fill(
2045 uri,
2046 text,
2047 compilation_unit,
2048 target_range,
2049 ));
2050 }
2051
2052 if let Some(name) = unknown_var_name_from_message(&diagnostic.message) {
2053 if let Some(compilation_unit) = compilation_unit {
2054 let mut candidates: Vec<String> =
2055 values_in_scope_at_position(compilation_unit, target_range.start)
2056 .into_keys()
2057 .filter(|candidate| candidate != name)
2058 .collect();
2059 candidates.sort_by_key(|candidate| levenshtein_distance(candidate, name));
2060 for candidate in candidates.into_iter().take(3) {
2061 actions.push(code_action_replace(
2062 format!("Replace `{name}` with `{candidate}`"),
2063 uri,
2064 target_range,
2065 candidate,
2066 diagnostic.clone(),
2067 ));
2068 }
2069 }
2070
2071 actions.push(code_action_insert(
2072 format!("Introduce `let {name} = null`"),
2073 uri,
2074 Position {
2075 line: 0,
2076 character: 0,
2077 },
2078 format!("let {name} = null in\n"),
2079 diagnostic.clone(),
2080 ));
2081 }
2082
2083 if is_list_scalar_unification_error(&diagnostic.message)
2084 && let Some(selected) = text_for_range(text, target_range)
2085 {
2086 let trimmed = selected.trim();
2087 if !trimmed.is_empty() {
2088 actions.push(code_action_replace(
2089 "Wrap expression in list literal".to_string(),
2090 uri,
2091 target_range,
2092 format!("[{selected}]"),
2093 diagnostic.clone(),
2094 ));
2095 if trimmed.starts_with('[') && trimmed.ends_with(']') && trimmed.len() >= 2 {
2096 let unwrapped = trimmed[1..trimmed.len() - 1].to_string();
2097 actions.push(code_action_replace(
2098 "Unwrap list literal".to_string(),
2099 uri,
2100 target_range,
2101 unwrapped,
2102 diagnostic.clone(),
2103 ));
2104 }
2105 }
2106 }
2107
2108 if is_array_list_unification_error(&diagnostic.message) {
2109 let selected_range =
2110 if !range_is_empty(request_range) && range_is_usable_for_text(text, request_range) {
2111 request_range
2112 } else {
2113 target_range
2114 };
2115 if let Some(selected) = text_for_range(text, selected_range) {
2116 let trimmed = selected.trim();
2117 if !trimmed.is_empty() && !trimmed.starts_with("to_list") {
2118 actions.push(code_action_replace(
2119 "Convert expression to list with `to_list`".to_string(),
2120 uri,
2121 selected_range,
2122 format!("to_list ({selected})"),
2123 diagnostic.clone(),
2124 ));
2125 }
2126 }
2127 }
2128
2129 if is_function_value_unification_error(&diagnostic.message)
2130 && let Some(selected) = text_for_range(text, target_range)
2131 {
2132 let trimmed = selected.trim();
2133 if !trimmed.is_empty() {
2134 actions.push(code_action_replace(
2135 "Apply expression to missing argument".to_string(),
2136 uri,
2137 target_range,
2138 format!("({selected} null)"),
2139 diagnostic.clone(),
2140 ));
2141 actions.push(code_action_replace(
2142 "Wrap expression in lambda".to_string(),
2143 uri,
2144 target_range,
2145 format!("(\\_ -> {selected})"),
2146 diagnostic.clone(),
2147 ));
2148 }
2149 }
2150
2151 if diagnostic.message.starts_with("non-exhaustive match for ") {
2152 let (insert_pos, new_text) = wildcard_match_arm_insert(text, diagnostic.range)
2153 .unwrap_or_else(|| {
2154 let newline = if diagnostic.range.start.line == diagnostic.range.end.line {
2155 " "
2156 } else {
2157 "\n"
2158 };
2159 (diagnostic.range.end, format!("{newline}case _ -> null;"))
2160 });
2161 actions.push(code_action_insert(
2162 "Add wildcard arm to match".to_string(),
2163 uri,
2164 insert_pos,
2165 new_text,
2166 diagnostic.clone(),
2167 ));
2168 }
2169
2170 if let Some(field) = field_not_definitely_available_from_message(&diagnostic.message)
2171 && let Some(compilation_unit) = compilation_unit
2172 && let Some(selected) = text_for_range(text, target_range)
2173 {
2174 let candidates = default_record_candidates_for_field(compilation_unit, field);
2175 for ty_name in &candidates {
2176 if let Some(new_text) = replace_first_default_with_is(&selected, ty_name) {
2177 actions.push(code_action_replace(
2178 format!("Disambiguate `default` as `{ty_name}`"),
2179 uri,
2180 target_range,
2181 new_text,
2182 diagnostic.clone(),
2183 ));
2184 }
2185 }
2186
2187 if let Some((binding_name, insert_pos)) =
2188 find_let_binding_for_def_range(compilation_unit, target_range)
2189 {
2190 for ty_name in &candidates {
2191 actions.push(code_action_insert(
2192 format!("Annotate `{binding_name}` as `{ty_name}`"),
2193 uri,
2194 insert_pos,
2195 format!(": {ty_name}"),
2196 diagnostic.clone(),
2197 ));
2198 }
2199 }
2200 }
2201
2202 actions
2203}
2204
2205fn code_action_replace(
2206 title: String,
2207 uri: &Url,
2208 range: Range,
2209 new_text: String,
2210 diagnostic: Diagnostic,
2211) -> CodeActionOrCommand {
2212 code_action_with_edit(title, uri, TextEdit { range, new_text }, diagnostic)
2213}
2214
2215fn code_action_insert(
2216 title: String,
2217 uri: &Url,
2218 position: Position,
2219 new_text: String,
2220 diagnostic: Diagnostic,
2221) -> CodeActionOrCommand {
2222 code_action_with_edit(
2223 title,
2224 uri,
2225 TextEdit {
2226 range: Range {
2227 start: position,
2228 end: position,
2229 },
2230 new_text,
2231 },
2232 diagnostic,
2233 )
2234}
2235
2236fn code_action_with_edit(
2237 title: String,
2238 uri: &Url,
2239 edit: TextEdit,
2240 diagnostic: Diagnostic,
2241) -> CodeActionOrCommand {
2242 let mut changes = HashMap::new();
2243 changes.insert(uri.clone(), vec![edit]);
2244 CodeActionOrCommand::CodeAction(CodeAction {
2245 title,
2246 kind: Some(CodeActionKind::QUICKFIX),
2247 diagnostics: Some(vec![diagnostic]),
2248 edit: Some(WorkspaceEdit {
2249 changes: Some(changes),
2250 document_changes: None,
2251 change_annotations: None,
2252 }),
2253 command: None,
2254 is_preferred: Some(true),
2255 disabled: None,
2256 data: None,
2257 })
2258}
2259
2260fn text_for_range(text: &str, range: Range) -> Option<String> {
2261 let start = offset_at(text, range.start)?;
2262 let end = offset_at(text, range.end)?;
2263 (start <= end && end <= text.len()).then(|| text[start..end].to_string())
2264}
2265
2266fn range_is_usable_for_text(text: &str, range: Range) -> bool {
2267 let Some(start) = offset_at(text, range.start) else {
2268 return false;
2269 };
2270 let Some(end) = offset_at(text, range.end) else {
2271 return false;
2272 };
2273 start <= end && end <= text.len()
2274}
2275
2276fn ranges_overlap(a: Range, b: Range) -> bool {
2277 position_leq_lsp(a.start, b.end) && position_leq_lsp(b.start, a.end)
2278}
2279
2280fn position_leq_lsp(left: Position, right: Position) -> bool {
2281 left.line < right.line || (left.line == right.line && left.character <= right.character)
2282}
2283
2284fn range_is_empty(range: Range) -> bool {
2285 range.start.line == range.end.line && range.start.character == range.end.character
2286}
2287
2288fn unknown_var_name_from_message(message: &str) -> Option<&str> {
2289 message.strip_prefix("unbound variable ").map(str::trim)
2290}
2291
2292fn field_not_definitely_available_from_message(message: &str) -> Option<&str> {
2293 let rest = message.strip_prefix("field `")?;
2294 let (field, tail) = rest.split_once('`')?;
2295 tail.contains("is not definitely available on")
2296 .then_some(field)
2297}
2298
2299fn default_record_candidates_for_field(
2300 compilation_unit: &CompilationUnit,
2301 field: &str,
2302) -> Vec<String> {
2303 let mut out = Vec::new();
2304 let mut seen = HashSet::new();
2305 for decl in &compilation_unit.decls {
2306 let Decl::Instance(inst) = decl else {
2307 continue;
2308 };
2309 if inst.class.as_ref() != "Default" {
2310 continue;
2311 }
2312 let TypeExpr::Name(_, ty_name) = &inst.head else {
2313 continue;
2314 };
2315 if !type_decl_has_record_field(compilation_unit, ty_name.as_ref(), field) {
2316 continue;
2317 }
2318 let ty_name = ty_name.as_ref().to_string();
2319 if seen.insert(ty_name.clone()) {
2320 out.push(ty_name);
2321 }
2322 }
2323 out
2324}
2325
2326fn type_decl_has_record_field(
2327 compilation_unit: &CompilationUnit,
2328 type_name: &str,
2329 field: &str,
2330) -> bool {
2331 compilation_unit.decls.iter().any(|decl| {
2332 let Decl::Type(td) = decl else {
2333 return false;
2334 };
2335 if td.name.as_ref() != type_name {
2336 return false;
2337 }
2338 td.variants.iter().any(|variant| {
2339 variant.args.iter().any(|arg| {
2340 let TypeExpr::Record(_, fields) = arg else {
2341 return false;
2342 };
2343 fields.iter().any(|(name, _)| name.as_ref() == field)
2344 })
2345 })
2346 })
2347}
2348
2349fn replace_first_default_with_is(source: &str, ty_name: &str) -> Option<String> {
2350 for (idx, _) in source.match_indices("default") {
2351 let left_ok = if idx == 0 {
2352 true
2353 } else {
2354 !is_ident_char(source[..idx].chars().next_back().unwrap_or('_'))
2355 };
2356 let right_idx = idx + "default".len();
2357 let right_ok = if right_idx >= source.len() {
2358 true
2359 } else {
2360 !is_ident_char(source[right_idx..].chars().next().unwrap_or('_'))
2361 };
2362 if !(left_ok && right_ok) {
2363 continue;
2364 }
2365
2366 let mut replaced = String::with_capacity(source.len() + ty_name.len() + 8);
2367 replaced.push_str(&source[..idx]);
2368 replaced.push_str("(default is ");
2369 replaced.push_str(ty_name);
2370 replaced.push(')');
2371 replaced.push_str(&source[right_idx..]);
2372 return Some(replaced);
2373 }
2374 None
2375}
2376
2377fn is_ident_char(c: char) -> bool {
2378 c.is_ascii_alphanumeric() || c == '_'
2379}
2380
2381fn is_hole_name(name: &str) -> bool {
2382 name == "_" || name.starts_with('_')
2383}
2384
2385pub fn is_list_scalar_unification_error(message: &str) -> bool {
2386 let Some(rest) = message.strip_prefix("types do not unify: ") else {
2387 return false;
2388 };
2389 let Some((left, right)) = rest.split_once(" vs ") else {
2390 return false;
2391 };
2392 list_inner_type(left.trim()).is_some_and(|inner| inner == right.trim())
2393 || list_inner_type(right.trim()).is_some_and(|inner| inner == left.trim())
2394}
2395
2396fn list_inner_type(typ: &str) -> Option<&str> {
2397 if let Some(inner) = typ
2398 .strip_prefix("List<")
2399 .and_then(|rest| rest.strip_suffix('>'))
2400 {
2401 return Some(inner);
2402 }
2403 typ.strip_prefix("(List ")
2404 .and_then(|rest| rest.strip_suffix(')'))
2405}
2406
2407pub fn is_array_list_unification_error(message: &str) -> bool {
2408 let Some(rest) = message.strip_prefix("types do not unify: ") else {
2409 return false;
2410 };
2411 let Some((left, right)) = rest.split_once(" vs ") else {
2412 return false;
2413 };
2414 let left = left.trim();
2415 let right = right.trim();
2416 let left_has_array = left.contains("Array");
2417 let left_has_list = left.contains("List");
2418 let right_has_array = right.contains("Array");
2419 let right_has_list = right.contains("List");
2420 (left_has_array && right_has_list) || (left_has_list && right_has_array)
2421}
2422
2423pub fn is_function_value_unification_error(message: &str) -> bool {
2424 let Some(rest) = message.strip_prefix("types do not unify: ") else {
2425 return false;
2426 };
2427 let Some((left, right)) = rest.split_once(" vs ") else {
2428 return false;
2429 };
2430 let left_is_fun = looks_like_fun_type(left.trim());
2431 let right_is_fun = looks_like_fun_type(right.trim());
2432 left_is_fun ^ right_is_fun
2433}
2434
2435fn looks_like_fun_type(typ: &str) -> bool {
2436 let mut depth = 0usize;
2437 let bytes = typ.as_bytes();
2438 let mut i = 0usize;
2439 while i + 1 < bytes.len() {
2440 match bytes[i] as char {
2441 '(' | '{' | '[' => depth += 1,
2442 ')' | '}' | ']' => depth = depth.saturating_sub(1),
2443 '-' if bytes[i + 1] as char == '>' && depth == 0 => return true,
2444 _ => {}
2445 }
2446 i += 1;
2447 }
2448
2449 if typ.starts_with('(') && typ.ends_with(')') {
2450 return looks_like_fun_type(&typ[1..typ.len() - 1]);
2451 }
2452 false
2453}
2454
2455fn split_fun_type(typ: &Type) -> (Vec<Type>, Type) {
2456 let mut args = Vec::new();
2457 let mut cur = typ.clone();
2458 while let TypeKind::Fun(arg, ret) = cur.as_ref() {
2459 args.push(arg.clone());
2460 cur = ret.clone();
2461 }
2462 (args, cur)
2463}
2464
2465fn in_scope_value_types_at_position(
2466 uri: &Url,
2467 text: &str,
2468 position: Position,
2469) -> Vec<(String, Type)> {
2470 let Ok((_tokens, program)) = tokenize_and_parse_cached(uri, text) else {
2471 return Vec::new();
2472 };
2473 let Ok((program, mut ts, _imports, _import_diags)) =
2474 prepare_program_with_imports(uri, &program)
2475 else {
2476 return Vec::new();
2477 };
2478 if inject_program_decls(&mut ts, &program, None).is_err() {
2479 return Vec::new();
2480 }
2481
2482 let Some(expr) = program.body_with_fns() else {
2483 return Vec::new();
2484 };
2485 let Ok((typed, _preds, _ty)) = infer_typed(&mut ts, expr.as_ref()) else {
2486 return Vec::new();
2487 };
2488 let pos = lsp_to_rex_position(position);
2489
2490 fn visit(
2491 expr: &Expr,
2492 typed: &TypedExpr,
2493 pos: RexPosition,
2494 scope: &mut Vec<(String, Type)>,
2495 best: &mut Option<Vec<(String, Type)>>,
2496 ) {
2497 if !position_in_span(pos, *expr.span()) {
2498 return;
2499 }
2500 *best = Some(scope.clone());
2501
2502 match (expr, typed.kind.as_ref()) {
2503 (
2504 Expr::Let(_span, var, _ann, def, body),
2505 TypedExprKind::Let {
2506 def: tdef,
2507 body: tbody,
2508 ..
2509 },
2510 ) => {
2511 if position_in_span(pos, *def.span()) {
2512 visit(def.as_ref(), tdef.as_ref(), pos, scope, best);
2513 return;
2514 }
2515 if position_in_span(pos, *body.span()) {
2516 scope.push((var.name.to_string(), tdef.typ.clone()));
2517 visit(body.as_ref(), tbody.as_ref(), pos, scope, best);
2518 scope.pop();
2519 }
2520 }
2521 (
2522 Expr::LetRec(_span, bindings, body),
2523 TypedExprKind::LetRec {
2524 bindings: typed_bindings,
2525 body: typed_body,
2526 },
2527 ) => {
2528 let base = scope.len();
2529 for ((name, _ann, _def), (_typed_name, typed_def)) in
2530 bindings.iter().zip(typed_bindings.iter())
2531 {
2532 scope.push((name.name.to_string(), typed_def.typ.clone()));
2533 }
2534 for ((_, _, def), (_, typed_def)) in bindings.iter().zip(typed_bindings.iter()) {
2535 if position_in_span(pos, *def.span()) {
2536 visit(def.as_ref(), typed_def, pos, scope, best);
2537 scope.truncate(base);
2538 return;
2539 }
2540 }
2541 if position_in_span(pos, *body.span()) {
2542 visit(body.as_ref(), typed_body.as_ref(), pos, scope, best);
2543 }
2544 scope.truncate(base);
2545 }
2546 (
2547 Expr::Lam(_span, _scope, param, _ann, _constraints, body),
2548 TypedExprKind::Lam {
2549 body: typed_body, ..
2550 },
2551 ) => {
2552 if let TypeKind::Fun(arg, _ret) = typed.typ.as_ref() {
2553 scope.push((param.name.to_string(), arg.clone()));
2554 visit(body.as_ref(), typed_body.as_ref(), pos, scope, best);
2555 scope.pop();
2556 }
2557 }
2558 (Expr::App(_span, fun, arg), TypedExprKind::App(tfun, targ)) => {
2559 if position_in_span(pos, *fun.span()) {
2560 visit(fun.as_ref(), tfun.as_ref(), pos, scope, best);
2561 } else if position_in_span(pos, *arg.span()) {
2562 visit(arg.as_ref(), targ.as_ref(), pos, scope, best);
2563 }
2564 }
2565 (Expr::Project(_span, base, _field), TypedExprKind::Project { expr: tbase, .. }) => {
2566 visit(base.as_ref(), tbase.as_ref(), pos, scope, best);
2567 }
2568 (
2569 Expr::Ite(_span, cond, then_expr, else_expr),
2570 TypedExprKind::Ite {
2571 cond: tcond,
2572 then_expr: tthen,
2573 else_expr: telse,
2574 },
2575 ) => {
2576 if position_in_span(pos, *cond.span()) {
2577 visit(cond.as_ref(), tcond.as_ref(), pos, scope, best);
2578 } else if position_in_span(pos, *then_expr.span()) {
2579 visit(then_expr.as_ref(), tthen.as_ref(), pos, scope, best);
2580 } else if position_in_span(pos, *else_expr.span()) {
2581 visit(else_expr.as_ref(), telse.as_ref(), pos, scope, best);
2582 }
2583 }
2584 (Expr::Tuple(_span, elems), TypedExprKind::Tuple(typed_elems))
2585 | (Expr::List(_span, elems), TypedExprKind::List(typed_elems)) => {
2586 for (elem, typed_elem) in elems.iter().zip(typed_elems.iter()) {
2587 if position_in_span(pos, *elem.span()) {
2588 visit(elem.as_ref(), typed_elem, pos, scope, best);
2589 break;
2590 }
2591 }
2592 }
2593 (Expr::Dict(_span, kvs), TypedExprKind::Dict(typed_kvs)) => {
2594 for (key, value) in kvs {
2595 if position_in_span(pos, *value.span())
2596 && let Some(typed_v) = typed_kvs.get(key)
2597 {
2598 visit(value.as_ref(), typed_v, pos, scope, best);
2599 break;
2600 }
2601 }
2602 }
2603 (
2604 Expr::RecordUpdate(_span, base, updates),
2605 TypedExprKind::RecordUpdate {
2606 base: tbase,
2607 updates: typed_updates,
2608 },
2609 ) => {
2610 if position_in_span(pos, *base.span()) {
2611 visit(base.as_ref(), tbase.as_ref(), pos, scope, best);
2612 } else {
2613 for (key, value) in updates {
2614 if position_in_span(pos, *value.span())
2615 && let Some(typed_v) = typed_updates.get(key)
2616 {
2617 visit(value.as_ref(), typed_v, pos, scope, best);
2618 break;
2619 }
2620 }
2621 }
2622 }
2623 (
2624 Expr::Match(_span, scrutinee, arms),
2625 TypedExprKind::Match {
2626 scrutinee: tscrutinee,
2627 arms: typed_arms,
2628 },
2629 ) => {
2630 if position_in_span(pos, *scrutinee.span()) {
2631 visit(scrutinee.as_ref(), tscrutinee.as_ref(), pos, scope, best);
2632 } else {
2633 for ((_pat, arm), (_typed_pat, typed_arm)) in arms.iter().zip(typed_arms.iter())
2634 {
2635 if position_in_span(pos, *arm.span()) {
2636 visit(arm.as_ref(), typed_arm, pos, scope, best);
2637 break;
2638 }
2639 }
2640 }
2641 }
2642 (Expr::Ann(_span, inner, _), _) => visit(inner.as_ref(), typed, pos, scope, best),
2643 _ => {}
2644 }
2645 }
2646
2647 let mut best = None;
2648 visit(expr.as_ref(), &typed, pos, &mut Vec::new(), &mut best);
2649 best.unwrap_or_default()
2650}
2651
2652fn hole_fill_candidates_at_position(
2653 uri: &Url,
2654 text: &str,
2655 position: Position,
2656) -> Vec<(String, String)> {
2657 let Some(target_type) = expected_type_at_position_type(uri, text, position) else {
2658 return Vec::new();
2659 };
2660 let Ok((_tokens, program)) = tokenize_and_parse_cached(uri, text) else {
2661 return Vec::new();
2662 };
2663 let Ok((program, mut ts, _imports, _import_diags)) =
2664 prepare_program_with_imports(uri, &program)
2665 else {
2666 return Vec::new();
2667 };
2668 if inject_program_decls(&mut ts, &program, None).is_err() {
2669 return Vec::new();
2670 }
2671 let mut in_scope = in_scope_value_types_at_position(uri, text, position)
2672 .into_iter()
2673 .filter(|(name, _)| is_ident_like(name))
2674 .collect::<Vec<_>>();
2675 if in_scope.len() > MAX_SEMANTIC_IN_SCOPE_VALUES {
2676 in_scope = in_scope.split_off(in_scope.len().saturating_sub(MAX_SEMANTIC_IN_SCOPE_VALUES));
2677 }
2678
2679 let values = semantic_candidate_values(&ts);
2680
2681 let mut adapters: Vec<(String, Type, Type)> = Vec::new();
2682 for (name, schemes) in &values {
2683 let name = name.to_string();
2684 if !is_ident_like(&name) {
2685 continue;
2686 }
2687 for scheme in schemes {
2688 let (_preds, inst_ty) = instantiate(scheme, &mut ts.supply);
2689 let (args, ret) = split_fun_type(&inst_ty);
2690 if args.len() == 1 {
2691 adapters.push((name.clone(), args[0].clone(), ret));
2692 }
2693 }
2694 }
2695
2696 let mut out: Vec<(usize, usize, String, String)> = Vec::new();
2697 for (name, schemes) in values {
2698 let name = name.to_string();
2699 if !is_ident_like(&name) {
2700 continue;
2701 }
2702 for scheme in schemes {
2703 let (_preds, inst_ty) = instantiate(&scheme, &mut ts.supply);
2704 let (args, ret) = split_fun_type(&inst_ty);
2705 if args.is_empty()
2706 || args.len() > MAX_SEMANTIC_HOLE_FILL_ARITY
2707 || unify(&ret, &target_type).is_err()
2708 {
2709 continue;
2710 }
2711
2712 let mut unresolved = 0usize;
2713 let mut adapter_uses = 0usize;
2714 let mut rendered_args = Vec::new();
2715 for arg_ty in args {
2716 if let Some((value_name, _value_ty)) = in_scope
2717 .iter()
2718 .rev()
2719 .find(|(_, value_ty)| unify(value_ty, &arg_ty).is_ok())
2720 {
2721 rendered_args.push(value_name.clone());
2722 continue;
2723 }
2724
2725 let mut adapted = None;
2726 for (adapter_name, adapter_arg, adapter_ret) in &adapters {
2727 if unify(adapter_ret, &arg_ty).is_err() {
2728 continue;
2729 }
2730 if let Some((value_name, _value_ty)) = in_scope
2731 .iter()
2732 .rev()
2733 .find(|(_, value_ty)| unify(value_ty, adapter_arg).is_ok())
2734 {
2735 adapted = Some(format!("({adapter_name} {value_name})"));
2736 break;
2737 }
2738 }
2739 if let Some(expr) = adapted {
2740 adapter_uses += 1;
2741 rendered_args.push(expr);
2742 } else {
2743 unresolved += 1;
2744 rendered_args.push("?".to_string());
2745 }
2746 }
2747
2748 let replacement = format!("{name} {}", rendered_args.join(" "));
2749 out.push((unresolved, adapter_uses, name.clone(), replacement));
2750 }
2751 }
2752
2753 out.sort_by(|a, b| a.0.cmp(&b.0).then(a.1.cmp(&b.1)).then(a.2.cmp(&b.2)));
2754 out.dedup_by(|a, b| a.2 == b.2 && a.3 == b.3);
2755 if out.len() > MAX_SEMANTIC_CANDIDATES {
2756 out.truncate(MAX_SEMANTIC_CANDIDATES);
2757 }
2758 out.into_iter()
2759 .map(|(_u, _a, name, replacement)| (name, replacement))
2760 .collect()
2761}
2762
2763fn levenshtein_distance(left: &str, right: &str) -> usize {
2764 if left == right {
2765 return 0;
2766 }
2767 if left.is_empty() {
2768 return right.chars().count();
2769 }
2770 if right.is_empty() {
2771 return left.chars().count();
2772 }
2773
2774 let right_len = right.chars().count();
2775 let mut prev: Vec<usize> = (0..=right_len).collect();
2776 let mut cur = vec![0usize; right_len + 1];
2777
2778 for (i, lc) in left.chars().enumerate() {
2779 cur[0] = i + 1;
2780 for (j, rc) in right.chars().enumerate() {
2781 let insert_cost = cur[j] + 1;
2782 let delete_cost = prev[j + 1] + 1;
2783 let replace_cost = prev[j] + usize::from(lc != rc);
2784 cur[j + 1] = insert_cost.min(delete_cost).min(replace_cost);
2785 }
2786 std::mem::swap(&mut prev, &mut cur);
2787 }
2788
2789 prev[right_len]
2790}
2791
2792#[allow(deprecated)]
2793fn symbol_for_decl(decl: &Decl) -> Option<DocumentSymbol> {
2794 match decl {
2795 Decl::Type(td) => Some(DocumentSymbol {
2796 name: td.name.to_string(),
2797 detail: Some("type".to_string()),
2798 kind: SymbolKind::ENUM,
2799 tags: None,
2800 deprecated: None,
2801 range: span_to_range(td.span),
2802 selection_range: span_to_range(td.span),
2803 children: Some(
2804 td.variants
2805 .iter()
2806 .map(|variant| DocumentSymbol {
2807 name: variant.name.to_string(),
2808 detail: Some("variant".to_string()),
2809 kind: SymbolKind::ENUM_MEMBER,
2810 tags: None,
2811 deprecated: None,
2812 range: span_to_range(td.span),
2813 selection_range: span_to_range(td.span),
2814 children: None,
2815 })
2816 .collect(),
2817 ),
2818 }),
2819 Decl::Fn(fd) => Some(DocumentSymbol {
2820 name: fd.name.name.to_string(),
2821 detail: Some("fn".to_string()),
2822 kind: SymbolKind::FUNCTION,
2823 tags: None,
2824 deprecated: None,
2825 range: span_to_range(fd.span),
2826 selection_range: span_to_range(fd.name.span),
2827 children: None,
2828 }),
2829 Decl::DeclareFn(df) => Some(DocumentSymbol {
2830 name: df.name.name.to_string(),
2831 detail: Some("declare fn".to_string()),
2832 kind: SymbolKind::FUNCTION,
2833 tags: None,
2834 deprecated: None,
2835 range: span_to_range(df.span),
2836 selection_range: span_to_range(df.name.span),
2837 children: None,
2838 }),
2839 Decl::Import(id) => Some(DocumentSymbol {
2840 name: id.alias.to_string(),
2841 detail: Some("import".to_string()),
2842 kind: SymbolKind::MODULE,
2843 tags: None,
2844 deprecated: None,
2845 range: span_to_range(id.span),
2846 selection_range: span_to_range(id.span),
2847 children: None,
2848 }),
2849 Decl::Class(cd) => Some(DocumentSymbol {
2850 name: cd.name.to_string(),
2851 detail: Some("class".to_string()),
2852 kind: SymbolKind::INTERFACE,
2853 tags: None,
2854 deprecated: None,
2855 range: span_to_range(cd.span),
2856 selection_range: span_to_range(cd.span),
2857 children: Some(
2858 cd.methods
2859 .iter()
2860 .map(|method| DocumentSymbol {
2861 name: method.name.to_string(),
2862 detail: Some("method".to_string()),
2863 kind: SymbolKind::METHOD,
2864 tags: None,
2865 deprecated: None,
2866 range: span_to_range(cd.span),
2867 selection_range: span_to_range(cd.span),
2868 children: None,
2869 })
2870 .collect(),
2871 ),
2872 }),
2873 Decl::Instance(id) => Some(DocumentSymbol {
2874 name: format!("instance {}", id.class),
2875 detail: Some("instance".to_string()),
2876 kind: SymbolKind::OBJECT,
2877 tags: None,
2878 deprecated: None,
2879 range: span_to_range(id.span),
2880 selection_range: span_to_range(id.span),
2881 children: Some(
2882 id.methods
2883 .iter()
2884 .map(|method| DocumentSymbol {
2885 name: method.name.to_string(),
2886 detail: Some("method".to_string()),
2887 kind: SymbolKind::METHOD,
2888 tags: None,
2889 deprecated: None,
2890 range: span_to_range(*method.body.span()),
2891 selection_range: span_to_range(*method.body.span()),
2892 children: None,
2893 })
2894 .collect(),
2895 ),
2896 }),
2897 }
2898}
2899
2900pub(crate) fn document_symbols_for_source(uri: &Url, text: &str) -> Vec<DocumentSymbol> {
2901 let Ok((_tokens, program)) = tokenize_and_parse_cached(uri, text) else {
2902 return Vec::new();
2903 };
2904 program.decls.iter().filter_map(symbol_for_decl).collect()
2905}
2906
2907pub fn full_document_range(text: &str) -> Range {
2908 let mut line = 0u32;
2909 let mut col = 0u32;
2910 for ch in text.chars() {
2911 if ch == '\n' {
2912 line += 1;
2913 col = 0;
2914 } else {
2915 col += 1;
2916 }
2917 }
2918 Range {
2919 start: Position {
2920 line: 0,
2921 character: 0,
2922 },
2923 end: Position {
2924 line,
2925 character: col,
2926 },
2927 }
2928}
2929
2930fn format_source(text: &str) -> String {
2931 let mut out = String::new();
2932 let mut first = true;
2933 for line in text.lines() {
2934 if !first {
2935 out.push('\n');
2936 }
2937 first = false;
2938 out.push_str(line.trim_end());
2939 }
2940 if text.ends_with('\n') || !out.is_empty() {
2941 out.push('\n');
2942 }
2943 out
2944}
2945
2946pub(crate) fn format_edits_for_source(text: &str) -> Option<Vec<TextEdit>> {
2947 let formatted = format_source(text);
2948 if formatted == text {
2949 return None;
2950 }
2951 Some(vec![TextEdit {
2952 range: full_document_range(text),
2953 new_text: formatted,
2954 }])
2955}
2956
2957pub fn diagnostics_from_text(uri: &Url, text: &str) -> Vec<Diagnostic> {
2958 let mut diagnostics = Vec::new();
2959
2960 match tokenize_and_parse_cached(uri, text) {
2961 Ok((tokens, program)) => {
2962 push_comment_diagnostics(&tokens, &mut diagnostics);
2963 if diagnostics.len() < MAX_DIAGNOSTICS {
2964 push_type_diagnostics(uri, text, &program, &mut diagnostics);
2965 }
2966 }
2967 Err(TokenizeOrParseError::Lex(err)) => {
2968 diagnostics.push(diagnostic_for_lexical_error(&err));
2969 }
2970 Err(TokenizeOrParseError::Parse(errors)) => {
2971 for err in errors {
2972 diagnostics.push(diagnostic_for_span(err.span, err.message));
2973 if diagnostics.len() >= MAX_DIAGNOSTICS {
2974 break;
2975 }
2976 }
2977 }
2978 }
2979
2980 diagnostics
2981}
2982
2983fn diagnostic_for_lexical_error(err: &LexicalError) -> Diagnostic {
2984 let (span, message) = match err {
2985 LexicalError::UnexpectedToken(span) => (*span, "Unexpected token".to_string()),
2986 LexicalError::InvalidLiteral {
2987 kind,
2988 text,
2989 error,
2990 span,
2991 } => (*span, format!("invalid {kind} literal `{text}`: {error}")),
2992 LexicalError::Internal(msg) => (
2993 Span::new(1, 1, 1, 1),
2994 format!("internal lexer error: {msg}"),
2995 ),
2996 };
2997 diagnostic_for_span(span, message)
2998}
2999
3000struct HoverType {
3001 span: Span,
3002 label: String,
3003 typ: String,
3004 overloads: Vec<String>,
3005}
3006
3007pub(crate) fn hover_type_contents(
3008 uri: &Url,
3009 text: &str,
3010 position: Position,
3011) -> Option<HoverContents> {
3012 let (tokens, program) = tokenize_and_parse_cached(uri, text).ok()?;
3013 let (name, name_span, name_is_ident) = name_token_at_position(&tokens, position)?;
3014 let (program, mut ts, _imports, _import_diags) =
3015 prepare_program_with_imports(uri, &program).ok()?;
3016
3017 let pos = lsp_to_rex_position(position);
3018
3019 let mut target_instance: Option<(usize, usize)> = None;
3022 for (decl_idx, decl) in program.decls.iter().enumerate() {
3023 let Decl::Instance(inst) = decl else {
3024 continue;
3025 };
3026 for (method_idx, method) in inst.methods.iter().enumerate() {
3027 if position_in_span(pos, *method.body.span()) {
3028 target_instance = Some((decl_idx, method_idx));
3029 break;
3030 }
3031 }
3032 if target_instance.is_some() {
3033 break;
3034 }
3035 }
3036
3037 let (_instances, prepared_target_instance) = inject_program_decls(
3038 &mut ts,
3039 &program,
3040 target_instance.map(|(decl_idx, _)| decl_idx),
3041 )
3042 .ok()?;
3043
3044 let body_with_fns = program.body_with_fns();
3045
3046 let root_expr: &Expr;
3047 let typed_root: TypedExpr;
3048
3049 if let Some((decl_idx, method_idx)) = target_instance {
3050 let Decl::Instance(inst) = &program.decls[decl_idx] else {
3051 return None;
3052 };
3053 let prepared = prepared_target_instance?;
3054 let method = inst.methods.get(method_idx)?;
3055 typed_root = ts.typecheck_instance_method(&prepared, method).ok()?;
3056 root_expr = method.body.as_ref();
3057 } else {
3058 let body_with_fns = body_with_fns.as_ref()?;
3059 let (typed, _preds, _) = infer_typed(&mut ts, body_with_fns.as_ref()).ok()?;
3060 typed_root = typed;
3061 root_expr = body_with_fns.as_ref();
3062 }
3063
3064 let hover = hover_type_in_expr(
3065 &mut ts,
3066 root_expr,
3067 &typed_root,
3068 pos,
3069 &name,
3070 name_span,
3071 name_is_ident,
3072 )?;
3073
3074 let mut md = String::new();
3075 md.push_str("```rex\n");
3076 md.push_str(&hover.label);
3077 md.push_str(" : ");
3078 md.push_str(&hover.typ);
3079 md.push_str("\n```");
3080
3081 if !hover.overloads.is_empty() {
3082 md.push_str("\n\nOverloads:\n");
3083 for ov in &hover.overloads {
3084 md.push_str("- `");
3085 md.push_str(ov);
3086 md.push_str("`\n");
3087 }
3088 }
3089
3090 Some(HoverContents::Markup(MarkupContent {
3091 kind: MarkupKind::Markdown,
3092 value: md,
3093 }))
3094}
3095
3096fn expected_type_at_position(uri: &Url, text: &str, position: Position) -> Option<String> {
3097 expected_type_at_position_type(uri, text, position).map(|ty| ty.to_string())
3098}
3099
3100fn inferred_type_at_position(uri: &Url, text: &str, position: Position) -> Option<String> {
3101 inferred_type_at_position_type(uri, text, position).map(|ty| ty.to_string())
3102}
3103
3104fn expected_type_at_position_type(uri: &Url, text: &str, position: Position) -> Option<Type> {
3105 let (_tokens, program) = tokenize_and_parse_cached(uri, text).ok()?;
3106 let (program, mut ts, _imports, _import_diags) =
3107 prepare_program_with_imports(uri, &program).ok()?;
3108
3109 let pos = lsp_to_rex_position(position);
3110
3111 let mut target_instance: Option<(usize, usize)> = None;
3113 for (decl_idx, decl) in program.decls.iter().enumerate() {
3114 let Decl::Instance(inst) = decl else {
3115 continue;
3116 };
3117 for (method_idx, method) in inst.methods.iter().enumerate() {
3118 if position_in_span(pos, *method.body.span()) {
3119 target_instance = Some((decl_idx, method_idx));
3120 break;
3121 }
3122 }
3123 if target_instance.is_some() {
3124 break;
3125 }
3126 }
3127
3128 let (_instances, prepared_target_instance) = inject_program_decls(
3129 &mut ts,
3130 &program,
3131 target_instance.map(|(decl_idx, _)| decl_idx),
3132 )
3133 .ok()?;
3134
3135 let body_with_fns = program.body_with_fns();
3136 let root_expr: &Expr;
3137 let typed_root: TypedExpr;
3138
3139 if let Some((decl_idx, method_idx)) = target_instance {
3140 let Decl::Instance(inst) = &program.decls[decl_idx] else {
3141 return None;
3142 };
3143 let prepared = prepared_target_instance?;
3144 let method = inst.methods.get(method_idx)?;
3145 typed_root = ts.typecheck_instance_method(&prepared, method).ok()?;
3146 root_expr = method.body.as_ref();
3147 } else {
3148 let body_with_fns = body_with_fns.as_ref()?;
3149 let (typed, _preds, _) = infer_typed(&mut ts, body_with_fns.as_ref()).ok()?;
3150 typed_root = typed;
3151 root_expr = body_with_fns.as_ref();
3152 }
3153
3154 expected_type_in_expr(root_expr, &typed_root, pos)
3155}
3156
3157fn inferred_type_at_position_type(uri: &Url, text: &str, position: Position) -> Option<Type> {
3158 let (_tokens, program) = tokenize_and_parse_cached(uri, text).ok()?;
3159 let (program, mut ts, _imports, _import_diags) =
3160 prepare_program_with_imports(uri, &program).ok()?;
3161
3162 let pos = lsp_to_rex_position(position);
3163
3164 let mut target_instance: Option<(usize, usize)> = None;
3165 for (decl_idx, decl) in program.decls.iter().enumerate() {
3166 let Decl::Instance(inst) = decl else {
3167 continue;
3168 };
3169 for (method_idx, method) in inst.methods.iter().enumerate() {
3170 if position_in_span(pos, *method.body.span()) {
3171 target_instance = Some((decl_idx, method_idx));
3172 break;
3173 }
3174 }
3175 if target_instance.is_some() {
3176 break;
3177 }
3178 }
3179
3180 let (_instances, prepared_target_instance) = inject_program_decls(
3181 &mut ts,
3182 &program,
3183 target_instance.map(|(decl_idx, _)| decl_idx),
3184 )
3185 .ok()?;
3186
3187 let body_with_fns = program.body_with_fns();
3188 let root_expr: &Expr;
3189 let typed_root: TypedExpr;
3190
3191 if let Some((decl_idx, method_idx)) = target_instance {
3192 let Decl::Instance(inst) = &program.decls[decl_idx] else {
3193 return None;
3194 };
3195 let prepared = prepared_target_instance?;
3196 let method = inst.methods.get(method_idx)?;
3197 typed_root = ts.typecheck_instance_method(&prepared, method).ok()?;
3198 root_expr = method.body.as_ref();
3199 } else {
3200 let body_with_fns = body_with_fns.as_ref()?;
3201 let (typed, _preds, _) = infer_typed(&mut ts, body_with_fns.as_ref()).ok()?;
3202 typed_root = typed;
3203 root_expr = body_with_fns.as_ref();
3204 }
3205
3206 inferred_type_in_expr(root_expr, &typed_root, pos)
3207}
3208
3209fn expected_type_in_expr(expr: &Expr, typed: &TypedExpr, pos: RexPosition) -> Option<Type> {
3210 #[derive(Clone)]
3211 struct Candidate {
3212 span: Span,
3213 typ: Type,
3214 }
3215
3216 fn span_size(span: Span) -> (usize, usize) {
3217 (
3218 span.end.line.saturating_sub(span.begin.line),
3219 span.end.column.saturating_sub(span.begin.column),
3220 )
3221 }
3222
3223 fn consider(best: &mut Option<Candidate>, span: Span, typ: &Type) {
3224 let replace = best
3225 .as_ref()
3226 .is_none_or(|cur| span_size(span) < span_size(cur.span));
3227 if replace {
3228 *best = Some(Candidate {
3229 span,
3230 typ: typ.clone(),
3231 });
3232 }
3233 }
3234
3235 fn visit(
3236 expr: &Expr,
3237 typed: &TypedExpr,
3238 pos: RexPosition,
3239 expected: Option<&Type>,
3240 best: &mut Option<Candidate>,
3241 ) {
3242 if !position_in_span(pos, *expr.span()) {
3243 return;
3244 }
3245
3246 if let Some(expected) = expected {
3247 consider(best, *expr.span(), expected);
3248 }
3249
3250 match (expr, typed.kind.as_ref()) {
3251 (
3252 Expr::Let(_span, _name, _ann, def, body),
3253 TypedExprKind::Let {
3254 def: tdef,
3255 body: tbody,
3256 ..
3257 },
3258 ) => {
3259 visit(def.as_ref(), tdef.as_ref(), pos, Some(&tdef.typ), best);
3260 visit(body.as_ref(), tbody.as_ref(), pos, Some(&typed.typ), best);
3261 }
3262 (
3263 Expr::LetRec(_span, bindings, body),
3264 TypedExprKind::LetRec {
3265 bindings: typed_bindings,
3266 body: typed_body,
3267 },
3268 ) => {
3269 for ((_name, _ann, def), (_typed_name, typed_def)) in
3270 bindings.iter().zip(typed_bindings.iter())
3271 {
3272 visit(def.as_ref(), typed_def, pos, Some(&typed_def.typ), best);
3273 }
3274 visit(
3275 body.as_ref(),
3276 typed_body.as_ref(),
3277 pos,
3278 Some(&typed.typ),
3279 best,
3280 );
3281 }
3282 (
3283 Expr::Lam(_span, _scope, _param, _ann, _constraints, body),
3284 TypedExprKind::Lam {
3285 body: typed_body, ..
3286 },
3287 ) => {
3288 let body_expected = match typed.typ.as_ref() {
3289 TypeKind::Fun(_arg, ret) => Some(ret),
3290 _ => None,
3291 };
3292 visit(body.as_ref(), typed_body.as_ref(), pos, body_expected, best);
3293 }
3294 (Expr::App(_span, f, x), TypedExprKind::App(tf, tx)) => {
3295 let expected_arg = match tf.typ.as_ref() {
3296 TypeKind::Fun(arg, _ret) => Some(arg),
3297 _ => None,
3298 };
3299 visit(x.as_ref(), tx.as_ref(), pos, expected_arg, best);
3300
3301 let expected_fun = Type::fun(tx.typ.clone(), typed.typ.clone());
3302 visit(f.as_ref(), tf.as_ref(), pos, Some(&expected_fun), best);
3303 }
3304 (Expr::Project(_span, base, _field), TypedExprKind::Project { expr: tbase, .. }) => {
3305 visit(base.as_ref(), tbase.as_ref(), pos, None, best);
3306 }
3307 (
3308 Expr::Ite(_span, cond, then_expr, else_expr),
3309 TypedExprKind::Ite {
3310 cond: tcond,
3311 then_expr: tthen,
3312 else_expr: telse,
3313 },
3314 ) => {
3315 let bool_ty = Type::builtin(BuiltinTypeId::Bool);
3316 visit(cond.as_ref(), tcond.as_ref(), pos, Some(&bool_ty), best);
3317 visit(
3318 then_expr.as_ref(),
3319 tthen.as_ref(),
3320 pos,
3321 Some(&typed.typ),
3322 best,
3323 );
3324 visit(
3325 else_expr.as_ref(),
3326 telse.as_ref(),
3327 pos,
3328 Some(&typed.typ),
3329 best,
3330 );
3331 }
3332 (Expr::Tuple(_span, elems), TypedExprKind::Tuple(typed_elems)) => {
3333 for (elem, typed_elem) in elems.iter().zip(typed_elems.iter()) {
3334 visit(elem.as_ref(), typed_elem, pos, Some(&typed_elem.typ), best);
3335 }
3336 }
3337 (Expr::List(_span, elems), TypedExprKind::List(typed_elems)) => {
3338 let list_elem_expected = match typed.typ.as_ref() {
3339 TypeKind::App(head, elem) => match head.as_ref() {
3340 TypeKind::Con(tc)
3341 if tc.is_builtin(BuiltinTypeId::List) && tc.arity() == 1 =>
3342 {
3343 Some(elem)
3344 }
3345 _ => None,
3346 },
3347 _ => None,
3348 };
3349 for (elem, typed_elem) in elems.iter().zip(typed_elems.iter()) {
3350 let expected = list_elem_expected.unwrap_or(&typed_elem.typ);
3351 visit(elem.as_ref(), typed_elem, pos, Some(expected), best);
3352 }
3353 }
3354 (Expr::Dict(_span, kvs), TypedExprKind::Dict(typed_kvs)) => {
3355 for (key, value) in kvs {
3356 if let Some(typed_value) = typed_kvs.get(key) {
3357 visit(
3358 value.as_ref(),
3359 typed_value,
3360 pos,
3361 Some(&typed_value.typ),
3362 best,
3363 );
3364 }
3365 }
3366 }
3367 (
3368 Expr::RecordUpdate(_span, base, updates),
3369 TypedExprKind::RecordUpdate {
3370 base: typed_base,
3371 updates: typed_updates,
3372 },
3373 ) => {
3374 visit(base.as_ref(), typed_base.as_ref(), pos, None, best);
3375 for (key, value) in updates {
3376 if let Some(typed_value) = typed_updates.get(key) {
3377 visit(
3378 value.as_ref(),
3379 typed_value,
3380 pos,
3381 Some(&typed_value.typ),
3382 best,
3383 );
3384 }
3385 }
3386 }
3387 (
3388 Expr::Match(_span, scrutinee, arms),
3389 TypedExprKind::Match {
3390 scrutinee: tscrutinee,
3391 arms: typed_arms,
3392 },
3393 ) => {
3394 visit(
3395 scrutinee.as_ref(),
3396 tscrutinee.as_ref(),
3397 pos,
3398 Some(&tscrutinee.typ),
3399 best,
3400 );
3401 for ((_pat, arm), (_typed_pat, typed_arm)) in arms.iter().zip(typed_arms.iter()) {
3402 visit(arm.as_ref(), typed_arm, pos, Some(&typed.typ), best);
3403 }
3404 }
3405 (Expr::Ann(_span, inner, _ann), _) => {
3406 visit(inner.as_ref(), typed, pos, Some(&typed.typ), best);
3407 }
3408 _ => {}
3409 }
3410 }
3411
3412 let mut best: Option<Candidate> = None;
3413 visit(expr, typed, pos, None, &mut best);
3414 best.map(|candidate| candidate.typ)
3415}
3416
3417fn inferred_type_in_expr(expr: &Expr, typed: &TypedExpr, pos: RexPosition) -> Option<Type> {
3418 fn span_size(span: Span) -> (usize, usize) {
3419 (
3420 span.end.line.saturating_sub(span.begin.line),
3421 span.end.column.saturating_sub(span.begin.column),
3422 )
3423 }
3424
3425 fn visit(expr: &Expr, typed: &TypedExpr, pos: RexPosition, best: &mut Option<(Span, Type)>) {
3426 let span = *expr.span();
3427 if !position_in_span(pos, span) {
3428 return;
3429 }
3430 if best
3431 .as_ref()
3432 .is_none_or(|(best_span, _)| span_size(span) < span_size(*best_span))
3433 {
3434 *best = Some((span, typed.typ.clone()));
3435 }
3436
3437 match (expr, typed.kind.as_ref()) {
3438 (
3439 Expr::Let(_, _, _, def, body),
3440 TypedExprKind::Let {
3441 def: tdef,
3442 body: tbody,
3443 ..
3444 },
3445 ) => {
3446 visit(def.as_ref(), tdef.as_ref(), pos, best);
3447 visit(body.as_ref(), tbody.as_ref(), pos, best);
3448 }
3449 (
3450 Expr::LetRec(_, bindings, body),
3451 TypedExprKind::LetRec {
3452 bindings: typed_bindings,
3453 body: typed_body,
3454 },
3455 ) => {
3456 for ((_, _, def), (_, typed_def)) in bindings.iter().zip(typed_bindings.iter()) {
3457 visit(def.as_ref(), typed_def, pos, best);
3458 }
3459 visit(body.as_ref(), typed_body.as_ref(), pos, best);
3460 }
3461 (
3462 Expr::Lam(_, _, _, _, _, body),
3463 TypedExprKind::Lam {
3464 body: typed_body, ..
3465 },
3466 ) => {
3467 visit(body.as_ref(), typed_body.as_ref(), pos, best);
3468 }
3469 (Expr::App(_, f, x), TypedExprKind::App(tf, tx)) => {
3470 visit(f.as_ref(), tf.as_ref(), pos, best);
3471 visit(x.as_ref(), tx.as_ref(), pos, best);
3472 }
3473 (Expr::Project(_, base, _), TypedExprKind::Project { expr: tbase, .. }) => {
3474 visit(base.as_ref(), tbase.as_ref(), pos, best);
3475 }
3476 (
3477 Expr::Ite(_, cond, then_expr, else_expr),
3478 TypedExprKind::Ite {
3479 cond: tcond,
3480 then_expr: tthen,
3481 else_expr: telse,
3482 },
3483 ) => {
3484 visit(cond.as_ref(), tcond.as_ref(), pos, best);
3485 visit(then_expr.as_ref(), tthen.as_ref(), pos, best);
3486 visit(else_expr.as_ref(), telse.as_ref(), pos, best);
3487 }
3488 (Expr::Tuple(_, elems), TypedExprKind::Tuple(typed_elems))
3489 | (Expr::List(_, elems), TypedExprKind::List(typed_elems)) => {
3490 for (elem, typed_elem) in elems.iter().zip(typed_elems.iter()) {
3491 visit(elem.as_ref(), typed_elem, pos, best);
3492 }
3493 }
3494 (Expr::Dict(_, kvs), TypedExprKind::Dict(typed_kvs)) => {
3495 for (key, value) in kvs {
3496 if let Some(typed_value) = typed_kvs.get(key) {
3497 visit(value.as_ref(), typed_value, pos, best);
3498 }
3499 }
3500 }
3501 (
3502 Expr::RecordUpdate(_, base, updates),
3503 TypedExprKind::RecordUpdate {
3504 base: typed_base,
3505 updates: typed_updates,
3506 },
3507 ) => {
3508 visit(base.as_ref(), typed_base.as_ref(), pos, best);
3509 for (key, value) in updates {
3510 if let Some(typed_value) = typed_updates.get(key) {
3511 visit(value.as_ref(), typed_value, pos, best);
3512 }
3513 }
3514 }
3515 (
3516 Expr::Match(_, scrutinee, arms),
3517 TypedExprKind::Match {
3518 scrutinee: tscrutinee,
3519 arms: typed_arms,
3520 },
3521 ) => {
3522 visit(scrutinee.as_ref(), tscrutinee.as_ref(), pos, best);
3523 for ((_pat, arm), (_typed_pat, typed_arm)) in arms.iter().zip(typed_arms.iter()) {
3524 visit(arm.as_ref(), typed_arm, pos, best);
3525 }
3526 }
3527 (Expr::Ann(_, inner, _), _) => visit(inner.as_ref(), typed, pos, best),
3528 _ => {}
3529 }
3530 }
3531
3532 let mut best: Option<(Span, Type)> = None;
3533 visit(expr, typed, pos, &mut best);
3534 best.map(|(_, ty)| ty)
3535}
3536
3537fn functions_producing_expected_type_at_position(
3538 uri: &Url,
3539 text: &str,
3540 position: Position,
3541) -> Vec<(String, String)> {
3542 let Some(target_type) = expected_type_at_position_type(uri, text, position) else {
3543 return Vec::new();
3544 };
3545
3546 let Ok((_tokens, program)) = tokenize_and_parse_cached(uri, text) else {
3547 return Vec::new();
3548 };
3549 let Ok((program, mut ts, _imports, _import_diags)) =
3550 prepare_program_with_imports(uri, &program)
3551 else {
3552 return Vec::new();
3553 };
3554 if inject_program_decls(&mut ts, &program, None).is_err() {
3555 return Vec::new();
3556 }
3557
3558 let values = semantic_candidate_values(&ts);
3559
3560 let mut out = Vec::new();
3561 for (name, schemes) in values {
3562 for scheme in schemes {
3563 let (_preds, inst_ty) = instantiate(&scheme, &mut ts.supply);
3564 let mut cur = &inst_ty;
3565 let mut is_function = false;
3566 while let TypeKind::Fun(_, ret) = cur.as_ref() {
3567 is_function = true;
3568 cur = ret;
3569 }
3570 if !is_function {
3571 continue;
3572 }
3573 if unify(cur, &target_type).is_ok() {
3574 out.push((name.to_string(), scheme.typ.to_string()));
3575 }
3576 }
3577 }
3578
3579 out.sort_by(|a, b| a.0.cmp(&b.0).then(a.1.cmp(&b.1)));
3580 out.dedup();
3581 if out.len() > MAX_SEMANTIC_CANDIDATES {
3582 out.truncate(MAX_SEMANTIC_CANDIDATES);
3583 }
3584 out
3585}
3586
3587fn functions_accepting_inferred_type_at_position(
3588 uri: &Url,
3589 text: &str,
3590 position: Position,
3591) -> Vec<(String, String)> {
3592 let Some(source_type) = inferred_type_at_position_type(uri, text, position) else {
3593 return Vec::new();
3594 };
3595
3596 let Ok((_tokens, program)) = tokenize_and_parse_cached(uri, text) else {
3597 return Vec::new();
3598 };
3599 let Ok((program, mut ts, _imports, _import_diags)) =
3600 prepare_program_with_imports(uri, &program)
3601 else {
3602 return Vec::new();
3603 };
3604 if inject_program_decls(&mut ts, &program, None).is_err() {
3605 return Vec::new();
3606 }
3607
3608 let values = semantic_candidate_values(&ts);
3609
3610 let mut out = Vec::new();
3611 for (name, schemes) in values {
3612 let name = name.to_string();
3613 if !is_ident_like(&name) {
3614 continue;
3615 }
3616 for scheme in schemes {
3617 let (_preds, inst_ty) = instantiate(&scheme, &mut ts.supply);
3618 let (args, _ret) = split_fun_type(&inst_ty);
3619 if let Some(first_arg) = args.first()
3620 && unify(first_arg, &source_type).is_ok()
3621 {
3622 out.push((name.clone(), scheme.typ.to_string()));
3623 }
3624 }
3625 }
3626
3627 out.sort_by(|a, b| a.0.cmp(&b.0).then(a.1.cmp(&b.1)));
3628 out.dedup();
3629 if out.len() > MAX_SEMANTIC_CANDIDATES {
3630 out.truncate(MAX_SEMANTIC_CANDIDATES);
3631 }
3632 out
3633}
3634
3635fn adapters_from_inferred_to_expected_at_position(
3636 uri: &Url,
3637 text: &str,
3638 position: Position,
3639) -> Vec<(String, String)> {
3640 let Some(source_type) = inferred_type_at_position_type(uri, text, position) else {
3641 return Vec::new();
3642 };
3643 let Some(target_type) = expected_type_at_position_type(uri, text, position) else {
3644 return Vec::new();
3645 };
3646
3647 let Ok((_tokens, program)) = tokenize_and_parse_cached(uri, text) else {
3648 return Vec::new();
3649 };
3650 let Ok((program, mut ts, _imports, _import_diags)) =
3651 prepare_program_with_imports(uri, &program)
3652 else {
3653 return Vec::new();
3654 };
3655 if inject_program_decls(&mut ts, &program, None).is_err() {
3656 return Vec::new();
3657 }
3658
3659 let values = semantic_candidate_values(&ts);
3660
3661 let mut out = Vec::new();
3662 for (name, schemes) in values {
3663 let name = name.to_string();
3664 if !is_ident_like(&name) {
3665 continue;
3666 }
3667 for scheme in schemes {
3668 let (_preds, inst_ty) = instantiate(&scheme, &mut ts.supply);
3669 let (args, ret) = split_fun_type(&inst_ty);
3670 if args.len() == 1
3671 && unify(&args[0], &source_type).is_ok()
3672 && unify(&ret, &target_type).is_ok()
3673 {
3674 out.push((name.clone(), scheme.typ.to_string()));
3675 }
3676 }
3677 }
3678
3679 out.sort_by(|a, b| a.0.cmp(&b.0).then(a.1.cmp(&b.1)));
3680 out.dedup();
3681 if out.len() > MAX_SEMANTIC_CANDIDATES {
3682 out.truncate(MAX_SEMANTIC_CANDIDATES);
3683 }
3684 out
3685}
3686
3687fn functions_compatible_with_in_scope_values_at_position(
3688 uri: &Url,
3689 text: &str,
3690 position: Position,
3691) -> Vec<String> {
3692 let produced = functions_producing_expected_type_at_position(uri, text, position);
3693 let mut produced_by_name: HashMap<String, Vec<String>> = HashMap::new();
3694 for (name, typ) in produced {
3695 produced_by_name.entry(name).or_default().push(typ);
3696 }
3697
3698 let mut out = Vec::new();
3699 for (name, replacement) in hole_fill_candidates_at_position(uri, text, position) {
3700 if replacement.contains('?') {
3701 continue;
3702 }
3703 if let Some(types) = produced_by_name.get(&name) {
3704 for typ in types {
3705 out.push(format!("{name} : {typ} => {replacement}"));
3706 }
3707 } else {
3708 out.push(format!("{name} => {replacement}"));
3709 }
3710 }
3711 out.sort();
3712 out.dedup();
3713 if out.len() > MAX_SEMANTIC_CANDIDATES {
3714 out.truncate(MAX_SEMANTIC_CANDIDATES);
3715 }
3716 out
3717}
3718
3719pub fn execute_query_command_for_document(
3720 command: &str,
3721 uri: &Url,
3722 text: &str,
3723 position: Position,
3724) -> Option<Value> {
3725 match command {
3726 CMD_EXPECTED_TYPE_AT => Some(match expected_type_at_position(uri, text, position) {
3727 Some(typ) => json!({ "expectedType": typ }),
3728 None => Value::Null,
3729 }),
3730 CMD_FUNCTIONS_ACCEPTING_INFERRED_TYPE_AT => Some(json!({
3731 "inferredType": inferred_type_at_position(uri, text, position),
3732 "items": functions_accepting_inferred_type_at_position(uri, text, position)
3733 .into_iter()
3734 .map(|(name, typ)| format!("{name} : {typ}"))
3735 .collect::<Vec<_>>()
3736 })),
3737 CMD_ADAPTERS_FROM_INFERRED_TO_EXPECTED_AT => Some(json!({
3738 "inferredType": inferred_type_at_position(uri, text, position),
3739 "expectedType": expected_type_at_position(uri, text, position),
3740 "items": adapters_from_inferred_to_expected_at_position(uri, text, position)
3741 .into_iter()
3742 .map(|(name, typ)| format!("{name} : {typ}"))
3743 .collect::<Vec<_>>()
3744 })),
3745 CMD_FUNCTIONS_COMPATIBLE_WITH_IN_SCOPE_VALUES_AT => Some(json!({
3746 "items": functions_compatible_with_in_scope_values_at_position(uri, text, position)
3747 })),
3748 CMD_FUNCTIONS_PRODUCING_EXPECTED_TYPE_AT => {
3749 let items = functions_producing_expected_type_at_position(uri, text, position)
3750 .into_iter()
3751 .map(|(name, typ)| format!("{name} : {typ}"))
3752 .collect::<Vec<_>>();
3753 Some(json!({ "items": items }))
3754 }
3755 _ => None,
3756 }
3757}
3758
3759pub fn execute_query_command_for_document_without_position(
3760 command: &str,
3761 uri: &Url,
3762 text: &str,
3763) -> Option<Value> {
3764 match command {
3765 CMD_HOLES_EXPECTED_TYPES => Some(json!({
3766 "holes": hole_expected_types_for_document(uri, text)
3767 })),
3768 _ => None,
3769 }
3770}
3771
3772fn workspace_edit_fingerprint(edit: &WorkspaceEdit) -> String {
3773 let mut payload = String::new();
3774 if let Some(changes) = &edit.changes {
3775 let mut uris = changes.keys().cloned().collect::<Vec<_>>();
3776 uris.sort_by(|a, b| a.as_str().cmp(b.as_str()));
3777 for uri in uris {
3778 payload.push_str(uri.as_str());
3779 payload.push('\n');
3780 if let Some(edits) = changes.get(&uri) {
3781 for edit in edits {
3782 payload.push_str(&format!(
3783 "{}:{}-{}:{}\n",
3784 edit.range.start.line,
3785 edit.range.start.character,
3786 edit.range.end.line,
3787 edit.range.end.character
3788 ));
3789 payload.push_str(&edit.new_text);
3790 payload.push('\n');
3791 }
3792 }
3793 }
3794 }
3795 if let Some(document_changes) = &edit.document_changes
3796 && let Ok(encoded) = serde_json::to_string(document_changes)
3797 {
3798 payload.push_str(&encoded);
3799 }
3800 if let Some(change_annotations) = &edit.change_annotations
3801 && let Ok(encoded) = serde_json::to_string(change_annotations)
3802 {
3803 payload.push_str(&encoded);
3804 }
3805 sha256_hex(payload.as_bytes())
3806}
3807
3808fn semantic_quick_fixes_for_range(
3809 uri: &Url,
3810 text: &str,
3811 cursor_range: Range,
3812 diagnostics: &[Diagnostic],
3813) -> Vec<Value> {
3814 let mut out = code_actions_for_source(uri, text, cursor_range, diagnostics)
3815 .into_iter()
3816 .filter_map(|action| match action {
3817 CodeActionOrCommand::CodeAction(action) => Some(action),
3818 CodeActionOrCommand::Command(_) => None,
3819 })
3820 .map(|action| {
3821 let kind = action
3822 .kind
3823 .and_then(|k| to_value(k).ok())
3824 .and_then(|v| v.as_str().map(str::to_string));
3825 let edit = action.edit.unwrap_or(WorkspaceEdit {
3826 changes: None,
3827 document_changes: None,
3828 change_annotations: None,
3829 });
3830 let fingerprint = workspace_edit_fingerprint(&edit);
3831 json!({
3832 "id": format!("qf-{}", &fingerprint[..16]),
3833 "title": action.title,
3834 "kind": kind,
3835 "edit": to_value(edit).unwrap_or(Value::Null),
3836 })
3837 })
3838 .collect::<Vec<_>>();
3839
3840 out.sort_by_key(|item| {
3841 (
3842 item.get("title")
3843 .and_then(Value::as_str)
3844 .unwrap_or("")
3845 .to_string(),
3846 item.get("id")
3847 .and_then(Value::as_str)
3848 .unwrap_or("")
3849 .to_string(),
3850 )
3851 });
3852 out.dedup_by(|a, b| a.get("id") == b.get("id"));
3853 out
3854}
3855
3856pub fn execute_semantic_loop_step(uri: &Url, text: &str, position: Position) -> Option<Value> {
3857 let expected_type = expected_type_at_position(uri, text, position)
3858 .or_else(|| expected_type_from_syntax_context(uri, text, position));
3859 let inferred_type = inferred_type_at_position(uri, text, position);
3860
3861 let mut in_scope_values = in_scope_value_types_at_position(uri, text, position)
3862 .into_iter()
3863 .filter(|(name, _)| is_ident_like(name))
3864 .map(|(name, typ)| format!("{name} : {typ}"))
3865 .collect::<Vec<_>>();
3866 in_scope_values.sort();
3867 in_scope_values.dedup();
3868 if in_scope_values.len() > MAX_SEMANTIC_IN_SCOPE_VALUES {
3869 in_scope_values.truncate(MAX_SEMANTIC_IN_SCOPE_VALUES);
3870 }
3871
3872 let function_candidates = functions_producing_expected_type_at_position(uri, text, position)
3873 .into_iter()
3874 .map(|(name, typ)| format!("{name} : {typ}"))
3875 .collect::<Vec<_>>();
3876
3877 let hole_fill_candidates = hole_fill_candidates_at_position(uri, text, position)
3878 .into_iter()
3879 .map(|(name, replacement)| json!({ "name": name, "replacement": replacement }))
3880 .collect::<Vec<_>>();
3881 let functions_accepting_inferred_type =
3882 functions_accepting_inferred_type_at_position(uri, text, position)
3883 .into_iter()
3884 .map(|(name, typ)| format!("{name} : {typ}"))
3885 .collect::<Vec<_>>();
3886 let adapters_from_inferred_to_expected =
3887 adapters_from_inferred_to_expected_at_position(uri, text, position)
3888 .into_iter()
3889 .map(|(name, typ)| format!("{name} : {typ}"))
3890 .collect::<Vec<_>>();
3891 let compatible_with_in_scope_values =
3892 functions_compatible_with_in_scope_values_at_position(uri, text, position);
3893
3894 let cursor_range = Range {
3895 start: position,
3896 end: position,
3897 };
3898 let mut local_diagnostics: Vec<Diagnostic> = diagnostics_from_text(uri, text)
3899 .into_iter()
3900 .filter(|diag| ranges_overlap(diag.range, cursor_range))
3901 .collect();
3902 local_diagnostics.sort_by_key(|diag| {
3903 (
3904 diag.range.start.line,
3905 diag.range.start.character,
3906 diag.range.end.line,
3907 diag.range.end.character,
3908 diag.message.clone(),
3909 )
3910 });
3911
3912 let quick_fixes = semantic_quick_fixes_for_range(uri, text, cursor_range, &local_diagnostics);
3913 let mut quick_fix_titles = quick_fixes
3914 .iter()
3915 .filter_map(|item| item.get("title").and_then(Value::as_str))
3916 .map(ToString::to_string)
3917 .collect::<Vec<_>>();
3918 quick_fix_titles.sort();
3919 quick_fix_titles.dedup();
3920
3921 Some(json!({
3922 "expectedType": expected_type,
3923 "inferredType": inferred_type,
3924 "inScopeValues": in_scope_values,
3925 "functionCandidates": function_candidates,
3926 "holeFillCandidates": hole_fill_candidates,
3927 "functionsAcceptingInferredType": functions_accepting_inferred_type,
3928 "adaptersFromInferredToExpectedType": adapters_from_inferred_to_expected,
3929 "functionsCompatibleWithInScopeValues": compatible_with_in_scope_values,
3930 "localDiagnostics": local_diagnostics.into_iter().map(|diag| {
3931 json!({
3932 "message": diag.message,
3933 "line": diag.range.start.line,
3934 "character": diag.range.start.character,
3935 })
3936 }).collect::<Vec<_>>(),
3937 "quickFixes": quick_fixes,
3938 "quickFixTitles": quick_fix_titles,
3939 "holes": hole_expected_types_for_document(uri, text),
3940 }))
3941}
3942
3943pub fn execute_semantic_loop_apply_quick_fix(
3944 uri: &Url,
3945 text: &str,
3946 position: Position,
3947 quick_fix_id: &str,
3948) -> Option<Value> {
3949 let cursor_range = Range {
3950 start: position,
3951 end: position,
3952 };
3953 let local_diagnostics: Vec<Diagnostic> = diagnostics_from_text(uri, text)
3954 .into_iter()
3955 .filter(|diag| ranges_overlap(diag.range, cursor_range))
3956 .collect();
3957 let quick_fixes = semantic_quick_fixes_for_range(uri, text, cursor_range, &local_diagnostics);
3958 let quick_fix = quick_fixes.into_iter().find(|item| {
3959 item.get("id")
3960 .and_then(Value::as_str)
3961 .is_some_and(|id| id == quick_fix_id)
3962 });
3963
3964 Some(match quick_fix {
3965 Some(quick_fix) => json!({ "quickFix": quick_fix }),
3966 None => Value::Null,
3967 })
3968}
3969
3970fn quick_fix_priority(strategy: BulkQuickFixStrategy, title: &str) -> usize {
3971 let aggressive_introduce =
3972 strategy == BulkQuickFixStrategy::Aggressive && title.starts_with("Introduce `let ");
3973 if title.starts_with("Fill hole with `") {
3974 0
3975 } else if title.starts_with("Replace `") || aggressive_introduce {
3976 1
3977 } else if title.starts_with("Add wildcard arm") {
3978 2
3979 } else if title.starts_with("Wrap expression in list literal") {
3980 3
3981 } else if title.starts_with("Unwrap single-item list literal") {
3982 4
3983 } else if title.starts_with("Apply expression to missing argument") {
3984 5
3985 } else if title.starts_with("Wrap expression in lambda") {
3986 6
3987 } else if title.starts_with("Introduce `let ") {
3988 7
3989 } else {
3990 10
3991 }
3992}
3993
3994pub fn best_quick_fix_from_candidates(
3995 candidates: &[Value],
3996 strategy: BulkQuickFixStrategy,
3997) -> Option<Value> {
3998 candidates
3999 .iter()
4000 .min_by_key(|item| {
4001 let title = item.get("title").and_then(Value::as_str).unwrap_or("");
4002 let id = item.get("id").and_then(Value::as_str).unwrap_or("");
4003 (
4004 quick_fix_priority(strategy, title),
4005 title.to_string(),
4006 id.to_string(),
4007 )
4008 })
4009 .cloned()
4010}
4011
4012pub fn apply_workspace_edit_to_text(uri: &Url, text: &str, edit: &WorkspaceEdit) -> Option<String> {
4013 let changes = edit.changes.as_ref()?;
4014 let edits = changes.get(uri)?.clone();
4015 if edits.is_empty() {
4016 return Some(text.to_string());
4017 }
4018 let mut with_offsets = Vec::new();
4019 for edit in edits {
4020 let start = offset_at(text, edit.range.start)?;
4021 let end = offset_at(text, edit.range.end)?;
4022 if start > end || end > text.len() {
4023 return None;
4024 }
4025 with_offsets.push((start, end, edit.new_text));
4026 }
4027 with_offsets.sort_by(|a, b| b.0.cmp(&a.0).then(b.1.cmp(&a.1)));
4028
4029 let mut out = text.to_string();
4030 for (start, end, replacement) in with_offsets {
4031 out.replace_range(start..end, &replacement);
4032 }
4033 Some(out)
4034}
4035
4036pub fn text_state_hash(text: &str) -> String {
4037 sha256_hex(text.as_bytes())
4038}
4039
4040pub fn next_no_improvement_streak(streak: usize, diagnostics_delta: i64) -> usize {
4041 if diagnostics_delta > 0 { 0 } else { streak + 1 }
4042}
4043
4044pub fn execute_semantic_loop_apply_best_quick_fixes(
4045 uri: &Url,
4046 text: &str,
4047 position: Position,
4048 max_steps: usize,
4049 strategy: BulkQuickFixStrategy,
4050 dry_run: bool,
4051) -> Option<Value> {
4052 let cursor_range = Range {
4053 start: position,
4054 end: position,
4055 };
4056 let mut current_text = text.to_string();
4057 let mut applied = Vec::new();
4058 let mut steps = Vec::new();
4059 let mut stopped_reason = "noQuickFix".to_string();
4060 let mut stopped_reason_detail = "no quick-fixes available at cursor".to_string();
4061 let mut no_improvement_streak = 0usize;
4062 let mut last_diagnostics_delta = 0i64;
4063 let mut seen_states: HashSet<String> = HashSet::new();
4064 seen_states.insert(text_state_hash(¤t_text));
4065
4066 for step_index in 0..max_steps {
4067 let local_diagnostics: Vec<Diagnostic> = diagnostics_from_text(uri, ¤t_text)
4068 .into_iter()
4069 .filter(|diag| ranges_overlap(diag.range, cursor_range))
4070 .collect();
4071 let diagnostics_before = local_diagnostics
4072 .iter()
4073 .map(|diag| {
4074 json!({
4075 "message": diag.message,
4076 "line": diag.range.start.line,
4077 "character": diag.range.start.character,
4078 })
4079 })
4080 .collect::<Vec<_>>();
4081 let quick_fixes =
4082 semantic_quick_fixes_for_range(uri, ¤t_text, cursor_range, &local_diagnostics);
4083 let Some(best) = best_quick_fix_from_candidates(&quick_fixes, strategy) else {
4084 stopped_reason = "noQuickFix".to_string();
4085 stopped_reason_detail = "no candidate quick-fix was available".to_string();
4086 break;
4087 };
4088 let edit_value = best.get("edit").cloned().unwrap_or(Value::Null);
4089 let Ok(edit) = serde_json::from_value::<WorkspaceEdit>(edit_value) else {
4090 stopped_reason = "invalidEdit".to_string();
4091 stopped_reason_detail = "selected quick-fix edit was invalid".to_string();
4092 break;
4093 };
4094 let Some(next_text) = apply_workspace_edit_to_text(uri, ¤t_text, &edit) else {
4095 stopped_reason = "applyFailed".to_string();
4096 stopped_reason_detail = "failed to apply selected workspace edit".to_string();
4097 break;
4098 };
4099 if next_text == current_text {
4100 stopped_reason = "noTextChange".to_string();
4101 stopped_reason_detail = "selected quick-fix did not change text".to_string();
4102 break;
4103 }
4104 let next_hash = text_state_hash(&next_text);
4105 if seen_states.contains(&next_hash) {
4106 stopped_reason = "cycleDetected".to_string();
4107 stopped_reason_detail = "next text state already seen in this run".to_string();
4108 break;
4109 }
4110 let diagnostics_after_step: Vec<Value> = diagnostics_from_text(uri, &next_text)
4111 .into_iter()
4112 .filter(|diag| ranges_overlap(diag.range, cursor_range))
4113 .map(|diag| {
4114 json!({
4115 "message": diag.message,
4116 "line": diag.range.start.line,
4117 "character": diag.range.start.character,
4118 })
4119 })
4120 .collect();
4121 let before_count = diagnostics_before.len();
4122 let after_count = diagnostics_after_step.len();
4123 let diagnostics_delta = (before_count as i64) - (after_count as i64);
4124 last_diagnostics_delta = diagnostics_delta;
4125 no_improvement_streak =
4126 next_no_improvement_streak(no_improvement_streak, diagnostics_delta);
4127 steps.push(json!({
4128 "index": step_index,
4129 "quickFix": best.clone(),
4130 "diagnosticsBefore": diagnostics_before,
4131 "diagnosticsAfter": diagnostics_after_step,
4132 "diagnosticsBeforeCount": before_count,
4133 "diagnosticsAfterCount": after_count,
4134 "diagnosticsDelta": diagnostics_delta,
4135 "noImprovementStreak": no_improvement_streak,
4136 }));
4137 applied.push(best);
4138 current_text = next_text;
4139 seen_states.insert(next_hash);
4140 if no_improvement_streak >= NO_IMPROVEMENT_STREAK_LIMIT {
4141 stopped_reason = "noImprovementStreak".to_string();
4142 stopped_reason_detail =
4143 format!("diagnostics did not improve for {NO_IMPROVEMENT_STREAK_LIMIT} step(s)");
4144 break;
4145 }
4146 stopped_reason = "maxStepsReached".to_string();
4147 stopped_reason_detail = format!("reached maxSteps={max_steps}");
4148 }
4149
4150 let diagnostics_after: Vec<Value> = diagnostics_from_text(uri, ¤t_text)
4151 .into_iter()
4152 .filter(|diag| ranges_overlap(diag.range, cursor_range))
4153 .map(|diag| {
4154 json!({
4155 "message": diag.message,
4156 "line": diag.range.start.line,
4157 "character": diag.range.start.character,
4158 })
4159 })
4160 .collect();
4161
4162 Some(json!({
4163 "strategy": strategy.as_str(),
4164 "dryRun": dry_run,
4165 "appliedQuickFixes": applied,
4166 "appliedCount": applied.len(),
4167 "steps": steps,
4168 "updatedText": current_text,
4169 "localDiagnosticsAfter": diagnostics_after,
4170 "stoppedReason": stopped_reason,
4171 "stoppedReasonDetail": stopped_reason_detail,
4172 "lastDiagnosticsDelta": last_diagnostics_delta,
4173 "noImprovementStreak": no_improvement_streak,
4174 "seenStatesCount": seen_states.len(),
4175 }))
4176}
4177
4178pub fn hole_expected_types_for_document(uri: &Url, text: &str) -> Vec<Value> {
4179 let mut holes = Vec::new();
4180
4181 if let Ok((_tokens, program)) = tokenize_and_parse_cached(uri, text)
4183 && let Some(body) = program.body_with_fns()
4184 {
4185 let mut spans = Vec::new();
4186 collect_hole_spans(body.as_ref(), &mut spans);
4187 for span in spans {
4188 let pos = span_to_range(span).start;
4189 if let Some(expected_type) = expected_type_at_position(uri, text, pos)
4190 .or_else(|| expected_type_from_syntax_context(uri, text, pos))
4191 {
4192 holes.push(json!({
4193 "name": "?",
4194 "line": pos.line,
4195 "character": pos.character,
4196 "expectedType": expected_type
4197 }));
4198 }
4199 }
4200 }
4201
4202 let diagnostics = diagnostics_from_text(uri, text);
4204 for diag in diagnostics {
4205 let Some(name) = unknown_var_name_from_message(&diag.message) else {
4206 continue;
4207 };
4208 if !is_hole_name(name) {
4209 continue;
4210 }
4211 if !range_is_usable_for_text(text, diag.range) {
4212 continue;
4213 }
4214 let pos = diag.range.start;
4215 if let Some(expected_type) = expected_type_at_position(uri, text, pos)
4216 .or_else(|| expected_type_from_syntax_context(uri, text, pos))
4217 {
4218 holes.push(json!({
4219 "name": name,
4220 "line": pos.line,
4221 "character": pos.character,
4222 "expectedType": expected_type
4223 }));
4224 }
4225 }
4226 holes.sort_by_key(|item| {
4227 let line = item.get("line").and_then(Value::as_u64).unwrap_or(0);
4228 let ch = item.get("character").and_then(Value::as_u64).unwrap_or(0);
4229 let name = item
4230 .get("name")
4231 .and_then(Value::as_str)
4232 .unwrap_or("")
4233 .to_string();
4234 (line, ch, name)
4235 });
4236 holes.dedup_by(|a, b| {
4237 a.get("name") == b.get("name")
4238 && a.get("line") == b.get("line")
4239 && a.get("character") == b.get("character")
4240 });
4241 if holes.len() > MAX_SEMANTIC_HOLES {
4242 holes.truncate(MAX_SEMANTIC_HOLES);
4243 }
4244 holes
4245}
4246
4247fn collect_hole_spans(expr: &Expr, out: &mut Vec<Span>) {
4248 match expr {
4249 Expr::Hole(span) => out.push(*span),
4250 Expr::App(_, f, x) => {
4251 collect_hole_spans(f, out);
4252 collect_hole_spans(x, out);
4253 }
4254 Expr::Project(_, base, _) => collect_hole_spans(base, out),
4255 Expr::Lam(_, _scope, _param, _ann, _constraints, body) => collect_hole_spans(body, out),
4256 Expr::Let(_, _var, _ann, def, body) => {
4257 collect_hole_spans(def, out);
4258 collect_hole_spans(body, out);
4259 }
4260 Expr::LetRec(_, bindings, body) => {
4261 for (_var, _ann, def) in bindings {
4262 collect_hole_spans(def, out);
4263 }
4264 collect_hole_spans(body, out);
4265 }
4266 Expr::Ite(_, cond, then_expr, else_expr) => {
4267 collect_hole_spans(cond, out);
4268 collect_hole_spans(then_expr, out);
4269 collect_hole_spans(else_expr, out);
4270 }
4271 Expr::Match(_, scrutinee, arms) => {
4272 collect_hole_spans(scrutinee, out);
4273 for (_pat, arm) in arms {
4274 collect_hole_spans(arm, out);
4275 }
4276 }
4277 Expr::Ann(_, inner, _) => collect_hole_spans(inner, out),
4278 Expr::Tuple(_, elems) | Expr::List(_, elems) => {
4279 for elem in elems {
4280 collect_hole_spans(elem, out);
4281 }
4282 }
4283 Expr::Dict(_, kvs) => {
4284 for value in kvs.values() {
4285 collect_hole_spans(value, out);
4286 }
4287 }
4288 Expr::RecordUpdate(_, base, updates) => {
4289 collect_hole_spans(base, out);
4290 for value in updates.values() {
4291 collect_hole_spans(value, out);
4292 }
4293 }
4294 Expr::Var(_)
4295 | Expr::Bool(..)
4296 | Expr::Uint(..)
4297 | Expr::Int(..)
4298 | Expr::Float(..)
4299 | Expr::String(..)
4300 | Expr::Uuid(..)
4301 | Expr::DateTime(..) => {}
4302 }
4303}
4304
4305fn expected_type_from_syntax_context(uri: &Url, text: &str, position: Position) -> Option<String> {
4306 let (_tokens, program) = tokenize_and_parse_cached(uri, text).ok()?;
4307 let pos = lsp_to_rex_position(position);
4308
4309 fn visit(expr: &Expr, pos: RexPosition) -> Option<String> {
4310 if !position_in_span(pos, *expr.span()) {
4311 return None;
4312 }
4313 match expr {
4314 Expr::Let(_span, _name, ann, def, body) => {
4315 if position_in_span(pos, *def.span())
4316 && let Some(ann) = ann
4317 {
4318 return Some(ann.to_string());
4319 }
4320 visit(def.as_ref(), pos).or_else(|| visit(body.as_ref(), pos))
4321 }
4322 Expr::Ann(_span, inner, ann) => {
4323 if position_in_span(pos, *inner.span()) {
4324 return Some(ann.to_string());
4325 }
4326 visit(inner.as_ref(), pos)
4327 }
4328 Expr::Ite(_span, cond, then_expr, else_expr) => {
4329 if position_in_span(pos, *cond.span()) {
4330 return Some("bool".to_string());
4331 }
4332 visit(cond.as_ref(), pos)
4333 .or_else(|| visit(then_expr.as_ref(), pos))
4334 .or_else(|| visit(else_expr.as_ref(), pos))
4335 }
4336 Expr::App(_span, f, x) => visit(f.as_ref(), pos).or_else(|| visit(x.as_ref(), pos)),
4337 Expr::Project(_span, base, _field) => visit(base.as_ref(), pos),
4338 Expr::Lam(_span, _scope, _param, _ann, _constraints, body) => visit(body.as_ref(), pos),
4339 Expr::LetRec(_span, bindings, body) => {
4340 for (_name, _ann, def) in bindings {
4341 if let Some(found) = visit(def.as_ref(), pos) {
4342 return Some(found);
4343 }
4344 }
4345 visit(body.as_ref(), pos)
4346 }
4347 Expr::Match(_span, scrutinee, arms) => {
4348 if let Some(found) = visit(scrutinee.as_ref(), pos) {
4349 return Some(found);
4350 }
4351 for (_pat, arm) in arms {
4352 if let Some(found) = visit(arm.as_ref(), pos) {
4353 return Some(found);
4354 }
4355 }
4356 None
4357 }
4358 Expr::Tuple(_span, elems) | Expr::List(_span, elems) => {
4359 for elem in elems {
4360 if let Some(found) = visit(elem.as_ref(), pos) {
4361 return Some(found);
4362 }
4363 }
4364 None
4365 }
4366 Expr::Dict(_span, kvs) => {
4367 for value in kvs.values() {
4368 if let Some(found) = visit(value.as_ref(), pos) {
4369 return Some(found);
4370 }
4371 }
4372 None
4373 }
4374 Expr::RecordUpdate(_span, base, updates) => {
4375 if let Some(found) = visit(base.as_ref(), pos) {
4376 return Some(found);
4377 }
4378 for value in updates.values() {
4379 if let Some(found) = visit(value.as_ref(), pos) {
4380 return Some(found);
4381 }
4382 }
4383 None
4384 }
4385 Expr::Var(_)
4386 | Expr::Bool(..)
4387 | Expr::Uint(..)
4388 | Expr::Int(..)
4389 | Expr::Float(..)
4390 | Expr::String(..)
4391 | Expr::Uuid(..)
4392 | Expr::DateTime(..)
4393 | Expr::Hole(..) => None,
4394 }
4395 }
4396
4397 let body = program.body_with_fns()?;
4398 visit(body.as_ref(), pos)
4399}
4400
4401pub fn command_uri_and_position(arguments: &[Value]) -> Option<(Url, Position)> {
4402 if arguments.len() >= 3 {
4403 let uri = arguments.first()?.as_str()?;
4404 let line = arguments.get(1)?.as_u64()? as u32;
4405 let character = arguments.get(2)?.as_u64()? as u32;
4406 let uri = Url::parse(uri).ok()?;
4407 return Some((uri, Position { line, character }));
4408 }
4409
4410 let obj = arguments.first()?.as_object()?;
4411 let uri = obj.get("uri")?.as_str()?;
4412 let line = obj.get("line")?.as_u64()? as u32;
4413 let character = obj.get("character")?.as_u64()? as u32;
4414 let uri = Url::parse(uri).ok()?;
4415 Some((uri, Position { line, character }))
4416}
4417
4418#[cfg_attr(target_arch = "wasm32", allow(dead_code))]
4419pub(crate) fn command_uri(arguments: &[Value]) -> Option<Url> {
4420 if arguments.is_empty() {
4421 return None;
4422 }
4423 if let Some(uri) = arguments.first().and_then(Value::as_str) {
4424 return Url::parse(uri).ok();
4425 }
4426 let obj = arguments.first()?.as_object()?;
4427 let uri = obj.get("uri")?.as_str()?;
4428 Url::parse(uri).ok()
4429}
4430
4431pub fn command_uri_position_and_id(arguments: &[Value]) -> Option<(Url, Position, String)> {
4432 if arguments.len() >= 4 {
4433 let uri = arguments.first()?.as_str()?;
4434 let line = arguments.get(1)?.as_u64()? as u32;
4435 let character = arguments.get(2)?.as_u64()? as u32;
4436 let id = arguments.get(3)?.as_str()?.to_string();
4437 let uri = Url::parse(uri).ok()?;
4438 return Some((uri, Position { line, character }, id));
4439 }
4440
4441 let obj = arguments.first()?.as_object()?;
4442 let uri = obj.get("uri")?.as_str()?;
4443 let line = obj.get("line")?.as_u64()? as u32;
4444 let character = obj.get("character")?.as_u64()? as u32;
4445 let id = obj.get("id")?.as_str()?.to_string();
4446 let uri = Url::parse(uri).ok()?;
4447 Some((uri, Position { line, character }, id))
4448}
4449
4450pub fn command_uri_position_max_steps_strategy_and_dry_run(
4451 arguments: &[Value],
4452) -> Option<(Url, Position, usize, BulkQuickFixStrategy, bool)> {
4453 if arguments.len() >= 3 {
4454 let uri = arguments.first()?.as_str()?;
4455 let line = arguments.get(1)?.as_u64()? as u32;
4456 let character = arguments.get(2)?.as_u64()? as u32;
4457 let max_steps = arguments
4458 .get(3)
4459 .and_then(Value::as_u64)
4460 .map(|n| n as usize)
4461 .unwrap_or(3);
4462 let strategy = arguments
4463 .get(4)
4464 .and_then(Value::as_str)
4465 .map(BulkQuickFixStrategy::parse)
4466 .unwrap_or(BulkQuickFixStrategy::Conservative);
4467 let dry_run = arguments.get(5).and_then(Value::as_bool).unwrap_or(false);
4468 let uri = Url::parse(uri).ok()?;
4469 return Some((
4470 uri,
4471 Position { line, character },
4472 max_steps.clamp(1, 20),
4473 strategy,
4474 dry_run,
4475 ));
4476 }
4477
4478 let obj = arguments.first()?.as_object()?;
4479 let uri = obj.get("uri")?.as_str()?;
4480 let line = obj.get("line")?.as_u64()? as u32;
4481 let character = obj.get("character")?.as_u64()? as u32;
4482 let max_steps = obj
4483 .get("maxSteps")
4484 .and_then(Value::as_u64)
4485 .map(|n| n as usize)
4486 .unwrap_or(3)
4487 .clamp(1, 20);
4488 let strategy = obj
4489 .get("strategy")
4490 .and_then(Value::as_str)
4491 .map(BulkQuickFixStrategy::parse)
4492 .unwrap_or(BulkQuickFixStrategy::Conservative);
4493 let dry_run = obj.get("dryRun").and_then(Value::as_bool).unwrap_or(false);
4494 let uri = Url::parse(uri).ok()?;
4495 Some((
4496 uri,
4497 Position { line, character },
4498 max_steps,
4499 strategy,
4500 dry_run,
4501 ))
4502}
4503
4504fn hover_type_in_expr(
4505 ts: &mut TypeSystem,
4506 expr: &Expr,
4507 typed: &TypedExpr,
4508 pos: RexPosition,
4509 name: &str,
4510 name_span: Span,
4511 name_is_ident: bool,
4512) -> Option<HoverType> {
4513 fn span_contains_pos(span: Span, pos: RexPosition) -> bool {
4514 position_in_span(pos, span)
4515 }
4516
4517 fn span_contains_span(outer: Span, inner: Span) -> bool {
4518 position_leq(outer.begin, inner.begin) && position_leq(inner.end, outer.end)
4519 }
4520
4521 fn span_size(span: Span) -> (usize, usize) {
4522 (
4523 span.end.line.saturating_sub(span.begin.line),
4524 span.end.column.saturating_sub(span.begin.column),
4525 )
4526 }
4527
4528 fn peel_fun(ty: &Type) -> (Vec<Type>, Type) {
4529 let mut args = Vec::new();
4530 let mut cur = ty.clone();
4531 while let TypeKind::Fun(a, b) = cur.as_ref() {
4532 args.push(a.clone());
4533 cur = b.clone();
4534 }
4535 (args, cur)
4536 }
4537
4538 fn add_bindings_from_pattern(
4539 ts: &mut TypeSystem,
4540 scrutinee_ty: &Type,
4541 pat: &Pattern,
4542 out: &mut HashMap<String, Type>,
4543 ) {
4544 match pat {
4545 Pattern::Wildcard(..) => {}
4546 Pattern::Var(v) => {
4547 out.insert(v.name.as_ref().to_string(), scrutinee_ty.clone());
4548 }
4549 Pattern::Named(_span, ctor, args) => {
4550 let ctor_name = ctor.to_dotted_symbol();
4551 let Some(schemes) = ts.env.lookup(&ctor_name) else {
4552 return;
4553 };
4554 let Some(scheme) = schemes.first() else {
4555 return;
4556 };
4557
4558 let (_preds, ctor_ty) = instantiate(scheme, &mut ts.supply);
4559 let (arg_tys, result_ty) = peel_fun(&ctor_ty);
4560 let Ok(s) = unify(&result_ty, scrutinee_ty) else {
4561 return;
4562 };
4563
4564 for (subpat, arg_ty) in args.iter().zip(arg_tys.iter()) {
4565 add_bindings_from_pattern(ts, &arg_ty.apply(&s), subpat, out);
4566 }
4567 }
4568 Pattern::Tuple(_span, elems) => {
4569 let elem_tys: Vec<Type> = (0..elems.len())
4570 .map(|_| Type::var(ts.supply.fresh(None)))
4571 .collect();
4572 let expected = Type::tuple(elem_tys.clone());
4573 let Ok(s) = unify(scrutinee_ty, &expected) else {
4574 return;
4575 };
4576 for (p, ty) in elems.iter().zip(elem_tys.iter()) {
4577 add_bindings_from_pattern(ts, &ty.apply(&s), p, out);
4578 }
4579 }
4580 Pattern::List(_span, elems) => {
4581 let tv = ts.supply.fresh(None);
4582 let elem = Type::var(tv.clone());
4583 let list_ty = Type::app(Type::builtin(BuiltinTypeId::List), elem.clone());
4584 let Ok(s) = unify(scrutinee_ty, &list_ty) else {
4585 return;
4586 };
4587 let elem_ty = elem.apply(&s);
4588 for p in elems {
4589 add_bindings_from_pattern(ts, &elem_ty, p, out);
4590 }
4591 }
4592 Pattern::Cons(_span, head, tail) => {
4593 let tv = ts.supply.fresh(None);
4594 let elem = Type::var(tv.clone());
4595 let list_ty = Type::app(Type::builtin(BuiltinTypeId::List), elem.clone());
4596 let Ok(s) = unify(scrutinee_ty, &list_ty) else {
4597 return;
4598 };
4599 let elem_ty = elem.apply(&s);
4600 let list_ty = list_ty.apply(&s);
4601 add_bindings_from_pattern(ts, &elem_ty, head.as_ref(), out);
4602 add_bindings_from_pattern(ts, &list_ty, tail.as_ref(), out);
4603 }
4604 Pattern::Dict(_span, keys) => match scrutinee_ty.as_ref() {
4605 TypeKind::Record(fields) => {
4606 for (key, pat) in keys {
4607 if let Some((_, ty)) = fields.iter().find(|(n, _)| n == key) {
4608 add_bindings_from_pattern(ts, ty, pat, out);
4609 }
4610 }
4611 }
4612 _ => {
4613 let tv = ts.supply.fresh(None);
4614 let elem = Type::var(tv.clone());
4615 let dict_ty = Type::app(Type::builtin(BuiltinTypeId::Dict), elem.clone());
4616 let Ok(s) = unify(scrutinee_ty, &dict_ty) else {
4617 return;
4618 };
4619 let elem_ty = elem.apply(&s);
4620 for (_key, pat) in keys {
4621 add_bindings_from_pattern(ts, &elem_ty, pat, out);
4622 }
4623 }
4624 },
4625 }
4626 }
4627
4628 struct VisitCtx<'a> {
4629 pos: RexPosition,
4630 name: &'a str,
4631 name_span: Span,
4632 name_is_ident: bool,
4633 best: &'a mut Option<HoverType>,
4634 }
4635
4636 fn visit(ts: &mut TypeSystem, expr: &Expr, typed: &TypedExpr, ctx: &mut VisitCtx<'_>) {
4637 if !span_contains_pos(*expr.span(), ctx.pos) {
4638 return;
4639 }
4640
4641 let consider = |best: &mut Option<HoverType>, candidate: HoverType| {
4642 let take = best
4643 .as_ref()
4644 .is_none_or(|b| span_size(candidate.span) < span_size(b.span));
4645 if take {
4646 *best = Some(candidate);
4647 }
4648 };
4649
4650 if ctx.name_is_ident
4652 && let (
4653 Expr::Match(_span, _scrutinee, arms),
4654 TypedExprKind::Match {
4655 scrutinee,
4656 arms: typed_arms,
4657 },
4658 ) = (&expr, typed.kind.as_ref())
4659 && span_contains_span(*expr.span(), ctx.name_span)
4660 {
4661 for ((_pat, _arm_body), (typed_pat, _typed_arm_body)) in
4662 arms.iter().zip(typed_arms.iter())
4663 {
4664 let pat_span = *typed_pat.span();
4666 if span_contains_span(pat_span, ctx.name_span) {
4667 let mut bindings: HashMap<String, Type> = HashMap::new();
4668 add_bindings_from_pattern(ts, &scrutinee.typ, typed_pat, &mut bindings);
4669 if let Some(ty) = bindings.get(ctx.name) {
4670 consider(
4671 ctx.best,
4672 HoverType {
4673 span: ctx.name_span,
4674 label: ctx.name.to_string(),
4675 typ: ty.to_string(),
4676 overloads: Vec::new(),
4677 },
4678 );
4679 }
4680 break;
4681 }
4682 }
4683 }
4684
4685 match (expr, typed.kind.as_ref()) {
4687 (
4688 Expr::Let(_span, binding, _ann, def, body),
4689 TypedExprKind::Let {
4690 def: tdef,
4691 body: tbody,
4692 ..
4693 },
4694 ) => {
4695 if span_contains_pos(binding.span, ctx.pos) {
4696 consider(
4697 ctx.best,
4698 HoverType {
4699 span: binding.span,
4700 label: binding.name.as_ref().to_string(),
4701 typ: tdef.typ.to_string(),
4702 overloads: Vec::new(),
4703 },
4704 );
4705 }
4706 visit(ts, def.as_ref(), tdef.as_ref(), ctx);
4707 visit(ts, body.as_ref(), tbody.as_ref(), ctx);
4708 }
4709 (
4710 Expr::LetRec(_span, bindings, body),
4711 TypedExprKind::LetRec {
4712 bindings: typed_bindings,
4713 body: typed_body,
4714 },
4715 ) => {
4716 for ((binding, _ann, def), (_name, typed_def)) in
4717 bindings.iter().zip(typed_bindings.iter())
4718 {
4719 if span_contains_pos(binding.span, ctx.pos) {
4720 consider(
4721 ctx.best,
4722 HoverType {
4723 span: binding.span,
4724 label: binding.name.as_ref().to_string(),
4725 typ: typed_def.typ.to_string(),
4726 overloads: Vec::new(),
4727 },
4728 );
4729 }
4730 visit(ts, def.as_ref(), typed_def, ctx);
4731 }
4732 visit(ts, body.as_ref(), typed_body.as_ref(), ctx);
4733 }
4734 (
4735 Expr::Lam(_span, _scope, param, _ann, _constraints, body),
4736 TypedExprKind::Lam { body: tbody, .. },
4737 ) => {
4738 if span_contains_pos(param.span, ctx.pos) {
4739 let param_ty = match typed.typ.as_ref() {
4740 TypeKind::Fun(a, _b) => a.to_string(),
4741 _ => "<unknown>".to_string(),
4742 };
4743 consider(
4744 ctx.best,
4745 HoverType {
4746 span: param.span,
4747 label: param.name.as_ref().to_string(),
4748 typ: param_ty,
4749 overloads: Vec::new(),
4750 },
4751 );
4752 }
4753 visit(ts, body.as_ref(), tbody.as_ref(), ctx);
4754 }
4755 (Expr::Var(v), TypedExprKind::Var { overloads, .. })
4756 if span_contains_pos(v.span, ctx.pos) =>
4757 {
4758 consider(
4759 ctx.best,
4760 HoverType {
4761 span: v.span,
4762 label: v.name.as_ref().to_string(),
4763 typ: typed.typ.to_string(),
4764 overloads: overloads.iter().map(|t| t.to_string()).collect(),
4765 },
4766 );
4767 }
4768 (Expr::Ann(_span, inner, _ann), _) => {
4769 visit(ts, inner.as_ref(), typed, ctx);
4770 }
4771 (Expr::Tuple(_span, elems), TypedExprKind::Tuple(telems)) => {
4772 for (e, t) in elems.iter().zip(telems.iter()) {
4773 visit(ts, e.as_ref(), t, ctx);
4774 }
4775 }
4776 (Expr::List(_span, elems), TypedExprKind::List(telems)) => {
4777 for (e, t) in elems.iter().zip(telems.iter()) {
4778 visit(ts, e.as_ref(), t, ctx);
4779 }
4780 }
4781 (Expr::Dict(_span, kvs), TypedExprKind::Dict(tkvs)) => {
4782 for (k, v) in kvs {
4783 if let Some(tv) = tkvs.get(k) {
4784 visit(ts, v.as_ref(), tv, ctx);
4785 }
4786 }
4787 }
4788 (
4789 Expr::RecordUpdate(_span, base, updates),
4790 TypedExprKind::RecordUpdate {
4791 base: tbase,
4792 updates: tupdates,
4793 },
4794 ) => {
4795 visit(ts, base.as_ref(), tbase.as_ref(), ctx);
4796 for (k, v) in updates {
4797 if let Some(tv) = tupdates.get(k) {
4798 visit(ts, v.as_ref(), tv, ctx);
4799 }
4800 }
4801 }
4802 (Expr::App(_span, f, x), TypedExprKind::App(tf, tx)) => {
4803 visit(ts, f.as_ref(), tf.as_ref(), ctx);
4804 visit(ts, x.as_ref(), tx.as_ref(), ctx);
4805 }
4806 (Expr::Project(_span, e, _field), TypedExprKind::Project { expr: te, .. }) => {
4807 visit(ts, e.as_ref(), te.as_ref(), ctx);
4808 }
4809 (
4810 Expr::Ite(_span, c, t, e),
4811 TypedExprKind::Ite {
4812 cond,
4813 then_expr,
4814 else_expr,
4815 },
4816 ) => {
4817 visit(ts, c.as_ref(), cond.as_ref(), ctx);
4818 visit(ts, t.as_ref(), then_expr.as_ref(), ctx);
4819 visit(ts, e.as_ref(), else_expr.as_ref(), ctx);
4820 }
4821 (
4822 Expr::Match(_span, scrutinee, arms),
4823 TypedExprKind::Match {
4824 scrutinee: tscrut,
4825 arms: tarms,
4826 },
4827 ) => {
4828 visit(ts, scrutinee.as_ref(), tscrut.as_ref(), ctx);
4829 for ((_pat, arm_body), (_tpat, tarm_body)) in arms.iter().zip(tarms.iter()) {
4830 visit(ts, arm_body.as_ref(), tarm_body, ctx);
4831 }
4832 }
4833 _ => {}
4834 }
4835 }
4836
4837 let mut best = None;
4838 let mut ctx = VisitCtx {
4839 pos,
4840 name,
4841 name_span,
4842 name_is_ident,
4843 best: &mut best,
4844 };
4845 visit(ts, expr, typed, &mut ctx);
4846 best
4847}
4848
4849fn name_token_at_position(tokens: &Tokens, position: Position) -> Option<(String, Span, bool)> {
4850 for token in &tokens.items {
4851 let (name, span, is_ident) = match token {
4852 Token::Ident(name, span, ..) => (name.clone(), *span, true),
4853 Token::Add(span) => ("+".to_string(), *span, false),
4854 Token::Sub(span) => ("-".to_string(), *span, false),
4855 Token::Mul(span) => ("*".to_string(), *span, false),
4856 Token::Div(span) => ("/".to_string(), *span, false),
4857 Token::Mod(span) => ("%".to_string(), *span, false),
4858 Token::Concat(span) => ("++".to_string(), *span, false),
4859 Token::Eq(span) => ("==".to_string(), *span, false),
4860 Token::Ne(span) => ("!=".to_string(), *span, false),
4861 Token::Lt(span) => ("<".to_string(), *span, false),
4862 Token::Le(span) => ("<=".to_string(), *span, false),
4863 Token::Gt(span) => (">".to_string(), *span, false),
4864 Token::Ge(span) => (">=".to_string(), *span, false),
4865 Token::And(span) => ("&&".to_string(), *span, false),
4866 Token::Or(span) => ("||".to_string(), *span, false),
4867 _ => continue,
4868 };
4869 if range_touches_position(span_to_range(span), position) {
4870 return Some((name, span, is_ident));
4871 }
4872 }
4873 None
4874}
4875
4876fn push_comment_diagnostics(tokens: &Tokens, diagnostics: &mut Vec<Diagnostic>) {
4877 let mut index = 0;
4878
4879 while index < tokens.items.len() && diagnostics.len() < MAX_DIAGNOSTICS {
4880 match tokens.items[index] {
4881 Token::CommentL(span) => {
4882 let mut cursor = index + 1;
4883 while cursor < tokens.items.len() {
4884 if matches!(tokens.items[cursor], Token::CommentR(_)) {
4885 break;
4886 }
4887 cursor += 1;
4888 }
4889
4890 if cursor >= tokens.items.len() {
4891 diagnostics.push(diagnostic_for_span(
4892 span,
4893 "Unclosed block comment opener ({-).",
4894 ));
4895 break;
4896 }
4897
4898 index = cursor + 1;
4899 }
4900 Token::CommentR(span) => {
4901 diagnostics.push(diagnostic_for_span(
4902 span,
4903 "Unmatched block comment closer (-}).",
4904 ));
4905 index += 1;
4906 }
4907 _ => index += 1,
4908 }
4909 }
4910}
4911
4912fn diagnostic_for_span(span: Span, message: impl Into<String>) -> Diagnostic {
4913 Diagnostic {
4914 range: span_to_range(span),
4915 severity: Some(DiagnosticSeverity::ERROR),
4916 message: message.into(),
4917 source: Some("rex-lsp".to_string()),
4918 ..Diagnostic::default()
4919 }
4920}
4921
4922fn push_type_diagnostics(
4923 uri: &Url,
4924 text: &str,
4925 compilation_unit: &CompilationUnit,
4926 diagnostics: &mut Vec<Diagnostic>,
4927) {
4928 const MAX_TYPECHECK_BYTES: usize = 256 * 1024;
4931 if text.len() > MAX_TYPECHECK_BYTES {
4932 return;
4933 }
4934
4935 let mut engine = match Engine::with_prelude(()) {
4936 Ok(engine) => engine,
4937 Err(err) => {
4938 push_engine_error(err, diagnostics, compilation_unit);
4939 return;
4940 }
4941 };
4942 engine.add_default_resolvers();
4943
4944 let result = if let Some(path) = uri_to_file_path(uri) {
4945 engine.infer_snippet_at(text, path)
4946 } else {
4947 engine.infer_snippet(text)
4948 };
4949
4950 if let Err(err) = result {
4951 push_engine_error(err.into_engine_error(), diagnostics, compilation_unit);
4952 return;
4953 }
4954
4955 push_hole_diagnostics(compilation_unit, diagnostics);
4956}
4957
4958fn push_engine_error(
4959 err: EngineError,
4960 diagnostics: &mut Vec<Diagnostic>,
4961 compilation_unit: &CompilationUnit,
4962) {
4963 match err {
4964 EngineError::Type(err) => {
4965 let expr = compilation_unit.body_with_fns();
4966 let before = diagnostics.len();
4967 push_ts_error(err, diagnostics, expr.as_deref(), None, None);
4968 if let Some(primary) = diagnostics.get(before).cloned()
4969 && let Some(expr) = expr.as_deref()
4970 {
4971 push_additional_default_record_update_ambiguity_diagnostics(
4972 expr,
4973 &primary.message,
4974 diagnostics,
4975 );
4976 }
4977 }
4978 EngineError::Module(module_err) => {
4979 push_module_error(&module_err, diagnostics, compilation_unit);
4980 }
4981 other => {
4982 diagnostics.push(diagnostic_for_span(
4983 primary_program_span(compilation_unit),
4984 other.to_string(),
4985 ));
4986 }
4987 }
4988}
4989
4990fn push_module_error(
4991 err: &ModuleError,
4992 diagnostics: &mut Vec<Diagnostic>,
4993 compilation_unit: &CompilationUnit,
4994) {
4995 match err {
4996 ModuleError::Lex { source } => {
4997 diagnostics.push(diagnostic_for_lexical_error(source));
4998 }
4999 ModuleError::Parse { errors } => {
5000 for err in errors {
5001 diagnostics.push(diagnostic_for_span(err.span, err.message.clone()));
5002 if diagnostics.len() >= MAX_DIAGNOSTICS {
5003 break;
5004 }
5005 }
5006 }
5007 _ => {
5008 diagnostics.push(diagnostic_for_span(
5009 primary_program_span(compilation_unit),
5010 err.to_string(),
5011 ));
5012 }
5013 }
5014}
5015
5016fn primary_program_span(compilation_unit: &CompilationUnit) -> Span {
5017 match compilation_unit.decls.first() {
5018 Some(Decl::Type(d)) => d.span,
5019 Some(Decl::Fn(d)) => d.span,
5020 Some(Decl::DeclareFn(d)) => d.span,
5021 Some(Decl::Import(d)) => d.span,
5022 Some(Decl::Class(d)) => d.span,
5023 Some(Decl::Instance(d)) => d.span,
5024 None => compilation_unit
5025 .body
5026 .as_deref()
5027 .map(|expr| *expr.span())
5028 .unwrap_or_default(),
5029 }
5030}
5031
5032fn push_hole_diagnostics(compilation_unit: &CompilationUnit, diagnostics: &mut Vec<Diagnostic>) {
5033 let Some(body) = compilation_unit.body_with_fns() else {
5034 return;
5035 };
5036 let mut spans = Vec::new();
5037 collect_hole_spans(body.as_ref(), &mut spans);
5038 spans.sort_unstable_by_key(|s| (s.begin.line, s.begin.column, s.end.line, s.end.column));
5039 spans.dedup();
5040
5041 for span in spans {
5042 if diagnostics.len() >= MAX_DIAGNOSTICS {
5043 break;
5044 }
5045 diagnostics.push(Diagnostic {
5046 range: span_to_range(span),
5047 severity: Some(DiagnosticSeverity::ERROR),
5048 message: "typed hole `?` must be filled before evaluation".to_string(),
5049 source: Some("rex-typesystem".to_string()),
5050 ..Diagnostic::default()
5051 });
5052 }
5053}
5054
5055fn unknown_var_name(err: &TsTypeError) -> Option<Symbol> {
5056 match err {
5057 TsTypeError::UnknownVar(name) => Some(name.clone()),
5058 TsTypeError::Spanned { error, .. } => unknown_var_name(error),
5059 _ => None,
5060 }
5061}
5062
5063fn field_not_definitely_available_tail(message: &str) -> Option<(&str, &str)> {
5064 let rest = message.strip_prefix("field `")?;
5065 let (field, tail) = rest.split_once('`')?;
5066 tail.contains("is not definitely available on")
5067 .then_some((field, tail))
5068}
5069
5070fn push_additional_default_record_update_ambiguity_diagnostics(
5071 expr: &Expr,
5072 primary_message: &str,
5073 diagnostics: &mut Vec<Diagnostic>,
5074) {
5075 let Some((_field, tail)) = field_not_definitely_available_tail(primary_message) else {
5076 return;
5077 };
5078 let mut updates = Vec::new();
5079 collect_default_record_updates(expr, &mut updates);
5080 for (span, fields) in updates {
5081 if diagnostics.len() >= MAX_DIAGNOSTICS {
5082 break;
5083 }
5084 let Some(field) = fields.first() else {
5085 continue;
5086 };
5087 let message = format!("field `{field}`{tail}");
5088 let range = span_to_range(span);
5089 if diagnostics
5090 .iter()
5091 .any(|d| d.range == range && d.message == message)
5092 {
5093 continue;
5094 }
5095 diagnostics.push(Diagnostic {
5096 range,
5097 severity: Some(DiagnosticSeverity::ERROR),
5098 message,
5099 source: Some("rex-typesystem".to_string()),
5100 ..Diagnostic::default()
5101 });
5102 }
5103}
5104
5105fn collect_default_record_updates(expr: &Expr, out: &mut Vec<(Span, Vec<String>)>) {
5106 match expr {
5107 Expr::RecordUpdate(span, base, updates) => {
5108 if matches!(base.as_ref(), Expr::Var(v) if v.name.as_ref() == "default") {
5109 let fields = updates
5110 .keys()
5111 .map(|name| name.as_ref().to_string())
5112 .collect::<Vec<_>>();
5113 if !fields.is_empty() {
5114 out.push((*span, fields));
5115 }
5116 }
5117 collect_default_record_updates(base, out);
5118 for value in updates.values() {
5119 collect_default_record_updates(value, out);
5120 }
5121 }
5122 Expr::App(_, fun, arg) => {
5123 collect_default_record_updates(fun, out);
5124 collect_default_record_updates(arg, out);
5125 }
5126 Expr::Project(_, base, _) => collect_default_record_updates(base, out),
5127 Expr::Lam(_, _, _, _, _, body) => collect_default_record_updates(body, out),
5128 Expr::Let(_, _, _, def, body) => {
5129 collect_default_record_updates(def, out);
5130 collect_default_record_updates(body, out);
5131 }
5132 Expr::LetRec(_, bindings, body) => {
5133 for (_var, _ann, def) in bindings {
5134 collect_default_record_updates(def, out);
5135 }
5136 collect_default_record_updates(body, out);
5137 }
5138 Expr::Ite(_, cond, then_expr, else_expr) => {
5139 collect_default_record_updates(cond, out);
5140 collect_default_record_updates(then_expr, out);
5141 collect_default_record_updates(else_expr, out);
5142 }
5143 Expr::Match(_, scrutinee, arms) => {
5144 collect_default_record_updates(scrutinee, out);
5145 for (_pat, arm) in arms {
5146 collect_default_record_updates(arm, out);
5147 }
5148 }
5149 Expr::Ann(_, inner, _) => collect_default_record_updates(inner, out),
5150 Expr::Tuple(_, items) | Expr::List(_, items) => {
5151 for item in items {
5152 collect_default_record_updates(item, out);
5153 }
5154 }
5155 Expr::Dict(_, entries) => {
5156 for value in entries.values() {
5157 collect_default_record_updates(value, out);
5158 }
5159 }
5160 Expr::Var(..)
5161 | Expr::Bool(..)
5162 | Expr::Uint(..)
5163 | Expr::Int(..)
5164 | Expr::Float(..)
5165 | Expr::String(..)
5166 | Expr::Uuid(..)
5167 | Expr::DateTime(..)
5168 | Expr::Hole(..) => {}
5169 }
5170}
5171
5172fn find_let_binding_for_def_range(
5173 compilation_unit: &CompilationUnit,
5174 target: Range,
5175) -> Option<(String, Position)> {
5176 let body = compilation_unit.body_with_fns()?;
5177 find_let_binding_for_def_range_in_expr(body.as_ref(), target)
5178}
5179
5180fn find_let_binding_for_def_range_in_expr(
5181 expr: &Expr,
5182 target: Range,
5183) -> Option<(String, Position)> {
5184 match expr {
5185 Expr::Let(_, var, ann, def, body) => {
5186 let def_range = span_to_range(*def.span());
5187 if ranges_overlap(def_range, target) && ann.is_none() {
5188 return Some((var.name.as_ref().to_string(), span_to_range(var.span).end));
5189 }
5190 find_let_binding_for_def_range_in_expr(def.as_ref(), target)
5191 .or_else(|| find_let_binding_for_def_range_in_expr(body.as_ref(), target))
5192 }
5193 Expr::LetRec(_, bindings, body) => {
5194 for (var, ann, def) in bindings {
5195 let def_range = span_to_range(*def.span());
5196 if ranges_overlap(def_range, target) && ann.is_none() {
5197 return Some((var.name.as_ref().to_string(), span_to_range(var.span).end));
5198 }
5199 if let Some(found) = find_let_binding_for_def_range_in_expr(def.as_ref(), target) {
5200 return Some(found);
5201 }
5202 }
5203 find_let_binding_for_def_range_in_expr(body.as_ref(), target)
5204 }
5205 Expr::App(_, fun, arg) => find_let_binding_for_def_range_in_expr(fun.as_ref(), target)
5206 .or_else(|| find_let_binding_for_def_range_in_expr(arg.as_ref(), target)),
5207 Expr::Project(_, base, _) => find_let_binding_for_def_range_in_expr(base.as_ref(), target),
5208 Expr::RecordUpdate(_, base, updates) => {
5209 find_let_binding_for_def_range_in_expr(base.as_ref(), target).or_else(|| {
5210 updates
5211 .values()
5212 .find_map(|expr| find_let_binding_for_def_range_in_expr(expr.as_ref(), target))
5213 })
5214 }
5215 Expr::Lam(_, _, _, _, _, body) => {
5216 find_let_binding_for_def_range_in_expr(body.as_ref(), target)
5217 }
5218 Expr::Ite(_, cond, then_expr, else_expr) => {
5219 find_let_binding_for_def_range_in_expr(cond.as_ref(), target)
5220 .or_else(|| find_let_binding_for_def_range_in_expr(then_expr.as_ref(), target))
5221 .or_else(|| find_let_binding_for_def_range_in_expr(else_expr.as_ref(), target))
5222 }
5223 Expr::Match(_, scrutinee, arms) => {
5224 find_let_binding_for_def_range_in_expr(scrutinee.as_ref(), target).or_else(|| {
5225 arms.iter().find_map(|(_, arm)| {
5226 find_let_binding_for_def_range_in_expr(arm.as_ref(), target)
5227 })
5228 })
5229 }
5230 Expr::Ann(_, inner, _) => find_let_binding_for_def_range_in_expr(inner.as_ref(), target),
5231 Expr::Tuple(_, items) | Expr::List(_, items) => items
5232 .iter()
5233 .find_map(|item| find_let_binding_for_def_range_in_expr(item.as_ref(), target)),
5234 Expr::Dict(_, entries) => entries
5235 .values()
5236 .find_map(|value| find_let_binding_for_def_range_in_expr(value.as_ref(), target)),
5237 Expr::Var(..)
5238 | Expr::Bool(..)
5239 | Expr::Uint(..)
5240 | Expr::Int(..)
5241 | Expr::Float(..)
5242 | Expr::String(..)
5243 | Expr::Uuid(..)
5244 | Expr::DateTime(..)
5245 | Expr::Hole(..) => None,
5246 }
5247}
5248
5249fn collect_unbound_var_spans(
5250 expr: &Expr,
5251 target: &Symbol,
5252 bound: &mut Vec<Symbol>,
5253 out: &mut Vec<Span>,
5254) {
5255 match expr {
5256 Expr::Var(var) => {
5257 if var.name == *target && !bound.iter().any(|name| name == &var.name) {
5258 out.push(var.span);
5259 }
5260 }
5261 Expr::App(_, fun, arg) => {
5262 collect_unbound_var_spans(fun, target, bound, out);
5263 collect_unbound_var_spans(arg, target, bound, out);
5264 }
5265 Expr::Project(_, base, _) => {
5266 collect_unbound_var_spans(base, target, bound, out);
5267 }
5268 Expr::Lam(_, _scope, param, _ann, _constraints, body) => {
5269 bound.push(param.name.clone());
5270 collect_unbound_var_spans(body, target, bound, out);
5271 bound.pop();
5272 }
5273 Expr::Let(_, var, _ann, def, body) => {
5274 collect_unbound_var_spans(def, target, bound, out);
5275 bound.push(var.name.clone());
5276 collect_unbound_var_spans(body, target, bound, out);
5277 bound.pop();
5278 }
5279 Expr::LetRec(_, bindings, body) => {
5280 let base_len = bound.len();
5281 for (var, _ann, _def) in bindings {
5282 bound.push(var.name.clone());
5283 }
5284 for (_var, _ann, def) in bindings {
5285 collect_unbound_var_spans(def, target, bound, out);
5286 }
5287 collect_unbound_var_spans(body, target, bound, out);
5288 bound.truncate(base_len);
5289 }
5290 Expr::Ite(_, cond, then_expr, else_expr) => {
5291 collect_unbound_var_spans(cond, target, bound, out);
5292 collect_unbound_var_spans(then_expr, target, bound, out);
5293 collect_unbound_var_spans(else_expr, target, bound, out);
5294 }
5295 Expr::Match(_, scrutinee, arms) => {
5296 collect_unbound_var_spans(scrutinee, target, bound, out);
5297 for (pat, arm) in arms {
5298 let base_len = bound.len();
5299 let mut pat_bindings = Vec::new();
5300 collect_pattern_bindings(pat, &mut pat_bindings);
5301 bound.extend(pat_bindings);
5302 collect_unbound_var_spans(arm, target, bound, out);
5303 bound.truncate(base_len);
5304 }
5305 }
5306 Expr::Ann(_, inner, _) => {
5307 collect_unbound_var_spans(inner, target, bound, out);
5308 }
5309 Expr::Tuple(_, items) | Expr::List(_, items) => {
5310 for item in items {
5311 collect_unbound_var_spans(item, target, bound, out);
5312 }
5313 }
5314 Expr::Dict(_, kvs) | Expr::RecordUpdate(_, _, kvs) => {
5315 for expr in kvs.values() {
5316 collect_unbound_var_spans(expr, target, bound, out);
5317 }
5318 if let Expr::RecordUpdate(_, base, _) = expr {
5319 collect_unbound_var_spans(base, target, bound, out);
5320 }
5321 }
5322 Expr::Bool(..)
5323 | Expr::Uint(..)
5324 | Expr::Int(..)
5325 | Expr::Float(..)
5326 | Expr::String(..)
5327 | Expr::Uuid(..)
5328 | Expr::DateTime(..)
5329 | Expr::Hole(..) => {}
5330 }
5331}
5332
5333fn push_ts_error(
5334 err: TsTypeError,
5335 diagnostics: &mut Vec<Diagnostic>,
5336 expr: Option<&Expr>,
5337 ts: Option<&TypeSystem>,
5338 fallback_span: Option<Span>,
5339) {
5340 let unknown_target = unknown_var_name(&err);
5341 let (span, message) = match &err {
5342 TsTypeError::Spanned { span, error } => (*span, error.to_string()),
5343 other => (
5344 fallback_span
5345 .or_else(|| expr.map(|e| *e.span()))
5346 .unwrap_or_default(),
5347 other.to_string(),
5348 ),
5349 };
5350
5351 if let (Some(target), Some(expr)) = (unknown_target, expr)
5352 && ts.is_none_or(|ts| ts.env.lookup(&target).is_none())
5353 {
5354 let mut spans = Vec::new();
5355 collect_unbound_var_spans(expr, &target, &mut Vec::new(), &mut spans);
5356 spans.sort_unstable_by_key(|s| (s.begin.line, s.begin.column, s.end.line, s.end.column));
5357 spans.dedup();
5358 if !spans.is_empty() {
5359 for unbound_span in spans {
5360 if diagnostics.len() >= MAX_DIAGNOSTICS {
5361 break;
5362 }
5363 diagnostics.push(Diagnostic {
5364 range: span_to_range(unbound_span),
5365 severity: Some(DiagnosticSeverity::ERROR),
5366 message: message.clone(),
5367 source: Some("rex-typesystem".to_string()),
5368 ..Diagnostic::default()
5369 });
5370 }
5371 return;
5372 }
5373 }
5374
5375 diagnostics.push(Diagnostic {
5376 range: span_to_range(span),
5377 severity: Some(DiagnosticSeverity::ERROR),
5378 message,
5379 source: Some("rex-typesystem".to_string()),
5380 ..Diagnostic::default()
5381 });
5382}
5383
5384fn range_contains_position(range: Range, position: Position) -> bool {
5385 let after_start = position.line > range.start.line
5386 || (position.line == range.start.line && position.character >= range.start.character);
5387 let before_end = position.line < range.end.line
5388 || (position.line == range.end.line && position.character < range.end.character);
5389 after_start && before_end
5390}
5391
5392fn range_touches_position(range: Range, position: Position) -> bool {
5393 if range_contains_position(range, position) {
5398 return true;
5399 }
5400 if position.line != range.end.line || position.character != range.end.character {
5401 return false;
5402 }
5403 position.line != range.start.line || position.character != range.start.character
5405}
5406
5407fn span_to_range(span: Span) -> Range {
5408 Range {
5409 start: position_from_span(span.begin.line, span.begin.column),
5410 end: position_from_span(span.end.line, span.end.column),
5411 }
5412}
5413
5414fn position_from_span(line: usize, column: usize) -> Position {
5415 Position {
5416 line: line.saturating_sub(1) as u32,
5417 character: column.saturating_sub(1) as u32,
5418 }
5419}
5420
5421pub(crate) fn hover_contents(word: &str) -> Option<HoverContents> {
5422 if let Some(doc) = keyword_doc(word) {
5423 return Some(markdown_hover(word, "keyword", doc));
5424 }
5425
5426 if let Some(doc) = type_doc(word) {
5427 return Some(markdown_hover(word, "type", doc));
5428 }
5429
5430 if let Some(doc) = value_doc(word) {
5431 return Some(markdown_hover(word, "value", doc));
5432 }
5433
5434 None
5435}
5436
5437fn markdown_hover(word: &str, kind: &str, doc: &str) -> HoverContents {
5438 HoverContents::Markup(MarkupContent {
5439 kind: MarkupKind::Markdown,
5440 value: format!("**{}** {}\n\n{}", word, kind, doc),
5441 })
5442}
5443
5444fn keyword_doc(word: &str) -> Option<&'static str> {
5445 match word {
5446 "let" => Some("Introduces local bindings."),
5447 "in" => Some("Begins the expression body for a let binding."),
5448 "type" => Some("Declares a type or ADT."),
5449 "match" => Some("Starts a pattern match expression."),
5450 "with" => {
5451 Some("Separates a match scrutinee from its arm block, or introduces a record update.")
5452 }
5453 "case" => Some("Introduces a match arm."),
5454 "if" => Some("Conditional expression keyword."),
5455 "then" => Some("Conditional expression branch."),
5456 "else" => Some("Fallback branch of a conditional expression."),
5457 "as" => Some("Type ascription or aliasing keyword."),
5458 "for" => Some("List/dict comprehension keyword (when supported)."),
5459 "is" => Some("Type assertion keyword."),
5460 _ => None,
5461 }
5462}
5463
5464fn type_doc(word: &str) -> Option<&'static str> {
5465 match word {
5466 "bool" => Some("Boolean type."),
5467 "string" => Some("UTF-8 string type."),
5468 "uuid" => Some("UUID type."),
5469 "datetime" => Some("Datetime type."),
5470 "u8" => Some("Unsigned 8-bit integer."),
5471 "u16" => Some("Unsigned 16-bit integer."),
5472 "u32" => Some("Unsigned 32-bit integer."),
5473 "u64" => Some("Unsigned 64-bit integer."),
5474 "i8" => Some("Signed 8-bit integer."),
5475 "i16" => Some("Signed 16-bit integer."),
5476 "i32" => Some("Signed 32-bit integer."),
5477 "i64" => Some("Signed 64-bit integer."),
5478 "f32" => Some("32-bit float."),
5479 "f64" => Some("64-bit float."),
5480 "List" => Some("List type constructor."),
5481 "Dict" => Some("Dictionary type constructor."),
5482 "Array" => Some("Array type constructor."),
5483 "Option" => Some("Optional type constructor."),
5484 "Result" => Some("Result type constructor."),
5485 _ => None,
5486 }
5487}
5488
5489fn value_doc(word: &str) -> Option<&'static str> {
5490 match word {
5491 "true" => Some("Boolean literal."),
5492 "false" => Some("Boolean literal."),
5493 "null" => Some("Null literal."),
5494 "Some" => Some("Option constructor."),
5495 "None" => Some("Option empty constructor."),
5496 "Ok" => Some("Result success constructor."),
5497 "Err" => Some("Result error constructor."),
5498 _ => None,
5499 }
5500}
5501
5502pub(crate) fn completion_items(uri: &Url, text: &str, position: Position) -> Vec<CompletionItem> {
5503 let field_mode = is_field_completion(text, position);
5504 let base_ident = if field_mode {
5505 field_base_ident(text, position)
5506 } else {
5507 None
5508 };
5509 if let Ok((_tokens, program)) = tokenize_and_parse_cached(uri, text) {
5510 return completion_items_from_program(
5511 &program,
5512 position,
5513 field_mode,
5514 base_ident.as_deref(),
5515 uri,
5516 );
5517 }
5518
5519 completion_items_fallback(text, base_ident.as_deref(), field_mode)
5520}
5521
5522fn completion_items_from_program(
5523 compilation_unit: &CompilationUnit,
5524 position: Position,
5525 field_mode: bool,
5526 base_ident: Option<&str>,
5527 uri: &Url,
5528) -> Vec<CompletionItem> {
5529 if field_mode {
5530 if let Some(base_ident) = base_ident
5531 && let Ok(exports) =
5532 completion_exports_for_module_alias(uri, compilation_unit, base_ident)
5533 && !exports.is_empty()
5534 {
5535 return exports
5536 .into_iter()
5537 .map(|label| completion_item(label, CompletionItemKind::FIELD))
5538 .collect();
5539 }
5540 if let Some(fields) = field_completion_for_position(compilation_unit, position, base_ident)
5541 {
5542 return fields
5543 .into_iter()
5544 .map(|label| completion_item(label, CompletionItemKind::FIELD))
5545 .collect();
5546 }
5547 return Vec::new();
5548 }
5549
5550 let mut value_kinds = values_in_scope_at_position(compilation_unit, position);
5551 let pos = lsp_to_rex_position(position);
5552 for decl in &compilation_unit.decls {
5553 let Decl::Import(id) = decl else { continue };
5554 if position_in_span(pos, id.span) || position_leq(id.span.end, pos) {
5555 value_kinds
5556 .entry(id.alias.as_ref().to_string())
5557 .or_insert(CompletionItemKind::MODULE);
5558 }
5559 }
5560 for value in BUILTIN_VALUES {
5561 value_kinds
5562 .entry((*value).to_string())
5563 .or_insert(CompletionItemKind::VARIABLE);
5564 }
5565 for (value, kind) in prelude_completion_values() {
5566 value_kinds.entry(value.clone()).or_insert(*kind);
5567 }
5568 for ctor in collect_constructors(compilation_unit) {
5569 value_kinds
5570 .entry(ctor)
5571 .or_insert(CompletionItemKind::CONSTRUCTOR);
5572 }
5573
5574 let mut type_names = collect_type_names(compilation_unit);
5575 type_names.extend(BUILTIN_TYPES.iter().map(|value| value.to_string()));
5576
5577 let mut items = Vec::new();
5578 items.extend(
5579 value_kinds
5580 .into_iter()
5581 .map(|(label, kind)| completion_item(label, kind)),
5582 );
5583 items.extend(
5584 type_names
5585 .into_iter()
5586 .map(|label| completion_item(label, CompletionItemKind::CLASS)),
5587 );
5588
5589 items
5590}
5591
5592fn completion_items_fallback(
5593 text: &str,
5594 base_ident: Option<&str>,
5595 field_mode: bool,
5596) -> Vec<CompletionItem> {
5597 let mut identifiers: HashMap<String, CompletionItemKind> = HashMap::new();
5598
5599 if let Ok(tokens) = Token::tokenize(text) {
5600 identifiers.extend(function_defs_from_tokens(&tokens));
5601
5602 let mut index = 0usize;
5603 while index < tokens.items.len() {
5604 if let Token::Ident(name, ..) = &tokens.items[index] {
5605 identifiers
5606 .entry(name.clone())
5607 .or_insert(CompletionItemKind::VARIABLE);
5608 }
5609 index += 1;
5610 }
5611
5612 if field_mode {
5613 if let Some(base_ident) = base_ident
5614 && let Some(fields) = fallback_field_map(&tokens).get(base_ident)
5615 {
5616 return fields
5617 .iter()
5618 .cloned()
5619 .map(|label| completion_item(label, CompletionItemKind::FIELD))
5620 .collect();
5621 }
5622 return Vec::new();
5623 }
5624 }
5625
5626 let mut items: Vec<CompletionItem> = identifiers
5627 .into_iter()
5628 .map(|(label, kind)| completion_item(label, kind))
5629 .collect();
5630 items.extend(
5631 BUILTIN_TYPES
5632 .iter()
5633 .map(|label| completion_item((*label).to_string(), CompletionItemKind::CLASS)),
5634 );
5635 items
5636}
5637
5638fn completion_item(label: String, kind: CompletionItemKind) -> CompletionItem {
5639 CompletionItem {
5640 label,
5641 kind: Some(kind),
5642 ..CompletionItem::default()
5643 }
5644}
5645
5646fn values_in_scope_at_position(
5647 compilation_unit: &CompilationUnit,
5648 position: Position,
5649) -> HashMap<String, CompletionItemKind> {
5650 let pos = lsp_to_rex_position(position);
5651 let Some(expr) = compilation_unit.body_with_fns() else {
5652 return HashMap::new();
5653 };
5654 values_in_scope_at_expr(expr.as_ref(), pos, &mut Vec::new()).unwrap_or_default()
5655}
5656
5657fn values_in_scope_at_expr(
5658 expr: &Expr,
5659 position: RexPosition,
5660 scope: &mut Vec<(String, CompletionItemKind)>,
5661) -> Option<HashMap<String, CompletionItemKind>> {
5662 if !position_in_span(position, *expr.span()) {
5663 return None;
5664 }
5665
5666 fn scope_to_map(scope: &[(String, CompletionItemKind)]) -> HashMap<String, CompletionItemKind> {
5667 let mut map = HashMap::new();
5670 for (name, kind) in scope {
5671 let slot = map.entry(name.clone()).or_insert(*kind);
5672 if *slot != CompletionItemKind::FUNCTION && *kind == CompletionItemKind::FUNCTION {
5673 *slot = *kind;
5674 }
5675 }
5676 map
5677 }
5678
5679 match expr {
5680 Expr::Let(_span, var, _ann, def, body) => {
5681 if position_in_span(position, *def.span()) {
5682 return values_in_scope_at_expr(def, position, scope)
5683 .or_else(|| Some(scope_to_map(scope)));
5684 }
5685
5686 if position_in_span(position, *body.span()) {
5687 let kind = matches!(def.as_ref(), Expr::Lam(..))
5688 .then_some(CompletionItemKind::FUNCTION)
5689 .unwrap_or(CompletionItemKind::VARIABLE);
5690 scope.push((var.name.to_string(), kind));
5691 let out = values_in_scope_at_expr(body, position, scope)
5692 .or_else(|| Some(scope_to_map(scope)));
5693 scope.pop();
5694 return out;
5695 }
5696
5697 Some(scope_to_map(scope))
5698 }
5699 Expr::LetRec(_span, bindings, body) => {
5700 let base_len = scope.len();
5701 scope.extend(bindings.iter().map(|(var, _ann, def)| {
5702 let kind = matches!(def.as_ref(), Expr::Lam(..))
5703 .then_some(CompletionItemKind::FUNCTION)
5704 .unwrap_or(CompletionItemKind::VARIABLE);
5705 (var.name.to_string(), kind)
5706 }));
5707
5708 for (_, _, def) in bindings {
5709 if position_in_span(position, *def.span()) {
5710 let out = values_in_scope_at_expr(def, position, scope)
5711 .or_else(|| Some(scope_to_map(scope)));
5712 scope.truncate(base_len);
5713 return out;
5714 }
5715 }
5716
5717 if position_in_span(position, *body.span()) {
5718 let out = values_in_scope_at_expr(body, position, scope)
5719 .or_else(|| Some(scope_to_map(scope)));
5720 scope.truncate(base_len);
5721 return out;
5722 }
5723
5724 scope.truncate(base_len);
5725 Some(scope_to_map(scope))
5726 }
5727 Expr::Lam(_span, _scope, param, _ann, _constraints, body) => {
5728 if position_in_span(position, *body.span()) {
5729 scope.push((param.name.to_string(), CompletionItemKind::VARIABLE));
5730 let out = values_in_scope_at_expr(body, position, scope)
5731 .or_else(|| Some(scope_to_map(scope)));
5732 scope.pop();
5733 return out;
5734 }
5735 Some(scope_to_map(scope))
5736 }
5737 Expr::Match(_span, scrutinee, arms) => {
5738 if position_in_span(position, *scrutinee.span()) {
5739 return values_in_scope_at_expr(scrutinee, position, scope)
5740 .or_else(|| Some(scope_to_map(scope)));
5741 }
5742 for (pattern, arm) in arms {
5743 if position_in_span(position, *pattern.span()) {
5744 return Some(scope_to_map(scope));
5745 }
5746 if position_in_span(position, *arm.span()) {
5747 let base_len = scope.len();
5748 scope.extend(
5749 pattern_vars(pattern)
5750 .into_iter()
5751 .map(|name| (name, CompletionItemKind::VARIABLE)),
5752 );
5753 let out = values_in_scope_at_expr(arm, position, scope)
5754 .or_else(|| Some(scope_to_map(scope)));
5755 scope.truncate(base_len);
5756 return out;
5757 }
5758 }
5759 Some(scope_to_map(scope))
5760 }
5761 Expr::App(_span, fun, arg) => {
5762 if position_in_span(position, *fun.span()) {
5763 return values_in_scope_at_expr(fun, position, scope)
5764 .or_else(|| Some(scope_to_map(scope)));
5765 }
5766 if position_in_span(position, *arg.span()) {
5767 return values_in_scope_at_expr(arg, position, scope)
5768 .or_else(|| Some(scope_to_map(scope)));
5769 }
5770 Some(scope_to_map(scope))
5771 }
5772 Expr::Project(_span, base, _field) => {
5773 if position_in_span(position, *base.span()) {
5774 return values_in_scope_at_expr(base, position, scope)
5775 .or_else(|| Some(scope_to_map(scope)));
5776 }
5777 Some(scope_to_map(scope))
5778 }
5779 Expr::Tuple(_span, elems) | Expr::List(_span, elems) => {
5780 for elem in elems {
5781 if position_in_span(position, *elem.span()) {
5782 return values_in_scope_at_expr(elem, position, scope)
5783 .or_else(|| Some(scope_to_map(scope)));
5784 }
5785 }
5786 Some(scope_to_map(scope))
5787 }
5788 Expr::Dict(_span, entries) => {
5789 for value in entries.values() {
5790 if position_in_span(position, *value.span()) {
5791 return values_in_scope_at_expr(value, position, scope)
5792 .or_else(|| Some(scope_to_map(scope)));
5793 }
5794 }
5795 Some(scope_to_map(scope))
5796 }
5797 Expr::Ite(_span, cond, then_expr, else_expr) => {
5798 if position_in_span(position, *cond.span()) {
5799 return values_in_scope_at_expr(cond, position, scope)
5800 .or_else(|| Some(scope_to_map(scope)));
5801 }
5802 if position_in_span(position, *then_expr.span()) {
5803 return values_in_scope_at_expr(then_expr, position, scope)
5804 .or_else(|| Some(scope_to_map(scope)));
5805 }
5806 if position_in_span(position, *else_expr.span()) {
5807 return values_in_scope_at_expr(else_expr, position, scope)
5808 .or_else(|| Some(scope_to_map(scope)));
5809 }
5810 Some(scope_to_map(scope))
5811 }
5812 Expr::Ann(_span, inner, _ann) => {
5813 if position_in_span(position, *inner.span()) {
5814 return values_in_scope_at_expr(inner, position, scope)
5815 .or_else(|| Some(scope_to_map(scope)));
5816 }
5817 Some(scope_to_map(scope))
5818 }
5819 _ => Some(scope_to_map(scope)),
5820 }
5821}
5822
5823fn function_defs_from_tokens(tokens: &Tokens) -> HashMap<String, CompletionItemKind> {
5824 let mut out = HashMap::new();
5832 let items = &tokens.items;
5833 let mut index = 0usize;
5834
5835 let next_non_ws = |i: usize| -> Option<usize> { (i < items.len()).then_some(i) };
5836
5837 while index < items.len() {
5838 if matches!(items[index], Token::Fn(..)) {
5839 let Some(i) = next_non_ws(index + 1) else {
5840 break;
5841 };
5842 if let Token::Ident(name, ..) = &items[i] {
5843 out.insert(name.clone(), CompletionItemKind::FUNCTION);
5844 }
5845 index += 1;
5846 continue;
5847 }
5848
5849 if !matches!(items[index], Token::Let(..)) {
5850 index += 1;
5851 continue;
5852 }
5853
5854 let Some(mut i) = next_non_ws(index + 1) else {
5855 break;
5856 };
5857
5858 let name = match &items[i] {
5859 Token::Ident(name, ..) => name.clone(),
5860 _ => {
5861 index += 1;
5862 continue;
5863 }
5864 };
5865
5866 i += 1;
5868 while let Some(j) = next_non_ws(i) {
5869 match &items[j] {
5870 Token::Assign(..) => {
5871 let Some(k) = next_non_ws(j + 1) else {
5872 break;
5873 };
5874 if matches!(items[k], Token::BackSlash(..)) {
5875 out.insert(name, CompletionItemKind::FUNCTION);
5876 }
5877 break;
5878 }
5879 Token::SemiColon(..) => break,
5880 _ => i = j + 1,
5881 }
5882 }
5883
5884 index += 1;
5885 }
5886
5887 out
5888}
5889
5890fn collect_type_names(compilation_unit: &CompilationUnit) -> BTreeSet<String> {
5891 let mut names = BTreeSet::new();
5892 for decl in &compilation_unit.decls {
5893 if let Decl::Type(TypeDecl { name, .. }) = decl {
5894 names.insert(name.to_string());
5895 }
5896 }
5897 names
5898}
5899
5900fn collect_constructors(compilation_unit: &CompilationUnit) -> BTreeSet<String> {
5901 let mut names = BTreeSet::new();
5902 for decl in &compilation_unit.decls {
5903 if let Decl::Type(TypeDecl { variants, .. }) = decl {
5904 for variant in variants {
5905 names.insert(variant.name.to_string());
5906 }
5907 }
5908 }
5909 names
5910}
5911
5912fn collect_fields_type_expr(typ: &TypeExpr, fields: &mut BTreeSet<String>) {
5913 match typ {
5914 TypeExpr::Record(_, entries) => {
5915 for (name, _ty) in entries {
5916 fields.insert(name.to_string());
5917 }
5918 }
5919 TypeExpr::App(_, fun, arg) => {
5920 collect_fields_type_expr(fun, fields);
5921 collect_fields_type_expr(arg, fields);
5922 }
5923 TypeExpr::Fun(_, arg, ret) => {
5924 collect_fields_type_expr(arg, fields);
5925 collect_fields_type_expr(ret, fields);
5926 }
5927 TypeExpr::Tuple(_, elems) => {
5928 for elem in elems {
5929 collect_fields_type_expr(elem, fields);
5930 }
5931 }
5932 TypeExpr::Name(..) => {}
5933 }
5934}
5935
5936fn field_completion_for_position(
5937 compilation_unit: &CompilationUnit,
5938 position: Position,
5939 base_ident: Option<&str>,
5940) -> Option<BTreeSet<String>> {
5941 let type_fields = type_fields_map(compilation_unit);
5942 let env = field_env_at_position(compilation_unit, position, &type_fields);
5943 let pos = lsp_to_rex_position(position);
5944
5945 if let Some(expr) = compilation_unit.body_with_fns()
5946 && let Some(base) = project_base_at_position(expr.as_ref(), pos)
5947 && let Some(fields) = fields_for_expr(base, &env, &type_fields)
5948 {
5949 return Some(fields);
5950 }
5951
5952 if let Some(base_ident) = base_ident {
5953 if let Some(fields) = env.get(base_ident) {
5954 return Some(fields.clone());
5955 }
5956 if let Some(fields) = type_fields.get(base_ident) {
5957 return Some(fields.clone());
5958 }
5959 }
5960
5961 None
5962}
5963
5964fn type_fields_map(compilation_unit: &CompilationUnit) -> HashMap<String, BTreeSet<String>> {
5965 let mut map = HashMap::new();
5966 for decl in &compilation_unit.decls {
5967 if let Decl::Type(TypeDecl { name, variants, .. }) = decl {
5968 let mut fields = BTreeSet::new();
5969 for variant in variants {
5970 for arg in &variant.args {
5971 collect_fields_type_expr(arg, &mut fields);
5972 }
5973 }
5974 if !fields.is_empty() {
5975 map.insert(name.to_string(), fields);
5976 }
5977 }
5978 }
5979 map
5980}
5981
5982fn field_env_at_position(
5983 compilation_unit: &CompilationUnit,
5984 position: Position,
5985 type_fields: &HashMap<String, BTreeSet<String>>,
5986) -> HashMap<String, BTreeSet<String>> {
5987 let pos = lsp_to_rex_position(position);
5988 let Some(expr) = compilation_unit.body_with_fns() else {
5989 return HashMap::new();
5990 };
5991 field_env_at_expr(expr.as_ref(), pos, &HashMap::new(), type_fields).unwrap_or_default()
5992}
5993
5994fn field_env_at_expr(
5995 expr: &Expr,
5996 position: RexPosition,
5997 env: &HashMap<String, BTreeSet<String>>,
5998 type_fields: &HashMap<String, BTreeSet<String>>,
5999) -> Option<HashMap<String, BTreeSet<String>>> {
6000 if !position_in_span(position, *expr.span()) {
6001 return None;
6002 }
6003
6004 match expr {
6005 Expr::Let(_, var, ann, def, body) => {
6006 if position_in_span(position, *def.span()) {
6007 return field_env_at_expr(def, position, env, type_fields)
6008 .or_else(|| Some(env.clone()));
6009 }
6010 if position_in_span(position, *body.span()) {
6011 let mut env_with = env.clone();
6012 let fields = binding_fields(ann.as_ref(), def, type_fields).unwrap_or_default();
6013 env_with.insert(var.name.to_string(), fields);
6014 if let Some(inner) = field_env_at_expr(body, position, &env_with, type_fields) {
6015 return Some(inner);
6016 }
6017 return Some(env_with);
6018 }
6019 Some(env.clone())
6020 }
6021 Expr::LetRec(_, bindings, body) => {
6022 let mut env_with = env.clone();
6023 for (var, ann, def) in bindings {
6024 let fields = binding_fields(ann.as_ref(), def, type_fields).unwrap_or_default();
6025 env_with.insert(var.name.to_string(), fields);
6026 }
6027 for (_, _, def) in bindings {
6028 if position_in_span(position, *def.span()) {
6029 return field_env_at_expr(def, position, &env_with, type_fields)
6030 .or_else(|| Some(env_with.clone()));
6031 }
6032 }
6033 if position_in_span(position, *body.span()) {
6034 if let Some(inner) = field_env_at_expr(body, position, &env_with, type_fields) {
6035 return Some(inner);
6036 }
6037 return Some(env_with);
6038 }
6039 Some(env.clone())
6040 }
6041 Expr::Lam(_, _scope, param, ann, _constraints, body) => {
6042 if position_in_span(position, *body.span()) {
6043 let mut env_with = env.clone();
6044 let fields = ann
6045 .as_ref()
6046 .and_then(|ann| fields_from_type_expr(ann, type_fields))
6047 .unwrap_or_default();
6048 env_with.insert(param.name.to_string(), fields);
6049 if let Some(inner) = field_env_at_expr(body, position, &env_with, type_fields) {
6050 return Some(inner);
6051 }
6052 return Some(env_with);
6053 }
6054 Some(env.clone())
6055 }
6056 Expr::Match(_, scrutinee, arms) => {
6057 if position_in_span(position, *scrutinee.span()) {
6058 return field_env_at_expr(scrutinee, position, env, type_fields)
6059 .or_else(|| Some(env.clone()));
6060 }
6061 for (pattern, arm) in arms {
6062 if position_in_span(position, *pattern.span()) {
6063 return Some(env.clone());
6064 }
6065 if position_in_span(position, *arm.span()) {
6066 let mut env_with = env.clone();
6067 env_with.extend(
6068 pattern_vars(pattern)
6069 .into_iter()
6070 .map(|name| (name, BTreeSet::new())),
6071 );
6072 if let Some(inner) = field_env_at_expr(arm, position, &env_with, type_fields) {
6073 return Some(inner);
6074 }
6075 return Some(env_with);
6076 }
6077 }
6078 Some(env.clone())
6079 }
6080 Expr::App(_, fun, arg) => {
6081 if position_in_span(position, *fun.span()) {
6082 return field_env_at_expr(fun, position, env, type_fields)
6083 .or_else(|| Some(env.clone()));
6084 }
6085 if position_in_span(position, *arg.span()) {
6086 return field_env_at_expr(arg, position, env, type_fields)
6087 .or_else(|| Some(env.clone()));
6088 }
6089 Some(env.clone())
6090 }
6091 Expr::Project(_, base, _field) => {
6092 if position_in_span(position, *base.span()) {
6093 return field_env_at_expr(base, position, env, type_fields)
6094 .or_else(|| Some(env.clone()));
6095 }
6096 Some(env.clone())
6097 }
6098 Expr::Tuple(_, elems) | Expr::List(_, elems) => {
6099 for elem in elems {
6100 if position_in_span(position, *elem.span()) {
6101 return field_env_at_expr(elem, position, env, type_fields)
6102 .or_else(|| Some(env.clone()));
6103 }
6104 }
6105 Some(env.clone())
6106 }
6107 Expr::Dict(_, entries) => {
6108 for value in entries.values() {
6109 if position_in_span(position, *value.span()) {
6110 return field_env_at_expr(value, position, env, type_fields)
6111 .or_else(|| Some(env.clone()));
6112 }
6113 }
6114 Some(env.clone())
6115 }
6116 Expr::Ite(_, cond, then_expr, else_expr) => {
6117 if position_in_span(position, *cond.span()) {
6118 return field_env_at_expr(cond, position, env, type_fields)
6119 .or_else(|| Some(env.clone()));
6120 }
6121 if position_in_span(position, *then_expr.span()) {
6122 return field_env_at_expr(then_expr, position, env, type_fields)
6123 .or_else(|| Some(env.clone()));
6124 }
6125 if position_in_span(position, *else_expr.span()) {
6126 return field_env_at_expr(else_expr, position, env, type_fields)
6127 .or_else(|| Some(env.clone()));
6128 }
6129 Some(env.clone())
6130 }
6131 Expr::Ann(_, inner, _ann) => {
6132 if position_in_span(position, *inner.span()) {
6133 return field_env_at_expr(inner, position, env, type_fields)
6134 .or_else(|| Some(env.clone()));
6135 }
6136 Some(env.clone())
6137 }
6138 _ => Some(env.clone()),
6139 }
6140}
6141
6142fn binding_fields(
6143 ann: Option<&TypeExpr>,
6144 def: &Expr,
6145 type_fields: &HashMap<String, BTreeSet<String>>,
6146) -> Option<BTreeSet<String>> {
6147 if let Some(ann) = ann
6148 && let Some(fields) = fields_from_type_expr(ann, type_fields)
6149 {
6150 return Some(fields);
6151 }
6152
6153 if let Expr::Ann(_, _inner, ann) = def
6154 && let Some(fields) = fields_from_type_expr(ann, type_fields)
6155 {
6156 return Some(fields);
6157 }
6158
6159 if let Expr::Dict(_, entries) = def {
6160 let fields: BTreeSet<String> = entries.keys().map(|name| name.to_string()).collect();
6161 if !fields.is_empty() {
6162 return Some(fields);
6163 }
6164 }
6165
6166 None
6167}
6168
6169fn fields_from_type_expr(
6170 typ: &TypeExpr,
6171 type_fields: &HashMap<String, BTreeSet<String>>,
6172) -> Option<BTreeSet<String>> {
6173 match typ {
6174 TypeExpr::Record(_, entries) => {
6175 let fields: BTreeSet<String> =
6176 entries.iter().map(|(name, _)| name.to_string()).collect();
6177 if fields.is_empty() {
6178 None
6179 } else {
6180 Some(fields)
6181 }
6182 }
6183 _ => {
6184 if let Some(type_name) = type_name_from_type_expr(typ) {
6185 return type_fields.get(&type_name).cloned();
6186 }
6187 None
6188 }
6189 }
6190}
6191
6192fn type_name_from_type_expr(typ: &TypeExpr) -> Option<String> {
6193 match typ {
6194 TypeExpr::Name(_, name) => Some(name.to_string()),
6195 TypeExpr::App(_, fun, _) => type_name_from_type_expr(fun),
6196 _ => None,
6197 }
6198}
6199
6200fn fields_for_expr(
6201 expr: &Expr,
6202 env: &HashMap<String, BTreeSet<String>>,
6203 type_fields: &HashMap<String, BTreeSet<String>>,
6204) -> Option<BTreeSet<String>> {
6205 match expr {
6206 Expr::Dict(_, entries) => {
6207 let fields: BTreeSet<String> = entries.keys().map(|name| name.to_string()).collect();
6208 if fields.is_empty() {
6209 None
6210 } else {
6211 Some(fields)
6212 }
6213 }
6214 Expr::Var(var) => {
6215 if let Some(fields) = env.get(var.name.as_ref()) {
6216 return Some(fields.clone());
6217 }
6218 if let Some(fields) = type_fields.get(var.name.as_ref()) {
6219 return Some(fields.clone());
6220 }
6221 None
6222 }
6223 Expr::Ann(_, inner, ann) => fields_from_type_expr(ann, type_fields)
6224 .or_else(|| fields_for_expr(inner, env, type_fields)),
6225 Expr::Project(_, base, _) => fields_for_expr(base, env, type_fields),
6226 _ => None,
6227 }
6228}
6229
6230fn project_base_at_position(expr: &Expr, position: RexPosition) -> Option<&Expr> {
6231 if !position_in_span(position, *expr.span()) {
6232 return None;
6233 }
6234
6235 match expr {
6236 Expr::Project(_, base, _) => {
6237 if position_in_span(position, *base.span()) {
6238 return project_base_at_position(base, position);
6239 }
6240 Some(base.as_ref())
6241 }
6242 Expr::Let(_, _var, _ann, def, body) => {
6243 if let Some(found) = project_base_at_position(def, position) {
6244 return Some(found);
6245 }
6246 project_base_at_position(body, position)
6247 }
6248 Expr::LetRec(_, bindings, body) => {
6249 for (_, _, def) in bindings {
6250 if let Some(found) = project_base_at_position(def, position) {
6251 return Some(found);
6252 }
6253 }
6254 project_base_at_position(body, position)
6255 }
6256 Expr::Lam(_, _scope, _param, _ann, _constraints, body) => {
6257 project_base_at_position(body, position)
6258 }
6259 Expr::Match(_, scrutinee, arms) => {
6260 if let Some(found) = project_base_at_position(scrutinee, position) {
6261 return Some(found);
6262 }
6263 for (_pattern, arm) in arms {
6264 if let Some(found) = project_base_at_position(arm, position) {
6265 return Some(found);
6266 }
6267 }
6268 None
6269 }
6270 Expr::App(_, fun, arg) => {
6271 if let Some(found) = project_base_at_position(fun, position) {
6272 return Some(found);
6273 }
6274 project_base_at_position(arg, position)
6275 }
6276 Expr::Tuple(_, elems) | Expr::List(_, elems) => {
6277 for elem in elems {
6278 if let Some(found) = project_base_at_position(elem, position) {
6279 return Some(found);
6280 }
6281 }
6282 None
6283 }
6284 Expr::Dict(_, entries) => {
6285 for value in entries.values() {
6286 if let Some(found) = project_base_at_position(value, position) {
6287 return Some(found);
6288 }
6289 }
6290 None
6291 }
6292 Expr::Ite(_, cond, then_expr, else_expr) => {
6293 if let Some(found) = project_base_at_position(cond, position) {
6294 return Some(found);
6295 }
6296 if let Some(found) = project_base_at_position(then_expr, position) {
6297 return Some(found);
6298 }
6299 project_base_at_position(else_expr, position)
6300 }
6301 Expr::Ann(_, inner, _ann) => project_base_at_position(inner, position),
6302 _ => None,
6303 }
6304}
6305
6306fn fallback_field_map(tokens: &Tokens) -> HashMap<String, BTreeSet<String>> {
6307 let mut map = HashMap::new();
6308 let items = &tokens.items;
6309 let mut index = 0usize;
6310 while index + 2 < items.len() {
6311 if let Token::Ident(name, ..) = &items[index]
6312 && matches!(items[index + 1], Token::Assign(..) | Token::Colon(..))
6313 && matches!(items[index + 2], Token::BraceL(..))
6314 && let Some((fields, end_index)) = parse_record_fields(items, index + 2)
6315 {
6316 if !fields.is_empty() {
6317 map.insert(name.clone(), fields);
6318 }
6319 index = end_index + 1;
6320 continue;
6321 }
6322 index += 1;
6323 }
6324 map
6325}
6326
6327fn parse_record_fields(tokens: &[Token], start_index: usize) -> Option<(BTreeSet<String>, usize)> {
6328 if !matches!(tokens.get(start_index), Some(Token::BraceL(..))) {
6329 return None;
6330 }
6331
6332 let mut depth = 0usize;
6333 let mut fields = BTreeSet::new();
6334 let mut index = start_index;
6335 while index < tokens.len() {
6336 match &tokens[index] {
6337 Token::BraceL(..) => depth += 1,
6338 Token::BraceR(..) => {
6339 depth = depth.saturating_sub(1);
6340 if depth == 0 {
6341 return Some((fields, index));
6342 }
6343 }
6344 Token::Ident(name, ..) if depth == 1 => {
6345 if let Some(next) = tokens.get(index + 1)
6346 && matches!(next, Token::Assign(..) | Token::Colon(..))
6347 {
6348 fields.insert(name.clone());
6349 }
6350 }
6351 _ => {}
6352 }
6353 index += 1;
6354 }
6355
6356 None
6357}
6358
6359fn field_base_ident(text: &str, position: Position) -> Option<String> {
6360 let offset = offset_at(text, position)?;
6361 if offset == 0 {
6362 return None;
6363 }
6364
6365 let bytes = text.as_bytes();
6366 let mut index = offset.min(bytes.len());
6367
6368 while index > 0 && bytes[index - 1].is_ascii_whitespace() {
6369 index -= 1;
6370 }
6371 while index > 0 && is_word_byte(bytes[index - 1]) {
6372 index -= 1;
6373 }
6374 while index > 0 && bytes[index - 1].is_ascii_whitespace() {
6375 index -= 1;
6376 }
6377
6378 if index == 0 || bytes[index - 1] != b'.' {
6379 return None;
6380 }
6381
6382 index -= 1;
6383 while index > 0 && bytes[index - 1].is_ascii_whitespace() {
6384 index -= 1;
6385 }
6386
6387 let end = index;
6388 while index > 0 && is_word_byte(bytes[index - 1]) {
6389 index -= 1;
6390 }
6391
6392 if index == end {
6393 return None;
6394 }
6395
6396 Some(text[index..end].to_string())
6397}
6398
6399fn is_word_byte(byte: u8) -> bool {
6400 let ch = byte as char;
6401 ch.is_ascii_alphanumeric() || ch == '_'
6402}
6403
6404fn ident_token_at_position(tokens: &Tokens, position: Position) -> Option<(String, Span)> {
6405 for token in &tokens.items {
6406 let Token::Ident(name, span, ..) = token else {
6407 continue;
6408 };
6409 if range_touches_position(span_to_range(*span), position) {
6410 return Some((name.clone(), *span));
6411 }
6412 }
6413 None
6414}
6415
6416fn imported_projection_at_position(
6417 tokens: &Tokens,
6418 position: Position,
6419) -> Option<(String, String)> {
6420 fn is_trivia(token: &Token) -> bool {
6421 matches!(token, Token::CommentL(..) | Token::CommentR(..))
6422 }
6423
6424 fn prev_non_trivia(tokens: &Tokens, start: usize) -> Option<usize> {
6425 let mut idx = start;
6426 while idx > 0 {
6427 idx -= 1;
6428 if !is_trivia(&tokens.items[idx]) {
6429 return Some(idx);
6430 }
6431 }
6432 None
6433 }
6434
6435 let mut ident_index = None;
6436 let mut field = None;
6437 for (idx, token) in tokens.items.iter().enumerate() {
6438 let Token::Ident(name, span, ..) = token else {
6439 continue;
6440 };
6441 if range_touches_position(span_to_range(*span), position) {
6442 ident_index = Some(idx);
6443 field = Some(name.clone());
6444 break;
6445 }
6446 }
6447 let ident_index = ident_index?;
6448 let field = field?;
6449
6450 let dot_idx = prev_non_trivia(tokens, ident_index)?;
6451 if !matches!(tokens.items[dot_idx], Token::Dot(..)) {
6452 return None;
6453 }
6454 let base_idx = prev_non_trivia(tokens, dot_idx)?;
6455 let Token::Ident(base, ..) = &tokens.items[base_idx] else {
6456 return None;
6457 };
6458
6459 Some((base.clone(), field))
6460}
6461
6462struct DeclSpanIndex {
6463 type_defs: HashMap<String, Span>,
6464 ctor_defs: HashMap<String, Span>,
6465 class_defs: HashMap<String, Span>,
6466 fn_defs: HashMap<String, Span>,
6467 class_method_defs: HashMap<String, Span>,
6468 instance_method_defs: Vec<(Span, HashMap<String, Span>)>,
6469}
6470
6471fn index_decl_spans(compilation_unit: &CompilationUnit, tokens: &Tokens) -> DeclSpanIndex {
6472 fn span_contains_span(outer: Span, inner: Span) -> bool {
6473 position_leq(outer.begin, inner.begin) && position_leq(inner.end, outer.end)
6474 }
6475
6476 let mut type_defs = HashMap::new();
6477 let mut ctor_defs = HashMap::new();
6478 let mut class_defs = HashMap::new();
6479 let mut fn_defs = HashMap::new();
6480 let mut class_method_defs = HashMap::new();
6481 let mut instance_method_defs = Vec::new();
6482
6483 for decl in &compilation_unit.decls {
6484 match decl {
6485 Decl::Type(td) => {
6486 let decl_span = td.span;
6487 let mut expect_type_name = false;
6488 let mut expect_ctor_name = false;
6489
6490 for token in &tokens.items {
6491 let token_span = *token.span();
6492 if !span_contains_span(decl_span, token_span) {
6493 continue;
6494 }
6495
6496 match token {
6497 Token::Type(..) => {
6498 expect_type_name = true;
6499 expect_ctor_name = false;
6500 }
6501 Token::Ident(name, span, ..) if expect_type_name => {
6502 type_defs.insert(name.clone(), *span);
6503 expect_type_name = false;
6504 }
6505 Token::Assign(..) | Token::Pipe(..) => {
6506 expect_ctor_name = true;
6507 }
6508 Token::Ident(name, span, ..) if expect_ctor_name => {
6509 ctor_defs.insert(name.clone(), *span);
6510 expect_ctor_name = false;
6511 }
6512 _ => {}
6513 }
6514 }
6515 }
6516 Decl::Class(cd) => {
6517 let decl_span = cd.span;
6518 let mut expect_class_name = false;
6519 for i in 0..tokens.items.len() {
6520 let token = &tokens.items[i];
6521 let token_span = *token.span();
6522 if !span_contains_span(decl_span, token_span) {
6523 continue;
6524 }
6525 match token {
6526 Token::Class(..) => expect_class_name = true,
6527 Token::Ident(name, span, ..) if expect_class_name => {
6528 class_defs.insert(name.clone(), *span);
6529 expect_class_name = false;
6530 }
6531 Token::Ident(name, span, ..) => {
6532 if let Some(next) = tokens.items.get(i + 1)
6533 && matches!(next, Token::Colon(..))
6534 {
6535 class_method_defs.insert(name.clone(), *span);
6536 }
6537 }
6538 _ => {}
6539 }
6540 }
6541 }
6542 Decl::Instance(id) => {
6543 let decl_span = id.span;
6544 let mut methods = HashMap::new();
6545 for i in 0..tokens.items.len() {
6546 let token = &tokens.items[i];
6547 let token_span = *token.span();
6548 if !span_contains_span(decl_span, token_span) {
6549 continue;
6550 }
6551 if let Token::Ident(name, span, ..) = token
6552 && let Some(next) = tokens.items.get(i + 1)
6553 && matches!(next, Token::Assign(..))
6554 {
6555 methods.insert(name.clone(), *span);
6556 }
6557 }
6558 instance_method_defs.push((decl_span, methods));
6559 }
6560 Decl::Fn(fd) => {
6561 fn_defs.insert(fd.name.name.as_ref().to_string(), fd.name.span);
6562 }
6563 Decl::DeclareFn(fd) => {
6564 fn_defs.insert(fd.name.name.as_ref().to_string(), fd.name.span);
6565 }
6566 Decl::Import(..) => {}
6567 }
6568 }
6569
6570 DeclSpanIndex {
6571 type_defs,
6572 ctor_defs,
6573 class_defs,
6574 fn_defs,
6575 class_method_defs,
6576 instance_method_defs,
6577 }
6578}
6579
6580fn definition_span_for_value_ident(
6581 expr: &Expr,
6582 position: RexPosition,
6583 ident: &str,
6584 bindings: &mut Vec<(String, Span)>,
6585 tokens: &Tokens,
6586) -> Option<Span> {
6587 if !position_in_span(position, *expr.span()) {
6588 return None;
6589 }
6590
6591 fn lookup_binding(bindings: &[(String, Span)], ident: &str) -> Option<Span> {
6592 bindings
6593 .iter()
6594 .rev()
6595 .find_map(|(name, span)| (name == ident).then_some(*span))
6596 }
6597
6598 fn definition_in_pattern(
6599 pat: &Pattern,
6600 position: RexPosition,
6601 ident: &str,
6602 _tokens: &Tokens,
6603 ) -> Option<Span> {
6604 if !position_in_span(position, *pat.span()) {
6605 return None;
6606 }
6607
6608 match pat {
6609 Pattern::Var(var) => (var.name.as_ref() == ident).then_some(var.span),
6610 Pattern::Named(_span, _name, args) => args
6611 .iter()
6612 .find_map(|arg| definition_in_pattern(arg, position, ident, _tokens)),
6613 Pattern::Tuple(_span, elems) => elems
6614 .iter()
6615 .find_map(|elem| definition_in_pattern(elem, position, ident, _tokens)),
6616 Pattern::List(_span, elems) => elems
6617 .iter()
6618 .find_map(|elem| definition_in_pattern(elem, position, ident, _tokens)),
6619 Pattern::Cons(_span, head, tail) => {
6620 definition_in_pattern(head, position, ident, _tokens)
6621 .or_else(|| definition_in_pattern(tail, position, ident, _tokens))
6622 }
6623 Pattern::Dict(_span, fields) => fields
6624 .iter()
6625 .find_map(|(_, p)| definition_in_pattern(p, position, ident, _tokens)),
6626 Pattern::Wildcard(..) => None,
6627 }
6628 }
6629
6630 fn push_pattern_bindings(pat: &Pattern, bindings: &mut Vec<(String, Span)>, _tokens: &Tokens) {
6631 match pat {
6632 Pattern::Var(var) => bindings.push((var.name.to_string(), var.span)),
6633 Pattern::Named(_span, _name, args) => {
6634 for arg in args {
6635 push_pattern_bindings(arg, bindings, _tokens);
6636 }
6637 }
6638 Pattern::Tuple(_span, elems) => {
6639 for elem in elems {
6640 push_pattern_bindings(elem, bindings, _tokens);
6641 }
6642 }
6643 Pattern::List(_span, elems) => {
6644 for elem in elems {
6645 push_pattern_bindings(elem, bindings, _tokens);
6646 }
6647 }
6648 Pattern::Cons(_span, head, tail) => {
6649 push_pattern_bindings(head, bindings, _tokens);
6650 push_pattern_bindings(tail, bindings, _tokens);
6651 }
6652 Pattern::Dict(_span, fields) => {
6653 for (_key, pat) in fields {
6654 push_pattern_bindings(pat, bindings, _tokens);
6655 }
6656 }
6657 Pattern::Wildcard(..) => {}
6658 }
6659 }
6660
6661 match expr {
6662 Expr::Var(var) => {
6663 if position_in_span(position, var.span) && var.name.as_ref() == ident {
6664 return lookup_binding(bindings, ident);
6665 }
6666 None
6667 }
6668 Expr::Let(_span, var, _ann, def, body) => {
6669 if position_in_span(position, var.span) && var.name.as_ref() == ident {
6670 return Some(var.span);
6671 }
6672
6673 if position_in_span(position, *def.span()) {
6674 return definition_span_for_value_ident(def, position, ident, bindings, tokens);
6675 }
6676 if position_in_span(position, *body.span()) {
6677 bindings.push((var.name.to_string(), var.span));
6678 let out = definition_span_for_value_ident(body, position, ident, bindings, tokens);
6679 bindings.pop();
6680 return out;
6681 }
6682 None
6683 }
6684 Expr::LetRec(_span, rec_bindings, body) => {
6685 for (var, _ann, _def) in rec_bindings {
6686 if position_in_span(position, var.span) && var.name.as_ref() == ident {
6687 return Some(var.span);
6688 }
6689 }
6690
6691 let base_len = bindings.len();
6692 for (var, _ann, _def) in rec_bindings {
6693 bindings.push((var.name.to_string(), var.span));
6694 }
6695
6696 for (_var, _ann, def) in rec_bindings {
6697 if position_in_span(position, *def.span()) {
6698 let out =
6699 definition_span_for_value_ident(def, position, ident, bindings, tokens);
6700 bindings.truncate(base_len);
6701 return out;
6702 }
6703 }
6704 if position_in_span(position, *body.span()) {
6705 let out = definition_span_for_value_ident(body, position, ident, bindings, tokens);
6706 bindings.truncate(base_len);
6707 return out;
6708 }
6709
6710 bindings.truncate(base_len);
6711 None
6712 }
6713 Expr::Lam(_span, _scope, param, _ann, _constraints, body) => {
6714 if position_in_span(position, param.span) && param.name.as_ref() == ident {
6715 return Some(param.span);
6716 }
6717
6718 if position_in_span(position, *body.span()) {
6719 bindings.push((param.name.to_string(), param.span));
6720 let out = definition_span_for_value_ident(body, position, ident, bindings, tokens);
6721 bindings.pop();
6722 return out;
6723 }
6724 None
6725 }
6726 Expr::Match(_span, scrutinee, arms) => {
6727 if position_in_span(position, *scrutinee.span()) {
6728 return definition_span_for_value_ident(
6729 scrutinee, position, ident, bindings, tokens,
6730 );
6731 }
6732
6733 for (pat, arm) in arms {
6734 if position_in_span(position, *pat.span()) {
6735 return definition_in_pattern(pat, position, ident, tokens);
6736 }
6737
6738 if position_in_span(position, *arm.span()) {
6739 let base_len = bindings.len();
6740 push_pattern_bindings(pat, bindings, tokens);
6741 let out =
6742 definition_span_for_value_ident(arm, position, ident, bindings, tokens);
6743 bindings.truncate(base_len);
6744 return out;
6745 }
6746 }
6747 None
6748 }
6749 Expr::App(_span, fun, arg) => {
6750 if position_in_span(position, *fun.span()) {
6751 return definition_span_for_value_ident(fun, position, ident, bindings, tokens);
6752 }
6753 if position_in_span(position, *arg.span()) {
6754 return definition_span_for_value_ident(arg, position, ident, bindings, tokens);
6755 }
6756 None
6757 }
6758 Expr::Project(_span, base, _field) => {
6759 if position_in_span(position, *base.span()) {
6760 return definition_span_for_value_ident(base, position, ident, bindings, tokens);
6761 }
6762 None
6763 }
6764 Expr::Tuple(_span, elems) | Expr::List(_span, elems) => elems.iter().find_map(|elem| {
6765 position_in_span(position, *elem.span())
6766 .then(|| definition_span_for_value_ident(elem, position, ident, bindings, tokens))
6767 .flatten()
6768 }),
6769 Expr::Dict(_span, entries) => entries.values().find_map(|value| {
6770 position_in_span(position, *value.span())
6771 .then(|| definition_span_for_value_ident(value, position, ident, bindings, tokens))
6772 .flatten()
6773 }),
6774 Expr::Ite(_span, cond, then_expr, else_expr) => {
6775 if position_in_span(position, *cond.span()) {
6776 return definition_span_for_value_ident(cond, position, ident, bindings, tokens);
6777 }
6778 if position_in_span(position, *then_expr.span()) {
6779 return definition_span_for_value_ident(
6780 then_expr, position, ident, bindings, tokens,
6781 );
6782 }
6783 if position_in_span(position, *else_expr.span()) {
6784 return definition_span_for_value_ident(
6785 else_expr, position, ident, bindings, tokens,
6786 );
6787 }
6788 None
6789 }
6790 Expr::Ann(_span, inner, _ann) => {
6791 if position_in_span(position, *inner.span()) {
6792 return definition_span_for_value_ident(inner, position, ident, bindings, tokens);
6793 }
6794 None
6795 }
6796 _ => None,
6797 }
6798}
6799
6800fn pattern_vars(pattern: &Pattern) -> Vec<String> {
6804 let mut vars = Vec::new();
6805 collect_pattern_vars(pattern, &mut vars);
6806 vars
6807}
6808
6809fn collect_pattern_vars(pattern: &Pattern, vars: &mut Vec<String>) {
6810 match pattern {
6811 Pattern::Var(var) => vars.push(var.name.to_string()),
6812 Pattern::Named(_, _name, args) => {
6813 for arg in args {
6814 collect_pattern_vars(arg, vars);
6815 }
6816 }
6817 Pattern::Tuple(_, elems) => {
6818 for elem in elems {
6819 collect_pattern_vars(elem, vars);
6820 }
6821 }
6822 Pattern::List(_, elems) => {
6823 for elem in elems {
6824 collect_pattern_vars(elem, vars);
6825 }
6826 }
6827 Pattern::Cons(_, head, tail) => {
6828 collect_pattern_vars(head, vars);
6829 collect_pattern_vars(tail, vars);
6830 }
6831 Pattern::Dict(_, fields) => {
6832 for (_key, pat) in fields {
6833 collect_pattern_vars(pat, vars);
6834 }
6835 }
6836 Pattern::Wildcard(_) => {}
6837 }
6838}
6839
6840fn is_field_completion(text: &str, position: Position) -> bool {
6841 let offset = match offset_at(text, position) {
6842 Some(offset) => offset,
6843 None => return false,
6844 };
6845
6846 if offset == 0 {
6847 return false;
6848 }
6849
6850 let mut start = offset;
6851 while start > 0 {
6852 let prev = text.as_bytes()[start - 1] as char;
6853 if is_word_char(prev) {
6854 start -= 1;
6855 continue;
6856 }
6857 break;
6858 }
6859
6860 if start > 0 && text.as_bytes()[start - 1] as char == '.' {
6861 return true;
6862 }
6863
6864 text.as_bytes()[offset.saturating_sub(1)] as char == '.'
6865}
6866
6867fn lsp_to_rex_position(position: Position) -> RexPosition {
6868 RexPosition::new(position.line as usize + 1, position.character as usize + 1)
6869}
6870
6871fn position_in_span(position: RexPosition, span: Span) -> bool {
6872 position_leq(span.begin, position) && position_leq(position, span.end)
6873}
6874
6875fn position_leq(left: RexPosition, right: RexPosition) -> bool {
6876 left.line < right.line || (left.line == right.line && left.column <= right.column)
6877}
6878
6879pub(crate) fn word_at_position(text: &str, position: Position) -> Option<String> {
6880 let offset = offset_at(text, position)?;
6881 if offset >= text.len() {
6882 return None;
6883 }
6884
6885 let chars: Vec<(usize, char)> = text.char_indices().collect();
6886 let mut idx = None;
6887 for (i, (byte_index, _)) in chars.iter().enumerate() {
6888 if *byte_index == offset {
6889 idx = Some(i);
6890 break;
6891 }
6892 }
6893
6894 let idx = idx?;
6895 if !is_word_char(chars[idx].1) {
6896 return None;
6897 }
6898
6899 let mut start = idx;
6900 while start > 0 && is_word_char(chars[start - 1].1) {
6901 start -= 1;
6902 }
6903
6904 let mut end = idx + 1;
6905 while end < chars.len() && is_word_char(chars[end].1) {
6906 end += 1;
6907 }
6908
6909 let start_byte = chars[start].0;
6910 let end_byte = if end < chars.len() {
6911 chars[end].0
6912 } else {
6913 text.len()
6914 };
6915
6916 Some(text[start_byte..end_byte].to_string())
6917}
6918
6919fn offset_at(text: &str, position: Position) -> Option<usize> {
6920 let mut offset = 0usize;
6921 let mut current_line = 0u32;
6922
6923 for mut line in text.split('\n') {
6924 if line.ends_with('\r') {
6925 line = &line[..line.len().saturating_sub(1)];
6926 }
6927
6928 if current_line == position.line {
6929 let mut remaining = position.character as usize;
6930 for (byte_index, _) in line.char_indices() {
6931 if remaining == 0 {
6932 return Some(offset + byte_index);
6933 }
6934 remaining -= 1;
6935 }
6936 return Some(offset + line.len());
6937 }
6938
6939 offset += line.len() + 1;
6940 current_line += 1;
6941 }
6942
6943 if current_line == position.line {
6944 Some(offset)
6945 } else {
6946 None
6947 }
6948}
6949
6950fn position_at_offset(text: &str, target: usize) -> Position {
6951 let mut line = 0u32;
6952 let mut character = 0u32;
6953
6954 for (offset, ch) in text.char_indices() {
6955 if offset >= target {
6956 break;
6957 }
6958 if ch == '\n' {
6959 line += 1;
6960 character = 0;
6961 } else {
6962 character += 1;
6963 }
6964 }
6965
6966 Position { line, character }
6967}
6968
6969fn wildcard_match_arm_insert(text: &str, range: Range) -> Option<(Position, String)> {
6970 let end = offset_at(text, range.end)?;
6971 let search = &text[..end.min(text.len())];
6972 let close_offset = search.rfind('}')?;
6973 let line_start = text[..close_offset]
6974 .rfind('\n')
6975 .map(|idx| idx + 1)
6976 .unwrap_or(0);
6977 let closing_prefix = &text[line_start..close_offset];
6978
6979 if closing_prefix.chars().all(char::is_whitespace) {
6980 let arm_indent = format!("{closing_prefix} ");
6981 Some((
6982 position_at_offset(text, line_start),
6983 format!("{arm_indent}case _ -> null;\n"),
6984 ))
6985 } else {
6986 Some((
6987 position_at_offset(text, close_offset),
6988 " case _ -> null;".to_string(),
6989 ))
6990 }
6991}
6992
6993fn is_word_char(ch: char) -> bool {
6994 ch.is_ascii_alphanumeric() || ch == '_'
6995}
6996
6997pub fn in_memory_doc_uri() -> Url {
6998 match Url::parse("inmemory:///docs.rex") {
6999 Ok(url) => url,
7000 Err(_) => panic!("static in-memory URI must parse"),
7001 }
7002}
7003
7004pub fn diagnostics_for_source(source: &str) -> Vec<Diagnostic> {
7005 let uri = in_memory_doc_uri();
7006 clear_parse_cache(&uri);
7007 diagnostics_from_text(&uri, source)
7008}
7009
7010pub fn completion_for_source(source: &str, line: u32, character: u32) -> Vec<CompletionItem> {
7011 let uri = in_memory_doc_uri();
7012 clear_parse_cache(&uri);
7013 completion_items(&uri, source, Position { line, character })
7014}
7015
7016pub fn hover_for_source(source: &str, line: u32, character: u32) -> Option<Hover> {
7017 let uri = in_memory_doc_uri();
7018 clear_parse_cache(&uri);
7019 let position = Position { line, character };
7020 let contents = hover_type_contents(&uri, source, position).or_else(|| {
7021 let word = word_at_position(source, position)?;
7022 hover_contents(&word)
7023 })?;
7024 Some(Hover {
7025 contents,
7026 range: None,
7027 })
7028}
7029
7030pub fn expected_type_for_source_public(source: &str, line: u32, character: u32) -> Option<String> {
7031 let uri = in_memory_doc_uri();
7032 clear_parse_cache(&uri);
7033 expected_type_at_position(&uri, source, Position { line, character })
7034}
7035
7036pub fn functions_producing_expected_type_for_source_public(
7037 source: &str,
7038 line: u32,
7039 character: u32,
7040) -> Vec<String> {
7041 let uri = in_memory_doc_uri();
7042 clear_parse_cache(&uri);
7043 functions_producing_expected_type_at_position(&uri, source, Position { line, character })
7044 .into_iter()
7045 .map(|(name, typ)| format!("{name} : {typ}"))
7046 .collect()
7047}
7048
7049pub fn references_for_source_public(
7050 source: &str,
7051 line: u32,
7052 character: u32,
7053 include_declaration: bool,
7054) -> Vec<Location> {
7055 let uri = in_memory_doc_uri();
7056 clear_parse_cache(&uri);
7057 references_for_source(
7058 &uri,
7059 source,
7060 Position { line, character },
7061 include_declaration,
7062 )
7063}
7064
7065pub fn rename_for_source_public(
7066 source: &str,
7067 line: u32,
7068 character: u32,
7069 new_name: &str,
7070) -> Option<WorkspaceEdit> {
7071 let uri = in_memory_doc_uri();
7072 clear_parse_cache(&uri);
7073 rename_for_source(&uri, source, Position { line, character }, new_name)
7074}
7075
7076pub fn document_symbols_for_source_public(source: &str) -> Vec<DocumentSymbol> {
7077 let uri = in_memory_doc_uri();
7078 clear_parse_cache(&uri);
7079 document_symbols_for_source(&uri, source)
7080}
7081
7082pub fn format_for_source_public(source: &str) -> Option<Vec<TextEdit>> {
7083 format_edits_for_source(source)
7084}
7085
7086pub fn code_actions_for_source_public(
7087 source: &str,
7088 line: u32,
7089 character: u32,
7090) -> Vec<CodeActionOrCommand> {
7091 let uri = in_memory_doc_uri();
7092 clear_parse_cache(&uri);
7093 let position = Position { line, character };
7094 let range = Range {
7095 start: position,
7096 end: position,
7097 };
7098 let diagnostics: Vec<Diagnostic> = diagnostics_from_text(&uri, source)
7099 .into_iter()
7100 .filter(|diag| {
7101 range_contains_position(diag.range, position)
7102 || range_touches_position(diag.range, position)
7103 })
7104 .collect();
7105 code_actions_for_source(&uri, source, range, &diagnostics)
7106}
7107
7108pub fn goto_definition_for_source(source: &str, line: u32, character: u32) -> Option<Location> {
7109 let uri = in_memory_doc_uri();
7110 clear_parse_cache(&uri);
7111 let pos = Position { line, character };
7112 let response = goto_definition_response(&uri, source, pos)?;
7113 match response {
7114 GotoDefinitionResponse::Scalar(location) => Some(location),
7115 GotoDefinitionResponse::Array(locations) => locations.into_iter().next(),
7116 GotoDefinitionResponse::Link(links) => links.into_iter().next().map(|link| Location {
7117 uri: link.target_uri,
7118 range: link.target_range,
7119 }),
7120 }
7121}