@@ -8,6 +8,60 @@ open FScript.Language
88module LspSymbols =
99 open LspModel
1010
11+ let private collectMapKeyDomainVars ( t : Type ) =
12+ let rec collect acc ty =
13+ match ty with
14+ | TMap ( TVar v, valueType) ->
15+ collect ( Set.add v acc) valueType
16+ | TMap ( keyType, valueType) ->
17+ collect ( collect acc keyType) valueType
18+ | TList inner
19+ | TOption inner -> collect acc inner
20+ | TTuple items ->
21+ items |> List.fold collect acc
22+ | TRecord fields ->
23+ fields |> Map.values |> Seq.fold collect acc
24+ | TFun ( a, b) ->
25+ collect ( collect acc a) b
26+ | _ -> acc
27+
28+ collect Set.empty t
29+
30+ let private lspTypeToStringWithKeyDomainVars ( keyDomainVars : Set < int >) ( t : Type ) =
31+ let rec go t =
32+ match t with
33+ | TUnit -> " unit"
34+ | TInt -> " int"
35+ | TFloat -> " float"
36+ | TBool -> " bool"
37+ | TString -> " string"
38+ | TList t1 -> sprintf " %s list" ( postfixArg t1)
39+ | TTuple ts -> ts |> List.map go |> String.concat " * " |> sprintf " (%s )"
40+ | TRecord fields ->
41+ fields
42+ |> Map.toList
43+ |> List.map ( fun ( name , fieldType ) -> sprintf " %s : %s " name ( go fieldType))
44+ |> String.concat " ; "
45+ |> sprintf " { %s }"
46+ | TMap (_, tv) ->
47+ sprintf " %s map" ( postfixArg tv)
48+ | TOption t1 -> sprintf " %s option" ( postfixArg t1)
49+ | TFun ( a, b) -> sprintf " (%s -> %s )" ( go a) ( go b)
50+ | TNamed n -> n
51+ | TUnion ( name, _) -> name
52+ | TTypeToken -> " type"
53+ | TVar v when Set.contains v keyDomainVars -> " int|string"
54+ | TVar _ -> " unknown"
55+ and postfixArg t =
56+ match t with
57+ | TFun _ | TTuple _ | TRecord _ -> sprintf " (%s )" ( go t)
58+ | _ -> go t
59+ go t
60+
61+ let private lspTypeToString ( t : Type ) =
62+ let keyDomainVars = collectMapKeyDomainVars t
63+ lspTypeToStringWithKeyDomainVars keyDomainVars t
64+
1165 let rec private typeRefToString ( typeRef : TypeRef ) =
1266 match typeRef with
1367 | TRName name -> name
@@ -91,15 +145,15 @@ module LspSymbols =
91145 | true , TypeInfer.TSLet(_, _, t, _, _, _) ->
92146 { Name = name
93147 Kind = symbolKindForType t
94- TypeText = Some ( Types.typeToString t)
148+ TypeText = Some ( lspTypeToString t)
95149 TypeTargetName = typeTargetFromType t
96150 Span = span }
97151 | true , TypeInfer.TSLetRecGroup( bindings, _, _) ->
98152 match bindings |> List.tryFind ( fun ( n , _ , _ , _ ) -> n = name) with
99153 | Some (_, _, t, _) ->
100154 { Name = name
101155 Kind = symbolKindForType t
102- TypeText = Some ( Types.typeToString t)
156+ TypeText = Some ( lspTypeToString t)
103157 TypeTargetName = typeTargetFromType t
104158 Span = span }
105159 | None ->
@@ -419,13 +473,14 @@ module LspSymbols =
419473 match typedByName.TryGetValue( name) with
420474 | true , t when not parameters.IsEmpty ->
421475 let argTypes = takeParamTypes t parameters.Length
476+ let keyDomainVars = collectMapKeyDomainVars t
422477 ( parameters, argTypes)
423478 ||> List.zip
424479 |> List.choose ( fun ( param , argType ) ->
425480 if param.Annotation.IsSome then
426481 None
427482 else
428- Some ( param.Span, $" : {Types.typeToString argType}" ))
483+ Some ( param.Span, $" : {lspTypeToStringWithKeyDomainVars keyDomainVars argType}" ))
429484 | _ ->
430485 []
431486
@@ -445,6 +500,134 @@ module LspSymbols =
445500 | _ ->
446501 [])
447502
503+ let private collectPatternVariableSpans ( program : Program ) =
504+ let rec collectPattern ( acc : ( string * Span ) list ) ( pattern : Pattern ) =
505+ match pattern with
506+ | PVar ( name, span) ->
507+ ( name, span) :: acc
508+ | PCons ( head, tail, _) ->
509+ collectPattern ( collectPattern acc head) tail
510+ | PTuple ( items, _) ->
511+ items |> List.fold collectPattern acc
512+ | PRecord ( fields, _) ->
513+ fields |> List.fold ( fun state ( _ , p ) -> collectPattern state p) acc
514+ | PMap ( clauses, tailOpt, _) ->
515+ let withClauses =
516+ clauses
517+ |> List.fold ( fun state ( k , v ) ->
518+ let withKey = collectPattern state k
519+ collectPattern withKey v) acc
520+ match tailOpt with
521+ | Some tail -> collectPattern withClauses tail
522+ | None -> withClauses
523+ | PSome ( inner, _) ->
524+ collectPattern acc inner
525+ | PUnionCase (_, _, payload, _) ->
526+ match payload with
527+ | Some p -> collectPattern acc p
528+ | None -> acc
529+ | PWildcard _
530+ | PLiteral _
531+ | PNil _
532+ | PNone _
533+ | PTypeRef _ -> acc
534+
535+ let rec collectExpr ( acc : ( string * Span ) list ) ( expr : Expr ) =
536+ match expr with
537+ | EMatch ( scrutinee, cases, _) ->
538+ let withScrutinee = collectExpr acc scrutinee
539+ cases
540+ |> List.fold ( fun state ( pat , guard , body , _ ) ->
541+ let withPattern = collectPattern state pat
542+ let withGuard =
543+ match guard with
544+ | Some g -> collectExpr withPattern g
545+ | None -> withPattern
546+ collectExpr withGuard body) withScrutinee
547+ | ELambda (_, body, _) ->
548+ collectExpr acc body
549+ | EApply ( f, a, _) ->
550+ collectExpr ( collectExpr acc f) a
551+ | EIf ( c, t, f, _) ->
552+ collectExpr ( collectExpr ( collectExpr acc c) t) f
553+ | ERaise ( inner, _) ->
554+ collectExpr acc inner
555+ | EFor (_, source, body, _) ->
556+ collectExpr ( collectExpr acc source) body
557+ | ELet (_, value, body, _, _) ->
558+ collectExpr ( collectExpr acc value) body
559+ | ELetRecGroup ( bindings, body, _) ->
560+ let withBindings =
561+ bindings |> List.fold ( fun state ( _ , _ , value , _ ) -> collectExpr state value) acc
562+ collectExpr withBindings body
563+ | EList ( items, _)
564+ | ETuple ( items, _) ->
565+ items |> List.fold collectExpr acc
566+ | ERange ( startExpr, endExpr, _) ->
567+ collectExpr ( collectExpr acc startExpr) endExpr
568+ | ERecord ( fields, _)
569+ | EStructuralRecord ( fields, _) ->
570+ fields |> List.fold ( fun state ( _ , value ) -> collectExpr state value) acc
571+ | EMap ( entries, _) ->
572+ entries
573+ |> List.fold ( fun state entry ->
574+ match entry with
575+ | MEKeyValue ( k, v) ->
576+ collectExpr ( collectExpr state k) v
577+ | MESpread spread ->
578+ collectExpr state spread) acc
579+ | ERecordUpdate ( baseExpr, fields, _)
580+ | EStructuralRecordUpdate ( baseExpr, fields, _) ->
581+ let withBase = collectExpr acc baseExpr
582+ fields |> List.fold ( fun state ( _ , value ) -> collectExpr state value) withBase
583+ | EFieldGet ( target, _, _) ->
584+ collectExpr acc target
585+ | EIndexGet ( target, key, _)
586+ | ECons ( target, key, _)
587+ | EAppend ( target, key, _)
588+ | EBinOp (_, target, key, _) ->
589+ collectExpr ( collectExpr acc target) key
590+ | ESome ( inner, _)
591+ | EParen ( inner, _) ->
592+ collectExpr acc inner
593+ | EInterpolatedString ( parts, _) ->
594+ parts
595+ |> List.fold ( fun state part ->
596+ match part with
597+ | IPText _ -> state
598+ | IPExpr embedded -> collectExpr state embedded) acc
599+ | EUnit _
600+ | ELiteral _
601+ | EVar _
602+ | ENone _
603+ | ETypeOf _
604+ | ENameOf _ -> acc
605+
606+ program
607+ |> List.fold ( fun state stmt ->
608+ match stmt with
609+ | SLet (_, _, expr, _, _, _) ->
610+ collectExpr state expr
611+ | SLetRecGroup ( bindings, _, _) ->
612+ bindings |> List.fold ( fun inner ( _ , _ , expr , _ ) -> collectExpr inner expr) state
613+ | SExpr expr ->
614+ collectExpr state expr
615+ | _ -> state) []
616+ |> List.rev
617+
618+ let private buildPatternTypeHints ( program : Program ) ( localTypes : TypeInfer.LocalVariableTypeInfo list ) =
619+ let localByNameAndSpan =
620+ localTypes
621+ |> List.map ( fun entry -> ( entry.Name, entry.Span.Start.Line, entry.Span.Start.Column, entry.Span.End.Line, entry.Span.End.Column), entry.Type)
622+ |> Map.ofList
623+
624+ collectPatternVariableSpans program
625+ |> List.choose ( fun ( name , span ) ->
626+ let key = ( name, span.Start.Line, span.Start.Column, span.End.Line, span.End.Column)
627+ localByNameAndSpan
628+ |> Map.tryFind key
629+ |> Option.map ( fun t -> span, $" : {lspTypeToString t}" ))
630+
448631 let analyzeDocument ( uri : string ) ( text : string ) =
449632 let sourceName =
450633 if uri.StartsWith( " file://" , StringComparison.OrdinalIgnoreCase) then
@@ -459,6 +642,7 @@ module LspSymbols =
459642 let mutable parameterTypeTargets : Map < string , string > = Map.empty
460643 let mutable functionParameters : Map < string , string list > = Map.empty
461644 let mutable parameterTypeHints : ( Span * string ) list = []
645+ let mutable patternTypeHints : ( Span * string ) list = []
462646 let mutable localVariableTypeHints : ( Span * string * string ) list = []
463647
464648 let mutable parsedProgram : Program option = None
@@ -473,23 +657,26 @@ module LspSymbols =
473657 if isIncludeProgram program then
474658 symbols <- buildSymbolsFromProgram program None
475659 parameterTypeHints <- buildParameterTypeHints program None
660+ patternTypeHints <- []
476661 else
477662 try
478663 let typed , localTypes = TypeInfer.inferProgramWithLocalVariableTypes program
479664 symbols <- buildSymbolsFromProgram program ( Some typed)
480665 parameterTypeHints <- buildParameterTypeHints program ( Some typed)
666+ patternTypeHints <- buildPatternTypeHints program localTypes
481667 localVariableTypeHints <-
482668 localTypes
483669 |> List.filter ( fun entry ->
484670 match entry.Span.Start.File with
485671 | Some file -> String.Equals( file, sourceName, StringComparison.OrdinalIgnoreCase)
486672 | None -> true )
487- |> List.map ( fun entry -> entry.Span, entry.Name, Types.typeToString entry.Type)
673+ |> List.map ( fun entry -> entry.Span, entry.Name, lspTypeToString entry.Type)
488674 with
489675 | TypeException err ->
490676 diagnostics.Add( diagnostic 1 " type" err.Span err.Message)
491677 symbols <- buildSymbolsFromProgram program None
492678 parameterTypeHints <- buildParameterTypeHints program None
679+ patternTypeHints <- []
493680
494681 with
495682 | ParseException err ->
@@ -512,7 +699,7 @@ module LspSymbols =
512699 diagnostics.Add( diagnostic 2 " unused" span $" Unused top-level binding '{name}'" )
513700 | None -> ()
514701
515- documents[ uri] <- { Text = text; Symbols = symbols; RecordParameterFields = recordParamFields; ParameterTypeTargets = parameterTypeTargets; FunctionParameters = functionParameters; ParameterTypeHints = parameterTypeHints; LocalVariableTypeHints = localVariableTypeHints; VariableOccurrences = occurrences }
702+ documents[ uri] <- { Text = text; Symbols = symbols; RecordParameterFields = recordParamFields; ParameterTypeTargets = parameterTypeTargets; FunctionParameters = functionParameters; ParameterTypeHints = parameterTypeHints; PatternTypeHints = patternTypeHints ; LocalVariableTypeHints = localVariableTypeHints; VariableOccurrences = occurrences }
516703 publishDiagnostics uri ( diagnostics |> Seq.toList)
517704
518705 let tryResolveSymbol ( doc : DocumentState ) ( line : int ) ( character : int ) : TopLevelSymbol option =
0 commit comments