@@ -3,6 +3,7 @@ namespace FScript.LanguageServer
33open System
44open System.Collections .Generic
55open System.IO
6+ open System.Text .RegularExpressions
67open System.Text .Json .Nodes
78open FScript.Language
89open FScript.CSharpInterop
@@ -146,6 +147,71 @@ module LspSymbols =
146147 |> String.concat " ; "
147148 |> sprintf " {| %s |}"
148149
150+ let private tryBuildImportAliasMaps ( sourcePath : string ) ( text : string ) : Map < string , string > * Map < string , string > =
151+ try
152+ let parsed = Parser.parseProgramWithSourceName ( Some sourcePath) text
153+ let imports =
154+ parsed
155+ |> List.takeWhile ( function | SImport _ -> true | _ -> false )
156+ |> List.choose ( function | SImport( alias, importPath, _) -> Some ( alias, importPath) | _ -> None)
157+
158+ let sourceDirectory =
159+ try
160+ match Path.GetDirectoryName( sourcePath) with
161+ | null
162+ | " " -> Directory.GetCurrentDirectory()
163+ | dir -> dir
164+ with _ ->
165+ Directory.GetCurrentDirectory()
166+
167+ let pathToPrefix = Dictionary< string, string>( StringComparer.OrdinalIgnoreCase)
168+ let mutable prefixCounter = 0
169+ let mutable aliasToInternal = Map.empty< string, string>
170+ let mutable internalToAlias = Map.empty< string, string>
171+
172+ for ( alias, importPath) in imports do
173+ let resolvedPath =
174+ if Path.IsPathRooted( importPath) then
175+ Path.GetFullPath( importPath)
176+ else
177+ Path.GetFullPath( Path.Combine( sourceDirectory, importPath))
178+
179+ let internalPrefix =
180+ match pathToPrefix.TryGetValue( resolvedPath) with
181+ | true , existing -> existing
182+ | false , _ ->
183+ prefixCounter <- prefixCounter + 1
184+ let created = $" __imp{prefixCounter}"
185+ pathToPrefix[ resolvedPath] <- created
186+ created
187+
188+ aliasToInternal <- aliasToInternal |> Map.add alias internalPrefix
189+ if not ( internalToAlias.ContainsKey( internalPrefix)) then
190+ internalToAlias <- internalToAlias |> Map.add internalPrefix alias
191+
192+ aliasToInternal, internalToAlias
193+ with _ ->
194+ Map.empty, Map.empty
195+
196+ let private mapInternalQualifiedName ( internalToAlias : Map < string , string >) ( name : string ) =
197+ if String.IsNullOrWhiteSpace( name) then name
198+ else
199+ match name.Split( '.' ) |> Array.toList with
200+ | head :: tail when internalToAlias.ContainsKey( head) ->
201+ let alias = internalToAlias[ head]
202+ match tail with
203+ | [] -> alias
204+ | _ -> alias + " ." + ( String.concat " ." tail)
205+ | _ -> name
206+
207+ let private mapTypeTextForDisplay ( internalToAlias : Map < string , string >) ( typeText : string ) =
208+ if Map.isEmpty internalToAlias then typeText
209+ else
210+ internalToAlias
211+ |> Map.fold ( fun current internalPrefix alias ->
212+ let pattern = @" \b" + Regex.Escape( internalPrefix) + @" \b"
213+ Regex.Replace( current, pattern, alias)) typeText
214+
149215 let private canonicalRecordSignatureFromFields ( fields : ( string * string ) list ) =
150216 fields
151217 |> List.sortBy fst
@@ -1395,6 +1461,11 @@ module LspSymbols =
13951461 Uri( uri) .LocalPath
13961462 else
13971463 uri
1464+ let importAliasToInternal , importInternalToAlias =
1465+ if uri.StartsWith( " file://" , StringComparison.OrdinalIgnoreCase) then
1466+ tryBuildImportAliasMaps sourceName text
1467+ else
1468+ Map.empty, Map.empty
13981469 let runtimeExterns = LspRuntimeExterns.forSourcePath sourceName
13991470
14001471 let diagnostics = ResizeArray< JsonNode>()
@@ -1511,6 +1582,39 @@ module LspSymbols =
15111582 | ParseException err ->
15121583 diagnostics.Add( diagnostic 1 " parse" err.Span err.Message)
15131584
1585+ let displayTypeText ( typeText : string ) = mapTypeTextForDisplay importInternalToAlias typeText
1586+
1587+ let symbols =
1588+ symbols
1589+ |> List.map ( fun sym ->
1590+ { sym with
1591+ TypeText = sym.TypeText |> Option.map displayTypeText })
1592+
1593+ let recordParamFields =
1594+ recordParamFields
1595+ |> Map.map ( fun _ fields ->
1596+ fields |> List.map ( fun ( fieldName , fieldType ) -> fieldName, displayTypeText fieldType))
1597+
1598+ let functionAnnotationTypes =
1599+ functionAnnotationTypes
1600+ |> Map.map ( fun _ typeTexts -> typeTexts |> List.map displayTypeText)
1601+
1602+ let parameterTypeHints =
1603+ parameterTypeHints
1604+ |> List.map ( fun ( span , label ) -> span, displayTypeText label)
1605+
1606+ let functionReturnTypeHints =
1607+ functionReturnTypeHints
1608+ |> List.map ( fun ( span , label ) -> span, displayTypeText label)
1609+
1610+ let patternTypeHints =
1611+ patternTypeHints
1612+ |> List.map ( fun ( span , label ) -> span, displayTypeText label)
1613+
1614+ let localVariableTypeHints =
1615+ localVariableTypeHints
1616+ |> List.map ( fun ( span , name , typeText ) -> span, name, displayTypeText typeText)
1617+
15141618 documents[ uri] <-
15151619 { SourcePath = sourceName
15161620 Text = text
@@ -1529,6 +1633,8 @@ module LspSymbols =
15291633 InjectedFunctionSignatures = injectedFunctionSignatures
15301634 InjectedFunctionParameterNames = injectedFunctionParameterNames
15311635 InjectedFunctionDefinitions = injectedFunctionDefinitions
1636+ ImportAliasToInternal = importAliasToInternal
1637+ ImportInternalToAlias = importInternalToAlias
15321638 VariableOccurrences = occurrences }
15331639 publishDiagnostics uri ( diagnostics |> Seq.toList)
15341640
@@ -1853,7 +1959,12 @@ module LspSymbols =
18531959 let idx = word.LastIndexOf( '.' )
18541960 if idx > 0 then
18551961 let qualifier = word.Substring( 0 , idx)
1856- tryResolveTypeNameForQualifier doc qualifier
1962+ let memberName = word.Substring( idx + 1 )
1963+ match doc.ImportAliasToInternal |> Map.tryFind qualifier with
1964+ | Some internalPrefix when not ( String.IsNullOrWhiteSpace( memberName)) ->
1965+ Some $" {internalPrefix}.{memberName}"
1966+ | _ ->
1967+ tryResolveTypeNameForQualifier doc qualifier
18571968 else
18581969 None
18591970 | Some word ->
0 commit comments