From e547fc3481b2bbbba31e7279a7ac3fdc2e3b7172 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 6 Feb 2024 15:13:28 +0100 Subject: [PATCH 01/60] LSP extension prototype --- VisualFSharp.sln | 42 +++ .../Executable.fs | 21 ++ .../FSharp.Compiler.LanguageServer.fsproj | 40 +++ .../FSharpLanguageServer.fs | 250 ++++++++++++++++++ .../FSharpWorkspace.fs | 12 + src/FSharp.Compiler.LanguageServer/Utils.fs | 25 ++ .../.vsextension/string-resources.json | 4 + .../ExtensionEntrypoint.cs | 34 +++ .../FSharp.VisualStudio.Extension.csproj | 29 ++ .../FSharpLanguageServerProvider.cs | 123 +++++++++ .../FSharp.Compiler.ComponentTests.fsproj | 3 +- ...Sharp.Compiler.LanguageServer.Tests.fsproj | 55 ++++ .../LanguageServerTests.fs | 63 +++++ .../Program.fs | 1 + 14 files changed, 700 insertions(+), 2 deletions(-) create mode 100644 src/FSharp.Compiler.LanguageServer/Executable.fs create mode 100644 src/FSharp.Compiler.LanguageServer/FSharp.Compiler.LanguageServer.fsproj create mode 100644 src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs create mode 100644 src/FSharp.Compiler.LanguageServer/FSharpWorkspace.fs create mode 100644 src/FSharp.Compiler.LanguageServer/Utils.fs create mode 100644 src/FSharp.VisualStudio.Extension/.vsextension/string-resources.json create mode 100644 src/FSharp.VisualStudio.Extension/ExtensionEntrypoint.cs create mode 100644 src/FSharp.VisualStudio.Extension/FSharp.VisualStudio.Extension.csproj create mode 100644 src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs create mode 100644 tests/FSharp.Compiler.LanguageServer.Tests/FSharp.Compiler.LanguageServer.Tests.fsproj create mode 100644 tests/FSharp.Compiler.LanguageServer.Tests/LanguageServerTests.fs create mode 100644 tests/FSharp.Compiler.LanguageServer.Tests/Program.fs diff --git a/VisualFSharp.sln b/VisualFSharp.sln index 486cc03a163..cafe67b60c9 100644 --- a/VisualFSharp.sln +++ b/VisualFSharp.sln @@ -194,6 +194,11 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FSharp.Editor.IntegrationTests", "vsintegration\tests\FSharp.Editor.IntegrationTests\FSharp.Editor.IntegrationTests.csproj", "{E31F9B59-FCF1-4D04-8762-C7BB60285A7B}" EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Benchmarks.Common", "tests\benchmarks\FSharp.Benchmarks.Common\FSharp.Benchmarks.Common.fsproj", "{6734FC6F-B5F3-45E1-9A72-720378BB49C9}" +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Compiler.LanguageServer", "src\FSharp.Compiler.LanguageServer\FSharp.Compiler.LanguageServer.fsproj", "{D72F6593-DB2D-47AC-8E15-8DCE8527972E}" +EndProject +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Compiler.LanguageServer.Tests", "tests\FSharp.Compiler.LanguageServer.Tests\FSharp.Compiler.LanguageServer.Tests.fsproj", "{1E83A6C8-FA4D-42BD-B4A5-B7F9AAD1B388}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FSharp.VisualStudio.Extension", "src\FSharp.VisualStudio.Extension\FSharp.VisualStudio.Extension.csproj", "{E1013576-6257-47BA-AAFB-F95B68DAB1FF}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -1033,6 +1038,42 @@ Global {6734FC6F-B5F3-45E1-9A72-720378BB49C9}.Release|Any CPU.Build.0 = Release|Any CPU {6734FC6F-B5F3-45E1-9A72-720378BB49C9}.Release|x86.ActiveCfg = Release|Any CPU {6734FC6F-B5F3-45E1-9A72-720378BB49C9}.Release|x86.Build.0 = Release|Any CPU + {D72F6593-DB2D-47AC-8E15-8DCE8527972E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D72F6593-DB2D-47AC-8E15-8DCE8527972E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D72F6593-DB2D-47AC-8E15-8DCE8527972E}.Debug|x86.ActiveCfg = Debug|Any CPU + {D72F6593-DB2D-47AC-8E15-8DCE8527972E}.Debug|x86.Build.0 = Debug|Any CPU + {D72F6593-DB2D-47AC-8E15-8DCE8527972E}.Proto|Any CPU.ActiveCfg = Debug|Any CPU + {D72F6593-DB2D-47AC-8E15-8DCE8527972E}.Proto|Any CPU.Build.0 = Debug|Any CPU + {D72F6593-DB2D-47AC-8E15-8DCE8527972E}.Proto|x86.ActiveCfg = Debug|Any CPU + {D72F6593-DB2D-47AC-8E15-8DCE8527972E}.Proto|x86.Build.0 = Debug|Any CPU + {D72F6593-DB2D-47AC-8E15-8DCE8527972E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D72F6593-DB2D-47AC-8E15-8DCE8527972E}.Release|Any CPU.Build.0 = Release|Any CPU + {D72F6593-DB2D-47AC-8E15-8DCE8527972E}.Release|x86.ActiveCfg = Release|Any CPU + {D72F6593-DB2D-47AC-8E15-8DCE8527972E}.Release|x86.Build.0 = Release|Any CPU + {1E83A6C8-FA4D-42BD-B4A5-B7F9AAD1B388}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1E83A6C8-FA4D-42BD-B4A5-B7F9AAD1B388}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1E83A6C8-FA4D-42BD-B4A5-B7F9AAD1B388}.Debug|x86.ActiveCfg = Debug|Any CPU + {1E83A6C8-FA4D-42BD-B4A5-B7F9AAD1B388}.Debug|x86.Build.0 = Debug|Any CPU + {1E83A6C8-FA4D-42BD-B4A5-B7F9AAD1B388}.Proto|Any CPU.ActiveCfg = Debug|Any CPU + {1E83A6C8-FA4D-42BD-B4A5-B7F9AAD1B388}.Proto|Any CPU.Build.0 = Debug|Any CPU + {1E83A6C8-FA4D-42BD-B4A5-B7F9AAD1B388}.Proto|x86.ActiveCfg = Debug|Any CPU + {1E83A6C8-FA4D-42BD-B4A5-B7F9AAD1B388}.Proto|x86.Build.0 = Debug|Any CPU + {1E83A6C8-FA4D-42BD-B4A5-B7F9AAD1B388}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1E83A6C8-FA4D-42BD-B4A5-B7F9AAD1B388}.Release|Any CPU.Build.0 = Release|Any CPU + {1E83A6C8-FA4D-42BD-B4A5-B7F9AAD1B388}.Release|x86.ActiveCfg = Release|Any CPU + {1E83A6C8-FA4D-42BD-B4A5-B7F9AAD1B388}.Release|x86.Build.0 = Release|Any CPU + {E1013576-6257-47BA-AAFB-F95B68DAB1FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E1013576-6257-47BA-AAFB-F95B68DAB1FF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E1013576-6257-47BA-AAFB-F95B68DAB1FF}.Debug|x86.ActiveCfg = Debug|Any CPU + {E1013576-6257-47BA-AAFB-F95B68DAB1FF}.Debug|x86.Build.0 = Debug|Any CPU + {E1013576-6257-47BA-AAFB-F95B68DAB1FF}.Proto|Any CPU.ActiveCfg = Debug|Any CPU + {E1013576-6257-47BA-AAFB-F95B68DAB1FF}.Proto|Any CPU.Build.0 = Debug|Any CPU + {E1013576-6257-47BA-AAFB-F95B68DAB1FF}.Proto|x86.ActiveCfg = Debug|Any CPU + {E1013576-6257-47BA-AAFB-F95B68DAB1FF}.Proto|x86.Build.0 = Debug|Any CPU + {E1013576-6257-47BA-AAFB-F95B68DAB1FF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E1013576-6257-47BA-AAFB-F95B68DAB1FF}.Release|Any CPU.Build.0 = Release|Any CPU + {E1013576-6257-47BA-AAFB-F95B68DAB1FF}.Release|x86.ActiveCfg = Release|Any CPU + {E1013576-6257-47BA-AAFB-F95B68DAB1FF}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1114,6 +1155,7 @@ Global {CBC96CC7-65AB-46EA-A82E-F6A788DABF80} = {F7876C9B-FB6A-4EFB-B058-D6967DB75FB2} {E31F9B59-FCF1-4D04-8762-C7BB60285A7B} = {F7876C9B-FB6A-4EFB-B058-D6967DB75FB2} {6734FC6F-B5F3-45E1-9A72-720378BB49C9} = {DFB6ADD7-3149-43D9-AFA0-FC4A818B472B} + {1E83A6C8-FA4D-42BD-B4A5-B7F9AAD1B388} = {CFE3259A-2D30-4EB0-80D5-E8B5F3D01449} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {48EDBBBE-C8EE-4E3C-8B19-97184A487B37} diff --git a/src/FSharp.Compiler.LanguageServer/Executable.fs b/src/FSharp.Compiler.LanguageServer/Executable.fs new file mode 100644 index 00000000000..9e9034e0275 --- /dev/null +++ b/src/FSharp.Compiler.LanguageServer/Executable.fs @@ -0,0 +1,21 @@ +module FSharp.Compiler.LanguageServer.Executable + +open System +open StreamJsonRpc + +[] +let main argv = + + let jsonRpc = new JsonRpc(Console.OpenStandardOutput(), Console.OpenStandardInput()) + + let s = new FSharpLanguageServer(jsonRpc, (LspLogger Console.Out.Write)) + + jsonRpc.StartListening() + + async { + while true do + do! Async.Sleep 1000 + } + |> Async.RunSynchronously + + 0 diff --git a/src/FSharp.Compiler.LanguageServer/FSharp.Compiler.LanguageServer.fsproj b/src/FSharp.Compiler.LanguageServer/FSharp.Compiler.LanguageServer.fsproj new file mode 100644 index 00000000000..471c6ee6b8c --- /dev/null +++ b/src/FSharp.Compiler.LanguageServer/FSharp.Compiler.LanguageServer.fsproj @@ -0,0 +1,40 @@ + + + + Exe + net8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs new file mode 100644 index 00000000000..28e78b827af --- /dev/null +++ b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs @@ -0,0 +1,250 @@ +namespace FSharp.Compiler.LanguageServer + +open System +open System.Threading.Tasks +open System.Threading +open Microsoft.CommonLanguageServerProtocol.Framework.Handlers +open Microsoft.CommonLanguageServerProtocol.Framework +open Microsoft.Extensions.DependencyInjection +open Microsoft.VisualStudio.LanguageServer.Protocol + +open StreamJsonRpc +open Nerdbank.Streams +open System.Diagnostics +open System.IO +open Workspace + +[] +module Stuff = + [] + let FSharpLanguageName = "F#" + +type FSharpRequestContext(lspServices: ILspServices, logger: ILspLogger, workspace: FSharpWorkspace) = + member _.LspServices = lspServices + member _.Logger = logger + member _.Workspace = workspace + +type ContextHolder(intialWorkspace, lspServices: ILspServices) = + + let logger = lspServices.GetRequiredService() + + let mutable context = FSharpRequestContext(lspServices, logger, intialWorkspace) + + member _.GetContext() = context + + member _.UpdateWorkspace(f) = + context <- FSharpRequestContext(lspServices, logger, f context.Workspace) + +type FShapRequestContextFactory(lspServices: ILspServices) = + inherit AbstractRequestContextFactory() + + override _.CreateRequestContextAsync<'TRequestParam> + ( + queueItem: IQueueItem, + methodHandler: IMethodHandler, + requestParam: 'TRequestParam, + cancellationToken: CancellationToken + ) = + lspServices.GetRequiredService() + |> _.GetContext() + |> Task.FromResult + +type DocumentStateHandler() = + interface IMethodHandler with + member _.MutatesSolutionState = true + + interface IRequestHandler with + [] + member _.HandleRequestAsync + ( + request: DidOpenTextDocumentParams, + context: FSharpRequestContext, + cancellationToken: CancellationToken + ) = + let contextHolder = context.LspServices.GetRequiredService() + + contextHolder.UpdateWorkspace(fun w -> + { w with + OpenFiles = Map.add request.TextDocument.Uri.AbsolutePath request.TextDocument.Text w.OpenFiles + }) + + Task.FromResult(SemanticTokensDeltaPartialResult()) + + interface IRequestHandler with + [] + member _.HandleRequestAsync + ( + request: DidChangeTextDocumentParams, + context: FSharpRequestContext, + cancellationToken: CancellationToken + ) = + let contextHolder = context.LspServices.GetRequiredService() + + contextHolder.UpdateWorkspace(fun w -> + { w with + OpenFiles = Map.add request.TextDocument.Uri.AbsolutePath request.ContentChanges.[0].Text w.OpenFiles + }) + + Task.FromResult(SemanticTokensDeltaPartialResult()) + + interface INotificationHandler with + [] + member _.HandleNotificationAsync + ( + request: DidCloseTextDocumentParams, + context: FSharpRequestContext, + cancellationToken: CancellationToken + ) = + let contextHolder = context.LspServices.GetRequiredService() + + contextHolder.UpdateWorkspace(fun w -> + { w with + OpenFiles = Map.remove request.TextDocument.Uri.AbsolutePath w.OpenFiles + }) + + Task.CompletedTask + +type LanguageFeaturesHandler() = + interface IMethodHandler with + member _.MutatesSolutionState = false + + // TODO: this is not getting called + interface IRequestHandler, FSharpRequestContext> with + [] + member _.HandleRequestAsync + ( + request: DocumentDiagnosticParams, + context: FSharpRequestContext, + cancellationToken: CancellationToken + ) = + Task.FromResult(new RelatedUnchangedDocumentDiagnosticReport()) + +type CapabilitiesManager() = + + let mutable initializeParams = None + + interface IInitializeManager with + member this.SetInitializeParams(request) = initializeParams <- Some request + + member this.GetInitializeResult() = + let serverCapabilities = + ServerCapabilities( + TextDocumentSync = TextDocumentSyncOptions(OpenClose = true, Change = TextDocumentSyncKind.Full), + DiagnosticOptions = DiagnosticOptions(InterFileDependencies = true, Identifier = "potato", WorkspaceDiagnostics = true) + ) + + InitializeResult(Capabilities = serverCapabilities) + + member this.GetInitializeParams() = + match initializeParams with + | Some params' -> params' + | None -> failwith "InitializeParams is null" + +type LspServiceLifeCycleManager() = + + interface ILifeCycleManager with + member this.ShutdownAsync(message: string) = + task { + try + printfn "Shutting down" + with + | :? ObjectDisposedException + | :? ConnectionLostException -> () + } + + member this.ExitAsync() = Task.CompletedTask + +type FSharpLspServices(serviceCollection: IServiceCollection) as this = + + do serviceCollection.AddSingleton(this) |> ignore + + let serviceProvider = serviceCollection.BuildServiceProvider() + + interface ILspServices with + member this.GetRequiredService<'T>() : 'T = + serviceProvider.GetRequiredService<'T>() + + member this.TryGetService(t) = serviceProvider.GetService(t) + + member this.GetRequiredServices() = serviceProvider.GetServices() + + member this.GetRegisteredServices() = failwith "Not implemented" + + member this.SupportsGetRegisteredServices() = false + + member this.Dispose() = () + +type FSharpLanguageServer + (jsonRpc: JsonRpc, logger: ILspLogger, ?initialWorkspace: FSharpWorkspace, ?addExtraHandlers: Action) = + inherit AbstractLanguageServer(jsonRpc, logger) + + let mutable _addExtraHandlers = addExtraHandlers + + let initialWorkspace = + defaultArg + initialWorkspace + { + Projects = Map.empty + OpenFiles = Map.empty + } + + do + // This spins up the queue and ensure the LSP is ready to start receiving requests + base.Initialize() + + member private this.GetBaseHandlerProvider() = base.HandlerProvider + + override this.ConstructLspServices() = + let serviceCollection = new ServiceCollection() + + let _ = + serviceCollection + .AddSingleton(initialWorkspace) + .AddSingleton() + .AddSingleton>() + .AddSingleton>() + .AddSingleton() + .AddSingleton() + .AddSingleton(logger) + .AddSingleton, FShapRequestContextFactory>() + .AddSingleton(fun _ -> this.GetBaseHandlerProvider()) + .AddSingleton, CapabilitiesManager>() + .AddSingleton(this) + .AddSingleton(new LspServiceLifeCycleManager()) + + match _addExtraHandlers with + | Some handler -> handler.Invoke(serviceCollection) + | None -> () + + let lspServices = new FSharpLspServices(serviceCollection) + + lspServices :> ILspServices + + static member Create() = + FSharpLanguageServer.Create(LspLogger System.Diagnostics.Trace.TraceInformation) + + static member Create(logger: ILspLogger) = + + let struct (clientStream, serverStream) = FullDuplexStream.CreatePair() + + // TODO: handle disposal of these + let formatter = new JsonMessageFormatter() + + let messageHandler = + new HeaderDelimitedMessageHandler(serverStream, serverStream, formatter) + + let jsonRpc = new JsonRpc(messageHandler) + + let rpcTrace = new StringWriter() + + let listener = new TextWriterTraceListener(rpcTrace) + + jsonRpc.TraceSource.Listeners.Add(listener) |> ignore + + jsonRpc.TraceSource.Switch.Level <- SourceLevels.Information + + let server = new FSharpLanguageServer(jsonRpc, logger) + + jsonRpc.StartListening() + + (clientStream, clientStream), server, rpcTrace diff --git a/src/FSharp.Compiler.LanguageServer/FSharpWorkspace.fs b/src/FSharp.Compiler.LanguageServer/FSharpWorkspace.fs new file mode 100644 index 00000000000..3a4596cb3a8 --- /dev/null +++ b/src/FSharp.Compiler.LanguageServer/FSharpWorkspace.fs @@ -0,0 +1,12 @@ +module FSharp.Compiler.LanguageServer.Workspace + +#nowarn "57" + +open System +open FSharp.Compiler.CodeAnalysis.ProjectSnapshot + +type FSharpWorkspace = + { + Projects: Map + OpenFiles: Map + } diff --git a/src/FSharp.Compiler.LanguageServer/Utils.fs b/src/FSharp.Compiler.LanguageServer/Utils.fs new file mode 100644 index 00000000000..abae720654e --- /dev/null +++ b/src/FSharp.Compiler.LanguageServer/Utils.fs @@ -0,0 +1,25 @@ +[] +module FSharp.Compiler.LanguageServer.Utils + +open Microsoft.CommonLanguageServerProtocol.Framework + +let LspLogger (output: string -> unit) = + { new ILspLogger with + member this.LogEndContext(message: string, ``params``: obj array) : unit = + output $"EndContext :: {message} %A{``params``}" + + member this.LogError(message: string, ``params``: obj array) : unit = + output $"ERROR :: {message} %A{``params``}" + + member this.LogException(``exception``: exn, message: string, ``params``: obj array) : unit = + output $"EXCEPTION :: %A{``exception``} {message} %A{``params``}" + + member this.LogInformation(message: string, ``params``: obj array) : unit = + output $"INFO :: {message} %A{``params``}" + + member this.LogStartContext(message: string, ``params``: obj array) : unit = + output $"StartContext :: {message} %A{``params``}" + + member this.LogWarning(message: string, ``params``: obj array) : unit = + output $"WARNING :: {message} %A{``params``}" + } diff --git a/src/FSharp.VisualStudio.Extension/.vsextension/string-resources.json b/src/FSharp.VisualStudio.Extension/.vsextension/string-resources.json new file mode 100644 index 00000000000..5f1633293d7 --- /dev/null +++ b/src/FSharp.VisualStudio.Extension/.vsextension/string-resources.json @@ -0,0 +1,4 @@ +{ + "FSharpLspExtension.FSharpLanguageServerProvider.DisplayName": "FSharp Analyzer LSP server" + +} diff --git a/src/FSharp.VisualStudio.Extension/ExtensionEntrypoint.cs b/src/FSharp.VisualStudio.Extension/ExtensionEntrypoint.cs new file mode 100644 index 00000000000..7957511b29e --- /dev/null +++ b/src/FSharp.VisualStudio.Extension/ExtensionEntrypoint.cs @@ -0,0 +1,34 @@ +namespace FSharp.VisualStudio.Extension; + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.Extensibility; +using Extension = Microsoft.VisualStudio.Extensibility.Extension; + +/// +/// Extension entrypoint for the VisualStudio.Extensibility extension. +/// +[VisualStudioContribution] +internal class ExtensionEntrypoint : Extension +{ + + + + /// + public override ExtensionConfiguration ExtensionConfiguration => new() + { + Metadata = new( + id: "FSharp.VisualStudio.Extension.4fd40904-7bdd-40b0-82ab-588cbee624d1", + version: this.ExtensionAssemblyVersion, + publisherName: "Publisher name", + displayName: "FSharp.VisualStudio.Extension", + description: "Extension description"), + }; + + /// + protected override void InitializeServices(IServiceCollection serviceCollection) + { + base.InitializeServices(serviceCollection); + + // You can configure dependency injection here by adding services to the serviceCollection. + } +} diff --git a/src/FSharp.VisualStudio.Extension/FSharp.VisualStudio.Extension.csproj b/src/FSharp.VisualStudio.Extension/FSharp.VisualStudio.Extension.csproj new file mode 100644 index 00000000000..cdd5772cf93 --- /dev/null +++ b/src/FSharp.VisualStudio.Extension/FSharp.VisualStudio.Extension.csproj @@ -0,0 +1,29 @@ + + + net8.0-windows + enable + 12 + en-US + + + + + + + + + + + + + + + + + + false + + + + + diff --git a/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs b/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs new file mode 100644 index 00000000000..99279bc99b2 --- /dev/null +++ b/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs @@ -0,0 +1,123 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace FSharp.VisualStudio.Extension; + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.IO.Pipelines; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Documents; +using FSharp.Compiler.LanguageServer; +using Microsoft.VisualStudio.Extensibility; +using Microsoft.VisualStudio.Extensibility.Editor; +using Microsoft.VisualStudio.Extensibility.LanguageServer; +using Microsoft.VisualStudio.ProjectSystem.Query; +using Microsoft.VisualStudio.RpcContracts.LanguageServerProvider; +using Nerdbank.Streams; + +/// +#pragma warning disable VSEXTPREVIEW_LSP // Type is for evaluation purposes only and is subject to change or removal in future updates. + +#pragma warning disable VSEXTPREVIEW_PROJECTQUERY_PROPERTIES_BUILDPROPERTIES // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + + +internal static class Extensions +{ + public static List> Please(this IAsyncQueryable x) => x.QueryAsync(CancellationToken.None).ToBlockingEnumerable().ToList(); +} + + +[VisualStudioContribution] +internal class FSharpLanguageServerProvider : LanguageServerProvider +{ + /// + /// Gets the document type for FSharp code files. + /// + [VisualStudioContribution] + public static DocumentTypeConfiguration FSharpDocumentType => new("F#") + { + FileExtensions = [".fs", ".fsi", ".fsx"], + BaseDocumentType = LanguageServerBaseDocumentType, + }; + + /// + public override LanguageServerProviderConfiguration LanguageServerProviderConfiguration => new( + "%FSharpLspExtension.FSharpLanguageServerProvider.DisplayName%", + [DocumentFilter.FromDocumentType(FSharpDocumentType)]); + + /// + public override async Task CreateServerConnectionAsync(CancellationToken cancellationToken) + { + var what = this.Extensibility.Workspaces(); + //var result = what.QueryProjectsAsync(project => project.With(p => p.Kind == "6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705"), cancellationToken).Result; + IQueryResults? result = await what.QueryProjectsAsync(project => project, cancellationToken); + + foreach (var project in result) + { + try + { + this.ProcessProject(project); + } + catch (Exception ex) + { + Debug.WriteLine(ex); + } + } + + + var ((clientStream, serverStream), _server, _trace) = FSharpLanguageServer.Create(); + + return new DuplexPipe( + PipeReader.Create(clientStream), + PipeWriter.Create(serverStream)); + } + + private void ProcessProject(IProjectSnapshot project) + { + List>? files = project.Files.Please(); + var references = project.ProjectReferences.Please(); + + var properties = project.Properties.Please(); + + var configurationDimensions = project.ConfigurationDimensions.Please(); + var configurations = project.Configurations.Please(); + + foreach (var configuration in configurations) + { + this.ProcessConfiguration(configuration.Value); + } + } + + private void ProcessConfiguration(IProjectConfigurationSnapshot configuration) + { + var properties = configuration.Properties.Please(); + var packageReferences = configuration.PackageReferences.Please(); + var assemblyReferences = configuration.AssemblyReferences.Please(); + var refNames = assemblyReferences.Select(r => r.Value.Name).ToList(); + var dimensions = configuration.ConfigurationDimensions.Please(); + var outputGroups = configuration.OutputGroups.Please(); + var buildProperties = configuration.BuildProperties.Please(); + var buildPropDictionary = buildProperties.Select(p => (p.Value.Name, p.Value.Value)).ToList(); + return; + } + + + /// + public override Task OnServerInitializationResultAsync(ServerInitializationResult serverInitializationResult, LanguageServerInitializationFailureInfo? initializationFailureInfo, CancellationToken cancellationToken) + { + if (serverInitializationResult == ServerInitializationResult.Failed) + { + // Log telemetry for failure and disable the server from being activated again. + this.Enabled = false; + } + + return base.OnServerInitializationResultAsync(serverInitializationResult, initializationFailureInfo, cancellationToken); + } +} +#pragma warning restore VSEXTPREVIEW_LSP // Type is for evaluation purposes only and is subject to change or removal in future updates. diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index ec4fc441f29..f98c5c874c0 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -310,8 +310,6 @@ - - %(RelativeDir)\BaseLine\%(Filename)%(Extension) @@ -337,4 +335,5 @@ + diff --git a/tests/FSharp.Compiler.LanguageServer.Tests/FSharp.Compiler.LanguageServer.Tests.fsproj b/tests/FSharp.Compiler.LanguageServer.Tests/FSharp.Compiler.LanguageServer.Tests.fsproj new file mode 100644 index 00000000000..40703a0e518 --- /dev/null +++ b/tests/FSharp.Compiler.LanguageServer.Tests/FSharp.Compiler.LanguageServer.Tests.fsproj @@ -0,0 +1,55 @@ + + + + net8.0 + false + false + true + true + xunit + true + false + false + $(OtherFlags) --warnon:1182 + $(NoWarn);FS0988 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/FSharp.Compiler.LanguageServer.Tests/LanguageServerTests.fs b/tests/FSharp.Compiler.LanguageServer.Tests/LanguageServerTests.fs new file mode 100644 index 00000000000..df5fc6f41df --- /dev/null +++ b/tests/FSharp.Compiler.LanguageServer.Tests/LanguageServerTests.fs @@ -0,0 +1,63 @@ +module LanguageServerTests + +open System +open Xunit + +open FSharp.Compiler.LanguageServer +open StreamJsonRpc +open System.IO +open System.Diagnostics + +open Microsoft.VisualStudio.LanguageServer.Protocol +open Nerdbank.Streams + + +[] +let ``The server can process the initialization message`` () = + + // Create a StringWriter to capture the output + let rpcTrace = new StringWriter() + + try + + let struct (clientStream, _serverStream) = FullDuplexStream.CreatePair() + + use formatter = new JsonMessageFormatter() + + use messageHandler = new HeaderDelimitedMessageHandler(clientStream, clientStream, formatter) + + use jsonRpc = new JsonRpc(messageHandler) + + + // Create a new TraceListener with the StringWriter + let listener = new TextWriterTraceListener(rpcTrace) + + // Add the listener to the JsonRpc TraceSource + jsonRpc.TraceSource.Listeners.Add(listener) |> ignore + + // Set the TraceLevel to Information to get all informational, warning and error messages + jsonRpc.TraceSource.Switch.Level <- SourceLevels.Information + + //jsonRpc.inv + + // Now all JsonRpc debug information will be written to the StringWriter + + let log = ResizeArray() + + let _s = new FSharpLanguageServer(jsonRpc, (LspLogger log.Add)) + + jsonRpc.StartListening() + + //let initializeParams = InitializeParams( + // ProcessId = System.Diagnostics.Process.GetCurrentProcess().Id, + // RootUri = Uri("file:///c:/temp"), + // InitializationOptions = None, + // RootPath = "file:///c:/temp") + + + + + finally + + let _output = rpcTrace.ToString() + () \ No newline at end of file diff --git a/tests/FSharp.Compiler.LanguageServer.Tests/Program.fs b/tests/FSharp.Compiler.LanguageServer.Tests/Program.fs new file mode 100644 index 00000000000..0695f84c683 --- /dev/null +++ b/tests/FSharp.Compiler.LanguageServer.Tests/Program.fs @@ -0,0 +1 @@ +module Program = let [] main _ = 0 From 81d9c411a9dd4ad70888be80076b40e1d0aec383 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 6 Feb 2024 17:36:26 +0100 Subject: [PATCH 02/60] Clasp update --- VisualFSharp.sln | 15 ++++++++ .../FSharp.Compiler.LanguageServer.fsproj | 5 ++- .../FSharpLanguageServer.fs | 37 +++++++++---------- 3 files changed, 36 insertions(+), 21 deletions(-) diff --git a/VisualFSharp.sln b/VisualFSharp.sln index cafe67b60c9..5ea230c7a58 100644 --- a/VisualFSharp.sln +++ b/VisualFSharp.sln @@ -194,12 +194,15 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FSharp.Editor.IntegrationTests", "vsintegration\tests\FSharp.Editor.IntegrationTests\FSharp.Editor.IntegrationTests.csproj", "{E31F9B59-FCF1-4D04-8762-C7BB60285A7B}" EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Benchmarks.Common", "tests\benchmarks\FSharp.Benchmarks.Common\FSharp.Benchmarks.Common.fsproj", "{6734FC6F-B5F3-45E1-9A72-720378BB49C9}" +EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Compiler.LanguageServer", "src\FSharp.Compiler.LanguageServer\FSharp.Compiler.LanguageServer.fsproj", "{D72F6593-DB2D-47AC-8E15-8DCE8527972E}" EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Compiler.LanguageServer.Tests", "tests\FSharp.Compiler.LanguageServer.Tests\FSharp.Compiler.LanguageServer.Tests.fsproj", "{1E83A6C8-FA4D-42BD-B4A5-B7F9AAD1B388}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FSharp.VisualStudio.Extension", "src\FSharp.VisualStudio.Extension\FSharp.VisualStudio.Extension.csproj", "{E1013576-6257-47BA-AAFB-F95B68DAB1FF}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CommonLanguageServerProtocol.Framework", "..\roslyn\src\Features\LanguageServer\Microsoft.CommonLanguageServerProtocol.Framework\Microsoft.CommonLanguageServerProtocol.Framework.csproj", "{A5C8D385-308E-4EBB-A15E-79CF58CD861C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1074,6 +1077,18 @@ Global {E1013576-6257-47BA-AAFB-F95B68DAB1FF}.Release|Any CPU.Build.0 = Release|Any CPU {E1013576-6257-47BA-AAFB-F95B68DAB1FF}.Release|x86.ActiveCfg = Release|Any CPU {E1013576-6257-47BA-AAFB-F95B68DAB1FF}.Release|x86.Build.0 = Release|Any CPU + {A5C8D385-308E-4EBB-A15E-79CF58CD861C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A5C8D385-308E-4EBB-A15E-79CF58CD861C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A5C8D385-308E-4EBB-A15E-79CF58CD861C}.Debug|x86.ActiveCfg = Debug|Any CPU + {A5C8D385-308E-4EBB-A15E-79CF58CD861C}.Debug|x86.Build.0 = Debug|Any CPU + {A5C8D385-308E-4EBB-A15E-79CF58CD861C}.Proto|Any CPU.ActiveCfg = Debug|Any CPU + {A5C8D385-308E-4EBB-A15E-79CF58CD861C}.Proto|Any CPU.Build.0 = Debug|Any CPU + {A5C8D385-308E-4EBB-A15E-79CF58CD861C}.Proto|x86.ActiveCfg = Debug|Any CPU + {A5C8D385-308E-4EBB-A15E-79CF58CD861C}.Proto|x86.Build.0 = Debug|Any CPU + {A5C8D385-308E-4EBB-A15E-79CF58CD861C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A5C8D385-308E-4EBB-A15E-79CF58CD861C}.Release|Any CPU.Build.0 = Release|Any CPU + {A5C8D385-308E-4EBB-A15E-79CF58CD861C}.Release|x86.ActiveCfg = Release|Any CPU + {A5C8D385-308E-4EBB-A15E-79CF58CD861C}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/FSharp.Compiler.LanguageServer/FSharp.Compiler.LanguageServer.fsproj b/src/FSharp.Compiler.LanguageServer/FSharp.Compiler.LanguageServer.fsproj index 471c6ee6b8c..8ea3cde0786 100644 --- a/src/FSharp.Compiler.LanguageServer/FSharp.Compiler.LanguageServer.fsproj +++ b/src/FSharp.Compiler.LanguageServer/FSharp.Compiler.LanguageServer.fsproj @@ -7,9 +7,9 @@ - + - + @@ -34,6 +34,7 @@ + diff --git a/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs index 28e78b827af..298b64e463d 100644 --- a/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs +++ b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs @@ -36,25 +36,24 @@ type ContextHolder(intialWorkspace, lspServices: ILspServices) = context <- FSharpRequestContext(lspServices, logger, f context.Workspace) type FShapRequestContextFactory(lspServices: ILspServices) = - inherit AbstractRequestContextFactory() - - override _.CreateRequestContextAsync<'TRequestParam> - ( - queueItem: IQueueItem, - methodHandler: IMethodHandler, - requestParam: 'TRequestParam, - cancellationToken: CancellationToken - ) = - lspServices.GetRequiredService() - |> _.GetContext() - |> Task.FromResult + + interface IRequestContextFactory with + + member _.CreateRequestContextAsync<'TRequestParam>( + queueItem: IQueueItem, + requestParam: 'TRequestParam, + cancellationToken: CancellationToken + ) = + lspServices.GetRequiredService() + |> _.GetContext() + |> Task.FromResult type DocumentStateHandler() = interface IMethodHandler with member _.MutatesSolutionState = true interface IRequestHandler with - [] + [] member _.HandleRequestAsync ( request: DidOpenTextDocumentParams, @@ -71,7 +70,7 @@ type DocumentStateHandler() = Task.FromResult(SemanticTokensDeltaPartialResult()) interface IRequestHandler with - [] + [] member _.HandleRequestAsync ( request: DidChangeTextDocumentParams, @@ -88,7 +87,7 @@ type DocumentStateHandler() = Task.FromResult(SemanticTokensDeltaPartialResult()) interface INotificationHandler with - [] + [] member _.HandleNotificationAsync ( request: DidCloseTextDocumentParams, @@ -110,7 +109,7 @@ type LanguageFeaturesHandler() = // TODO: this is not getting called interface IRequestHandler, FSharpRequestContext> with - [] + [] member _.HandleRequestAsync ( request: DocumentDiagnosticParams, @@ -192,7 +191,7 @@ type FSharpLanguageServer // This spins up the queue and ensure the LSP is ready to start receiving requests base.Initialize() - member private this.GetBaseHandlerProvider() = base.HandlerProvider + member private this.GetBaseHandlerProvider() = base.GetHandlerProvider() override this.ConstructLspServices() = let serviceCollection = new ServiceCollection() @@ -206,8 +205,8 @@ type FSharpLanguageServer .AddSingleton() .AddSingleton() .AddSingleton(logger) - .AddSingleton, FShapRequestContextFactory>() - .AddSingleton(fun _ -> this.GetBaseHandlerProvider()) + .AddSingleton, FShapRequestContextFactory>() + .AddSingleton(fun _ -> this.GetBaseHandlerProvider()) .AddSingleton, CapabilitiesManager>() .AddSingleton(this) .AddSingleton(new LspServiceLifeCycleManager()) From bb760ccf24079861b820c36404f0168ec02ab862 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 6 Feb 2024 18:05:02 +0100 Subject: [PATCH 03/60] Dump JsonRpc trace to console --- src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs | 6 ++---- .../FSharpLanguageServerProvider.cs | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs index 298b64e463d..49e799f3e4c 100644 --- a/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs +++ b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs @@ -234,9 +234,7 @@ type FSharpLanguageServer let jsonRpc = new JsonRpc(messageHandler) - let rpcTrace = new StringWriter() - - let listener = new TextWriterTraceListener(rpcTrace) + let listener = new TextWriterTraceListener(Console.Out) jsonRpc.TraceSource.Listeners.Add(listener) |> ignore @@ -246,4 +244,4 @@ type FSharpLanguageServer jsonRpc.StartListening() - (clientStream, clientStream), server, rpcTrace + (clientStream, clientStream), server diff --git a/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs b/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs index 99279bc99b2..17b137086e1 100644 --- a/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs +++ b/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs @@ -70,8 +70,7 @@ internal class FSharpLanguageServerProvider : LanguageServerProvider } } - - var ((clientStream, serverStream), _server, _trace) = FSharpLanguageServer.Create(); + var ((clientStream, serverStream), _server) = FSharpLanguageServer.Create(); return new DuplexPipe( PipeReader.Create(clientStream), From 6105dfc93c95cd456dfb05573c84bb8cb7f66041 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 7 Feb 2024 15:17:52 +0100 Subject: [PATCH 04/60] Workspace.Create method --- .../FSharpLanguageServer.fs | 16 ++++++++++------ .../FSharpWorkspace.fs | 6 ++++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs index 49e799f3e4c..99850673228 100644 --- a/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs +++ b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs @@ -37,9 +37,10 @@ type ContextHolder(intialWorkspace, lspServices: ILspServices) = type FShapRequestContextFactory(lspServices: ILspServices) = - interface IRequestContextFactory with - - member _.CreateRequestContextAsync<'TRequestParam>( + interface IRequestContextFactory with + + member _.CreateRequestContextAsync<'TRequestParam> + ( queueItem: IQueueItem, requestParam: 'TRequestParam, cancellationToken: CancellationToken @@ -220,9 +221,12 @@ type FSharpLanguageServer lspServices :> ILspServices static member Create() = - FSharpLanguageServer.Create(LspLogger System.Diagnostics.Trace.TraceInformation) + FSharpLanguageServer.Create(FSharpWorkspace.Create Seq.empty) + + static member Create(initialWorkspace) = + FSharpLanguageServer.Create(LspLogger System.Diagnostics.Trace.TraceInformation, initialWorkspace) - static member Create(logger: ILspLogger) = + static member Create(logger: ILspLogger, initialWorkspace) = let struct (clientStream, serverStream) = FullDuplexStream.CreatePair() @@ -240,7 +244,7 @@ type FSharpLanguageServer jsonRpc.TraceSource.Switch.Level <- SourceLevels.Information - let server = new FSharpLanguageServer(jsonRpc, logger) + let server = new FSharpLanguageServer(jsonRpc, logger, initialWorkspace) jsonRpc.StartListening() diff --git a/src/FSharp.Compiler.LanguageServer/FSharpWorkspace.fs b/src/FSharp.Compiler.LanguageServer/FSharpWorkspace.fs index 3a4596cb3a8..9d792ffda24 100644 --- a/src/FSharp.Compiler.LanguageServer/FSharpWorkspace.fs +++ b/src/FSharp.Compiler.LanguageServer/FSharpWorkspace.fs @@ -10,3 +10,9 @@ type FSharpWorkspace = Projects: Map OpenFiles: Map } + + static member Create(projects: FSharpProjectSnapshot seq) = + { + Projects = Map.ofSeq (projects |> Seq.map (fun p -> p.Identifier, p)) + OpenFiles = Map.empty + } From d056cb974b27c7acffa4e65a9a46be3d17960687 Mon Sep 17 00:00:00 2001 From: Adam Boniecki <20281641+abonie@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:28:18 +0100 Subject: [PATCH 05/60] Remove reference to local roslyn project (#16664) Co-authored-by: Adam Boniecki --- VisualFSharp.sln | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/VisualFSharp.sln b/VisualFSharp.sln index 5ea230c7a58..42158c3431d 100644 --- a/VisualFSharp.sln +++ b/VisualFSharp.sln @@ -201,8 +201,6 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Compiler.LanguageSer EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FSharp.VisualStudio.Extension", "src\FSharp.VisualStudio.Extension\FSharp.VisualStudio.Extension.csproj", "{E1013576-6257-47BA-AAFB-F95B68DAB1FF}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CommonLanguageServerProtocol.Framework", "..\roslyn\src\Features\LanguageServer\Microsoft.CommonLanguageServerProtocol.Framework\Microsoft.CommonLanguageServerProtocol.Framework.csproj", "{A5C8D385-308E-4EBB-A15E-79CF58CD861C}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1077,18 +1075,6 @@ Global {E1013576-6257-47BA-AAFB-F95B68DAB1FF}.Release|Any CPU.Build.0 = Release|Any CPU {E1013576-6257-47BA-AAFB-F95B68DAB1FF}.Release|x86.ActiveCfg = Release|Any CPU {E1013576-6257-47BA-AAFB-F95B68DAB1FF}.Release|x86.Build.0 = Release|Any CPU - {A5C8D385-308E-4EBB-A15E-79CF58CD861C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A5C8D385-308E-4EBB-A15E-79CF58CD861C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A5C8D385-308E-4EBB-A15E-79CF58CD861C}.Debug|x86.ActiveCfg = Debug|Any CPU - {A5C8D385-308E-4EBB-A15E-79CF58CD861C}.Debug|x86.Build.0 = Debug|Any CPU - {A5C8D385-308E-4EBB-A15E-79CF58CD861C}.Proto|Any CPU.ActiveCfg = Debug|Any CPU - {A5C8D385-308E-4EBB-A15E-79CF58CD861C}.Proto|Any CPU.Build.0 = Debug|Any CPU - {A5C8D385-308E-4EBB-A15E-79CF58CD861C}.Proto|x86.ActiveCfg = Debug|Any CPU - {A5C8D385-308E-4EBB-A15E-79CF58CD861C}.Proto|x86.Build.0 = Debug|Any CPU - {A5C8D385-308E-4EBB-A15E-79CF58CD861C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A5C8D385-308E-4EBB-A15E-79CF58CD861C}.Release|Any CPU.Build.0 = Release|Any CPU - {A5C8D385-308E-4EBB-A15E-79CF58CD861C}.Release|x86.ActiveCfg = Release|Any CPU - {A5C8D385-308E-4EBB-A15E-79CF58CD861C}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 6dfca570f868abdaf7b174db7ed6c31a05733000 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Mon, 12 Feb 2024 17:19:08 +0100 Subject: [PATCH 06/60] Overriding diagnostics for VS, dummy workspace creation --- src/Compiler/Service/FSharpProjectSnapshot.fs | 54 +++++++++++++ .../FSharpLanguageServer.fs | 44 ++++++----- .../FSharp.VisualStudio.Extension.csproj | 2 + .../FSharpLanguageServerProvider.cs | 79 ++++++++++++++++++- 4 files changed, 157 insertions(+), 22 deletions(-) diff --git a/src/Compiler/Service/FSharpProjectSnapshot.fs b/src/Compiler/Service/FSharpProjectSnapshot.fs index cdc7ffe9252..8727ee50cbd 100644 --- a/src/Compiler/Service/FSharpProjectSnapshot.fs +++ b/src/Compiler/Service/FSharpProjectSnapshot.fs @@ -652,6 +652,60 @@ and [] FSha FSharpProjectSnapshot.FromOptions(options, getFileSnapshot) + static member FromResponseFile(responseFile: FileInfo, projectFileName) = + if not responseFile.Exists then + failwith $"%s{responseFile.FullName} does not exist" + + let compilerArgs = File.ReadAllLines responseFile.FullName + + let fsharpFileExtensions = set [| ".fs"; ".fsi"; ".fsx" |] + + let isFSharpFile (file: string) = + Set.exists (fun (ext: string) -> file.EndsWith(ext, StringComparison.Ordinal)) fsharpFileExtensions + + let isReference: string -> bool = _.StartsWith("-r:") + + let fsharpFiles = + compilerArgs + |> Array.choose (fun (line: string) -> + if not (isFSharpFile line) then + None + else + + let fullPath = Path.Combine(responseFile.DirectoryName, line) + if not (File.Exists fullPath) then None else Some fullPath) + |> Array.toList + + let referencesOnDisk = + compilerArgs |> Seq.filter isReference |> Seq.map _.Substring(3) |> Seq.toList + + let otherOptions = + compilerArgs + |> Seq.filter (not << isReference) + |> Seq.filter (not << isFSharpFile) + |> Seq.toList + + FSharpProjectSnapshot.Create( + projectFileName = projectFileName, + projectId = None, + sourceFiles = (fsharpFiles |> List.map FSharpFileSnapshot.CreateFromFileSystem), + referencesOnDisk = + (referencesOnDisk + |> List.map (fun x -> + { + Path = x + LastModified = FileSystem.GetLastWriteTimeShim(x) + })), + otherOptions = otherOptions, + referencedProjects = [], + isIncompleteTypeCheckEnvironment = false, + useScriptResolutionRules = false, + loadTime = DateTime.Now, + unresolvedReferences = None, + originalLoadReferences = [], + stamp = None + ) + let rec internal snapshotToOptions (projectSnapshot: ProjectSnapshotBase<_>) = { ProjectFileName = projectSnapshot.ProjectFileName diff --git a/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs index 99850673228..defc5df2dc8 100644 --- a/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs +++ b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs @@ -119,19 +119,28 @@ type LanguageFeaturesHandler() = ) = Task.FromResult(new RelatedUnchangedDocumentDiagnosticReport()) -type CapabilitiesManager() = +type IServerCapabilitiesOverride = + abstract member OverrideServerCapabilities: ServerCapabilities -> ServerCapabilities + +type CapabilitiesManager(scOverrides: IServerCapabilitiesOverride seq) = let mutable initializeParams = None + let defaultCapabilities = + ServerCapabilities( + TextDocumentSync = TextDocumentSyncOptions(OpenClose = true, Change = TextDocumentSyncKind.Full), + DiagnosticOptions = + DiagnosticOptions(WorkDoneProgress = true, InterFileDependencies = true, Identifier = "potato", WorkspaceDiagnostics = true) + // CompletionProvider = CompletionOptions(TriggerCharacters=[|"."; " "|], ResolveProvider=true, WorkDoneProgress=true) + ) + interface IInitializeManager with member this.SetInitializeParams(request) = initializeParams <- Some request member this.GetInitializeResult() = let serverCapabilities = - ServerCapabilities( - TextDocumentSync = TextDocumentSyncOptions(OpenClose = true, Change = TextDocumentSyncKind.Full), - DiagnosticOptions = DiagnosticOptions(InterFileDependencies = true, Identifier = "potato", WorkspaceDiagnostics = true) - ) + (defaultCapabilities, scOverrides) + ||> Seq.fold (fun acc (x: IServerCapabilitiesOverride) -> x.OverrideServerCapabilities acc) InitializeResult(Capabilities = serverCapabilities) @@ -178,20 +187,14 @@ type FSharpLanguageServer (jsonRpc: JsonRpc, logger: ILspLogger, ?initialWorkspace: FSharpWorkspace, ?addExtraHandlers: Action) = inherit AbstractLanguageServer(jsonRpc, logger) - let mutable _addExtraHandlers = addExtraHandlers - - let initialWorkspace = - defaultArg - initialWorkspace - { - Projects = Map.empty - OpenFiles = Map.empty - } + let initialWorkspace = defaultArg initialWorkspace (FSharpWorkspace.Create []) do // This spins up the queue and ensure the LSP is ready to start receiving requests base.Initialize() + member _.JsonRpc: JsonRpc = jsonRpc + member private this.GetBaseHandlerProvider() = base.GetHandlerProvider() override this.ConstructLspServices() = @@ -212,7 +215,7 @@ type FSharpLanguageServer .AddSingleton(this) .AddSingleton(new LspServiceLifeCycleManager()) - match _addExtraHandlers with + match addExtraHandlers with | Some handler -> handler.Invoke(serviceCollection) | None -> () @@ -221,12 +224,12 @@ type FSharpLanguageServer lspServices :> ILspServices static member Create() = - FSharpLanguageServer.Create(FSharpWorkspace.Create Seq.empty) + FSharpLanguageServer.Create(FSharpWorkspace.Create Seq.empty, (fun _ -> ())) - static member Create(initialWorkspace) = - FSharpLanguageServer.Create(LspLogger System.Diagnostics.Trace.TraceInformation, initialWorkspace) + static member Create(initialWorkspace, addExtraHandlers: Action) = + FSharpLanguageServer.Create(LspLogger System.Diagnostics.Trace.TraceInformation, initialWorkspace, addExtraHandlers) - static member Create(logger: ILspLogger, initialWorkspace) = + static member Create(logger: ILspLogger, initialWorkspace, ?addExtraHandlers: Action) = let struct (clientStream, serverStream) = FullDuplexStream.CreatePair() @@ -244,7 +247,8 @@ type FSharpLanguageServer jsonRpc.TraceSource.Switch.Level <- SourceLevels.Information - let server = new FSharpLanguageServer(jsonRpc, logger, initialWorkspace) + let server = + new FSharpLanguageServer(jsonRpc, logger, initialWorkspace, ?addExtraHandlers = addExtraHandlers) jsonRpc.StartListening() diff --git a/src/FSharp.VisualStudio.Extension/FSharp.VisualStudio.Extension.csproj b/src/FSharp.VisualStudio.Extension/FSharp.VisualStudio.Extension.csproj index cdd5772cf93..4e7fee4d770 100644 --- a/src/FSharp.VisualStudio.Extension/FSharp.VisualStudio.Extension.csproj +++ b/src/FSharp.VisualStudio.Extension/FSharp.VisualStudio.Extension.csproj @@ -11,6 +11,8 @@ + + diff --git a/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs b/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs index 17b137086e1..ef4f8476166 100644 --- a/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs +++ b/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs @@ -14,12 +14,17 @@ namespace FSharp.VisualStudio.Extension; using System.Threading.Tasks; using System.Windows.Documents; using FSharp.Compiler.LanguageServer; +using Microsoft.CommonLanguageServerProtocol.Framework; +using Microsoft.Extensions.DependencyInjection; using Microsoft.VisualStudio.Extensibility; using Microsoft.VisualStudio.Extensibility.Editor; using Microsoft.VisualStudio.Extensibility.LanguageServer; +using Microsoft.VisualStudio.LanguageServer.Protocol; using Microsoft.VisualStudio.ProjectSystem.Query; using Microsoft.VisualStudio.RpcContracts.LanguageServerProvider; using Nerdbank.Streams; +using static FSharp.Compiler.CodeAnalysis.ProjectSnapshot; +using static FSharp.Compiler.LanguageServer.Workspace; /// #pragma warning disable VSEXTPREVIEW_LSP // Type is for evaluation purposes only and is subject to change or removal in future updates. @@ -33,6 +38,53 @@ internal static class Extensions } +internal class VsServerCapabilitiesOverride : IServerCapabilitiesOverride +{ + public ServerCapabilities OverrideServerCapabilities(ServerCapabilities value) + { + var capabilities = new VSInternalServerCapabilities + { + TextDocumentSync = value.TextDocumentSync, + SupportsDiagnosticRequests = true + }; + return capabilities; + } +} + + +internal class VsDiagnosticsHandler : IRequestHandler +{ + public bool MutatesSolutionState => false; + + [LanguageServerEndpoint(VSInternalMethods.DocumentPullDiagnosticName)] + public Task HandleRequestAsync(VSInternalDocumentDiagnosticsParams request, FSharpRequestContext context, CancellationToken cancellationToken) + { + var rep = new VSInternalDiagnosticReport + { + ResultId = "potato1", // Has to be present for diagnostic to show up + //Identifier = 69, + //Version = 1, + Diagnostics = + [ + new Diagnostic + { + Range = new Microsoft.VisualStudio.LanguageServer.Protocol.Range + { + Start = new Position { Line = 0, Character = 0 }, + End = new Position { Line = 0, Character = 1 } + }, + Severity = DiagnosticSeverity.Error, + Message = "This is a test diagnostic", + //Source = "Intellisense", + Code = "1234" + } + ] + }; + return Task.FromResult(new[] { rep }); + } +} + + [VisualStudioContribution] internal class FSharpLanguageServerProvider : LanguageServerProvider { @@ -49,7 +101,7 @@ internal class FSharpLanguageServerProvider : LanguageServerProvider /// public override LanguageServerProviderConfiguration LanguageServerProviderConfiguration => new( "%FSharpLspExtension.FSharpLanguageServerProvider.DisplayName%", - [DocumentFilter.FromDocumentType(FSharpDocumentType)]); + [Microsoft.VisualStudio.Extensibility.DocumentFilter.FromDocumentType(FSharpDocumentType)]); /// public override async Task CreateServerConnectionAsync(CancellationToken cancellationToken) @@ -70,7 +122,30 @@ internal class FSharpLanguageServerProvider : LanguageServerProvider } } - var ((clientStream, serverStream), _server) = FSharpLanguageServer.Create(); + FSharpWorkspace workspace; + + try + { + // Some hardcoded projects before we create them from the ProjectQuery + var projectsRoot = @"D:\code\FS"; + var giraffe = FSharpProjectSnapshot.FromResponseFile( + new FileInfo(Path.Combine(projectsRoot, @"Giraffe\src\Giraffe\Giraffe.rsp")), + Path.Combine(projectsRoot, @"Giraffe\src\Giraffe\Giraffe.fsproj")); + var giraffeTests = FSharpProjectSnapshot.FromResponseFile( + new FileInfo(Path.Combine(projectsRoot, @"Giraffe\tests\Giraffe.Tests\Giraffe.Tests.rsp")), + Path.Combine(projectsRoot, @"Giraffe\tests\Giraffe.Tests\Giraffe.Tests.fsproj")); + workspace = FSharpWorkspace.Create([giraffe, giraffeTests]); + } + catch + { + workspace = FSharpWorkspace.Create([]); + } + + var ((clientStream, serverStream), _server) = FSharpLanguageServer.Create(workspace, (serviceCollection) => + { + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + }); return new DuplexPipe( PipeReader.Create(clientStream), From 42df2dd2ea9ebc6180a3792539c8b65e9f60899f Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Tue, 13 Feb 2024 12:06:55 +0100 Subject: [PATCH 07/60] checker --- .../FSharpLanguageServer.fs | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs index defc5df2dc8..2f6818ccc83 100644 --- a/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs +++ b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs @@ -13,27 +13,40 @@ open Nerdbank.Streams open System.Diagnostics open System.IO open Workspace +open FSharp.Compiler.CodeAnalysis [] module Stuff = [] let FSharpLanguageName = "F#" -type FSharpRequestContext(lspServices: ILspServices, logger: ILspLogger, workspace: FSharpWorkspace) = +type FSharpRequestContext(lspServices: ILspServices, logger: ILspLogger, workspace: FSharpWorkspace, checker) = member _.LspServices = lspServices member _.Logger = logger member _.Workspace = workspace + member _.Checker = checker type ContextHolder(intialWorkspace, lspServices: ILspServices) = let logger = lspServices.GetRequiredService() - - let mutable context = FSharpRequestContext(lspServices, logger, intialWorkspace) + let checker = + FSharpChecker.Create( + keepAssemblyContents=true, + keepAllBackgroundResolutions=true, + keepAllBackgroundSymbolUses=true, + enableBackgroundItemKeyStoreAndSemanticClassification=true, + enablePartialTypeChecking=true, + parallelReferenceResolution=true, + captureIdentifiersWhenParsing=true, + useSyntaxTreeCache=true, + useTransparentCompiler=true) + + let mutable context = FSharpRequestContext(lspServices, logger, intialWorkspace, checker) member _.GetContext() = context member _.UpdateWorkspace(f) = - context <- FSharpRequestContext(lspServices, logger, f context.Workspace) + context <- FSharpRequestContext(lspServices, logger, f context.Workspace, checker) type FShapRequestContextFactory(lspServices: ILspServices) = From 9a1de4181c9129fbee3ee8d7f725ef0982daf4a4 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 13 Feb 2024 18:19:55 +0100 Subject: [PATCH 08/60] wip --- .../FSharpLanguageServer.fs | 3 +- .../FSharp.VisualStudio.Extension.csproj | 4 +- .../FSharpLanguageServerProvider.cs | 58 +++++++++++++++++-- 3 files changed, 57 insertions(+), 8 deletions(-) diff --git a/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs index 2f6818ccc83..bf32ea29677 100644 --- a/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs +++ b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs @@ -29,9 +29,10 @@ type FSharpRequestContext(lspServices: ILspServices, logger: ILspLogger, workspa type ContextHolder(intialWorkspace, lspServices: ILspServices) = let logger = lspServices.GetRequiredService() + + // TODO: We need to get configuration for this somehow. Also make it replaceable when configuration changes. let checker = FSharpChecker.Create( - keepAssemblyContents=true, keepAllBackgroundResolutions=true, keepAllBackgroundSymbolUses=true, enableBackgroundItemKeyStoreAndSemanticClassification=true, diff --git a/src/FSharp.VisualStudio.Extension/FSharp.VisualStudio.Extension.csproj b/src/FSharp.VisualStudio.Extension/FSharp.VisualStudio.Extension.csproj index 4e7fee4d770..bbcbdb6e3af 100644 --- a/src/FSharp.VisualStudio.Extension/FSharp.VisualStudio.Extension.csproj +++ b/src/FSharp.VisualStudio.Extension/FSharp.VisualStudio.Extension.csproj @@ -1,4 +1,4 @@ - + net8.0-windows enable @@ -12,7 +12,7 @@ - + diff --git a/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs b/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs index ef4f8476166..c520bf22207 100644 --- a/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs +++ b/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs @@ -45,21 +45,44 @@ public ServerCapabilities OverrideServerCapabilities(ServerCapabilities value) var capabilities = new VSInternalServerCapabilities { TextDocumentSync = value.TextDocumentSync, - SupportsDiagnosticRequests = true + SupportsDiagnosticRequests = true, + ProjectContextProvider = true, + DiagnosticProvider = new() + { + SupportsMultipleContextsDiagnostics = true, + DiagnosticKinds = [ + // Support a specialized requests dedicated to task-list items. This way the client can ask just + // for these, independently of other diagnostics. They can also throttle themselves to not ask if + // the task list would not be visible. + VSInternalDiagnosticKind.Task, + // Dedicated request for workspace-diagnostics only. We will only respond to these if FSA is on. + VSInternalDiagnosticKind.Syntax, + // Fine-grained diagnostics requests. Importantly, this separates out syntactic vs semantic + // requests, allowing the former to quickly reach the user without blocking on the latter. In a + // similar vein, compiler diagnostics are explicitly distinct from analyzer-diagnostics, allowing + // the former to appear as soon as possible as they are much more critical for the user and should + // not be delayed by a slow analyzer. + new("Semantic"), + //new(PullDiagnosticCategories.DocumentAnalyzerSyntax), + //new(PullDiagnosticCategories.DocumentAnalyzerSemantic), + ] + } }; return capabilities; } } -internal class VsDiagnosticsHandler : IRequestHandler +internal class VsDiagnosticsHandler + : IRequestHandler, + IRequestHandler { public bool MutatesSolutionState => false; [LanguageServerEndpoint(VSInternalMethods.DocumentPullDiagnosticName)] public Task HandleRequestAsync(VSInternalDocumentDiagnosticsParams request, FSharpRequestContext context, CancellationToken cancellationToken) { - var rep = new VSInternalDiagnosticReport + var rep = new VSInternalDiagnosticReport { ResultId = "potato1", // Has to be present for diagnostic to show up //Identifier = 69, @@ -82,6 +105,28 @@ public Task HandleRequestAsync(VSInternalDocumentD }; return Task.FromResult(new[] { rep }); } + + [LanguageServerEndpoint("textDocument/_vs_getProjectContexts")] + public Task HandleRequestAsync(VSGetProjectContextsParams request, FSharpRequestContext context, CancellationToken cancellationToken) + { + return Task.FromResult(new VSProjectContextList() + { + DefaultIndex = 0, + ProjectContexts = [ + new() { + Id = "potato", + Label = "Potato", + Kind = VSProjectKind.VisualBasic + }, + new () { + Id = "potato2", + Label = "Potato2", + Kind = VSProjectKind.VisualBasic + } + + ] + }); + } } @@ -106,9 +151,11 @@ internal class FSharpLanguageServerProvider : LanguageServerProvider /// public override async Task CreateServerConnectionAsync(CancellationToken cancellationToken) { - var what = this.Extensibility.Workspaces(); + var ws = this.Extensibility.Workspaces(); //var result = what.QueryProjectsAsync(project => project.With(p => p.Kind == "6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705"), cancellationToken).Result; - IQueryResults? result = await what.QueryProjectsAsync(project => project, cancellationToken); + IQueryResults? result = await ws.QueryProjectsAsync(project => project.With(p => new { p.ActiveConfigurations, p.Id, p.Guid }), cancellationToken); + + var x = await ws.QuerySolutionAsync(solution => solution.With(s => new { s.Path, s.Guid, s.ActiveConfiguration, s.ActivePlatform }), cancellationToken); foreach (var project in result) { @@ -158,6 +205,7 @@ private void ProcessProject(IProjectSnapshot project) var references = project.ProjectReferences.Please(); var properties = project.Properties.Please(); + var id = project.Id; var configurationDimensions = project.ConfigurationDimensions.Please(); var configurations = project.Configurations.Please(); From d557b62f766f7c1567642f5e3ef81f6b4b6d5660 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Thu, 22 Feb 2024 19:23:29 +0100 Subject: [PATCH 09/60] WIP workspace state management, diagnostics --- src/Compiler/FSharp.Compiler.Service.fsproj | 5 +- src/Compiler/Facilities/Hashing.fs | 6 +- .../FSharpLanguageServer.fs | 63 +++++--- .../FSharpWorkspace.fs | 134 ++++++++++++++++-- .../FSharpLanguageServerProvider.cs | 43 +++--- 5 files changed, 198 insertions(+), 53 deletions(-) diff --git a/src/Compiler/FSharp.Compiler.Service.fsproj b/src/Compiler/FSharp.Compiler.Service.fsproj index dd7b6e25c1a..301395f7204 100644 --- a/src/Compiler/FSharp.Compiler.Service.fsproj +++ b/src/Compiler/FSharp.Compiler.Service.fsproj @@ -77,8 +77,9 @@ - - + + + diff --git a/src/Compiler/Facilities/Hashing.fs b/src/Compiler/Facilities/Hashing.fs index 2dfbb38b7ee..fa9ff776b6f 100644 --- a/src/Compiler/Facilities/Hashing.fs +++ b/src/Compiler/Facilities/Hashing.fs @@ -46,7 +46,11 @@ module internal Md5Hasher = let private md5 = new ThreadLocal<_>(fun () -> System.Security.Cryptography.MD5.Create()) - let computeHash (bytes: byte array) = md5.Value.ComputeHash(bytes) + let computeHash (bytes: byte array) = + // md5.Value.ComputeHash(bytes) TODO: the threadlocal is not working in new VS extension + ignore md5 + let md5 = System.Security.Cryptography.MD5.Create() + md5.ComputeHash(bytes) let empty = Array.empty diff --git a/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs index bf32ea29677..cd0ba8ddb9e 100644 --- a/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs +++ b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs @@ -1,5 +1,9 @@ namespace FSharp.Compiler.LanguageServer +open System.Runtime.CompilerServices + +#nowarn "57" + open System open System.Threading.Tasks open System.Threading @@ -20,12 +24,34 @@ module Stuff = [] let FSharpLanguageName = "F#" -type FSharpRequestContext(lspServices: ILspServices, logger: ILspLogger, workspace: FSharpWorkspace, checker) = +[] +type Extensions = + + [] + static member Please(this: Async<'t>, ct) = + Async.StartAsTask(this, cancellationToken = ct) + +type FSharpRequestContext(lspServices: ILspServices, logger: ILspLogger, workspace: FSharpWorkspace, checker: FSharpChecker) = member _.LspServices = lspServices member _.Logger = logger member _.Workspace = workspace member _.Checker = checker + // TODO: split to parse and check diagnostics + member _.GetDiagnosticsForFile(file: Uri) = + + workspace.GetSnapshotForFile file + |> Option.map (fun snapshot -> + async { + let! parseResult, checkFileAnswer = checker.ParseAndCheckFileInProject(file.LocalPath, snapshot, "LSP Get diagnostics") + + return + match checkFileAnswer with + | FSharpCheckFileAnswer.Succeeded result -> result.Diagnostics + | FSharpCheckFileAnswer.Aborted -> parseResult.Diagnostics + }) + |> Option.defaultValue (async.Return [||]) + type ContextHolder(intialWorkspace, lspServices: ILspServices) = let logger = lspServices.GetRequiredService() @@ -33,16 +59,18 @@ type ContextHolder(intialWorkspace, lspServices: ILspServices) = // TODO: We need to get configuration for this somehow. Also make it replaceable when configuration changes. let checker = FSharpChecker.Create( - keepAllBackgroundResolutions=true, - keepAllBackgroundSymbolUses=true, - enableBackgroundItemKeyStoreAndSemanticClassification=true, - enablePartialTypeChecking=true, - parallelReferenceResolution=true, - captureIdentifiersWhenParsing=true, - useSyntaxTreeCache=true, - useTransparentCompiler=true) + keepAllBackgroundResolutions = true, + keepAllBackgroundSymbolUses = true, + enableBackgroundItemKeyStoreAndSemanticClassification = true, + enablePartialTypeChecking = true, + parallelReferenceResolution = true, + captureIdentifiersWhenParsing = true, + useSyntaxTreeCache = true, + useTransparentCompiler = true + ) - let mutable context = FSharpRequestContext(lspServices, logger, intialWorkspace, checker) + let mutable context = + FSharpRequestContext(lspServices, logger, intialWorkspace, checker) member _.GetContext() = context @@ -77,10 +105,7 @@ type DocumentStateHandler() = ) = let contextHolder = context.LspServices.GetRequiredService() - contextHolder.UpdateWorkspace(fun w -> - { w with - OpenFiles = Map.add request.TextDocument.Uri.AbsolutePath request.TextDocument.Text w.OpenFiles - }) + contextHolder.UpdateWorkspace _.OpenFile(request.TextDocument.Uri, request.TextDocument.Text) Task.FromResult(SemanticTokensDeltaPartialResult()) @@ -94,10 +119,7 @@ type DocumentStateHandler() = ) = let contextHolder = context.LspServices.GetRequiredService() - contextHolder.UpdateWorkspace(fun w -> - { w with - OpenFiles = Map.add request.TextDocument.Uri.AbsolutePath request.ContentChanges.[0].Text w.OpenFiles - }) + contextHolder.UpdateWorkspace _.ChangeFile(request.TextDocument.Uri, request.ContentChanges.[0].Text) Task.FromResult(SemanticTokensDeltaPartialResult()) @@ -111,10 +133,7 @@ type DocumentStateHandler() = ) = let contextHolder = context.LspServices.GetRequiredService() - contextHolder.UpdateWorkspace(fun w -> - { w with - OpenFiles = Map.remove request.TextDocument.Uri.AbsolutePath w.OpenFiles - }) + contextHolder.UpdateWorkspace _.CloseFile(request.TextDocument.Uri) Task.CompletedTask diff --git a/src/FSharp.Compiler.LanguageServer/FSharpWorkspace.fs b/src/FSharp.Compiler.LanguageServer/FSharpWorkspace.fs index 9d792ffda24..c5a302bb54c 100644 --- a/src/FSharp.Compiler.LanguageServer/FSharpWorkspace.fs +++ b/src/FSharp.Compiler.LanguageServer/FSharpWorkspace.fs @@ -1,18 +1,134 @@ module FSharp.Compiler.LanguageServer.Workspace +open FSharp.Compiler.Text + #nowarn "57" open System +open System.Threading.Tasks open FSharp.Compiler.CodeAnalysis.ProjectSnapshot -type FSharpWorkspace = - { - Projects: Map - OpenFiles: Map - } +/// Holds a project snapshot and a queue of changes that will be applied to it when it's requested +/// +/// The assumption is that this is faster than actually applying the changes to the snapshot immediately and that +/// we will be doing this on potentially every keystroke. But this should probably be measured at some point. +type SnapshotHolder(snapshot: FSharpProjectSnapshot, changedFiles: Set, openFiles: Map) = + + let applyFileChangesToSnapshot () = + let files = + changedFiles + |> Seq.map (fun filePath -> + match openFiles.TryFind filePath with + | Some content -> + FSharpFileSnapshot.Create( + filePath, + DateTime.Now.Ticks.ToString(), + fun () -> content |> SourceTextNew.ofString |> Task.FromResult + ) + | None -> FSharpFileSnapshot.CreateFromFileSystem(filePath)) + |> Seq.toList + + snapshot.Replace files + + // We don't want to mutate the workspace by applying the changes when snapshot is requested because that would force the language + // requests to be processed sequentially. So instead we keep the change application under lazy so it's still only computed if needed + // and only once and workspace doesn't change. + let appliedChanges = + lazy SnapshotHolder(applyFileChangesToSnapshot (), Set.empty, openFiles) + + member private _.snapshot = snapshot + member private _.changedFiles = changedFiles + + member private this.GetMostUpToDateInstance() = + if appliedChanges.IsValueCreated then + appliedChanges.Value + else + this + + member this.WithFileChanged(file, openFiles) = + let previous = this.GetMostUpToDateInstance() + SnapshotHolder(previous.snapshot, previous.changedFiles.Add file, openFiles) + + member this.WithoutFileChanged(file, openFiles) = + let previous = this.GetMostUpToDateInstance() + SnapshotHolder(previous.snapshot, previous.changedFiles.Remove file, openFiles) + + member _.GetSnapshot() = appliedChanges.Value.snapshot + + static member Of(snapshot: FSharpProjectSnapshot) = + SnapshotHolder(snapshot, Set.empty, Map.empty) + +type FSharpWorkspace + private + ( + projects: Map, + openFiles: Map, + fileMap: Map> + ) = + + let updateProjectsWithFile (file: Uri) f (projects: Map) = + fileMap + |> Map.tryFind file.LocalPath + |> Option.map (fun identifier -> + (projects, identifier) + ||> Seq.fold (fun projects identifier -> + let snapshotHolder = projects[identifier] + projects.Add(identifier, f snapshotHolder))) + |> Option.defaultValue projects + + member _.Projects = projects + member _.OpenFiles = openFiles + member _.FileMap = fileMap + + member this.OpenFile(file: Uri, content: string) = this.ChangeFile(file, content) + + member _.CloseFile(file: Uri) = + let openFiles = openFiles.Remove(file.LocalPath) + + FSharpWorkspace( + projects = + (projects + |> updateProjectsWithFile file _.WithoutFileChanged(file.LocalPath, openFiles)), + openFiles = openFiles, + fileMap = fileMap + ) + + member _.ChangeFile(file: Uri, content: string) = + + // TODO: should we assert that the file is open? + + let openFiles = openFiles.Add(file.LocalPath, content) + + FSharpWorkspace( + projects = + (projects + |> updateProjectsWithFile file _.WithFileChanged(file.LocalPath, openFiles)), + openFiles = openFiles, + fileMap = fileMap + ) + + member _.GetSnapshotForFile(file: Uri) = + fileMap + |> Map.tryFind file.LocalPath + + // TODO: eventually we need to deal with choosing the appropriate project here + // Hopefully we will be able to do it through receiving project context from LSP + // Otherwise we have to keep track of which project/configuration is active + |> Option.bind Seq.tryHead + + |> Option.bind projects.TryFind + |> Option.map _.GetSnapshot() static member Create(projects: FSharpProjectSnapshot seq) = - { - Projects = Map.ofSeq (projects |> Seq.map (fun p -> p.Identifier, p)) - OpenFiles = Map.empty - } + FSharpWorkspace( + projects = Map.ofSeq (projects |> Seq.map (fun p -> p.Identifier, SnapshotHolder.Of p)), + openFiles = Map.empty, + fileMap = + (projects + |> Seq.collect (fun p -> + p.ProjectSnapshot.SourceFileNames + |> Seq.map (fun f -> Uri(f).LocalPath, p.Identifier)) + |> Seq.groupBy fst + |> Seq.map (fun (f, ps) -> f, Set.ofSeq (ps |> Seq.map snd)) + |> Map.ofSeq) + ) diff --git a/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs b/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs index c520bf22207..d8880e08835 100644 --- a/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs +++ b/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs @@ -73,37 +73,41 @@ public ServerCapabilities OverrideServerCapabilities(ServerCapabilities value) } -internal class VsDiagnosticsHandler +internal class VsDiagnosticsHandler : IRequestHandler, IRequestHandler { public bool MutatesSolutionState => false; [LanguageServerEndpoint(VSInternalMethods.DocumentPullDiagnosticName)] - public Task HandleRequestAsync(VSInternalDocumentDiagnosticsParams request, FSharpRequestContext context, CancellationToken cancellationToken) + public async Task HandleRequestAsync(VSInternalDocumentDiagnosticsParams request, FSharpRequestContext context, CancellationToken cancellationToken) { - var rep = new VSInternalDiagnosticReport + var result = await context.GetDiagnosticsForFile(request!.TextDocument!.Uri).Please(cancellationToken); + + var rep = new VSInternalDiagnosticReport { ResultId = "potato1", // Has to be present for diagnostic to show up //Identifier = 69, //Version = 1, Diagnostics = - [ - new Diagnostic - { - Range = new Microsoft.VisualStudio.LanguageServer.Protocol.Range - { - Start = new Position { Line = 0, Character = 0 }, - End = new Position { Line = 0, Character = 1 } - }, - Severity = DiagnosticSeverity.Error, - Message = "This is a test diagnostic", - //Source = "Intellisense", - Code = "1234" - } - ] + result.Select(d => + + new Diagnostic + { + Range = new Microsoft.VisualStudio.LanguageServer.Protocol.Range + { + Start = new Position { Line = d.StartLine, Character = d.StartColumn }, + End = new Position { Line = d.EndLine, Character = d.EndColumn } + }, + Severity = DiagnosticSeverity.Error, + Message = $"LSP: {d.Message}", + //Source = "Intellisense", + Code = d.ErrorNumberText + } + ).ToArray() }; - return Task.FromResult(new[] { rep }); + + return [rep]; } [LanguageServerEndpoint("textDocument/_vs_getProjectContexts")] @@ -116,6 +120,7 @@ public Task HandleRequestAsync(VSGetProjectContextsParams new() { Id = "potato", Label = "Potato", + // PR for F# project kind: https://devdiv.visualstudio.com/DevDiv/_git/VSLanguageServerClient/pullrequest/529882 Kind = VSProjectKind.VisualBasic }, new () { @@ -174,7 +179,7 @@ internal class FSharpLanguageServerProvider : LanguageServerProvider try { // Some hardcoded projects before we create them from the ProjectQuery - var projectsRoot = @"D:\code\FS"; + var projectsRoot = @"D:\code"; var giraffe = FSharpProjectSnapshot.FromResponseFile( new FileInfo(Path.Combine(projectsRoot, @"Giraffe\src\Giraffe\Giraffe.rsp")), Path.Combine(projectsRoot, @"Giraffe\src\Giraffe\Giraffe.fsproj")); From 586fff5df2a69ae09b65e99b557286dec8dddc42 Mon Sep 17 00:00:00 2001 From: Adam Boniecki Date: Fri, 15 Mar 2024 12:52:50 +0100 Subject: [PATCH 10/60] Add minimal solution file --- VSFSharpExtension.sln | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 VSFSharpExtension.sln diff --git a/VSFSharpExtension.sln b/VSFSharpExtension.sln new file mode 100644 index 00000000000..ea65dc0c6f4 --- /dev/null +++ b/VSFSharpExtension.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B4A1E626-4A48-4977-B291-219882DB3413}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.Compiler.Service", "src\Compiler\FSharp.Compiler.Service.fsproj", "{0F25FA8C-3A2A-4908-BDB6-23D90946502C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FSharp.VisualStudio.Extension", "src\FSharp.VisualStudio.Extension\FSharp.VisualStudio.Extension.csproj", "{14B9AB0E-2FC0-43F1-9775-20C262202D12}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0F25FA8C-3A2A-4908-BDB6-23D90946502C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0F25FA8C-3A2A-4908-BDB6-23D90946502C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0F25FA8C-3A2A-4908-BDB6-23D90946502C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0F25FA8C-3A2A-4908-BDB6-23D90946502C}.Release|Any CPU.Build.0 = Release|Any CPU + {14B9AB0E-2FC0-43F1-9775-20C262202D12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {14B9AB0E-2FC0-43F1-9775-20C262202D12}.Debug|Any CPU.Build.0 = Debug|Any CPU + {14B9AB0E-2FC0-43F1-9775-20C262202D12}.Release|Any CPU.ActiveCfg = Release|Any CPU + {14B9AB0E-2FC0-43F1-9775-20C262202D12}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {0F25FA8C-3A2A-4908-BDB6-23D90946502C} = {B4A1E626-4A48-4977-B291-219882DB3413} + {14B9AB0E-2FC0-43F1-9775-20C262202D12} = {B4A1E626-4A48-4977-B291-219882DB3413} + EndGlobalSection +EndGlobal From 524c1c25c6a7cd8b70522b6771d1c17483a184a4 Mon Sep 17 00:00:00 2001 From: Adam Boniecki Date: Tue, 26 Mar 2024 14:07:12 +0100 Subject: [PATCH 11/60] Get compiler cmdline args via ProjectQuery --- VSFSharpExtension.sln | 19 +++--- src/Compiler/Service/FSharpProjectSnapshot.fs | 5 +- .../FSharp.VisualStudio.Extension.csproj | 8 ++- .../FSharpLanguageServerProvider.cs | 59 +++++++++++++++---- 4 files changed, 65 insertions(+), 26 deletions(-) diff --git a/VSFSharpExtension.sln b/VSFSharpExtension.sln index ea65dc0c6f4..0c3c7d1a184 100644 --- a/VSFSharpExtension.sln +++ b/VSFSharpExtension.sln @@ -5,30 +5,29 @@ VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B4A1E626-4A48-4977-B291-219882DB3413}" EndProject -Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.Compiler.Service", "src\Compiler\FSharp.Compiler.Service.fsproj", "{0F25FA8C-3A2A-4908-BDB6-23D90946502C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FSharp.VisualStudio.Extension", "src\FSharp.VisualStudio.Extension\FSharp.VisualStudio.Extension.csproj", "{14B9AB0E-2FC0-43F1-9775-20C262202D12}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FSharp.VisualStudio.Extension", "src\FSharp.VisualStudio.Extension\FSharp.VisualStudio.Extension.csproj", "{14B9AB0E-2FC0-43F1-9775-20C262202D12}" +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Compiler.LanguageServer", "src\FSharp.Compiler.LanguageServer\FSharp.Compiler.LanguageServer.fsproj", "{7EDDFB35-2428-4433-8ABF-47233480B746}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0F25FA8C-3A2A-4908-BDB6-23D90946502C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0F25FA8C-3A2A-4908-BDB6-23D90946502C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0F25FA8C-3A2A-4908-BDB6-23D90946502C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0F25FA8C-3A2A-4908-BDB6-23D90946502C}.Release|Any CPU.Build.0 = Release|Any CPU {14B9AB0E-2FC0-43F1-9775-20C262202D12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {14B9AB0E-2FC0-43F1-9775-20C262202D12}.Debug|Any CPU.Build.0 = Debug|Any CPU {14B9AB0E-2FC0-43F1-9775-20C262202D12}.Release|Any CPU.ActiveCfg = Release|Any CPU {14B9AB0E-2FC0-43F1-9775-20C262202D12}.Release|Any CPU.Build.0 = Release|Any CPU + {7EDDFB35-2428-4433-8ABF-47233480B746}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7EDDFB35-2428-4433-8ABF-47233480B746}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7EDDFB35-2428-4433-8ABF-47233480B746}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7EDDFB35-2428-4433-8ABF-47233480B746}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {0F25FA8C-3A2A-4908-BDB6-23D90946502C} = {B4A1E626-4A48-4977-B291-219882DB3413} {14B9AB0E-2FC0-43F1-9775-20C262202D12} = {B4A1E626-4A48-4977-B291-219882DB3413} EndGlobalSection EndGlobal diff --git a/src/Compiler/Service/FSharpProjectSnapshot.fs b/src/Compiler/Service/FSharpProjectSnapshot.fs index 38d62f974f8..14fca7148b9 100644 --- a/src/Compiler/Service/FSharpProjectSnapshot.fs +++ b/src/Compiler/Service/FSharpProjectSnapshot.fs @@ -679,6 +679,9 @@ and [] FSha let compilerArgs = File.ReadAllLines responseFile.FullName + FSharpProjectSnapshot.FromCommandLineArgs(compilerArgs, responseFile.DirectoryName, projectFileName) + + static member FromCommandLineArgs(compilerArgs: string array, directoryPath: string, projectFileName) = let fsharpFileExtensions = set [| ".fs"; ".fsi"; ".fsx" |] let isFSharpFile (file: string) = @@ -693,7 +696,7 @@ and [] FSha None else - let fullPath = Path.Combine(responseFile.DirectoryName, line) + let fullPath = Path.Combine(directoryPath, line) if not (File.Exists fullPath) then None else Some fullPath) |> Array.toList diff --git a/src/FSharp.VisualStudio.Extension/FSharp.VisualStudio.Extension.csproj b/src/FSharp.VisualStudio.Extension/FSharp.VisualStudio.Extension.csproj index bbcbdb6e3af..bef467713a0 100644 --- a/src/FSharp.VisualStudio.Extension/FSharp.VisualStudio.Extension.csproj +++ b/src/FSharp.VisualStudio.Extension/FSharp.VisualStudio.Extension.csproj @@ -9,10 +9,12 @@ - - + + - + + + diff --git a/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs b/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs index d8880e08835..3a282fe12d3 100644 --- a/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs +++ b/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs @@ -157,13 +157,46 @@ internal class FSharpLanguageServerProvider : LanguageServerProvider public override async Task CreateServerConnectionAsync(CancellationToken cancellationToken) { var ws = this.Extensibility.Workspaces(); - //var result = what.QueryProjectsAsync(project => project.With(p => p.Kind == "6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705"), cancellationToken).Result; - IQueryResults? result = await ws.QueryProjectsAsync(project => project.With(p => new { p.ActiveConfigurations, p.Id, p.Guid }), cancellationToken); - var x = await ws.QuerySolutionAsync(solution => solution.With(s => new { s.Path, s.Guid, s.ActiveConfiguration, s.ActivePlatform }), cancellationToken); + IQueryResults? result = await ws.QueryProjectsAsync(project => project + .With(p => p.ActiveConfigurations + .With(c => c.RuleResultsByRuleName("CompilerCommandLineArgs") + .With(r => r.RuleName) + .With(r => r.Items))) + .With(p => new { p.ActiveConfigurations, p.Id, p.Guid }), cancellationToken); + + List<(string, string)> projectsAndCommandLineArgs = []; foreach (var project in result) { + project.Id.TryGetValue("ProjectPath", out var projectPath); + + List commandLineArgs = []; + if (projectPath != null) + { + // There can be multiple Active Configurations, e.g. one for net8.0 and one for net472 + // TODO For now taking any single one of them, but we might actually want to pick specific one + var config = project.ActiveConfigurations.FirstOrDefault(); + if (config != null) + { + foreach (var ruleResults in config.RuleResults) + { + // XXX Idk why `.Where` does not work with these IAsyncQuerable type + if (ruleResults?.RuleName == "CompilerCommandLineArguments") + { + // XXX Not sure why there would be more than one item for this rule result + // Taking first one, ignoring the rest + var args = ruleResults?.Items?.FirstOrDefault()?.Name; + if (args != null) commandLineArgs.Add(args); + } + } + } + if (commandLineArgs.Count > 0) + { + projectsAndCommandLineArgs.Add((projectPath, commandLineArgs[0])); + } + } + try { this.ProcessProject(project); @@ -178,15 +211,17 @@ internal class FSharpLanguageServerProvider : LanguageServerProvider try { - // Some hardcoded projects before we create them from the ProjectQuery - var projectsRoot = @"D:\code"; - var giraffe = FSharpProjectSnapshot.FromResponseFile( - new FileInfo(Path.Combine(projectsRoot, @"Giraffe\src\Giraffe\Giraffe.rsp")), - Path.Combine(projectsRoot, @"Giraffe\src\Giraffe\Giraffe.fsproj")); - var giraffeTests = FSharpProjectSnapshot.FromResponseFile( - new FileInfo(Path.Combine(projectsRoot, @"Giraffe\tests\Giraffe.Tests\Giraffe.Tests.rsp")), - Path.Combine(projectsRoot, @"Giraffe\tests\Giraffe.Tests\Giraffe.Tests.fsproj")); - workspace = FSharpWorkspace.Create([giraffe, giraffeTests]); + List snapshots = []; + foreach(var args in projectsAndCommandLineArgs) + { + var lines = args.Item2.Split(';'); // XXX Probably not robust enough + var path = args.Item1; + + var snapshot = FSharpProjectSnapshot.FromCommandLineArgs( + lines, Path.GetDirectoryName(path), Path.GetFileName(path)); + snapshots.Add(snapshot); + } + workspace = FSharpWorkspace.Create(snapshots); } catch { From c10068381881cd4c4806ad676ccf8c35c73dc99a Mon Sep 17 00:00:00 2001 From: Adam Boniecki Date: Tue, 30 Apr 2024 17:32:08 +0200 Subject: [PATCH 12/60] Adjust line numbers and fix a typo F# uses 1-based indexing, so we need to adjust line numbers in diagnostics sent to VS lsp client --- .../FSharpLanguageServerProvider.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs b/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs index 3a282fe12d3..f4a827e16bb 100644 --- a/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs +++ b/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs @@ -96,8 +96,9 @@ public async Task HandleRequestAsync(VSInternalDoc { Range = new Microsoft.VisualStudio.LanguageServer.Protocol.Range { - Start = new Position { Line = d.StartLine, Character = d.StartColumn }, - End = new Position { Line = d.EndLine, Character = d.EndColumn } + // F# uses 1-based indexing for lines, need to adjust + Start = new Position { Line = d.StartLine-1, Character = d.StartColumn }, + End = new Position { Line = d.EndLine-1, Character = d.EndColumn } }, Severity = DiagnosticSeverity.Error, Message = $"LSP: {d.Message}", @@ -182,7 +183,7 @@ internal class FSharpLanguageServerProvider : LanguageServerProvider foreach (var ruleResults in config.RuleResults) { // XXX Idk why `.Where` does not work with these IAsyncQuerable type - if (ruleResults?.RuleName == "CompilerCommandLineArguments") + if (ruleResults?.RuleName == "CompilerCommandLineArgs") { // XXX Not sure why there would be more than one item for this rule result // Taking first one, ignoring the rest From 3d58a1f66562b1ec9bf2b4c32de6c63359470d37 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Mon, 27 May 2024 11:41:26 +0200 Subject: [PATCH 13/60] Structure LSP for better colalboration (#17246) * Structure LSP for better colalboration * Add hover --- VSFSharpExtension.sln | 22 ++ .../Common/CapabilitiesManager.fs | 36 ++++ .../Common/FSharpRequestContext.fs | 70 +++++++ .../{ => Common}/FSharpWorkspace.fs | 2 +- .../Common/LifecycleManager.fs | 41 ++++ .../FSharp.Compiler.LanguageServer.fsproj | 7 +- .../FSharpLanguageServer.fs | 194 +----------------- .../Handlers/DocumentStateHandler.fs | 54 +++++ .../Handlers/LanguageFeaturesHandler.fs | 22 ++ .../FSharpLanguageServerProvider.cs | 9 +- 10 files changed, 260 insertions(+), 197 deletions(-) create mode 100644 src/FSharp.Compiler.LanguageServer/Common/CapabilitiesManager.fs create mode 100644 src/FSharp.Compiler.LanguageServer/Common/FSharpRequestContext.fs rename src/FSharp.Compiler.LanguageServer/{ => Common}/FSharpWorkspace.fs (99%) create mode 100644 src/FSharp.Compiler.LanguageServer/Common/LifecycleManager.fs create mode 100644 src/FSharp.Compiler.LanguageServer/Handlers/DocumentStateHandler.fs create mode 100644 src/FSharp.Compiler.LanguageServer/Handlers/LanguageFeaturesHandler.fs diff --git a/VSFSharpExtension.sln b/VSFSharpExtension.sln index 0c3c7d1a184..6bafe7599d9 100644 --- a/VSFSharpExtension.sln +++ b/VSFSharpExtension.sln @@ -9,20 +9,42 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FSharp.VisualStudio.Extensi EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Compiler.LanguageServer", "src\FSharp.Compiler.LanguageServer\FSharp.Compiler.LanguageServer.fsproj", "{7EDDFB35-2428-4433-8ABF-47233480B746}" EndProject +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Compiler.Service", "src\Compiler\FSharp.Compiler.Service.fsproj", "{0B24DCA6-902F-409E-A18C-7B1BFDDC8849}" +EndProject +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Core", "src\FSharp.Core\FSharp.Core.fsproj", "{5A4B66D5-A6C3-402C-A010-7A3635098DA7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Proto|Any CPU = Proto|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {14B9AB0E-2FC0-43F1-9775-20C262202D12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {14B9AB0E-2FC0-43F1-9775-20C262202D12}.Debug|Any CPU.Build.0 = Debug|Any CPU + {14B9AB0E-2FC0-43F1-9775-20C262202D12}.Proto|Any CPU.ActiveCfg = Release|Any CPU + {14B9AB0E-2FC0-43F1-9775-20C262202D12}.Proto|Any CPU.Build.0 = Release|Any CPU + {14B9AB0E-2FC0-43F1-9775-20C262202D12}.Proto|Any CPU.Deploy.0 = Release|Any CPU {14B9AB0E-2FC0-43F1-9775-20C262202D12}.Release|Any CPU.ActiveCfg = Release|Any CPU {14B9AB0E-2FC0-43F1-9775-20C262202D12}.Release|Any CPU.Build.0 = Release|Any CPU {7EDDFB35-2428-4433-8ABF-47233480B746}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7EDDFB35-2428-4433-8ABF-47233480B746}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7EDDFB35-2428-4433-8ABF-47233480B746}.Proto|Any CPU.ActiveCfg = Release|Any CPU + {7EDDFB35-2428-4433-8ABF-47233480B746}.Proto|Any CPU.Build.0 = Release|Any CPU {7EDDFB35-2428-4433-8ABF-47233480B746}.Release|Any CPU.ActiveCfg = Release|Any CPU {7EDDFB35-2428-4433-8ABF-47233480B746}.Release|Any CPU.Build.0 = Release|Any CPU + {0B24DCA6-902F-409E-A18C-7B1BFDDC8849}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0B24DCA6-902F-409E-A18C-7B1BFDDC8849}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0B24DCA6-902F-409E-A18C-7B1BFDDC8849}.Proto|Any CPU.ActiveCfg = Debug|Any CPU + {0B24DCA6-902F-409E-A18C-7B1BFDDC8849}.Proto|Any CPU.Build.0 = Debug|Any CPU + {0B24DCA6-902F-409E-A18C-7B1BFDDC8849}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0B24DCA6-902F-409E-A18C-7B1BFDDC8849}.Release|Any CPU.Build.0 = Release|Any CPU + {5A4B66D5-A6C3-402C-A010-7A3635098DA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5A4B66D5-A6C3-402C-A010-7A3635098DA7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5A4B66D5-A6C3-402C-A010-7A3635098DA7}.Proto|Any CPU.ActiveCfg = Proto|Any CPU + {5A4B66D5-A6C3-402C-A010-7A3635098DA7}.Proto|Any CPU.Build.0 = Proto|Any CPU + {5A4B66D5-A6C3-402C-A010-7A3635098DA7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5A4B66D5-A6C3-402C-A010-7A3635098DA7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/FSharp.Compiler.LanguageServer/Common/CapabilitiesManager.fs b/src/FSharp.Compiler.LanguageServer/Common/CapabilitiesManager.fs new file mode 100644 index 00000000000..0d2b42e7d05 --- /dev/null +++ b/src/FSharp.Compiler.LanguageServer/Common/CapabilitiesManager.fs @@ -0,0 +1,36 @@ +namespace FSharp.Compiler.LanguageServer.Common + +open Microsoft.VisualStudio.LanguageServer.Protocol +open Microsoft.CommonLanguageServerProtocol.Framework + +type IServerCapabilitiesOverride = + abstract member OverrideServerCapabilities: ServerCapabilities -> ServerCapabilities + +type CapabilitiesManager(scOverrides: IServerCapabilitiesOverride seq) = + + let mutable initializeParams = None + + let defaultCapabilities = + ServerCapabilities( + TextDocumentSync = TextDocumentSyncOptions(OpenClose = true, Change = TextDocumentSyncKind.Full), + DiagnosticOptions = + DiagnosticOptions(WorkDoneProgress = true, InterFileDependencies = true, Identifier = "potato", WorkspaceDiagnostics = true), + CompletionProvider = + CompletionOptions(TriggerCharacters=[|"."; " "|], ResolveProvider=true, WorkDoneProgress=true), + HoverProvider = SumType(HoverOptions(WorkDoneProgress = true)) + ) + + interface IInitializeManager with + member this.SetInitializeParams(request) = initializeParams <- Some request + + member this.GetInitializeResult() = + let serverCapabilities = + (defaultCapabilities, scOverrides) + ||> Seq.fold (fun acc (x: IServerCapabilitiesOverride) -> x.OverrideServerCapabilities acc) + + InitializeResult(Capabilities = serverCapabilities) + + member this.GetInitializeParams() = + match initializeParams with + | Some params' -> params' + | None -> failwith "InitializeParams is null" \ No newline at end of file diff --git a/src/FSharp.Compiler.LanguageServer/Common/FSharpRequestContext.fs b/src/FSharp.Compiler.LanguageServer/Common/FSharpRequestContext.fs new file mode 100644 index 00000000000..c360e7527e2 --- /dev/null +++ b/src/FSharp.Compiler.LanguageServer/Common/FSharpRequestContext.fs @@ -0,0 +1,70 @@ +namespace FSharp.Compiler.LanguageServer.Common + +open Microsoft.CommonLanguageServerProtocol.Framework +open FSharp.Compiler.CodeAnalysis +open System +open System.Threading +open System.Threading.Tasks + +#nowarn "57" + +type FSharpRequestContext(lspServices: ILspServices, logger: ILspLogger, workspace: FSharpWorkspace, checker: FSharpChecker) = + member _.LspServices = lspServices + member _.Logger = logger + member _.Workspace = workspace + member _.Checker = checker + + // TODO: split to parse and check diagnostics + member _.GetDiagnosticsForFile(file: Uri) = + + workspace.GetSnapshotForFile file + |> Option.map (fun snapshot -> + async { + let! parseResult, checkFileAnswer = checker.ParseAndCheckFileInProject(file.LocalPath, snapshot, "LSP Get diagnostics") + + return + match checkFileAnswer with + | FSharpCheckFileAnswer.Succeeded result -> result.Diagnostics + | FSharpCheckFileAnswer.Aborted -> parseResult.Diagnostics + }) + |> Option.defaultValue (async.Return [||]) + +type ContextHolder(intialWorkspace, lspServices: ILspServices) = + + let logger = lspServices.GetRequiredService() + + // TODO: We need to get configuration for this somehow. Also make it replaceable when configuration changes. + let checker = + FSharpChecker.Create( + keepAllBackgroundResolutions = true, + keepAllBackgroundSymbolUses = true, + enableBackgroundItemKeyStoreAndSemanticClassification = true, + enablePartialTypeChecking = true, + parallelReferenceResolution = true, + captureIdentifiersWhenParsing = true, + useSyntaxTreeCache = true, + useTransparentCompiler = true + ) + + let mutable context = + FSharpRequestContext(lspServices, logger, intialWorkspace, checker) + + member _.GetContext() = context + + member _.UpdateWorkspace(f) = + context <- FSharpRequestContext(lspServices, logger, f context.Workspace, checker) + +type FShapRequestContextFactory(lspServices: ILspServices) = + + interface IRequestContextFactory with + + member _.CreateRequestContextAsync<'TRequestParam> + ( + queueItem: IQueueItem, + requestParam: 'TRequestParam, + cancellationToken: CancellationToken + ) = + lspServices.GetRequiredService() + |> _.GetContext() + |> Task.FromResult + diff --git a/src/FSharp.Compiler.LanguageServer/FSharpWorkspace.fs b/src/FSharp.Compiler.LanguageServer/Common/FSharpWorkspace.fs similarity index 99% rename from src/FSharp.Compiler.LanguageServer/FSharpWorkspace.fs rename to src/FSharp.Compiler.LanguageServer/Common/FSharpWorkspace.fs index c5a302bb54c..6fda31fc9c5 100644 --- a/src/FSharp.Compiler.LanguageServer/FSharpWorkspace.fs +++ b/src/FSharp.Compiler.LanguageServer/Common/FSharpWorkspace.fs @@ -1,4 +1,4 @@ -module FSharp.Compiler.LanguageServer.Workspace +namespace FSharp.Compiler.LanguageServer.Common open FSharp.Compiler.Text diff --git a/src/FSharp.Compiler.LanguageServer/Common/LifecycleManager.fs b/src/FSharp.Compiler.LanguageServer/Common/LifecycleManager.fs new file mode 100644 index 00000000000..14c6c4382ea --- /dev/null +++ b/src/FSharp.Compiler.LanguageServer/Common/LifecycleManager.fs @@ -0,0 +1,41 @@ +namespace FSharp.Compiler.LanguageServer.Common + +open Microsoft.CommonLanguageServerProtocol.Framework +open System +open StreamJsonRpc +open System.Threading.Tasks +open Microsoft.Extensions.DependencyInjection + +type LspServiceLifeCycleManager() = + + interface ILifeCycleManager with + member this.ShutdownAsync(message: string) = + task { + try + printfn "Shutting down" + with + | :? ObjectDisposedException + | :? ConnectionLostException -> () + } + + member this.ExitAsync() = Task.CompletedTask + +type FSharpLspServices(serviceCollection: IServiceCollection) as this = + + do serviceCollection.AddSingleton(this) |> ignore + + let serviceProvider = serviceCollection.BuildServiceProvider() + + interface ILspServices with + member this.GetRequiredService<'T>() : 'T = + serviceProvider.GetRequiredService<'T>() + + member this.TryGetService(t) = serviceProvider.GetService(t) + + member this.GetRequiredServices() = serviceProvider.GetServices() + + member this.GetRegisteredServices() = failwith "Not implemented" + + member this.SupportsGetRegisteredServices() = false + + member this.Dispose() = () diff --git a/src/FSharp.Compiler.LanguageServer/FSharp.Compiler.LanguageServer.fsproj b/src/FSharp.Compiler.LanguageServer/FSharp.Compiler.LanguageServer.fsproj index 8ea3cde0786..5a714ffd86e 100644 --- a/src/FSharp.Compiler.LanguageServer/FSharp.Compiler.LanguageServer.fsproj +++ b/src/FSharp.Compiler.LanguageServer/FSharp.Compiler.LanguageServer.fsproj @@ -27,8 +27,13 @@ + + + + + + - diff --git a/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs index cd0ba8ddb9e..70770760b13 100644 --- a/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs +++ b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs @@ -1,12 +1,10 @@ namespace FSharp.Compiler.LanguageServer open System.Runtime.CompilerServices - -#nowarn "57" +open FSharp.Compiler.LanguageServer.Common +open FSharp.Compiler.LanguageServer.Handlers open System -open System.Threading.Tasks -open System.Threading open Microsoft.CommonLanguageServerProtocol.Framework.Handlers open Microsoft.CommonLanguageServerProtocol.Framework open Microsoft.Extensions.DependencyInjection @@ -15,9 +13,6 @@ open Microsoft.VisualStudio.LanguageServer.Protocol open StreamJsonRpc open Nerdbank.Streams open System.Diagnostics -open System.IO -open Workspace -open FSharp.Compiler.CodeAnalysis [] module Stuff = @@ -31,191 +26,6 @@ type Extensions = static member Please(this: Async<'t>, ct) = Async.StartAsTask(this, cancellationToken = ct) -type FSharpRequestContext(lspServices: ILspServices, logger: ILspLogger, workspace: FSharpWorkspace, checker: FSharpChecker) = - member _.LspServices = lspServices - member _.Logger = logger - member _.Workspace = workspace - member _.Checker = checker - - // TODO: split to parse and check diagnostics - member _.GetDiagnosticsForFile(file: Uri) = - - workspace.GetSnapshotForFile file - |> Option.map (fun snapshot -> - async { - let! parseResult, checkFileAnswer = checker.ParseAndCheckFileInProject(file.LocalPath, snapshot, "LSP Get diagnostics") - - return - match checkFileAnswer with - | FSharpCheckFileAnswer.Succeeded result -> result.Diagnostics - | FSharpCheckFileAnswer.Aborted -> parseResult.Diagnostics - }) - |> Option.defaultValue (async.Return [||]) - -type ContextHolder(intialWorkspace, lspServices: ILspServices) = - - let logger = lspServices.GetRequiredService() - - // TODO: We need to get configuration for this somehow. Also make it replaceable when configuration changes. - let checker = - FSharpChecker.Create( - keepAllBackgroundResolutions = true, - keepAllBackgroundSymbolUses = true, - enableBackgroundItemKeyStoreAndSemanticClassification = true, - enablePartialTypeChecking = true, - parallelReferenceResolution = true, - captureIdentifiersWhenParsing = true, - useSyntaxTreeCache = true, - useTransparentCompiler = true - ) - - let mutable context = - FSharpRequestContext(lspServices, logger, intialWorkspace, checker) - - member _.GetContext() = context - - member _.UpdateWorkspace(f) = - context <- FSharpRequestContext(lspServices, logger, f context.Workspace, checker) - -type FShapRequestContextFactory(lspServices: ILspServices) = - - interface IRequestContextFactory with - - member _.CreateRequestContextAsync<'TRequestParam> - ( - queueItem: IQueueItem, - requestParam: 'TRequestParam, - cancellationToken: CancellationToken - ) = - lspServices.GetRequiredService() - |> _.GetContext() - |> Task.FromResult - -type DocumentStateHandler() = - interface IMethodHandler with - member _.MutatesSolutionState = true - - interface IRequestHandler with - [] - member _.HandleRequestAsync - ( - request: DidOpenTextDocumentParams, - context: FSharpRequestContext, - cancellationToken: CancellationToken - ) = - let contextHolder = context.LspServices.GetRequiredService() - - contextHolder.UpdateWorkspace _.OpenFile(request.TextDocument.Uri, request.TextDocument.Text) - - Task.FromResult(SemanticTokensDeltaPartialResult()) - - interface IRequestHandler with - [] - member _.HandleRequestAsync - ( - request: DidChangeTextDocumentParams, - context: FSharpRequestContext, - cancellationToken: CancellationToken - ) = - let contextHolder = context.LspServices.GetRequiredService() - - contextHolder.UpdateWorkspace _.ChangeFile(request.TextDocument.Uri, request.ContentChanges.[0].Text) - - Task.FromResult(SemanticTokensDeltaPartialResult()) - - interface INotificationHandler with - [] - member _.HandleNotificationAsync - ( - request: DidCloseTextDocumentParams, - context: FSharpRequestContext, - cancellationToken: CancellationToken - ) = - let contextHolder = context.LspServices.GetRequiredService() - - contextHolder.UpdateWorkspace _.CloseFile(request.TextDocument.Uri) - - Task.CompletedTask - -type LanguageFeaturesHandler() = - interface IMethodHandler with - member _.MutatesSolutionState = false - - // TODO: this is not getting called - interface IRequestHandler, FSharpRequestContext> with - [] - member _.HandleRequestAsync - ( - request: DocumentDiagnosticParams, - context: FSharpRequestContext, - cancellationToken: CancellationToken - ) = - Task.FromResult(new RelatedUnchangedDocumentDiagnosticReport()) - -type IServerCapabilitiesOverride = - abstract member OverrideServerCapabilities: ServerCapabilities -> ServerCapabilities - -type CapabilitiesManager(scOverrides: IServerCapabilitiesOverride seq) = - - let mutable initializeParams = None - - let defaultCapabilities = - ServerCapabilities( - TextDocumentSync = TextDocumentSyncOptions(OpenClose = true, Change = TextDocumentSyncKind.Full), - DiagnosticOptions = - DiagnosticOptions(WorkDoneProgress = true, InterFileDependencies = true, Identifier = "potato", WorkspaceDiagnostics = true) - // CompletionProvider = CompletionOptions(TriggerCharacters=[|"."; " "|], ResolveProvider=true, WorkDoneProgress=true) - ) - - interface IInitializeManager with - member this.SetInitializeParams(request) = initializeParams <- Some request - - member this.GetInitializeResult() = - let serverCapabilities = - (defaultCapabilities, scOverrides) - ||> Seq.fold (fun acc (x: IServerCapabilitiesOverride) -> x.OverrideServerCapabilities acc) - - InitializeResult(Capabilities = serverCapabilities) - - member this.GetInitializeParams() = - match initializeParams with - | Some params' -> params' - | None -> failwith "InitializeParams is null" - -type LspServiceLifeCycleManager() = - - interface ILifeCycleManager with - member this.ShutdownAsync(message: string) = - task { - try - printfn "Shutting down" - with - | :? ObjectDisposedException - | :? ConnectionLostException -> () - } - - member this.ExitAsync() = Task.CompletedTask - -type FSharpLspServices(serviceCollection: IServiceCollection) as this = - - do serviceCollection.AddSingleton(this) |> ignore - - let serviceProvider = serviceCollection.BuildServiceProvider() - - interface ILspServices with - member this.GetRequiredService<'T>() : 'T = - serviceProvider.GetRequiredService<'T>() - - member this.TryGetService(t) = serviceProvider.GetService(t) - - member this.GetRequiredServices() = serviceProvider.GetServices() - - member this.GetRegisteredServices() = failwith "Not implemented" - - member this.SupportsGetRegisteredServices() = false - - member this.Dispose() = () - type FSharpLanguageServer (jsonRpc: JsonRpc, logger: ILspLogger, ?initialWorkspace: FSharpWorkspace, ?addExtraHandlers: Action) = inherit AbstractLanguageServer(jsonRpc, logger) diff --git a/src/FSharp.Compiler.LanguageServer/Handlers/DocumentStateHandler.fs b/src/FSharp.Compiler.LanguageServer/Handlers/DocumentStateHandler.fs new file mode 100644 index 00000000000..869d8a56146 --- /dev/null +++ b/src/FSharp.Compiler.LanguageServer/Handlers/DocumentStateHandler.fs @@ -0,0 +1,54 @@ +namespace FSharp.Compiler.LanguageServer.Handlers + +open Microsoft.CommonLanguageServerProtocol.Framework +open Microsoft.VisualStudio.LanguageServer.Protocol +open FSharp.Compiler.LanguageServer.Common + +open System.Threading +open System.Threading.Tasks + +type DocumentStateHandler() = + interface IMethodHandler with + member _.MutatesSolutionState = true + + interface IRequestHandler with + [] + member _.HandleRequestAsync + ( + request: DidOpenTextDocumentParams, + context: FSharpRequestContext, + cancellationToken: CancellationToken + ) = + let contextHolder = context.LspServices.GetRequiredService() + + contextHolder.UpdateWorkspace _.OpenFile(request.TextDocument.Uri, request.TextDocument.Text) + + Task.FromResult(SemanticTokensDeltaPartialResult()) + + interface IRequestHandler with + [] + member _.HandleRequestAsync + ( + request: DidChangeTextDocumentParams, + context: FSharpRequestContext, + cancellationToken: CancellationToken + ) = + let contextHolder = context.LspServices.GetRequiredService() + + contextHolder.UpdateWorkspace _.ChangeFile(request.TextDocument.Uri, request.ContentChanges.[0].Text) + + Task.FromResult(SemanticTokensDeltaPartialResult()) + + interface INotificationHandler with + [] + member _.HandleNotificationAsync + ( + request: DidCloseTextDocumentParams, + context: FSharpRequestContext, + cancellationToken: CancellationToken + ) = + let contextHolder = context.LspServices.GetRequiredService() + + contextHolder.UpdateWorkspace _.CloseFile(request.TextDocument.Uri) + + Task.CompletedTask diff --git a/src/FSharp.Compiler.LanguageServer/Handlers/LanguageFeaturesHandler.fs b/src/FSharp.Compiler.LanguageServer/Handlers/LanguageFeaturesHandler.fs new file mode 100644 index 00000000000..1609cdc06cc --- /dev/null +++ b/src/FSharp.Compiler.LanguageServer/Handlers/LanguageFeaturesHandler.fs @@ -0,0 +1,22 @@ +namespace FSharp.Compiler.LanguageServer.Handlers + +open Microsoft.CommonLanguageServerProtocol.Framework +open Microsoft.VisualStudio.LanguageServer.Protocol +open FSharp.Compiler.LanguageServer.Common +open System.Threading.Tasks +open System.Threading + +type LanguageFeaturesHandler() = + interface IMethodHandler with + member _.MutatesSolutionState = false + + // TODO: this is not getting called + interface IRequestHandler, FSharpRequestContext> with + [] + member _.HandleRequestAsync + ( + request: DocumentDiagnosticParams, + context: FSharpRequestContext, + cancellationToken: CancellationToken + ) = + Task.FromResult(new RelatedUnchangedDocumentDiagnosticReport()) \ No newline at end of file diff --git a/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs b/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs index f4a827e16bb..03bf9796389 100644 --- a/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs +++ b/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs @@ -9,11 +9,11 @@ namespace FSharp.VisualStudio.Extension; using System.IO; using System.IO.Pipelines; using System.Linq; -using System.Reflection; using System.Threading; using System.Threading.Tasks; -using System.Windows.Documents; using FSharp.Compiler.LanguageServer; +using FSharp.Compiler.LanguageServer.Common; + using Microsoft.CommonLanguageServerProtocol.Framework; using Microsoft.Extensions.DependencyInjection; using Microsoft.VisualStudio.Extensibility; @@ -24,7 +24,6 @@ namespace FSharp.VisualStudio.Extension; using Microsoft.VisualStudio.RpcContracts.LanguageServerProvider; using Nerdbank.Streams; using static FSharp.Compiler.CodeAnalysis.ProjectSnapshot; -using static FSharp.Compiler.LanguageServer.Workspace; /// #pragma warning disable VSEXTPREVIEW_LSP // Type is for evaluation purposes only and is subject to change or removal in future updates. @@ -66,6 +65,10 @@ public ServerCapabilities OverrideServerCapabilities(ServerCapabilities value) //new(PullDiagnosticCategories.DocumentAnalyzerSyntax), //new(PullDiagnosticCategories.DocumentAnalyzerSemantic), ] + }, + HoverProvider = new HoverOptions() + { + WorkDoneProgress = true } }; return capabilities; From ea93ecc07b36048c3c074e1f4d58ebe9e1b85f0e Mon Sep 17 00:00:00 2001 From: "Vlad Zarytovskii (from Dev Box)" Date: Mon, 24 Jun 2024 12:52:22 +0200 Subject: [PATCH 14/60] Fix build - remove compressed solution --- FSharp.Benchmarks.sln | 16 ---------------- VSFSharpExtension.sln | 11 +++++++++++ .../FSharp.Compiler.Interactive.Settings.fsproj | 2 +- .../FSharp.DependencyManager.Nuget.fsproj | 2 +- 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/FSharp.Benchmarks.sln b/FSharp.Benchmarks.sln index f690db4eaf8..83f1a6c3bf7 100644 --- a/FSharp.Benchmarks.sln +++ b/FSharp.Benchmarks.sln @@ -29,7 +29,6 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU - ReleaseCompressed|Any CPU = ReleaseCompressed|Any CPU Proto|Any CPU = Proto|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution @@ -37,64 +36,51 @@ Global {CA98804B-E371-45AB-814F-AC4996579B6F}.Debug|Any CPU.Build.0 = Debug|Any CPU {CA98804B-E371-45AB-814F-AC4996579B6F}.Release|Any CPU.ActiveCfg = Release|Any CPU {CA98804B-E371-45AB-814F-AC4996579B6F}.Release|Any CPU.Build.0 = Release|Any CPU - {CA98804B-E371-45AB-814F-AC4996579B6F}.ReleaseCompressed|Any CPU.ActiveCfg = Debug|Any CPU {CA98804B-E371-45AB-814F-AC4996579B6F}.Proto|Any CPU.ActiveCfg = Debug|Any CPU {9B4528F6-8883-475C-AAF0-4F646233D30A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9B4528F6-8883-475C-AAF0-4F646233D30A}.Debug|Any CPU.Build.0 = Debug|Any CPU {9B4528F6-8883-475C-AAF0-4F646233D30A}.Release|Any CPU.ActiveCfg = Release|Any CPU {9B4528F6-8883-475C-AAF0-4F646233D30A}.Release|Any CPU.Build.0 = Release|Any CPU - {9B4528F6-8883-475C-AAF0-4F646233D30A}.ReleaseCompressed|Any CPU.ActiveCfg = ReleaseCompressed|Any CPU - {9B4528F6-8883-475C-AAF0-4F646233D30A}.ReleaseCompressed|Any CPU.Build.0 = ReleaseCompressed|Any CPU {9B4528F6-8883-475C-AAF0-4F646233D30A}.Proto|Any CPU.ActiveCfg = Debug|Any CPU {ADB51E6C-2D63-4AB1-9F2C-8E869FEFD170}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {ADB51E6C-2D63-4AB1-9F2C-8E869FEFD170}.Debug|Any CPU.Build.0 = Debug|Any CPU {ADB51E6C-2D63-4AB1-9F2C-8E869FEFD170}.Release|Any CPU.ActiveCfg = Release|Any CPU {ADB51E6C-2D63-4AB1-9F2C-8E869FEFD170}.Release|Any CPU.Build.0 = Release|Any CPU - {ADB51E6C-2D63-4AB1-9F2C-8E869FEFD170}.ReleaseCompressed|Any CPU.ActiveCfg = ReleaseCompressed|Any CPU - {ADB51E6C-2D63-4AB1-9F2C-8E869FEFD170}.ReleaseCompressed|Any CPU.Build.0 = ReleaseCompressed|Any CPU {ADB51E6C-2D63-4AB1-9F2C-8E869FEFD170}.Proto|Any CPU.ActiveCfg = Proto|Any CPU {ADB51E6C-2D63-4AB1-9F2C-8E869FEFD170}.Proto|Any CPU.Build.0 = Proto|Any CPU {9B98A267-BE47-4711-A1B6-98F3F321E3DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9B98A267-BE47-4711-A1B6-98F3F321E3DD}.Debug|Any CPU.Build.0 = Debug|Any CPU {9B98A267-BE47-4711-A1B6-98F3F321E3DD}.Release|Any CPU.ActiveCfg = Release|Any CPU {9B98A267-BE47-4711-A1B6-98F3F321E3DD}.Release|Any CPU.Build.0 = Release|Any CPU - {9B98A267-BE47-4711-A1B6-98F3F321E3DD}.ReleaseCompressed|Any CPU.ActiveCfg = ReleaseCompressed|Any CPU - {9B98A267-BE47-4711-A1B6-98F3F321E3DD}.ReleaseCompressed|Any CPU.Build.0 = ReleaseCompressed|Any CPU {9B98A267-BE47-4711-A1B6-98F3F321E3DD}.Proto|Any CPU.ActiveCfg = Debug|Any CPU {ED766F38-BD2B-436B-AF73-7BE6FAE061DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {ED766F38-BD2B-436B-AF73-7BE6FAE061DD}.Debug|Any CPU.Build.0 = Debug|Any CPU {ED766F38-BD2B-436B-AF73-7BE6FAE061DD}.Release|Any CPU.ActiveCfg = Release|Any CPU {ED766F38-BD2B-436B-AF73-7BE6FAE061DD}.Release|Any CPU.Build.0 = Release|Any CPU - {ED766F38-BD2B-436B-AF73-7BE6FAE061DD}.ReleaseCompressed|Any CPU.ActiveCfg = Debug|Any CPU {ED766F38-BD2B-436B-AF73-7BE6FAE061DD}.Proto|Any CPU.ActiveCfg = Debug|Any CPU {66E23120-5E94-49AE-A263-24583007F5F5}.Debug|Any CPU.ActiveCfg = Release|Any CPU {66E23120-5E94-49AE-A263-24583007F5F5}.Release|Any CPU.ActiveCfg = Release|Any CPU {66E23120-5E94-49AE-A263-24583007F5F5}.Release|Any CPU.Build.0 = Release|Any CPU - {66E23120-5E94-49AE-A263-24583007F5F5}.ReleaseCompressed|Any CPU.ActiveCfg = Release|Any CPU {66E23120-5E94-49AE-A263-24583007F5F5}.Proto|Any CPU.ActiveCfg = Release|Any CPU {2A1289B1-1539-48CB-BE03-807FE4BC0387}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2A1289B1-1539-48CB-BE03-807FE4BC0387}.Debug|Any CPU.Build.0 = Debug|Any CPU {2A1289B1-1539-48CB-BE03-807FE4BC0387}.Release|Any CPU.ActiveCfg = Release|Any CPU {2A1289B1-1539-48CB-BE03-807FE4BC0387}.Release|Any CPU.Build.0 = Release|Any CPU - {2A1289B1-1539-48CB-BE03-807FE4BC0387}.ReleaseCompressed|Any CPU.ActiveCfg = Debug|Any CPU {2A1289B1-1539-48CB-BE03-807FE4BC0387}.Proto|Any CPU.ActiveCfg = Debug|Any CPU {50D1ECF3-DF38-4A8E-AC3B-2AF1E63263B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {50D1ECF3-DF38-4A8E-AC3B-2AF1E63263B8}.Debug|Any CPU.Build.0 = Debug|Any CPU {50D1ECF3-DF38-4A8E-AC3B-2AF1E63263B8}.Release|Any CPU.ActiveCfg = Release|Any CPU {50D1ECF3-DF38-4A8E-AC3B-2AF1E63263B8}.Release|Any CPU.Build.0 = Release|Any CPU - {50D1ECF3-DF38-4A8E-AC3B-2AF1E63263B8}.ReleaseCompressed|Any CPU.ActiveCfg = Debug|Any CPU {50D1ECF3-DF38-4A8E-AC3B-2AF1E63263B8}.Proto|Any CPU.ActiveCfg = Debug|Any CPU {0E2A7B27-3AD3-4C1D-BA0D-008A1200946F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0E2A7B27-3AD3-4C1D-BA0D-008A1200946F}.Debug|Any CPU.Build.0 = Debug|Any CPU {0E2A7B27-3AD3-4C1D-BA0D-008A1200946F}.Release|Any CPU.ActiveCfg = Release|Any CPU {0E2A7B27-3AD3-4C1D-BA0D-008A1200946F}.Release|Any CPU.Build.0 = Release|Any CPU - {0E2A7B27-3AD3-4C1D-BA0D-008A1200946F}.ReleaseCompressed|Any CPU.ActiveCfg = Debug|Any CPU {0E2A7B27-3AD3-4C1D-BA0D-008A1200946F}.Proto|Any CPU.ActiveCfg = Debug|Any CPU {0B149238-0912-493E-8877-F831AE01B942}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0B149238-0912-493E-8877-F831AE01B942}.Debug|Any CPU.Build.0 = Debug|Any CPU {0B149238-0912-493E-8877-F831AE01B942}.Release|Any CPU.ActiveCfg = Release|Any CPU {0B149238-0912-493E-8877-F831AE01B942}.Release|Any CPU.Build.0 = Release|Any CPU - {0B149238-0912-493E-8877-F831AE01B942}.ReleaseCompressed|Any CPU.ActiveCfg = Debug|Any CPU {0B149238-0912-493E-8877-F831AE01B942}.Proto|Any CPU.ActiveCfg = Debug|Any CPU {62DED1EA-6A33-4537-8ED2-118462D0FEE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {62DED1EA-6A33-4537-8ED2-118462D0FEE5}.Debug|Any CPU.Build.0 = Debug|Any CPU @@ -102,8 +88,6 @@ Global {62DED1EA-6A33-4537-8ED2-118462D0FEE5}.Proto|Any CPU.Build.0 = Debug|Any CPU {62DED1EA-6A33-4537-8ED2-118462D0FEE5}.Release|Any CPU.ActiveCfg = Release|Any CPU {62DED1EA-6A33-4537-8ED2-118462D0FEE5}.Release|Any CPU.Build.0 = Release|Any CPU - {62DED1EA-6A33-4537-8ED2-118462D0FEE5}.ReleaseCompressed|Any CPU.ActiveCfg = Release|Any CPU - {62DED1EA-6A33-4537-8ED2-118462D0FEE5}.ReleaseCompressed|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/VSFSharpExtension.sln b/VSFSharpExtension.sln index 6bafe7599d9..3a246468d30 100644 --- a/VSFSharpExtension.sln +++ b/VSFSharpExtension.sln @@ -13,6 +13,8 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Compiler.Service", " EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Core", "src\FSharp.Core\FSharp.Core.fsproj", "{5A4B66D5-A6C3-402C-A010-7A3635098DA7}" EndProject +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.DependencyManager.Nuget", "src\FSharp.DependencyManager.Nuget\FSharp.DependencyManager.Nuget.fsproj", "{860808CF-D092-4511-B011-6205B654031D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -45,6 +47,12 @@ Global {5A4B66D5-A6C3-402C-A010-7A3635098DA7}.Proto|Any CPU.Build.0 = Proto|Any CPU {5A4B66D5-A6C3-402C-A010-7A3635098DA7}.Release|Any CPU.ActiveCfg = Release|Any CPU {5A4B66D5-A6C3-402C-A010-7A3635098DA7}.Release|Any CPU.Build.0 = Release|Any CPU + {860808CF-D092-4511-B011-6205B654031D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {860808CF-D092-4511-B011-6205B654031D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {860808CF-D092-4511-B011-6205B654031D}.Proto|Any CPU.ActiveCfg = Debug|Any CPU + {860808CF-D092-4511-B011-6205B654031D}.Proto|Any CPU.Build.0 = Debug|Any CPU + {860808CF-D092-4511-B011-6205B654031D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {860808CF-D092-4511-B011-6205B654031D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -52,4 +60,7 @@ Global GlobalSection(NestedProjects) = preSolution {14B9AB0E-2FC0-43F1-9775-20C262202D12} = {B4A1E626-4A48-4977-B291-219882DB3413} EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BCE01FEF-1B00-471B-81E0-A06994EC90ED} + EndGlobalSection EndGlobal diff --git a/src/FSharp.Compiler.Interactive.Settings/FSharp.Compiler.Interactive.Settings.fsproj b/src/FSharp.Compiler.Interactive.Settings/FSharp.Compiler.Interactive.Settings.fsproj index 85814ac6e5f..dd5f7801629 100644 --- a/src/FSharp.Compiler.Interactive.Settings/FSharp.Compiler.Interactive.Settings.fsproj +++ b/src/FSharp.Compiler.Interactive.Settings/FSharp.Compiler.Interactive.Settings.fsproj @@ -7,7 +7,7 @@ netstandard2.0 FSharp.Compiler.Interactive.Settings true - Debug;Release;ReleaseCompressed + Debug;Release diff --git a/src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.Nuget.fsproj b/src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.Nuget.fsproj index 7b5aad10dba..eacd502af68 100644 --- a/src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.Nuget.fsproj +++ b/src/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.Nuget.fsproj @@ -10,7 +10,7 @@ $(DefineConstants);COMPILER $(OtherFlags) --warnon:1182 true - Debug;Release;ReleaseCompressed + Debug;Release From f0e48262636f54a08dbaecb03e55a77bb3cc2276 Mon Sep 17 00:00:00 2001 From: Adam Boniecki Date: Thu, 6 Jun 2024 17:15:55 +0200 Subject: [PATCH 15/60] Some prototype that almost works --- .../Common/FSharpRequestContext.fs | 75 +++++++++++++++++++ .../FSharpLanguageServerProvider.cs | 59 +++++++++++---- 2 files changed, 121 insertions(+), 13 deletions(-) diff --git a/src/FSharp.Compiler.LanguageServer/Common/FSharpRequestContext.fs b/src/FSharp.Compiler.LanguageServer/Common/FSharpRequestContext.fs index c360e7527e2..e3b50a3667a 100644 --- a/src/FSharp.Compiler.LanguageServer/Common/FSharpRequestContext.fs +++ b/src/FSharp.Compiler.LanguageServer/Common/FSharpRequestContext.fs @@ -1,7 +1,9 @@ namespace FSharp.Compiler.LanguageServer.Common open Microsoft.CommonLanguageServerProtocol.Framework +open Microsoft.VisualStudio.LanguageServer.Protocol open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.EditorServices open System open System.Threading open System.Threading.Tasks @@ -29,6 +31,79 @@ type FSharpRequestContext(lspServices: ILspServices, logger: ILspLogger, workspa }) |> Option.defaultValue (async.Return [||]) + member _.GetSemanticTokensForFile(file: Uri) = + + let FSharpTokenTypeToLSP (fst: SemanticClassificationType) = + // XXX arbitrary + match fst with + | SemanticClassificationType.ReferenceType -> SemanticTokenTypes.Class + | SemanticClassificationType.ValueType -> SemanticTokenTypes.Struct + | SemanticClassificationType.UnionCase -> SemanticTokenTypes.Enum + | SemanticClassificationType.UnionCaseField -> SemanticTokenTypes.EnumMember + | SemanticClassificationType.Function -> SemanticTokenTypes.Function + | SemanticClassificationType.Property -> SemanticTokenTypes.Property + | SemanticClassificationType.Module -> SemanticTokenTypes.Type + | SemanticClassificationType.Namespace -> SemanticTokenTypes.Namespace + | SemanticClassificationType.Interface -> SemanticTokenTypes.Interface + | SemanticClassificationType.TypeArgument -> SemanticTokenTypes.TypeParameter + | SemanticClassificationType.Operator -> SemanticTokenTypes.Operator + | SemanticClassificationType.Method -> SemanticTokenTypes.Method + | SemanticClassificationType.ExtensionMethod -> SemanticTokenTypes.Method + | SemanticClassificationType.Field -> SemanticTokenTypes.Property + | SemanticClassificationType.Event -> SemanticTokenTypes.Event + | SemanticClassificationType.Delegate -> SemanticTokenTypes.Function + | SemanticClassificationType.NamedArgument -> SemanticTokenTypes.Parameter + | SemanticClassificationType.LocalValue -> SemanticTokenTypes.Variable + | SemanticClassificationType.Plaintext -> SemanticTokenTypes.String + | SemanticClassificationType.Type -> SemanticTokenTypes.Type + | SemanticClassificationType.Printf -> SemanticTokenTypes.Keyword + | _ -> SemanticTokenTypes.Comment + + let toIndex (x: string) = SemanticTokenTypes.AllTypes |> Seq.findIndex (fun y -> y = x) + + workspace.GetSnapshotForFile file + |> Option.map (fun snapshot -> + async { + let! _, checkFileAnswer = checker.ParseAndCheckFileInProject(file.LocalPath, snapshot, "LSP Get semantic classification") + + let a = + match checkFileAnswer with + | FSharpCheckFileAnswer.Succeeded result -> result.GetSemanticClassification(None) // XXX not sure if range opt should be None + | FSharpCheckFileAnswer.Aborted -> [||] // XXX should be error maybe + + // XXX Not sure if this is needed, if so TODO should be declared out of the scope of this function + let compareRange (x: FSharp.Compiler.EditorServices.SemanticClassificationItem) (y: FSharp.Compiler.EditorServices.SemanticClassificationItem) = + let c = x.Range.StartLine.CompareTo(y.Range.StartLine) + if c <> 0 then c + else x.Range.StartColumn.CompareTo(y.Range.StartColumn) + + let b = + a + |> Array.sortWith compareRange + |> Array.map (fun y -> + let startLine = y.Range.StartLine - 1 + let startCol = y.Range.StartColumn + let length = y.Range.EndColumn - y.Range.StartColumn // XXX + let tokType = y.Type |> FSharpTokenTypeToLSP |> toIndex + let tokMods = 0 + (startLine, startCol, length, tokType, tokMods) + ) + let c = + b + |> Array.append [|(0,0,0,0,0)|] + |> Array.pairwise + |> Array.map (fun (prev, this) -> + let (prevSLine, prevSCol, _, _, _) = prev + let (thisSLine, thisSCol, length, tokType, tokMods) = this + (thisSLine - prevSLine, (if prevSLine = thisSLine then thisSCol - prevSCol else thisSCol), length, tokType, tokMods)) + + let _TODO_DELETE_readable_names = + b |> Array.map( fun (_, _, _, t, _) -> SemanticTokenTypes.AllTypes.[t]) + + return c |> Array.map (fun (v, w, x, y, z) -> [| v; w; x; y; z |]) |> Array.concat + }) + |> Option.defaultValue (async { return [||] }) + type ContextHolder(intialWorkspace, lspServices: ILspServices) = let logger = lspServices.GetRequiredService() diff --git a/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs b/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs index 03bf9796389..468438d4b7d 100644 --- a/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs +++ b/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs @@ -23,6 +23,8 @@ namespace FSharp.VisualStudio.Extension; using Microsoft.VisualStudio.ProjectSystem.Query; using Microsoft.VisualStudio.RpcContracts.LanguageServerProvider; using Nerdbank.Streams; +using Newtonsoft.Json.Linq; +using StreamJsonRpc; using static FSharp.Compiler.CodeAnalysis.ProjectSnapshot; /// @@ -53,7 +55,7 @@ public ServerCapabilities OverrideServerCapabilities(ServerCapabilities value) // Support a specialized requests dedicated to task-list items. This way the client can ask just // for these, independently of other diagnostics. They can also throttle themselves to not ask if // the task list would not be visible. - VSInternalDiagnosticKind.Task, + //VSInternalDiagnosticKind.Task, // Dedicated request for workspace-diagnostics only. We will only respond to these if FSA is on. VSInternalDiagnosticKind.Syntax, // Fine-grained diagnostics requests. Importantly, this separates out syntactic vs semantic @@ -61,11 +63,24 @@ public ServerCapabilities OverrideServerCapabilities(ServerCapabilities value) // similar vein, compiler diagnostics are explicitly distinct from analyzer-diagnostics, allowing // the former to appear as soon as possible as they are much more critical for the user and should // not be delayed by a slow analyzer. - new("Semantic"), + //new("Semantic"), //new(PullDiagnosticCategories.DocumentAnalyzerSyntax), //new(PullDiagnosticCategories.DocumentAnalyzerSemantic), ] }, + SemanticTokensOptions = new() + { + Legend = new() + { + TokenTypes = [ ..SemanticTokenTypes.AllTypes], // XXX should be extended + TokenModifiers = [ ..SemanticTokenModifiers.AllModifiers] + }, + Full = new SemanticTokensFullOptions() + { + Delta = false + }, + Range = false + }, HoverProvider = new HoverOptions() { WorkDoneProgress = true @@ -75,6 +90,23 @@ public ServerCapabilities OverrideServerCapabilities(ServerCapabilities value) } } +internal class SemanticTokensHandler + : IRequestHandler +{ + public bool MutatesSolutionState => false; + + [LanguageServerEndpoint("textDocument/semanticTokens/full")] + public async Task HandleRequestAsync( + SemanticTokensParams request, + FSharpRequestContext context, + CancellationToken cancellationToken) + { + var tokens = await context.GetSemanticTokensForFile(request!.TextDocument!.Uri).Please(cancellationToken); + + return new SemanticTokens { Data = tokens }; + } +} + internal class VsDiagnosticsHandler : IRequestHandler, @@ -121,17 +153,17 @@ public Task HandleRequestAsync(VSGetProjectContextsParams { DefaultIndex = 0, ProjectContexts = [ - new() { - Id = "potato", - Label = "Potato", - // PR for F# project kind: https://devdiv.visualstudio.com/DevDiv/_git/VSLanguageServerClient/pullrequest/529882 - Kind = VSProjectKind.VisualBasic - }, - new () { - Id = "potato2", - Label = "Potato2", - Kind = VSProjectKind.VisualBasic - } + //new() { + // Id = "potato", + // Label = "Potato", + // // PR for F# project kind: https://devdiv.visualstudio.com/DevDiv/_git/VSLanguageServerClient/pullrequest/529882 + // Kind = VSProjectKind.FSharp + //}, + //new () { + // Id = "potato2", + // Label = "Potato2", + // Kind = VSProjectKind.FSharp + //} ] }); @@ -236,6 +268,7 @@ internal class FSharpLanguageServerProvider : LanguageServerProvider { serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); }); return new DuplexPipe( From fa4f634676d070e5950d80571419d807f5584d08 Mon Sep 17 00:00:00 2001 From: Adam Boniecki Date: Wed, 26 Jun 2024 18:55:49 +0200 Subject: [PATCH 16/60] Use lexical classification in addition to semantic TODO does not work on comments for some reason TODO seems like color mapping for semantic classifications are off or for some other reason they do not get highlighted when they should --- .../Common/FSharpRequestContext.fs | 64 +++++++++++++++---- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/src/FSharp.Compiler.LanguageServer/Common/FSharpRequestContext.fs b/src/FSharp.Compiler.LanguageServer/Common/FSharpRequestContext.fs index e3b50a3667a..fb2e2c68a1c 100644 --- a/src/FSharp.Compiler.LanguageServer/Common/FSharpRequestContext.fs +++ b/src/FSharp.Compiler.LanguageServer/Common/FSharpRequestContext.fs @@ -4,6 +4,7 @@ open Microsoft.CommonLanguageServerProtocol.Framework open Microsoft.VisualStudio.LanguageServer.Protocol open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.EditorServices +open FSharp.Compiler.Tokenization open System open System.Threading open System.Threading.Tasks @@ -66,30 +67,72 @@ type FSharpRequestContext(lspServices: ILspServices, logger: ILspLogger, workspa async { let! _, checkFileAnswer = checker.ParseAndCheckFileInProject(file.LocalPath, snapshot, "LSP Get semantic classification") + let! source = + snapshot.ProjectSnapshot.SourceFiles + |> Seq.find (fun f -> f.FileName = file.LocalPath) + |> _.GetSource() + |> Async.AwaitTask + + let mutable a' = [] + + let tokenCallback = + fun (tok: FSharpToken) -> + let spanKind = + if tok.IsKeyword then + SemanticTokenTypes.Keyword + elif tok.IsNumericLiteral then + SemanticTokenTypes.Number + elif tok.IsCommentTrivia then + SemanticTokenTypes.Comment + elif tok.IsStringLiteral then + SemanticTokenTypes.String + else + SemanticTokenTypes.Function // XXX + + a' <- (tok.Range, spanKind)::a' + + FSharpLexer.Tokenize( + source, + tokenCallback, + flags = (FSharpLexerFlags.Default &&& ~~~FSharpLexerFlags.Compiling &&& ~~~FSharpLexerFlags.UseLexFilter), + filePath = file.LocalPath + ) + let a = match checkFileAnswer with | FSharpCheckFileAnswer.Succeeded result -> result.GetSemanticClassification(None) // XXX not sure if range opt should be None | FSharpCheckFileAnswer.Aborted -> [||] // XXX should be error maybe - // XXX Not sure if this is needed, if so TODO should be declared out of the scope of this function - let compareRange (x: FSharp.Compiler.EditorServices.SemanticClassificationItem) (y: FSharp.Compiler.EditorServices.SemanticClassificationItem) = - let c = x.Range.StartLine.CompareTo(y.Range.StartLine) - if c <> 0 then c - else x.Range.StartColumn.CompareTo(y.Range.StartColumn) - let b = a - |> Array.sortWith compareRange |> Array.map (fun y -> let startLine = y.Range.StartLine - 1 let startCol = y.Range.StartColumn - let length = y.Range.EndColumn - y.Range.StartColumn // XXX + let length = y.Range.EndColumn - y.Range.StartColumn // XXX Does not deal with multiline tokens? let tokType = y.Type |> FSharpTokenTypeToLSP |> toIndex let tokMods = 0 (startLine, startCol, length, tokType, tokMods) ) + let b' = + a' + |> List.map (fun (r, t) -> + let startLine = r.StartLine - 1 + let startCol = r.StartColumn + let length = r.EndColumn - r.StartColumn // XXX + let tokType = t |> toIndex + let tokMods = 0 + (startLine, startCol, length, tokType, tokMods) + ) + let b'' = + Array.ofList b' + |> Array.append b + |> Array.sortWith (fun (l1, c1, _, _, _) (l2, c2, _, _, _) -> + let c = l1.CompareTo(l2) + if c <> 0 then c + else c1.CompareTo(c2)) + let c = - b + b'' |> Array.append [|(0,0,0,0,0)|] |> Array.pairwise |> Array.map (fun (prev, this) -> @@ -97,9 +140,6 @@ type FSharpRequestContext(lspServices: ILspServices, logger: ILspLogger, workspa let (thisSLine, thisSCol, length, tokType, tokMods) = this (thisSLine - prevSLine, (if prevSLine = thisSLine then thisSCol - prevSCol else thisSCol), length, tokType, tokMods)) - let _TODO_DELETE_readable_names = - b |> Array.map( fun (_, _, _, t, _) -> SemanticTokenTypes.AllTypes.[t]) - return c |> Array.map (fun (v, w, x, y, z) -> [| v; w; x; y; z |]) |> Array.concat }) |> Option.defaultValue (async { return [||] }) From 01d0879c5d76b8068ea10486bc228f9988852be5 Mon Sep 17 00:00:00 2001 From: Adam Boniecki Date: Thu, 27 Jun 2024 18:24:37 +0200 Subject: [PATCH 17/60] Refactor --- .../Common/FSharpRequestContext.fs | 187 +++++++++--------- 1 file changed, 95 insertions(+), 92 deletions(-) diff --git a/src/FSharp.Compiler.LanguageServer/Common/FSharpRequestContext.fs b/src/FSharp.Compiler.LanguageServer/Common/FSharpRequestContext.fs index fb2e2c68a1c..db26ff46514 100644 --- a/src/FSharp.Compiler.LanguageServer/Common/FSharpRequestContext.fs +++ b/src/FSharp.Compiler.LanguageServer/Common/FSharpRequestContext.fs @@ -11,6 +11,68 @@ open System.Threading.Tasks #nowarn "57" +module TokenTypes = + + [] + let (|LexicalClassification|_|) (tok: FSharpToken) = + if tok.IsKeyword then + ValueSome SemanticTokenTypes.Keyword + elif tok.IsNumericLiteral then + ValueSome SemanticTokenTypes.Number + elif tok.IsCommentTrivia then + ValueSome SemanticTokenTypes.Comment + elif tok.IsStringLiteral then + ValueSome SemanticTokenTypes.String + else + ValueNone + + // Tokenizes the source code and returns a list of token ranges and their SemanticTokenTypes + let GetSyntacticTokenTypes (source: FSharp.Compiler.Text.ISourceText) (fileName: string) = + let mutable tokRangesAndTypes = [] + + let tokenCallback = + fun (tok: FSharpToken) -> + match tok with + | LexicalClassification tokType -> tokRangesAndTypes <- (tok.Range, tokType) :: tokRangesAndTypes + | _ -> () + + FSharpLexer.Tokenize( + source, + tokenCallback, + flags = (FSharpLexerFlags.Default &&& ~~~FSharpLexerFlags.Compiling &&& ~~~FSharpLexerFlags.UseLexFilter), + filePath = fileName + ) + + tokRangesAndTypes + + let FSharpTokenTypeToLSP (fst: SemanticClassificationType) = + // XXX kinda arbitrary mapping + match fst with + | SemanticClassificationType.ReferenceType -> SemanticTokenTypes.Class + | SemanticClassificationType.ValueType -> SemanticTokenTypes.Struct + | SemanticClassificationType.UnionCase -> SemanticTokenTypes.Enum + | SemanticClassificationType.UnionCaseField -> SemanticTokenTypes.EnumMember + | SemanticClassificationType.Function -> SemanticTokenTypes.Function + | SemanticClassificationType.Property -> SemanticTokenTypes.Property + | SemanticClassificationType.Module -> SemanticTokenTypes.Type + | SemanticClassificationType.Namespace -> SemanticTokenTypes.Namespace + | SemanticClassificationType.Interface -> SemanticTokenTypes.Interface + | SemanticClassificationType.TypeArgument -> SemanticTokenTypes.TypeParameter + | SemanticClassificationType.Operator -> SemanticTokenTypes.Operator + | SemanticClassificationType.Method -> SemanticTokenTypes.Method + | SemanticClassificationType.ExtensionMethod -> SemanticTokenTypes.Method + | SemanticClassificationType.Field -> SemanticTokenTypes.Property + | SemanticClassificationType.Event -> SemanticTokenTypes.Event + | SemanticClassificationType.Delegate -> SemanticTokenTypes.Function + | SemanticClassificationType.NamedArgument -> SemanticTokenTypes.Parameter + | SemanticClassificationType.LocalValue -> SemanticTokenTypes.Variable + | SemanticClassificationType.Plaintext -> SemanticTokenTypes.String + | SemanticClassificationType.Type -> SemanticTokenTypes.Type + | SemanticClassificationType.Printf -> SemanticTokenTypes.Keyword + | _ -> SemanticTokenTypes.Comment + + let toIndex (x: string) = SemanticTokenTypes.AllTypes |> Seq.findIndex (fun y -> y = x) + type FSharpRequestContext(lspServices: ILspServices, logger: ILspLogger, workspace: FSharpWorkspace, checker: FSharpChecker) = member _.LspServices = lspServices member _.Logger = logger @@ -34,113 +96,54 @@ type FSharpRequestContext(lspServices: ILspServices, logger: ILspLogger, workspa member _.GetSemanticTokensForFile(file: Uri) = - let FSharpTokenTypeToLSP (fst: SemanticClassificationType) = - // XXX arbitrary - match fst with - | SemanticClassificationType.ReferenceType -> SemanticTokenTypes.Class - | SemanticClassificationType.ValueType -> SemanticTokenTypes.Struct - | SemanticClassificationType.UnionCase -> SemanticTokenTypes.Enum - | SemanticClassificationType.UnionCaseField -> SemanticTokenTypes.EnumMember - | SemanticClassificationType.Function -> SemanticTokenTypes.Function - | SemanticClassificationType.Property -> SemanticTokenTypes.Property - | SemanticClassificationType.Module -> SemanticTokenTypes.Type - | SemanticClassificationType.Namespace -> SemanticTokenTypes.Namespace - | SemanticClassificationType.Interface -> SemanticTokenTypes.Interface - | SemanticClassificationType.TypeArgument -> SemanticTokenTypes.TypeParameter - | SemanticClassificationType.Operator -> SemanticTokenTypes.Operator - | SemanticClassificationType.Method -> SemanticTokenTypes.Method - | SemanticClassificationType.ExtensionMethod -> SemanticTokenTypes.Method - | SemanticClassificationType.Field -> SemanticTokenTypes.Property - | SemanticClassificationType.Event -> SemanticTokenTypes.Event - | SemanticClassificationType.Delegate -> SemanticTokenTypes.Function - | SemanticClassificationType.NamedArgument -> SemanticTokenTypes.Parameter - | SemanticClassificationType.LocalValue -> SemanticTokenTypes.Variable - | SemanticClassificationType.Plaintext -> SemanticTokenTypes.String - | SemanticClassificationType.Type -> SemanticTokenTypes.Type - | SemanticClassificationType.Printf -> SemanticTokenTypes.Keyword - | _ -> SemanticTokenTypes.Comment - - let toIndex (x: string) = SemanticTokenTypes.AllTypes |> Seq.findIndex (fun y -> y = x) - workspace.GetSnapshotForFile file |> Option.map (fun snapshot -> async { let! _, checkFileAnswer = checker.ParseAndCheckFileInProject(file.LocalPath, snapshot, "LSP Get semantic classification") + let semanticClassifications = + match checkFileAnswer with + | FSharpCheckFileAnswer.Succeeded result -> result.GetSemanticClassification(None) // XXX not sure if range opt should be None + | FSharpCheckFileAnswer.Aborted -> [||] // XXX should be error maybe + let! source = snapshot.ProjectSnapshot.SourceFiles |> Seq.find (fun f -> f.FileName = file.LocalPath) |> _.GetSource() |> Async.AwaitTask - let mutable a' = [] - - let tokenCallback = - fun (tok: FSharpToken) -> - let spanKind = - if tok.IsKeyword then - SemanticTokenTypes.Keyword - elif tok.IsNumericLiteral then - SemanticTokenTypes.Number - elif tok.IsCommentTrivia then - SemanticTokenTypes.Comment - elif tok.IsStringLiteral then - SemanticTokenTypes.String - else - SemanticTokenTypes.Function // XXX - - a' <- (tok.Range, spanKind)::a' - - FSharpLexer.Tokenize( - source, - tokenCallback, - flags = (FSharpLexerFlags.Default &&& ~~~FSharpLexerFlags.Compiling &&& ~~~FSharpLexerFlags.UseLexFilter), - filePath = file.LocalPath - ) - - let a = - match checkFileAnswer with - | FSharpCheckFileAnswer.Succeeded result -> result.GetSemanticClassification(None) // XXX not sure if range opt should be None - | FSharpCheckFileAnswer.Aborted -> [||] // XXX should be error maybe - - let b = - a - |> Array.map (fun y -> - let startLine = y.Range.StartLine - 1 - let startCol = y.Range.StartColumn - let length = y.Range.EndColumn - y.Range.StartColumn // XXX Does not deal with multiline tokens? - let tokType = y.Type |> FSharpTokenTypeToLSP |> toIndex - let tokMods = 0 - (startLine, startCol, length, tokType, tokMods) - ) - let b' = - a' - |> List.map (fun (r, t) -> - let startLine = r.StartLine - 1 - let startCol = r.StartColumn - let length = r.EndColumn - r.StartColumn // XXX - let tokType = t |> toIndex - let tokMods = 0 - (startLine, startCol, length, tokType, tokMods) - ) - let b'' = - Array.ofList b' - |> Array.append b - |> Array.sortWith (fun (l1, c1, _, _, _) (l2, c2, _, _, _) -> - let c = l1.CompareTo(l2) + let syntacticClassifications = TokenTypes.GetSyntacticTokenTypes source file.LocalPath + + let lspFormatTokens = + semanticClassifications + |> Array.map (fun item -> (item.Range, item.Type |> TokenTypes.FSharpTokenTypeToLSP |> TokenTypes.toIndex)) + |> Array.append (syntacticClassifications|> List.map (fun (r, t) -> (r, TokenTypes.toIndex t)) |> Array.ofList) + |> Array.map (fun (r, tokType) -> + let length = r.EndColumn - r.StartColumn // XXX Does not deal with multiline tokens? + {| startLine = r.StartLine - 1; startCol = r.StartColumn; length = length; tokType = tokType; tokMods = 0 |}) + //(startLine, startCol, length, tokType, tokMods)) + |> Array.sortWith (fun x1 x2 -> + let c = x1.startLine.CompareTo(x2.startLine) if c <> 0 then c - else c1.CompareTo(c2)) + else x1.startCol.CompareTo(x2.startCol)) - let c = - b'' - |> Array.append [|(0,0,0,0,0)|] + let tokensRelative = + lspFormatTokens + |> Array.append [| {| startLine = 0; startCol = 0; length = 0; tokType = 0; tokMods = 0 |} |] |> Array.pairwise |> Array.map (fun (prev, this) -> - let (prevSLine, prevSCol, _, _, _) = prev - let (thisSLine, thisSCol, length, tokType, tokMods) = this - (thisSLine - prevSLine, (if prevSLine = thisSLine then thisSCol - prevSCol else thisSCol), length, tokType, tokMods)) - - return c |> Array.map (fun (v, w, x, y, z) -> [| v; w; x; y; z |]) |> Array.concat + {| + startLine = this.startLine - prev.startLine + startCol = (if prev.startLine = this.startLine then this.startCol - prev.startCol else this.startCol) + length = this.length + tokType = this.tokType + tokMods = this.tokMods + |}) + + return tokensRelative + |> Array.map (fun tok -> + [| tok.startLine; tok.startCol; tok.length; tok.tokType; tok.tokMods |]) + |> Array.concat }) |> Option.defaultValue (async { return [||] }) From 08263436f3680939952c3a71c02476ec436f8286 Mon Sep 17 00:00:00 2001 From: Adam Boniecki Date: Tue, 3 Sep 2024 14:09:46 +0200 Subject: [PATCH 18/60] Add some null checks --- src/Compiler/Service/FSharpProjectSnapshot.fs | 7 ++++++- .../FSharpLanguageServerProvider.cs | 3 ++- .../FSharp.Compiler.LanguageServer.Tests.fsproj | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Compiler/Service/FSharpProjectSnapshot.fs b/src/Compiler/Service/FSharpProjectSnapshot.fs index 62263c0c670..2a88fcdee9a 100644 --- a/src/Compiler/Service/FSharpProjectSnapshot.fs +++ b/src/Compiler/Service/FSharpProjectSnapshot.fs @@ -683,7 +683,12 @@ and [] FSha let compilerArgs = File.ReadAllLines responseFile.FullName - FSharpProjectSnapshot.FromCommandLineArgs(compilerArgs, responseFile.DirectoryName, projectFileName) + let directoryName : string = + match responseFile.DirectoryName with + | null -> failwith "Directory name of the response file is null" + | str -> str + + FSharpProjectSnapshot.FromCommandLineArgs(compilerArgs, directoryName, projectFileName) static member FromCommandLineArgs(compilerArgs: string array, directoryPath: string, projectFileName) = let fsharpFileExtensions = set [| ".fs"; ".fsi"; ".fsx" |] diff --git a/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs b/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs index 468438d4b7d..ace7d25343e 100644 --- a/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs +++ b/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs @@ -253,8 +253,9 @@ internal class FSharpLanguageServerProvider : LanguageServerProvider var lines = args.Item2.Split(';'); // XXX Probably not robust enough var path = args.Item1; + string directoryPath = Path.GetDirectoryName(path) ?? throw new Exception("Directory path should not be null"); var snapshot = FSharpProjectSnapshot.FromCommandLineArgs( - lines, Path.GetDirectoryName(path), Path.GetFileName(path)); + lines, directoryPath, Path.GetFileName(path)); snapshots.Add(snapshot); } workspace = FSharpWorkspace.Create(snapshots); diff --git a/tests/FSharp.Compiler.LanguageServer.Tests/FSharp.Compiler.LanguageServer.Tests.fsproj b/tests/FSharp.Compiler.LanguageServer.Tests/FSharp.Compiler.LanguageServer.Tests.fsproj index 40703a0e518..dbffa2deb13 100644 --- a/tests/FSharp.Compiler.LanguageServer.Tests/FSharp.Compiler.LanguageServer.Tests.fsproj +++ b/tests/FSharp.Compiler.LanguageServer.Tests/FSharp.Compiler.LanguageServer.Tests.fsproj @@ -1,7 +1,7 @@  - net8.0 + $(FSharpNetCoreProductTargetFramework) false false true From 2fccf9bf0022ae75288581c6859502e3e7ef852a Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 6 Nov 2024 17:59:13 +0100 Subject: [PATCH 19/60] FSharpWorkspace (#17920) --- src/Compiler/Facilities/AsyncMemoize.fs | 1 + src/Compiler/Service/FSharpProjectSnapshot.fs | 301 ++++++++------ src/Compiler/Service/TransparentCompiler.fs | 44 +-- src/Compiler/Service/TransparentCompiler.fsi | 23 +- .../Common/CapabilitiesManager.fs | 5 +- .../Common/DependencyGraph.fs | 372 ++++++++++++++++++ .../Common/FSharpRequestContext.fs | 162 +------- .../Common/FSharpWorkspace.fs | 160 +++----- .../Common/FSharpWorkspaceQuery.fs | 212 ++++++++++ .../Common/FSharpWorkspaceState.fs | 366 +++++++++++++++++ .../Common/LifecycleManager.fs | 2 +- .../FSharp.Compiler.LanguageServer.fsproj | 8 + .../FSharpLanguageServer.fs | 4 +- .../Handlers/DocumentStateHandler.fs | 6 +- .../Handlers/LanguageFeaturesHandler.fs | 2 +- .../FSharpLanguageServerProvider.cs | 241 +++++++----- .../DependencyGraphTests.fs | 161 ++++++++ ...Sharp.Compiler.LanguageServer.Tests.fsproj | 2 + .../FSharpWorkspaceTests.fs | 300 ++++++++++++++ .../LanguageServerTests.fs | 40 +- .../Program.fs | 4 +- 21 files changed, 1866 insertions(+), 550 deletions(-) create mode 100644 src/FSharp.Compiler.LanguageServer/Common/DependencyGraph.fs create mode 100644 src/FSharp.Compiler.LanguageServer/Common/FSharpWorkspaceQuery.fs create mode 100644 src/FSharp.Compiler.LanguageServer/Common/FSharpWorkspaceState.fs create mode 100644 tests/FSharp.Compiler.LanguageServer.Tests/DependencyGraphTests.fs create mode 100644 tests/FSharp.Compiler.LanguageServer.Tests/FSharpWorkspaceTests.fs diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index d22093a2b4f..ce3ff5dd470 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -89,6 +89,7 @@ type internal JobEvent = | Cleared type internal ICacheKey<'TKey, 'TVersion> = + // TODO Key should probably be renamed to Identifier abstract member GetKey: unit -> 'TKey abstract member GetVersion: unit -> 'TVersion abstract member GetLabel: unit -> string diff --git a/src/Compiler/Service/FSharpProjectSnapshot.fs b/src/Compiler/Service/FSharpProjectSnapshot.fs index 2a88fcdee9a..3e3ba0325ef 100644 --- a/src/Compiler/Service/FSharpProjectSnapshot.fs +++ b/src/Compiler/Service/FSharpProjectSnapshot.fs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. module FSharp.Compiler.CodeAnalysis.ProjectSnapshot @@ -41,10 +41,10 @@ module internal Helpers = let addFileNameAndVersion (file: IFileSnapshot) = addFileName file >> Md5Hasher.addBytes file.Version - let signatureHash projectCoreVersion (sourceFiles: IFileSnapshot seq) = + let signatureHash projectBaseVersion (sourceFiles: IFileSnapshot seq) = let mutable lastFile = "" - ((projectCoreVersion, Set.empty), sourceFiles) + ((projectBaseVersion, Set.empty), sourceFiles) ||> Seq.fold (fun (res, sigs) file -> if file.IsSignatureFile then lastFile <- file.FileName @@ -72,6 +72,13 @@ type FSharpFileSnapshot(FileName: string, Version: string, GetSource: unit -> Ta static member Create(fileName: string, version: string, getSource: unit -> Task) = FSharpFileSnapshot(fileName, version, getSource) + static member CreateFromString(filename: string, content: string) = + FSharpFileSnapshot( + filename, + Md5Hasher.hashString content |> Md5Hasher.toString, + fun () -> Task.FromResult(SourceTextNew.ofString content) + ) + static member CreateFromFileSystem(fileName: string) = FSharpFileSnapshot( fileName, @@ -171,18 +178,34 @@ type ReferenceOnDisk = { Path: string; LastModified: DateTime } /// A snapshot of an F# project. The source file type can differ based on which stage of compilation the snapshot is used for. -type internal ProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: ProjectCore, sourceFiles: 'T list) = +type internal ProjectSnapshotBase<'T when 'T :> IFileSnapshot> + (projectConfig: ProjectConfig, referencedProjects: FSharpReferencedProjectSnapshot list, sourceFiles: 'T list) = + + // Version of project without source files + let baseVersion = + lazy + (projectConfig.Version + |> Md5Hasher.addBytes' (referencedProjects |> Seq.map _.Version)) + + let baseVersionString = lazy (baseVersion.Value |> Md5Hasher.toString) + + let baseCacheKeyWith (label, version) = + { new ICacheKey<_, _> with + member _.GetLabel() = $"{label} ({projectConfig.Label})" + member _.GetKey() = projectConfig.Identifier + member _.GetVersion() = baseVersionString.Value, version + } let noFileVersionsHash = lazy - (projectCore.Version + (baseVersion.Value |> Md5Hasher.addStrings (sourceFiles |> Seq.map (fun x -> x.FileName))) let noFileVersionsKey = lazy ({ new ICacheKey<_, _> with - member _.GetLabel() = projectCore.Label - member _.GetKey() = projectCore.Identifier + member _.GetLabel() = projectConfig.Label + member _.GetKey() = projectConfig.Identifier member _.GetVersion() = noFileVersionsHash.Value |> Md5Hasher.toString @@ -191,7 +214,7 @@ type internal ProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: Proj let fullHash = lazy - (projectCore.Version + (baseVersion.Value |> Md5Hasher.addStrings ( sourceFiles |> Seq.collect (fun x -> @@ -204,8 +227,8 @@ type internal ProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: Proj let fullKey = lazy ({ new ICacheKey<_, _> with - member _.GetLabel() = projectCore.Label - member _.GetKey() = projectCore.Identifier + member _.GetLabel() = projectConfig.Label + member _.GetKey() = projectConfig.Identifier member _.GetVersion() = fullHash.Value |> Md5Hasher.toString }) @@ -213,10 +236,10 @@ type internal ProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: Proj hash |> Md5Hasher.addString file.FileName |> Md5Hasher.addBytes file.Version let signatureHash = - lazy (signatureHash projectCore.Version (sourceFiles |> Seq.map (fun x -> x :> IFileSnapshot))) + lazy (signatureHash baseVersion.Value (sourceFiles |> Seq.map (fun x -> x :> IFileSnapshot))) let signatureKey = - lazy (projectCore.CacheKeyWith("Signature", signatureHash.Value |> fst |> Md5Hasher.toString)) + lazy (baseCacheKeyWith ("Signature", signatureHash.Value |> fst |> Md5Hasher.toString)) let lastFileHash = lazy @@ -234,48 +257,48 @@ type internal ProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: Proj (let hash, f = lastFileHash.Value { new ICacheKey<_, _> with - member _.GetLabel() = $"{f.FileName} ({projectCore.Label})" - member _.GetKey() = f.FileName, projectCore.Identifier + member _.GetLabel() = $"{f.FileName} ({projectConfig.Label})" + member _.GetKey() = f.FileName, projectConfig.Identifier member _.GetVersion() = hash |> Md5Hasher.toString }) let sourceFileNames = lazy (sourceFiles |> List.map (fun x -> x.FileName)) - member _.ProjectFileName = projectCore.ProjectFileName - member _.ProjectId = projectCore.ProjectId - member _.Identifier = projectCore.Identifier - member _.ReferencesOnDisk = projectCore.ReferencesOnDisk - member _.OtherOptions = projectCore.OtherOptions - member _.ReferencedProjects = projectCore.ReferencedProjects + member _.ProjectFileName = projectConfig.ProjectFileName + member _.ProjectId = projectConfig.ProjectId + member _.Identifier = projectConfig.Identifier + member _.ReferencesOnDisk = projectConfig.ReferencesOnDisk + member _.OtherOptions = projectConfig.OtherOptions + member _.ReferencedProjects = referencedProjects member _.IsIncompleteTypeCheckEnvironment = - projectCore.IsIncompleteTypeCheckEnvironment + projectConfig.IsIncompleteTypeCheckEnvironment - member _.UseScriptResolutionRules = projectCore.UseScriptResolutionRules - member _.LoadTime = projectCore.LoadTime - member _.UnresolvedReferences = projectCore.UnresolvedReferences - member _.OriginalLoadReferences = projectCore.OriginalLoadReferences - member _.Stamp = projectCore.Stamp - member _.CommandLineOptions = projectCore.CommandLineOptions - member _.ProjectDirectory = projectCore.ProjectDirectory + member _.UseScriptResolutionRules = projectConfig.UseScriptResolutionRules + member _.LoadTime = projectConfig.LoadTime + member _.UnresolvedReferences = projectConfig.UnresolvedReferences + member _.OriginalLoadReferences = projectConfig.OriginalLoadReferences + member _.Stamp = projectConfig.Stamp + member _.CommandLineOptions = projectConfig.CommandLineOptions + member _.ProjectDirectory = projectConfig.ProjectDirectory - member _.OutputFileName = projectCore.OutputFileName + member _.OutputFileName = projectConfig.OutputFileName - member _.ProjectCore = projectCore + member _.ProjectConfig = projectConfig member _.SourceFiles = sourceFiles member _.SourceFileNames = sourceFileNames.Value - member _.Label = projectCore.Label + member _.Label = projectConfig.Label member _.IndexOf fileName = sourceFiles |> List.tryFindIndex (fun x -> x.FileName = fileName) - |> Option.defaultWith (fun () -> failwith (sprintf "Unable to find file %s in project %s" fileName projectCore.ProjectFileName)) + |> Option.defaultWith (fun () -> failwith (sprintf "Unable to find file %s in project %s" fileName projectConfig.ProjectFileName)) member private _.With(sourceFiles: 'T list) = - ProjectSnapshotBase(projectCore, sourceFiles) + ProjectSnapshotBase(projectConfig, referencedProjects, sourceFiles) /// Create a new snapshot with given source files replacing files in this snapshot with the same name. Other files remain unchanged. member this.Replace(changedSourceFiles: 'T list) = @@ -312,7 +335,7 @@ type internal ProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: Proj /// The newest last modified time of any file in this snapshot including the project file member _.GetLastModifiedTimeOnDisk() = seq { - projectCore.ProjectFileName + projectConfig.ProjectFileName yield! sourceFiles @@ -328,7 +351,7 @@ type internal ProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: Proj member _.LastFileVersion = lastFileHash.Value |> fst /// Version for parsing - doesn't include any references because they don't affect parsing (...right?) - member _.ParsingVersion = projectCore.VersionForParsing |> Md5Hasher.toString + member _.ParsingVersion = projectConfig.VersionForParsing |> Md5Hasher.toString /// A key for this snapshot but without file versions. So it will be the same across any in-file changes. member _.NoFileVersionsKey = noFileVersionsKey.Value @@ -352,110 +375,136 @@ type internal ProjectSnapshotBase<'T when 'T :> IFileSnapshot>(projectCore: Proj fileKey.WithExtraVersion(fileSnapshot.Version |> Md5Hasher.toString) + /// Cache key for the project without source files + member this.BaseCacheKeyWith(label, version) = baseCacheKeyWith (label, version) + /// Project snapshot with filenames and versions given as initial input and internal ProjectSnapshot = ProjectSnapshotBase /// Project snapshot with file sources loaded and internal ProjectSnapshotWithSources = ProjectSnapshotBase -/// All required information for compiling a project except the source files. It's kept separate so it can be reused +/// All required information for compiling a project except the source files and referenced projects. It's kept separate so it can be reused /// for different stages of a project snapshot and also between changes to the source files. -and internal ProjectCore +and ProjectConfig + internal ( - ProjectFileName: string, - ProjectId: string option, - ReferencesOnDisk: ReferenceOnDisk list, - OtherOptions: string list, - ReferencedProjects: FSharpReferencedProjectSnapshot list, - IsIncompleteTypeCheckEnvironment: bool, - UseScriptResolutionRules: bool, - LoadTime: DateTime, - UnresolvedReferences: FSharpUnresolvedReferencesSet option, - OriginalLoadReferences: (range * string * string) list, - Stamp: int64 option - ) as self = + projectFileName: string, + outputFileName: string option, + referencesOnDisk: ReferenceOnDisk list, + otherOptions: string list, + isIncompleteTypeCheckEnvironment: bool, + useScriptResolutionRules: bool, + unresolvedReferences, + originalLoadReferences: (range * string * string) list, + loadTime: DateTime, + stamp: int64 option, + projectId: string option + ) = let hashForParsing = lazy (Md5Hasher.empty - |> Md5Hasher.addString ProjectFileName - |> Md5Hasher.addStrings OtherOptions - |> Md5Hasher.addBool IsIncompleteTypeCheckEnvironment - |> Md5Hasher.addBool UseScriptResolutionRules) + |> Md5Hasher.addString projectFileName + |> Md5Hasher.addStrings otherOptions + |> Md5Hasher.addBool isIncompleteTypeCheckEnvironment + |> Md5Hasher.addBool useScriptResolutionRules) let fullHash = lazy (hashForParsing.Value - |> Md5Hasher.addStrings (ReferencesOnDisk |> Seq.map (fun r -> r.Path)) - |> Md5Hasher.addDateTimes (ReferencesOnDisk |> Seq.map (fun r -> r.LastModified)) - |> Md5Hasher.addBytes' ( - ReferencedProjects - |> Seq.map (function - | FSharpReference(_name, p) -> p.ProjectSnapshot.SignatureVersion - | PEReference(getStamp, _) -> Md5Hasher.empty |> Md5Hasher.addDateTime (getStamp ()) - | ILModuleReference(_name, getStamp, _) -> Md5Hasher.empty |> Md5Hasher.addDateTime (getStamp ())) - )) - - let fullHashString = lazy (fullHash.Value |> Md5Hasher.toString) + |> Md5Hasher.addStrings (referencesOnDisk |> Seq.map (fun r -> r.Path)) + |> Md5Hasher.addDateTimes (referencesOnDisk |> Seq.map (fun r -> r.LastModified))) let commandLineOptions = lazy (seq { - yield! OtherOptions + yield! otherOptions - for r in ReferencesOnDisk do + for r in referencesOnDisk do $"-r:{r.Path}" } |> Seq.toList) - let outputFileName = lazy (OtherOptions |> findOutputFileName) - - let key = lazy (ProjectFileName, outputFileName.Value |> Option.defaultValue "") + let outputFileNameValue = + lazy + (outputFileName + |> Option.orElseWith (fun () -> otherOptions |> findOutputFileName)) - let cacheKey = + let identifier = lazy - ({ new ICacheKey<_, _> with - member _.GetLabel() = self.Label - member _.GetKey() = self.Identifier - member _.GetVersion() = fullHashString.Value - }) + ((projectFileName, outputFileNameValue.Value |> Option.defaultValue "") + |> FSharpProjectIdentifier) + + new(projectFileName: string, + outputFileName: string option, + referencesOnDisk: string seq, + otherOptions: string seq, + ?isIncompleteTypeCheckEnvironment: bool, + ?useScriptResolutionRules: bool, + ?loadTime: DateTime, + ?stamp: int64, + ?projectId: string) = - member val ProjectDirectory = !! Path.GetDirectoryName(ProjectFileName) - member _.OutputFileName = outputFileName.Value - member _.Identifier: ProjectIdentifier = key.Value + let referencesOnDisk = + referencesOnDisk + |> Seq.map (fun path -> + { + Path = path + LastModified = FileSystem.GetLastWriteTimeShim path + }) + |> Seq.toList + + ProjectConfig( + projectFileName, + outputFileName, + referencesOnDisk, + otherOptions |> Seq.toList, + isIncompleteTypeCheckEnvironment = defaultArg isIncompleteTypeCheckEnvironment false, + useScriptResolutionRules = defaultArg useScriptResolutionRules false, + unresolvedReferences = None, + originalLoadReferences = [], + loadTime = defaultArg loadTime DateTime.Now, + stamp = stamp, + projectId = projectId + ) + + member val ProjectDirectory = !! Path.GetDirectoryName(projectFileName) + member _.OutputFileName = outputFileNameValue.Value + member _.Identifier = identifier.Value member _.Version = fullHash.Value - member _.Label = ProjectFileName |> shortPath + member _.Label = projectFileName |> shortPath member _.VersionForParsing = hashForParsing.Value member _.CommandLineOptions = commandLineOptions.Value - member _.ProjectFileName = ProjectFileName - member _.ProjectId = ProjectId - member _.ReferencesOnDisk = ReferencesOnDisk - member _.OtherOptions = OtherOptions - member _.ReferencedProjects = ReferencedProjects - member _.IsIncompleteTypeCheckEnvironment = IsIncompleteTypeCheckEnvironment - member _.UseScriptResolutionRules = UseScriptResolutionRules - member _.LoadTime = LoadTime - member _.UnresolvedReferences = UnresolvedReferences - member _.OriginalLoadReferences = OriginalLoadReferences - member _.Stamp = Stamp - - member _.CacheKeyWith(label, version) = - { new ICacheKey<_, _> with - member _.GetLabel() = $"{label} ({self.Label})" - member _.GetKey() = self.Identifier - member _.GetVersion() = fullHashString.Value, version - } - - member _.CacheKeyWith(label, key, version) = - { new ICacheKey<_, _> with - member _.GetLabel() = $"{label} ({self.Label})" - member _.GetKey() = key, self.Identifier - member _.GetVersion() = fullHashString.Value, version - } - - member _.CacheKey = cacheKey.Value + member _.ProjectFileName = projectFileName + member _.ProjectId = projectId + member _.ReferencesOnDisk = referencesOnDisk + member _.OtherOptions = otherOptions + + member _.IsIncompleteTypeCheckEnvironment = isIncompleteTypeCheckEnvironment + member _.UseScriptResolutionRules = useScriptResolutionRules + member _.LoadTime = loadTime + member _.Stamp = stamp + member _.UnresolvedReferences = unresolvedReferences + member _.OriginalLoadReferences = originalLoadReferences + + /// Creates a copy of this project config with a new set of references + member internal _.With(newReferencesOnDisk) = + ProjectConfig( + projectFileName, + outputFileName, + newReferencesOnDisk, + otherOptions, + isIncompleteTypeCheckEnvironment, + useScriptResolutionRules, + unresolvedReferences, + originalLoadReferences, + loadTime, + stamp, + projectId + ) and [] FSharpReferencedProjectSnapshot = /// @@ -503,6 +552,12 @@ and [ p.ProjectSnapshot.SignatureVersion + | PEReference(getStamp, _) -> Md5Hasher.empty |> Md5Hasher.addDateTime (getStamp ()) + | ILModuleReference(_name, getStamp, _) -> Md5Hasher.empty |> Md5Hasher.addDateTime (getStamp ()) + override this.Equals(o) = match o with | :? FSharpReferencedProjectSnapshot as o -> @@ -523,6 +578,17 @@ and [] FSharpProjectIdentifier = | FSharpProjectIdentifier of projectFileName: string * outputFileName: string + member this.OutputFileName = + match this with + | FSharpProjectIdentifier(_, outputFileName) -> outputFileName + + member this.ProjectFileName = + match this with + | FSharpProjectIdentifier(projectFileName, _) -> projectFileName + + override this.ToString() = + $"{shortPath this.ProjectFileName} 🡒 {shortPath this.OutputFileName}" + /// A snapshot of an F# project. This type contains all the necessary information for type checking a project. and [] FSharpProjectSnapshot internal (projectSnapshot) = @@ -533,7 +599,7 @@ and [] FSha projectSnapshot.Replace(changedSourceFiles) |> FSharpProjectSnapshot member _.Label = projectSnapshot.Label - member _.Identifier = FSharpProjectIdentifier projectSnapshot.ProjectCore.Identifier + member _.Identifier = projectSnapshot.ProjectConfig.Identifier member _.ProjectFileName = projectSnapshot.ProjectFileName member _.ProjectId = projectSnapshot.ProjectId member _.SourceFiles = projectSnapshot.SourceFiles @@ -549,10 +615,12 @@ and [] FSha member _.UnresolvedReferences = projectSnapshot.UnresolvedReferences member _.OriginalLoadReferences = projectSnapshot.OriginalLoadReferences member _.Stamp = projectSnapshot.Stamp + member _.OutputFileName = projectSnapshot.OutputFileName static member Create ( projectFileName: string, + outputFileName: string option, projectId: string option, sourceFiles: FSharpFileSnapshot list, referencesOnDisk: ReferenceOnDisk list, @@ -566,22 +634,23 @@ and [] FSha stamp: int64 option ) = - let projectCore = - ProjectCore( + let projectConfig = + ProjectConfig( projectFileName, - projectId, + outputFileName, referencesOnDisk, otherOptions, - referencedProjects, isIncompleteTypeCheckEnvironment, useScriptResolutionRules, - loadTime, unresolvedReferences, originalLoadReferences, - stamp + loadTime, + stamp, + projectId ) - ProjectSnapshotBase(projectCore, sourceFiles) |> FSharpProjectSnapshot + ProjectSnapshotBase(projectConfig, referencedProjects, sourceFiles) + |> FSharpProjectSnapshot static member FromOptions(options: FSharpProjectOptions, getFileSnapshot, ?snapshotAccumulator) = let snapshotAccumulator = defaultArg snapshotAccumulator (Dictionary()) @@ -629,6 +698,7 @@ and [] FSha let snapshot = FSharpProjectSnapshot.Create( projectFileName = options.ProjectFileName, + outputFileName = None, projectId = options.ProjectId, sourceFiles = (sourceFiles |> List.ofArray), referencesOnDisk = (referencesOnDisk |> List.ofArray), @@ -683,7 +753,7 @@ and [] FSha let compilerArgs = File.ReadAllLines responseFile.FullName - let directoryName : string = + let directoryName: string = match responseFile.DirectoryName with | null -> failwith "Directory name of the response file is null" | str -> str @@ -720,6 +790,7 @@ and [] FSha FSharpProjectSnapshot.Create( projectFileName = projectFileName, + outputFileName = None, projectId = None, sourceFiles = (fsharpFiles |> List.map FSharpFileSnapshot.CreateFromFileSystem), referencesOnDisk = diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 5158ac7f25c..00290bd5cd5 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -298,7 +298,7 @@ type internal CompilerCaches(sizeFactor: int) = member val ScriptClosure = AsyncMemoize(sf, 2 * sf, name = "ScriptClosure") - member this.Clear(projects: Set) = + member this.Clear(projects: Set) = let shouldClear project = projects |> Set.contains project this.ParseFile.Clear(fst >> shouldClear) @@ -422,11 +422,11 @@ type internal TransparentCompiler (useFsiAuxLib: bool) (useSdkRefs: bool) (assumeDotNetFramework: bool) - (projectIdentifier: ProjectIdentifier) + (projectIdentifier: FSharpProjectIdentifier) (otherOptions: string list) (stamp: int64 option) = - { new ICacheKey with + { new ICacheKey with member _.GetKey() = fileName, projectIdentifier member _.GetLabel() = $"ScriptClosure for %s{fileName}" @@ -456,7 +456,7 @@ type internal TransparentCompiler (useSdkRefs: bool option) (sdkDirOverride: string option) (assumeDotNetFramework: bool option) - (projectIdentifier: ProjectIdentifier) + (projectIdentifier: FSharpProjectIdentifier) (otherOptions: string list) (stamp: int64 option) = @@ -464,7 +464,7 @@ type internal TransparentCompiler let useSdkRefs = defaultArg useSdkRefs true let assumeDotNetFramework = defaultArg assumeDotNetFramework false - let key: ICacheKey = + let key = mkScriptClosureCacheKey fileName source @@ -826,8 +826,8 @@ type internal TransparentCompiler let mutable BootstrapInfoIdCounter = 0 /// Bootstrap info that does not depend source files - let ComputeBootstrapInfoStatic (projectSnapshot: ProjectCore, tcConfig: TcConfig, assemblyName: string, loadClosureOpt) = - let cacheKey = projectSnapshot.CacheKeyWith("BootstrapInfoStatic", assemblyName) + let ComputeBootstrapInfoStatic (projectSnapshot: ProjectSnapshotBase<_>, tcConfig: TcConfig, assemblyName: string, loadClosureOpt) = + let cacheKey = projectSnapshot.BaseCacheKeyWith("BootstrapInfoStatic", assemblyName) caches.BootstrapInfoStatic.Get( cacheKey, @@ -957,7 +957,7 @@ type internal TransparentCompiler let outFile, _, assemblyName = tcConfigB.DecideNames sourceFiles let! bootstrapId, tcImports, tcGlobals, initialTcInfo, importsInvalidatedByTypeProvider = - ComputeBootstrapInfoStatic(projectSnapshot.ProjectCore, tcConfig, assemblyName, loadClosureOpt) + ComputeBootstrapInfoStatic(projectSnapshot, tcConfig, assemblyName, loadClosureOpt) // Check for the existence of loaded sources and prepend them to the sources list if present. let loadedSources = @@ -1056,7 +1056,7 @@ type internal TransparentCompiler |> Seq.map (fun f -> LoadSource f isExe (f.FileName = bootstrapInfo.LastFileName)) |> MultipleDiagnosticsLoggers.Parallel - return ProjectSnapshotWithSources(projectSnapshot.ProjectCore, sources |> Array.toList) + return ProjectSnapshotWithSources(projectSnapshot.ProjectConfig, projectSnapshot.ReferencedProjects, sources |> Array.toList) } @@ -1067,7 +1067,7 @@ type internal TransparentCompiler member _.GetLabel() = file.FileName |> shortPath member _.GetKey() = - projectSnapshot.ProjectCore.Identifier, file.FileName + projectSnapshot.ProjectConfig.Identifier, file.FileName member _.GetVersion() = projectSnapshot.ParsingVersion, @@ -1467,7 +1467,7 @@ type internal TransparentCompiler |> Seq.map (ComputeParseFile projectSnapshot tcConfig) |> MultipleDiagnosticsLoggers.Parallel - return ProjectSnapshotBase<_>(projectSnapshot.ProjectCore, parsedInputs |> Array.toList) + return ProjectSnapshotBase<_>(projectSnapshot.ProjectConfig, projectSnapshot.ReferencedProjects, parsedInputs |> Array.toList) } // Type check file and all its dependencies @@ -2137,19 +2137,19 @@ type internal TransparentCompiler use _ = Activity.start "TransparentCompiler.ClearCache" [| Activity.Tags.userOpName, userOpName |] - this.Caches.Clear( - projects - |> Seq.map (function - | FSharpProjectIdentifier(x, y) -> (x, y)) - |> Set - ) + this.Caches.Clear(Set projects) member this.ClearCache(options: seq, userOpName: string) : unit = use _ = Activity.start "TransparentCompiler.ClearCache" [| Activity.Tags.userOpName, userOpName |] backgroundCompiler.ClearCache(options, userOpName) - this.Caches.Clear(options |> Seq.map (fun o -> o.GetProjectIdentifier()) |> Set) + + this.Caches.Clear( + options + |> Seq.map (fun o -> o.GetProjectIdentifier() |> FSharpProjectIdentifier) + |> Set + ) member _.ClearCaches() : unit = backgroundCompiler.ClearCaches() @@ -2365,7 +2365,7 @@ type internal TransparentCompiler useFsiAuxLib useSdkRefs assumeDotNetFramework - (projectFileName, "") + (FSharpProjectIdentifier(projectFileName, "")) otherFlags optionsStamp @@ -2394,6 +2394,7 @@ type internal TransparentCompiler FSharpProjectSnapshot.Create( fileName + ".fsproj", None, + None, sourceFiles, references, otherFlags, @@ -2445,10 +2446,7 @@ type internal TransparentCompiler backgroundCompiler.InvalidateConfiguration(options, userOpName) member this.InvalidateConfiguration(projectSnapshot: FSharpProjectSnapshot, _userOpName: string) : unit = - let (FSharpProjectIdentifier(projectFileName, outputFileName)) = - projectSnapshot.Identifier - - this.Caches.Clear(Set.singleton (ProjectIdentifier(projectFileName, outputFileName))) + this.Caches.Clear(Set.singleton projectSnapshot.Identifier) member this.NotifyFileChanged(fileName: string, options: FSharpProjectOptions, userOpName: string) : Async = backgroundCompiler.NotifyFileChanged(fileName, options, userOpName) diff --git a/src/Compiler/Service/TransparentCompiler.fsi b/src/Compiler/Service/TransparentCompiler.fsi index 7746445c0af..ed4241f523d 100644 --- a/src/Compiler/Service/TransparentCompiler.fsi +++ b/src/Compiler/Service/TransparentCompiler.fsi @@ -21,6 +21,7 @@ open FSharp.Compiler.NameResolution open FSharp.Compiler.TypedTree open FSharp.Compiler.CheckDeclarations open FSharp.Compiler.EditorServices +open FSharp.Compiler.CodeAnalysis.ProjectSnapshot /// Accumulated results of type checking. The minimum amount of state in order to continue type-checking following files. [] @@ -101,41 +102,43 @@ type internal CompilerCaches = new: sizeFactor: int -> CompilerCaches - member AssemblyData: AsyncMemoize<(string * string), (string * string), ProjectAssemblyDataResult> + member AssemblyData: AsyncMemoize - member BootstrapInfo: AsyncMemoize<(string * string), string, (BootstrapInfo option * FSharpDiagnostic array)> + member BootstrapInfo: AsyncMemoize member BootstrapInfoStatic: - AsyncMemoize<(string * string), (string * string), (int * TcImports * TcGlobals * TcInfo * Event)> + AsyncMemoize)> member DependencyGraph: AsyncMemoize<(DependencyGraphType option * byte array), string, (Graph * Graph)> member FrameworkImports: AsyncMemoize - member ItemKeyStore: AsyncMemoize<(string * (string * string)), string, ItemKeyStore option> + member ItemKeyStore: AsyncMemoize<(string * FSharpProjectIdentifier), string, ItemKeyStore option> member ParseAndCheckAllFilesInProject: AsyncMemoizeDisabled member ParseAndCheckFileInProject: - AsyncMemoize<(string * (string * string)), string * string, (FSharpParseFileResults * FSharpCheckFileAnswer)> + AsyncMemoize<(string * FSharpProjectIdentifier), string * string, (FSharpParseFileResults * + FSharpCheckFileAnswer)> - member ParseAndCheckProject: AsyncMemoize<(string * string), string, FSharpCheckProjectResults> + member ParseAndCheckProject: AsyncMemoize member ParseFile: - AsyncMemoize<((string * string) * string), (string * string * bool), ProjectSnapshot.FSharpParsedFile> + AsyncMemoize<(FSharpProjectIdentifier * string), (string * string * bool), ProjectSnapshot.FSharpParsedFile> member ParseFileWithoutProject: AsyncMemoize member ProjectExtras: AsyncMemoizeDisabled - member SemanticClassification: AsyncMemoize<(string * (string * string)), string, SemanticClassificationView option> + member SemanticClassification: + AsyncMemoize<(string * FSharpProjectIdentifier), string, SemanticClassificationView option> member SizeFactor: int - member TcIntermediate: AsyncMemoize<(string * (string * string)), (string * int), TcIntermediate> + member TcIntermediate: AsyncMemoize<(string * FSharpProjectIdentifier), (string * int), TcIntermediate> - member ScriptClosure: AsyncMemoize<(string * (string * string)), string, LoadClosure> + member ScriptClosure: AsyncMemoize<(string * FSharpProjectIdentifier), string, LoadClosure> member TcLastFile: AsyncMemoizeDisabled diff --git a/src/FSharp.Compiler.LanguageServer/Common/CapabilitiesManager.fs b/src/FSharp.Compiler.LanguageServer/Common/CapabilitiesManager.fs index 0d2b42e7d05..7632a80ce62 100644 --- a/src/FSharp.Compiler.LanguageServer/Common/CapabilitiesManager.fs +++ b/src/FSharp.Compiler.LanguageServer/Common/CapabilitiesManager.fs @@ -15,8 +15,7 @@ type CapabilitiesManager(scOverrides: IServerCapabilitiesOverride seq) = TextDocumentSync = TextDocumentSyncOptions(OpenClose = true, Change = TextDocumentSyncKind.Full), DiagnosticOptions = DiagnosticOptions(WorkDoneProgress = true, InterFileDependencies = true, Identifier = "potato", WorkspaceDiagnostics = true), - CompletionProvider = - CompletionOptions(TriggerCharacters=[|"."; " "|], ResolveProvider=true, WorkDoneProgress=true), + CompletionProvider = CompletionOptions(TriggerCharacters = [| "."; " " |], ResolveProvider = true, WorkDoneProgress = true), HoverProvider = SumType(HoverOptions(WorkDoneProgress = true)) ) @@ -33,4 +32,4 @@ type CapabilitiesManager(scOverrides: IServerCapabilitiesOverride seq) = member this.GetInitializeParams() = match initializeParams with | Some params' -> params' - | None -> failwith "InitializeParams is null" \ No newline at end of file + | None -> failwith "InitializeParams is null" diff --git a/src/FSharp.Compiler.LanguageServer/Common/DependencyGraph.fs b/src/FSharp.Compiler.LanguageServer/Common/DependencyGraph.fs new file mode 100644 index 00000000000..b27228bb232 --- /dev/null +++ b/src/FSharp.Compiler.LanguageServer/Common/DependencyGraph.fs @@ -0,0 +1,372 @@ +/// This Dependency Graph provides a way to maintain an up-to-date but lazy set of dependent values. +/// When changes are applied to the graph (either vertices change value or edges change), no computation is performed. +/// Only when a value is requested it is lazily computed and thereafter stored until invalidated by further changes. +module FSharp.Compiler.LanguageServer.Common.DependencyGraph + +open System.Collections.Generic + +type DependencyNode<'Identifier, 'Value> = + { + Id: 'Identifier // TODO: probably not needed + Value: 'Value option + + // TODO: optional if it's root node + Compute: 'Value seq -> 'Value + } + +let insert key value (dict: Dictionary<_, _>) = + match dict.TryGetValue key with + | true, _ -> dict[key] <- value + | false, _ -> dict.Add(key, value) + +type IDependencyGraph<'Id, 'Val when 'Id: equality> = + + abstract member AddOrUpdateNode: id: 'Id * value: 'Val -> unit + abstract member AddList: nodes: ('Id * 'Val) seq -> 'Id seq + abstract member AddOrUpdateNode: id: 'Id * dependsOn: 'Id seq * compute: ('Val seq -> 'Val) -> unit + abstract member GetValue: id: 'Id -> 'Val + abstract member GetDependenciesOf: id: 'Id -> 'Id seq + abstract member GetDependentsOf: id: 'Id -> 'Id seq + abstract member AddDependency: node: 'Id * dependsOn: 'Id -> unit + abstract member RemoveDependency: node: 'Id * noLongerDependsOn: 'Id -> unit + abstract member UpdateNode: id: 'Id * update: ('Val -> 'Val) -> unit + abstract member RemoveNode: id: 'Id -> unit + abstract member Debug_GetNodes: ('Id -> bool) -> DependencyNode<'Id, 'Val> seq + abstract member Debug_RenderMermaid: ?mapping: ('Id -> 'Id) -> string + abstract member OnWarning: (string -> unit) -> unit + +and IThreadSafeDependencyGraph<'Id, 'Val when 'Id: equality> = + inherit IDependencyGraph<'Id, 'Val> + + abstract member Transact<'a> : (IDependencyGraph<'Id, 'Val> -> 'a) -> 'a + +module Internal = + + type DependencyGraph<'Id, 'Val when 'Id: equality and 'Id: not null>() as self = + let nodes = Dictionary<'Id, DependencyNode<'Id, 'Val>>() + let dependencies = Dictionary<'Id, HashSet<'Id>>() + let dependents = Dictionary<'Id, HashSet<'Id>>() + let warningSubscribers = ResizeArray() + + let rec invalidateDependents (id: 'Id) = + match dependents.TryGetValue id with + | true, set -> + for dependent in set do + nodes.[dependent] <- { nodes.[dependent] with Value = None } + invalidateDependents dependent + | false, _ -> () + + let invalidateNodeAndDependents id = + nodes[id] <- { nodes[id] with Value = None } + invalidateDependents id + + let addNode node = + nodes |> insert node.Id node + invalidateDependents node.Id + + member _.Debug = + {| + Nodes = nodes + Dependencies = dependencies + Dependents = dependents + |} + + member _.AddOrUpdateNode(id: 'Id, value: 'Val) = + addNode + { + Id = id + Value = Some value + Compute = (fun _ -> value) + } + + member _.AddList(nodes: ('Id * 'Val) seq) = + nodes + |> Seq.map (fun (id, value) -> + addNode + { + Id = id + Value = Some value + Compute = (fun _ -> value) + } + + id) + |> Seq.toList + + member _.AddOrUpdateNode(id: 'Id, dependsOn: 'Id seq, compute: 'Val seq -> 'Val) = + addNode + { + Id = id + Value = None + Compute = compute + } + + match dependencies.TryGetValue id with + | true, oldDependencies -> + for dep in oldDependencies do + match dependents.TryGetValue dep with + | true, set -> set.Remove id |> ignore + | _ -> () + | _ -> () + + dependencies |> insert id (HashSet dependsOn) + + for dep in dependsOn do + match dependents.TryGetValue dep with + | true, set -> set.Add id |> ignore + | false, _ -> dependents.Add(dep, HashSet([| id |])) + + member this.GetValue(id: 'Id) = + let node = nodes[id] + + match node.Value with + | Some value -> value + | None -> + let dependencies = dependencies.[id] + let values = dependencies |> Seq.map (fun id -> this.GetValue id) + let value = node.Compute values + nodes.[id] <- { node with Value = Some value } + value + + member this.GetDependenciesOf(identifier: 'Id) = + match dependencies.TryGetValue identifier with + | true, set -> set |> Seq.map id + | false, _ -> Seq.empty + + member this.GetDependentsOf(identifier: 'Id) = + match dependents.TryGetValue identifier with + | true, set -> set |> Seq.map id + | false, _ -> Seq.empty + + member this.AddDependency(node: 'Id, dependsOn: 'Id) = + match dependencies.TryGetValue node with + | true, deps -> deps.Add dependsOn |> ignore + | false, _ -> dependencies.Add(node, HashSet([| dependsOn |])) + + match dependents.TryGetValue dependsOn with + | true, deps -> deps.Add node |> ignore + | false, _ -> dependents.Add(dependsOn, HashSet([| node |])) + + invalidateDependents dependsOn + + member this.RemoveDependency(node: 'Id, noLongerDependsOn: 'Id) = + match dependencies.TryGetValue node with + | true, deps -> deps.Remove noLongerDependsOn |> ignore + | false, _ -> () + + match dependents.TryGetValue noLongerDependsOn with + | true, deps -> deps.Remove node |> ignore + | false, _ -> () + + invalidateNodeAndDependents node + + member this.UpdateNode(id: 'Id, update: 'Val -> 'Val) = + this.GetValue id + |> update + |> fun value -> this.AddOrUpdateNode(id, value) |> ignore + + member this.RemoveNode(id: 'Id) = + + match nodes.TryGetValue id with + | true, _ -> + // Invalidate dependents of the removed node + invalidateDependents id + + // Remove the node from the nodes dictionary + nodes.Remove id |> ignore + + // Remove the node from dependencies and update dependents + match dependencies.TryGetValue id with + | true, deps -> + for dep in deps do + match dependents.TryGetValue dep with + | true, set -> set.Remove id |> ignore + | false, _ -> () + + dependencies.Remove id |> ignore + | false, _ -> () + + // Remove the node from dependents and update dependencies + match dependents.TryGetValue id with + | true, deps -> + for dep in deps do + match dependencies.TryGetValue dep with + | true, set -> set.Remove id |> ignore + | false, _ -> () + + dependents.Remove id |> ignore + | false, _ -> () + | false, _ -> () + + member this.Debug_GetNodes(predicate: 'Id -> bool) : DependencyNode<'Id, 'Val> seq = + nodes.Values |> Seq.filter (fun node -> predicate node.Id) + + member _.Debug_RenderMermaid(?mapping) = + + let mapping = defaultArg mapping id + + // We need to give each node a number so the graph is easy to render + let nodeNumbersById = Dictionary() + + nodes.Keys + |> Seq.map mapping + |> Seq.distinct + |> Seq.indexed + |> Seq.iter (fun (x, y) -> nodeNumbersById.Add(y, x)) + + let content = + dependencies + |> Seq.collect (fun kv -> + let node = kv.Key + let nodeNumber = nodeNumbersById[mapping node] + + kv.Value + |> Seq.map (fun dep -> nodeNumbersById[mapping dep], mapping dep) + |> Seq.map (fun (depNumber, dep) -> $"{nodeNumber}[{node}] --> {depNumber}[{dep}]") + |> Seq.distinct) + |> String.concat "\n" + + $"```mermaid\n\ngraph LR\n\n{content}\n\n```" + + member _.OnWarning(f) = warningSubscribers.Add f |> ignore + + interface IDependencyGraph<'Id, 'Val> with + + member this.Debug_GetNodes(predicate) = self.Debug_GetNodes(predicate) + + member _.AddOrUpdateNode(id, value) = self.AddOrUpdateNode(id, value) + member _.AddList(nodes) = self.AddList(nodes) + + member _.AddOrUpdateNode(id, dependsOn, compute) = + self.AddOrUpdateNode(id, dependsOn, compute) + + member _.GetValue(id) = self.GetValue(id) + member _.GetDependenciesOf(id) = self.GetDependenciesOf(id) + member _.GetDependentsOf(id) = self.GetDependentsOf(id) + member _.AddDependency(node, dependsOn) = self.AddDependency(node, dependsOn) + + member _.RemoveDependency(node, noLongerDependsOn) = + self.RemoveDependency(node, noLongerDependsOn) + + member _.UpdateNode(id, update) = self.UpdateNode(id, update) + member _.RemoveNode(id) = self.RemoveNode(id) + + member _.OnWarning f = self.OnWarning f + + member _.Debug_RenderMermaid(x) = self.Debug_RenderMermaid(?mapping = x) + +/// This type can be used to chain together a series of dependent nodes when there is some kind of type hierarchy in the graph. +/// That is when 'T represents some subset of 'Val (e.g. a sub type or a case in DU). +/// It can also carry some state that is passed along the chain. +type GraphBuilder<'Id, 'Val, 'T, 'State when 'Id: equality> + (graph: IDependencyGraph<'Id, 'Val>, ids: 'Id seq, unwrap: 'Val seq -> 'T, state: 'State) = + + member _.Ids = ids + + member _.State = state + + member _.Graph = graph + + member _.AddDependentNode(id, compute, unwrapNext) = + graph.AddOrUpdateNode(id, ids, unwrap >> compute) + GraphBuilder(graph, Seq.singleton id, unwrapNext, state) + + member _.AddDependentNode(id, compute, unwrapNext, nextState) = + graph.AddOrUpdateNode(id, ids, unwrap >> compute) + GraphBuilder(graph, Seq.singleton id, unwrapNext, nextState) + +open Internal +open System.Runtime.CompilerServices + +type LockOperatedDependencyGraph<'Id, 'Val when 'Id: equality and 'Id: not null>() = + + let lockObj = System.Object() + let graph = DependencyGraph<_, _>() + + interface IThreadSafeDependencyGraph<'Id, 'Val> with + + member _.AddDependency(node, dependsOn) = + lock lockObj (fun () -> graph.AddDependency(node, dependsOn)) + + member _.AddList(nodes) = + lock lockObj (fun () -> graph.AddList(nodes)) + + member _.AddOrUpdateNode(id, value) = + lock lockObj (fun () -> graph.AddOrUpdateNode(id, value)) + + member _.AddOrUpdateNode(id, dependsOn, compute) = + lock lockObj (fun () -> graph.AddOrUpdateNode(id, dependsOn, compute)) + + member _.GetDependenciesOf(id) = + lock lockObj (fun () -> graph.GetDependenciesOf(id)) + + member _.GetDependentsOf(id) = + lock lockObj (fun () -> graph.GetDependentsOf(id)) + + member _.GetValue(id) = + lock lockObj (fun () -> graph.GetValue(id)) + + member _.UpdateNode(id, update) = + lock lockObj (fun () -> graph.UpdateNode(id, update)) + + member _.RemoveNode(id) = + lock lockObj (fun () -> graph.RemoveNode(id)) + + member _.RemoveDependency(node, noLongerDependsOn) = + lock lockObj (fun () -> graph.RemoveDependency(node, noLongerDependsOn)) + + member _.Transact(f) = lock lockObj (fun () -> f graph) + + member _.OnWarning(f) = + lock lockObj (fun () -> graph.OnWarning f) + + member _.Debug_GetNodes(predicate) = + lock lockObj (fun () -> graph.Debug_GetNodes(predicate)) + + member _.Debug_RenderMermaid(m) = + lock lockObj (fun () -> graph.Debug_RenderMermaid(?mapping = m)) + +[] +type GraphExtensions = + + [] + static member Unpack(node: 'NodeValue, unpacker) = + match unpacker node with + | Some value -> value + | None -> failwith $"Expected {unpacker} but got: {node}" + + [] + static member UnpackOne(dependencies: 'NodeValue seq, unpacker: 'NodeValue -> 'UnpackedDependency option) = + dependencies + |> Seq.tryExactlyOne + |> Option.bind unpacker + |> Option.defaultWith (fun () -> + failwith $"Expected exactly one dependency matching {unpacker} but got: %A{dependencies |> Seq.toArray}") + + [] + static member UnpackMany(dependencies: 'NodeValue seq, unpacker) = + let results = dependencies |> Seq.choose unpacker + + if dependencies |> Seq.length <> (results |> Seq.length) then + failwith $"Expected all dependencies to match {unpacker} but got: %A{dependencies |> Seq.toArray}" + + results + + [] + static member UnpackOneMany(dependencies: 'NodeValue seq, oneUnpacker, manyUnpacker) = + let mutable oneResult = None + let manyResult = new ResizeArray<_>() + let extras = new ResizeArray<_>() + + for dependency in dependencies do + match oneUnpacker dependency, manyUnpacker dependency with + | Some item, _ -> oneResult <- Some item + | None, Some item -> manyResult.Add item |> ignore + | None, None -> extras.Add dependency |> ignore + + match oneResult with + | None -> failwith $"Expected exactly one dependency matching {oneUnpacker} but didn't find any" + | Some head -> + if extras.Count > 0 then + failwith $"Found extra dependencies: %A{extras.ToArray()}" + + head, seq manyResult diff --git a/src/FSharp.Compiler.LanguageServer/Common/FSharpRequestContext.fs b/src/FSharp.Compiler.LanguageServer/Common/FSharpRequestContext.fs index db26ff46514..134cee8834b 100644 --- a/src/FSharp.Compiler.LanguageServer/Common/FSharpRequestContext.fs +++ b/src/FSharp.Compiler.LanguageServer/Common/FSharpRequestContext.fs @@ -1,176 +1,23 @@ namespace FSharp.Compiler.LanguageServer.Common open Microsoft.CommonLanguageServerProtocol.Framework -open Microsoft.VisualStudio.LanguageServer.Protocol -open FSharp.Compiler.CodeAnalysis -open FSharp.Compiler.EditorServices -open FSharp.Compiler.Tokenization -open System open System.Threading open System.Threading.Tasks -#nowarn "57" - -module TokenTypes = - - [] - let (|LexicalClassification|_|) (tok: FSharpToken) = - if tok.IsKeyword then - ValueSome SemanticTokenTypes.Keyword - elif tok.IsNumericLiteral then - ValueSome SemanticTokenTypes.Number - elif tok.IsCommentTrivia then - ValueSome SemanticTokenTypes.Comment - elif tok.IsStringLiteral then - ValueSome SemanticTokenTypes.String - else - ValueNone - - // Tokenizes the source code and returns a list of token ranges and their SemanticTokenTypes - let GetSyntacticTokenTypes (source: FSharp.Compiler.Text.ISourceText) (fileName: string) = - let mutable tokRangesAndTypes = [] - - let tokenCallback = - fun (tok: FSharpToken) -> - match tok with - | LexicalClassification tokType -> tokRangesAndTypes <- (tok.Range, tokType) :: tokRangesAndTypes - | _ -> () - - FSharpLexer.Tokenize( - source, - tokenCallback, - flags = (FSharpLexerFlags.Default &&& ~~~FSharpLexerFlags.Compiling &&& ~~~FSharpLexerFlags.UseLexFilter), - filePath = fileName - ) - - tokRangesAndTypes - - let FSharpTokenTypeToLSP (fst: SemanticClassificationType) = - // XXX kinda arbitrary mapping - match fst with - | SemanticClassificationType.ReferenceType -> SemanticTokenTypes.Class - | SemanticClassificationType.ValueType -> SemanticTokenTypes.Struct - | SemanticClassificationType.UnionCase -> SemanticTokenTypes.Enum - | SemanticClassificationType.UnionCaseField -> SemanticTokenTypes.EnumMember - | SemanticClassificationType.Function -> SemanticTokenTypes.Function - | SemanticClassificationType.Property -> SemanticTokenTypes.Property - | SemanticClassificationType.Module -> SemanticTokenTypes.Type - | SemanticClassificationType.Namespace -> SemanticTokenTypes.Namespace - | SemanticClassificationType.Interface -> SemanticTokenTypes.Interface - | SemanticClassificationType.TypeArgument -> SemanticTokenTypes.TypeParameter - | SemanticClassificationType.Operator -> SemanticTokenTypes.Operator - | SemanticClassificationType.Method -> SemanticTokenTypes.Method - | SemanticClassificationType.ExtensionMethod -> SemanticTokenTypes.Method - | SemanticClassificationType.Field -> SemanticTokenTypes.Property - | SemanticClassificationType.Event -> SemanticTokenTypes.Event - | SemanticClassificationType.Delegate -> SemanticTokenTypes.Function - | SemanticClassificationType.NamedArgument -> SemanticTokenTypes.Parameter - | SemanticClassificationType.LocalValue -> SemanticTokenTypes.Variable - | SemanticClassificationType.Plaintext -> SemanticTokenTypes.String - | SemanticClassificationType.Type -> SemanticTokenTypes.Type - | SemanticClassificationType.Printf -> SemanticTokenTypes.Keyword - | _ -> SemanticTokenTypes.Comment - - let toIndex (x: string) = SemanticTokenTypes.AllTypes |> Seq.findIndex (fun y -> y = x) - -type FSharpRequestContext(lspServices: ILspServices, logger: ILspLogger, workspace: FSharpWorkspace, checker: FSharpChecker) = +type FSharpRequestContext(lspServices: ILspServices, logger: ILspLogger, workspace: FSharpWorkspace) = member _.LspServices = lspServices member _.Logger = logger member _.Workspace = workspace - member _.Checker = checker - - // TODO: split to parse and check diagnostics - member _.GetDiagnosticsForFile(file: Uri) = - - workspace.GetSnapshotForFile file - |> Option.map (fun snapshot -> - async { - let! parseResult, checkFileAnswer = checker.ParseAndCheckFileInProject(file.LocalPath, snapshot, "LSP Get diagnostics") - return - match checkFileAnswer with - | FSharpCheckFileAnswer.Succeeded result -> result.Diagnostics - | FSharpCheckFileAnswer.Aborted -> parseResult.Diagnostics - }) - |> Option.defaultValue (async.Return [||]) - - member _.GetSemanticTokensForFile(file: Uri) = - - workspace.GetSnapshotForFile file - |> Option.map (fun snapshot -> - async { - let! _, checkFileAnswer = checker.ParseAndCheckFileInProject(file.LocalPath, snapshot, "LSP Get semantic classification") - - let semanticClassifications = - match checkFileAnswer with - | FSharpCheckFileAnswer.Succeeded result -> result.GetSemanticClassification(None) // XXX not sure if range opt should be None - | FSharpCheckFileAnswer.Aborted -> [||] // XXX should be error maybe - - let! source = - snapshot.ProjectSnapshot.SourceFiles - |> Seq.find (fun f -> f.FileName = file.LocalPath) - |> _.GetSource() - |> Async.AwaitTask - - let syntacticClassifications = TokenTypes.GetSyntacticTokenTypes source file.LocalPath - - let lspFormatTokens = - semanticClassifications - |> Array.map (fun item -> (item.Range, item.Type |> TokenTypes.FSharpTokenTypeToLSP |> TokenTypes.toIndex)) - |> Array.append (syntacticClassifications|> List.map (fun (r, t) -> (r, TokenTypes.toIndex t)) |> Array.ofList) - |> Array.map (fun (r, tokType) -> - let length = r.EndColumn - r.StartColumn // XXX Does not deal with multiline tokens? - {| startLine = r.StartLine - 1; startCol = r.StartColumn; length = length; tokType = tokType; tokMods = 0 |}) - //(startLine, startCol, length, tokType, tokMods)) - |> Array.sortWith (fun x1 x2 -> - let c = x1.startLine.CompareTo(x2.startLine) - if c <> 0 then c - else x1.startCol.CompareTo(x2.startCol)) - - let tokensRelative = - lspFormatTokens - |> Array.append [| {| startLine = 0; startCol = 0; length = 0; tokType = 0; tokMods = 0 |} |] - |> Array.pairwise - |> Array.map (fun (prev, this) -> - {| - startLine = this.startLine - prev.startLine - startCol = (if prev.startLine = this.startLine then this.startCol - prev.startCol else this.startCol) - length = this.length - tokType = this.tokType - tokMods = this.tokMods - |}) - - return tokensRelative - |> Array.map (fun tok -> - [| tok.startLine; tok.startCol; tok.length; tok.tokType; tok.tokMods |]) - |> Array.concat - }) - |> Option.defaultValue (async { return [||] }) - -type ContextHolder(intialWorkspace, lspServices: ILspServices) = +type ContextHolder(workspace, lspServices: ILspServices) = let logger = lspServices.GetRequiredService() - // TODO: We need to get configuration for this somehow. Also make it replaceable when configuration changes. - let checker = - FSharpChecker.Create( - keepAllBackgroundResolutions = true, - keepAllBackgroundSymbolUses = true, - enableBackgroundItemKeyStoreAndSemanticClassification = true, - enablePartialTypeChecking = true, - parallelReferenceResolution = true, - captureIdentifiersWhenParsing = true, - useSyntaxTreeCache = true, - useTransparentCompiler = true - ) - - let mutable context = - FSharpRequestContext(lspServices, logger, intialWorkspace, checker) + let context = FSharpRequestContext(lspServices, logger, workspace) member _.GetContext() = context - member _.UpdateWorkspace(f) = - context <- FSharpRequestContext(lspServices, logger, f context.Workspace, checker) + member _.UpdateWorkspace(f) = f context.Workspace type FShapRequestContextFactory(lspServices: ILspServices) = @@ -185,4 +32,3 @@ type FShapRequestContextFactory(lspServices: ILspServices) = lspServices.GetRequiredService() |> _.GetContext() |> Task.FromResult - diff --git a/src/FSharp.Compiler.LanguageServer/Common/FSharpWorkspace.fs b/src/FSharp.Compiler.LanguageServer/Common/FSharpWorkspace.fs index 6fda31fc9c5..f53abd7a9d3 100644 --- a/src/FSharp.Compiler.LanguageServer/Common/FSharpWorkspace.fs +++ b/src/FSharp.Compiler.LanguageServer/Common/FSharpWorkspace.fs @@ -1,134 +1,64 @@ namespace FSharp.Compiler.LanguageServer.Common -open FSharp.Compiler.Text +open System.IO +open System.Threading -#nowarn "57" +open FSharp.Compiler.CodeAnalysis -open System -open System.Threading.Tasks -open FSharp.Compiler.CodeAnalysis.ProjectSnapshot +open DependencyGraph +open FSharpWorkspaceState +open FSharpWorkspaceQuery -/// Holds a project snapshot and a queue of changes that will be applied to it when it's requested +/// This type holds the current state of an F# workspace. It's mutable but thread-safe. It accepts updates to the state and can be queried for +/// information about the workspace. /// -/// The assumption is that this is faster than actually applying the changes to the snapshot immediately and that -/// we will be doing this on potentially every keystroke. But this should probably be measured at some point. -type SnapshotHolder(snapshot: FSharpProjectSnapshot, changedFiles: Set, openFiles: Map) = - - let applyFileChangesToSnapshot () = - let files = - changedFiles - |> Seq.map (fun filePath -> - match openFiles.TryFind filePath with - | Some content -> - FSharpFileSnapshot.Create( - filePath, - DateTime.Now.Ticks.ToString(), - fun () -> content |> SourceTextNew.ofString |> Task.FromResult - ) - | None -> FSharpFileSnapshot.CreateFromFileSystem(filePath)) - |> Seq.toList - - snapshot.Replace files - - // We don't want to mutate the workspace by applying the changes when snapshot is requested because that would force the language - // requests to be processed sequentially. So instead we keep the change application under lazy so it's still only computed if needed - // and only once and workspace doesn't change. - let appliedChanges = - lazy SnapshotHolder(applyFileChangesToSnapshot (), Set.empty, openFiles) - - member private _.snapshot = snapshot - member private _.changedFiles = changedFiles - - member private this.GetMostUpToDateInstance() = - if appliedChanges.IsValueCreated then - appliedChanges.Value - else - this - - member this.WithFileChanged(file, openFiles) = - let previous = this.GetMostUpToDateInstance() - SnapshotHolder(previous.snapshot, previous.changedFiles.Add file, openFiles) - - member this.WithoutFileChanged(file, openFiles) = - let previous = this.GetMostUpToDateInstance() - SnapshotHolder(previous.snapshot, previous.changedFiles.Remove file, openFiles) - - member _.GetSnapshot() = appliedChanges.Value.snapshot - - static member Of(snapshot: FSharpProjectSnapshot) = - SnapshotHolder(snapshot, Set.empty, Map.empty) - -type FSharpWorkspace - private - ( - projects: Map, - openFiles: Map, - fileMap: Map> - ) = +/// The state can be built up incrementally by adding projects with one of `Projects.AddOrUpdate` overloads. Updates to any project properties are +/// done the same way. Each project is identified by its project file path and output path or by `FSharpProjectIdentifier`. When the same project is +/// added again it will be updated with the new information. +/// +/// Project references are discovered automatically as projects are added or updated. +/// +/// Updates to file contents are signaled through `Files.Open`, `Files.Edit` and `Files.Close` methods. +type FSharpWorkspace(checker: FSharpChecker) = - let updateProjectsWithFile (file: Uri) f (projects: Map) = - fileMap - |> Map.tryFind file.LocalPath - |> Option.map (fun identifier -> - (projects, identifier) - ||> Seq.fold (fun projects identifier -> - let snapshotHolder = projects[identifier] - projects.Add(identifier, f snapshotHolder))) - |> Option.defaultValue projects + let depGraph = LockOperatedDependencyGraph() :> IThreadSafeDependencyGraph<_, _> - member _.Projects = projects - member _.OpenFiles = openFiles - member _.FileMap = fileMap + let files = FSharpWorkspaceFiles depGraph - member this.OpenFile(file: Uri, content: string) = this.ChangeFile(file, content) + let projects = FSharpWorkspaceProjects(depGraph, files) - member _.CloseFile(file: Uri) = - let openFiles = openFiles.Remove(file.LocalPath) + let query = FSharpWorkspaceQuery(depGraph, checker) + new() = FSharpWorkspace( - projects = - (projects - |> updateProjectsWithFile file _.WithoutFileChanged(file.LocalPath, openFiles)), - openFiles = openFiles, - fileMap = fileMap + FSharpChecker.Create( + keepAllBackgroundResolutions = true, + keepAllBackgroundSymbolUses = true, + enableBackgroundItemKeyStoreAndSemanticClassification = true, + enablePartialTypeChecking = true, + parallelReferenceResolution = true, + captureIdentifiersWhenParsing = true, + useTransparentCompiler = true + ) ) - member _.ChangeFile(file: Uri, content: string) = - - // TODO: should we assert that the file is open? - - let openFiles = openFiles.Add(file.LocalPath, content) + member internal this.Debug_DumpMermaid(path) = + let content = + depGraph.Debug_RenderMermaid (function + // Collapse all reference on disk nodes into one. Otherwise the graph is too big to render. + | WorkspaceGraphTypes.WorkspaceNodeKey.ReferenceOnDisk _ -> WorkspaceGraphTypes.WorkspaceNodeKey.ReferenceOnDisk "..." + | x -> x) - FSharpWorkspace( - projects = - (projects - |> updateProjectsWithFile file _.WithFileChanged(file.LocalPath, openFiles)), - openFiles = openFiles, - fileMap = fileMap - ) + File.WriteAllText(__SOURCE_DIRECTORY__ + path, content) - member _.GetSnapshotForFile(file: Uri) = - fileMap - |> Map.tryFind file.LocalPath + /// The `FSharpChecker` instance used by this workspace. + member _.Checker = checker - // TODO: eventually we need to deal with choosing the appropriate project here - // Hopefully we will be able to do it through receiving project context from LSP - // Otherwise we have to keep track of which project/configuration is active - |> Option.bind Seq.tryHead + /// File management for this workspace + member _.Files = files - |> Option.bind projects.TryFind - |> Option.map _.GetSnapshot() + /// Project management for this workspace + member _.Projects = projects - static member Create(projects: FSharpProjectSnapshot seq) = - FSharpWorkspace( - projects = Map.ofSeq (projects |> Seq.map (fun p -> p.Identifier, SnapshotHolder.Of p)), - openFiles = Map.empty, - fileMap = - (projects - |> Seq.collect (fun p -> - p.ProjectSnapshot.SourceFileNames - |> Seq.map (fun f -> Uri(f).LocalPath, p.Identifier)) - |> Seq.groupBy fst - |> Seq.map (fun (f, ps) -> f, Set.ofSeq (ps |> Seq.map snd)) - |> Map.ofSeq) - ) + /// Use this to query the workspace for information + member _.Query = query diff --git a/src/FSharp.Compiler.LanguageServer/Common/FSharpWorkspaceQuery.fs b/src/FSharp.Compiler.LanguageServer/Common/FSharpWorkspaceQuery.fs new file mode 100644 index 00000000000..ec522eccf31 --- /dev/null +++ b/src/FSharp.Compiler.LanguageServer/Common/FSharpWorkspaceQuery.fs @@ -0,0 +1,212 @@ +/// Code to handle quries to F# workspace +module FSharp.Compiler.LanguageServer.Common.FSharpWorkspaceQuery + +open System +open System.Collections.Generic +open Microsoft.CommonLanguageServerProtocol.Framework +open Microsoft.VisualStudio.LanguageServer.Protocol + +open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.EditorServices +open FSharp.Compiler.Tokenization + +open DependencyGraph +open FSharpWorkspaceState +open System.Threading + +#nowarn "57" + +type FSharpDiagnosticReport internal (diagnostics, resultId) = + + member _.Diagnostics = diagnostics + + /// The result ID of the diagnostics. This needs to be unique for each version of the document in order to be able to clear old diagnostics. + member _.ResultId = resultId.ToString() + +module TokenTypes = + + [] + let (|LexicalClassification|_|) (tok: FSharpToken) = + if tok.IsKeyword then + ValueSome SemanticTokenTypes.Keyword + elif tok.IsNumericLiteral then + ValueSome SemanticTokenTypes.Number + elif tok.IsCommentTrivia then + ValueSome SemanticTokenTypes.Comment + elif tok.IsStringLiteral then + ValueSome SemanticTokenTypes.String + else + ValueNone + + // Tokenizes the source code and returns a list of token ranges and their SemanticTokenTypes + let GetSyntacticTokenTypes (source: FSharp.Compiler.Text.ISourceText) (fileName: string) = + let mutable tokRangesAndTypes = [] + + let tokenCallback = + fun (tok: FSharpToken) -> + match tok with + | LexicalClassification tokType -> tokRangesAndTypes <- (tok.Range, tokType) :: tokRangesAndTypes + | _ -> () + + FSharpLexer.Tokenize( + source, + tokenCallback, + flags = + (FSharpLexerFlags.Default + &&& ~~~FSharpLexerFlags.Compiling + &&& ~~~FSharpLexerFlags.UseLexFilter), + filePath = fileName + ) + + tokRangesAndTypes + + let FSharpTokenTypeToLSP (fst: SemanticClassificationType) = + // XXX kinda arbitrary mapping + match fst with + | SemanticClassificationType.ReferenceType -> SemanticTokenTypes.Class + | SemanticClassificationType.ValueType -> SemanticTokenTypes.Struct + | SemanticClassificationType.UnionCase -> SemanticTokenTypes.Enum + | SemanticClassificationType.UnionCaseField -> SemanticTokenTypes.EnumMember + | SemanticClassificationType.Function -> SemanticTokenTypes.Function + | SemanticClassificationType.Property -> SemanticTokenTypes.Property + | SemanticClassificationType.Module -> SemanticTokenTypes.Type + | SemanticClassificationType.Namespace -> SemanticTokenTypes.Namespace + | SemanticClassificationType.Interface -> SemanticTokenTypes.Interface + | SemanticClassificationType.TypeArgument -> SemanticTokenTypes.TypeParameter + | SemanticClassificationType.Operator -> SemanticTokenTypes.Operator + | SemanticClassificationType.Method -> SemanticTokenTypes.Method + | SemanticClassificationType.ExtensionMethod -> SemanticTokenTypes.Method + | SemanticClassificationType.Field -> SemanticTokenTypes.Property + | SemanticClassificationType.Event -> SemanticTokenTypes.Event + | SemanticClassificationType.Delegate -> SemanticTokenTypes.Function + | SemanticClassificationType.NamedArgument -> SemanticTokenTypes.Parameter + | SemanticClassificationType.LocalValue -> SemanticTokenTypes.Variable + | SemanticClassificationType.Plaintext -> SemanticTokenTypes.String + | SemanticClassificationType.Type -> SemanticTokenTypes.Type + | SemanticClassificationType.Printf -> SemanticTokenTypes.Keyword + | _ -> SemanticTokenTypes.Comment + + let toIndex (x: string) = + SemanticTokenTypes.AllTypes |> Seq.findIndex (fun y -> y = x) + +type FSharpWorkspaceQuery internal (depGraph: IThreadSafeDependencyGraph<_, _>, checker: FSharpChecker) = + + let mutable resultIdCounter = 0 + + // TODO: we might need something more sophisticated eventually + // for now it's important that the result id is unique every time + // in order to be able to clear previous diagnostics + let getDiagnosticResultId () = Interlocked.Increment(&resultIdCounter) + + member _.GetProjectSnapshot projectIdentifier = + try + depGraph.GetProjectSnapshot projectIdentifier |> Some + with :? KeyNotFoundException -> + None + + member _.GetProjectSnapshotForFile(file: Uri) = + + depGraph.GetProjectsContaining file.LocalPath + + // TODO: eventually we need to deal with choosing the appropriate project here + // Hopefully we will be able to do it through receiving project context from LSP + // Otherwise we have to keep track of which project/configuration is active + |> Seq.tryHead // For now just get the first one + + // TODO: split to parse and check diagnostics + member this.GetDiagnosticsForFile(file: Uri) = + async { + + let! diagnostics = + this.GetProjectSnapshotForFile file + |> Option.map (fun snapshot -> + async { + let! parseResult, checkFileAnswer = + checker.ParseAndCheckFileInProject(file.LocalPath, snapshot, "LSP Get diagnostics") + + return + match checkFileAnswer with + | FSharpCheckFileAnswer.Succeeded result -> result.Diagnostics + | FSharpCheckFileAnswer.Aborted -> parseResult.Diagnostics + }) + |> Option.defaultValue (async.Return [||]) + + return FSharpDiagnosticReport(diagnostics, getDiagnosticResultId ()) + } + + member this.GetSemanticTokensForFile(file) = + + this.GetProjectSnapshotForFile file + |> Option.map (fun snapshot -> + async { + let! _, checkFileAnswer = checker.ParseAndCheckFileInProject(file.LocalPath, snapshot, "LSP Get semantic classification") + + let semanticClassifications = + match checkFileAnswer with + | FSharpCheckFileAnswer.Succeeded result -> result.GetSemanticClassification(None) // XXX not sure if range opt should be None + | FSharpCheckFileAnswer.Aborted -> [||] // XXX should be error maybe + + let! source = + snapshot.ProjectSnapshot.SourceFiles + |> Seq.find (fun f -> f.FileName = file.LocalPath) + |> _.GetSource() + |> Async.AwaitTask + + let syntacticClassifications = + TokenTypes.GetSyntacticTokenTypes source file.LocalPath + + let lspFormatTokens = + semanticClassifications + |> Array.map (fun item -> (item.Range, item.Type |> TokenTypes.FSharpTokenTypeToLSP |> TokenTypes.toIndex)) + |> Array.append ( + syntacticClassifications + |> List.map (fun (r, t) -> (r, TokenTypes.toIndex t)) + |> Array.ofList + ) + |> Array.map (fun (r, tokType) -> + let length = r.EndColumn - r.StartColumn // XXX Does not deal with multiline tokens? + + {| + startLine = r.StartLine - 1 + startCol = r.StartColumn + length = length + tokType = tokType + tokMods = 0 + |}) + //(startLine, startCol, length, tokType, tokMods)) + |> Array.sortWith (fun x1 x2 -> + let c = x1.startLine.CompareTo(x2.startLine) + if c <> 0 then c else x1.startCol.CompareTo(x2.startCol)) + + let tokensRelative = + lspFormatTokens + |> Array.append + [| + {| + startLine = 0 + startCol = 0 + length = 0 + tokType = 0 + tokMods = 0 + |} + |] + |> Array.pairwise + |> Array.map (fun (prev, this) -> + {| + startLine = this.startLine - prev.startLine + startCol = + (if prev.startLine = this.startLine then + this.startCol - prev.startCol + else + this.startCol) + length = this.length + tokType = this.tokType + tokMods = this.tokMods + |}) + + return + tokensRelative + |> Array.map (fun tok -> [| tok.startLine; tok.startCol; tok.length; tok.tokType; tok.tokMods |]) + |> Array.concat + }) + |> Option.defaultValue (async { return [||] }) diff --git a/src/FSharp.Compiler.LanguageServer/Common/FSharpWorkspaceState.fs b/src/FSharp.Compiler.LanguageServer/Common/FSharpWorkspaceState.fs new file mode 100644 index 00000000000..3f874e51033 --- /dev/null +++ b/src/FSharp.Compiler.LanguageServer/Common/FSharpWorkspaceState.fs @@ -0,0 +1,366 @@ +/// Code to handle state management in an F# workspace. +module FSharp.Compiler.LanguageServer.Common.FSharpWorkspaceState + +open System +open System.IO +open System.Runtime.CompilerServices +open System.Collections.Concurrent + +open FSharp.Compiler.CodeAnalysis.ProjectSnapshot +open Internal.Utilities.Collections + +open DependencyGraph + +#nowarn "57" + +/// Types for the workspace graph. These should not be accessed directly, rather through the +/// extension methods in `WorkspaceDependencyGraphExtensions`. +module internal WorkspaceGraphTypes = + + /// All project information except source files + type ProjectWithoutFiles = ProjectConfig * FSharpReferencedProjectSnapshot list + + [] + type internal WorkspaceNodeKey = + // TODO: maybe this should be URI + | SourceFile of filePath: string + | ReferenceOnDisk of filePath: string + /// All project information except source files and (in-memory) project references + | ProjectConfig of FSharpProjectIdentifier + /// All project information except source files + | ProjectWithoutFiles of FSharpProjectIdentifier + /// Complete project information + | ProjectSnapshot of FSharpProjectIdentifier + + override this.ToString() = + match this with + | SourceFile path -> $"File {shortPath path}" + | ReferenceOnDisk path -> $"Reference on disk {shortPath path}" + | ProjectConfig id -> $"ProjectConfig {id}" + | ProjectWithoutFiles id -> $"ProjectWithoutFiles {id}" + | ProjectSnapshot id -> $"ProjectSnapshot {id}" + + [] + type internal WorkspaceNodeValue = + | SourceFile of FSharpFileSnapshot + | ReferenceOnDisk of ReferenceOnDisk + /// All project information except source files and (in-memory) project references + | ProjectConfig of ProjectConfig + /// All project information except source files + | ProjectWithoutFiles of ProjectWithoutFiles + /// Complete project information + | ProjectSnapshot of FSharpProjectSnapshot + + module internal WorkspaceNode = + + let projectConfig value = + match value with + | WorkspaceNodeValue.ProjectConfig p -> Some p + | _ -> None + + let projectSnapshot value = + match value with + | WorkspaceNodeValue.ProjectSnapshot p -> Some p + | _ -> None + + let projectWithoutFiles value = + match value with + | WorkspaceNodeValue.ProjectWithoutFiles(p, refs) -> Some(p, refs) + | _ -> None + + let sourceFile value = + match value with + | WorkspaceNodeValue.SourceFile f -> Some f + | _ -> None + + let referenceOnDisk value = + match value with + | WorkspaceNodeValue.ReferenceOnDisk r -> Some r + | _ -> None + + let projectConfigKey value = + match value with + | WorkspaceNodeKey.ProjectConfig p -> Some p + | _ -> None + + let projectSnapshotKey value = + match value with + | WorkspaceNodeKey.ProjectSnapshot p -> Some p + | _ -> None + + let projectWithoutFilesKey value = + match value with + | WorkspaceNodeKey.ProjectWithoutFiles x -> Some x + | _ -> None + + let sourceFileKey value = + match value with + | WorkspaceNodeKey.SourceFile f -> Some f + | _ -> None + + let referenceOnDiskKey value = + match value with + | WorkspaceNodeKey.ReferenceOnDisk r -> Some r + | _ -> None + +[] +module internal WorkspaceDependencyGraphExtensions = + + open WorkspaceGraphTypes + + /// This type adds extension methods to the dependency graph to constraint the types and type relations + /// that can be added to the graph. + /// + /// All unsafe operations that can throw at runtime, i.e. unpacking, are done here. + type internal WorkspaceDependencyGraphTypeExtensions = + + [] + static member AddOrUpdateFile(this: IDependencyGraph<_, _>, file: string, snapshot) = + this.AddOrUpdateNode(WorkspaceNodeKey.SourceFile file, WorkspaceNodeValue.SourceFile(snapshot)) + + [] + static member AddFiles(this: IDependencyGraph<_, _>, files: seq) = + let ids = + files + |> Seq.map (fun (file, snapshot) -> WorkspaceNodeKey.SourceFile file, WorkspaceNodeValue.SourceFile(snapshot)) + |> this.AddList + + GraphBuilder(this, ids, _.UnpackMany(WorkspaceNode.sourceFile), ()) + + [] + static member AddReferencesOnDisk(this: IDependencyGraph<_, _>, references: seq) = + let ids = + references + |> Seq.map (fun r -> WorkspaceNodeKey.ReferenceOnDisk r.Path, WorkspaceNodeValue.ReferenceOnDisk r) + |> this.AddList + + GraphBuilder(this, ids, _.UnpackMany(WorkspaceNode.referenceOnDisk), ()) + + [] + static member AddProjectConfig(this: GraphBuilder<_, _, ReferenceOnDisk seq, unit>, projectIdentifier, computeProjectConfig) = + this.AddDependentNode( + WorkspaceNodeKey.ProjectConfig projectIdentifier, + computeProjectConfig >> WorkspaceNodeValue.ProjectConfig, + _.UnpackOneMany(WorkspaceNode.projectConfig, WorkspaceNode.projectSnapshot), + projectIdentifier + ) + + [] + static member AddProjectWithoutFiles + ( + this: GraphBuilder<_, _, (ProjectConfig * FSharpProjectSnapshot seq), _>, + computeProjectWithoutFiles + ) = + this.AddDependentNode( + WorkspaceNodeKey.ProjectWithoutFiles this.State, + computeProjectWithoutFiles >> WorkspaceNodeValue.ProjectWithoutFiles, + _.UnpackOne(WorkspaceNode.projectWithoutFiles) + ) + + [] + static member AddSourceFiles(this: GraphBuilder<_, _, ProjectWithoutFiles, FSharpProjectIdentifier>, sourceFiles) = + let ids = + sourceFiles + |> Seq.map (fun (file, snapshot) -> WorkspaceNodeKey.SourceFile file, WorkspaceNodeValue.SourceFile(snapshot)) + |> this.Graph.AddList + + GraphBuilder( + this.Graph, + (Seq.append this.Ids ids), + (_.UnpackOneMany(WorkspaceNode.projectWithoutFiles, WorkspaceNode.sourceFile)), + this.State + ) + + [] + static member AddProjectSnapshot + ( + this: GraphBuilder<_, _, (ProjectWithoutFiles * FSharpFileSnapshot seq), _>, + computeProjectSnapshot + ) = + + this.AddDependentNode( + WorkspaceNodeKey.ProjectSnapshot this.State, + computeProjectSnapshot >> WorkspaceNodeValue.ProjectSnapshot, + ignore + ) + |> ignore + + [] + static member AddProjectReference(this: IDependencyGraph<_, _>, project, dependsOn) = + this.AddDependency(WorkspaceNodeKey.ProjectWithoutFiles project, dependsOn = WorkspaceNodeKey.ProjectSnapshot dependsOn) + + [] + static member RemoveProjectReference(this: IDependencyGraph<_, _>, project, noLongerDependsOn) = + this.RemoveDependency( + WorkspaceNodeKey.ProjectWithoutFiles project, + noLongerDependsOn = WorkspaceNodeKey.ProjectSnapshot noLongerDependsOn + ) + + [] + static member GetProjectSnapshot(this: IDependencyGraph<_, _>, project) = + this + .GetValue(WorkspaceNodeKey.ProjectSnapshot project) + .Unpack(WorkspaceNode.projectSnapshot) + + [] + static member GetProjectReferencesOf(this: IDependencyGraph<_, _>, project) = + this.GetDependenciesOf(WorkspaceNodeKey.ProjectWithoutFiles project) + |> Seq.choose (function + | WorkspaceNodeKey.ProjectSnapshot projectId -> Some projectId + | _ -> None) + + [] + static member GetProjectsThatReference(this: IDependencyGraph<_, _>, dllPath) = + this + .GetDependentsOf(WorkspaceNodeKey.ReferenceOnDisk dllPath) + .UnpackMany(WorkspaceNode.projectConfigKey) + + [] + static member GetProjectsContaining(this: IDependencyGraph<_, _>, file) = + this.GetDependentsOf(WorkspaceNodeKey.SourceFile file) + |> Seq.map this.GetValue + |> _.UnpackMany(WorkspaceNode.projectSnapshot) + +/// Interface for managing files in an F# workspace. +type FSharpWorkspaceFiles internal (depGraph: IThreadSafeDependencyGraph<_, _>) = + + /// Open files in the editor. + let openFiles = ConcurrentDictionary() + + /// Indicates that a file has been opened and has the given content. Any updates to the file should be done through `Files.Edit`. + member this.Open = this.Edit + + /// Indicates that a file has been changed and now has the given content. If it wasn't previously open it is considered open now. + member _.Edit(file: Uri, content) = + openFiles.AddOrUpdate(file.LocalPath, content, (fun _ _ -> content)) |> ignore + depGraph.AddOrUpdateFile(file.LocalPath, FSharpFileSnapshot.CreateFromString(file.LocalPath, content)) + + /// Indicates that a file has been closed. Any changes that were not saved to disk are undone and any further reading + /// of the file's contents will be from the filesystem. + member _.Close(file: Uri) = + openFiles.TryRemove(file.LocalPath) |> ignore + + // The file may have had changes that weren't saved to disk and are therefore undone by closing it. + depGraph.AddOrUpdateFile(file.LocalPath, FSharpFileSnapshot.CreateFromFileSystem(file.LocalPath)) + + member internal _.GetFileContentIfOpen(path: string) = + match openFiles.TryGetValue(path) with + | true, content -> Some content + | false, _ -> None + +/// Interface for managing with projects in an F# workspace. +type FSharpWorkspaceProjects internal (depGraph: IThreadSafeDependencyGraph<_, _>, files: FSharpWorkspaceFiles) = + + /// A map from project output path to project identifier. + let outputPathMap = ConcurrentDictionary() + + /// Adds or updates an F# project in the workspace. Project is identified by the project file and output path or FSharpProjectIdentifier. + member _.AddOrUpdate(projectConfig: ProjectConfig, sourceFilePaths: string seq) = + + let projectIdentifier = projectConfig.Identifier + + // Add the project identifier to the map + // TODO: do something if it's empty? + outputPathMap.AddOrUpdate(projectIdentifier.OutputFileName, (fun _ -> projectIdentifier), (fun _ _ -> projectIdentifier)) + |> ignore + + // Find any referenced projects that we aleady know about + let projectReferences = + projectConfig.ReferencesOnDisk + |> Seq.choose (fun ref -> + match outputPathMap.TryGetValue ref.Path with + | true, projectIdentifier -> Some projectIdentifier + | _ -> None) + |> Set + + depGraph.Transact(fun depGraph -> + + depGraph + .AddReferencesOnDisk(projectConfig.ReferencesOnDisk) + .AddProjectConfig(projectIdentifier, (fun refsOnDisk -> projectConfig.With(refsOnDisk |> Seq.toList))) + .AddProjectWithoutFiles( + (fun (projectConfig, referencedProjects) -> + + let referencedProjects = + referencedProjects + |> Seq.map (fun s -> + FSharpReferencedProjectSnapshot.FSharpReference( + s.OutputFileName + |> Option.defaultWith (fun () -> failwith "project doesn't have output filename"), + s + )) + |> Seq.toList + + projectConfig, referencedProjects) + ) + .AddSourceFiles( + sourceFilePaths + |> Seq.map (fun path -> + path, + files.GetFileContentIfOpen path + |> Option.map (fun content -> FSharpFileSnapshot.CreateFromString(path, content)) + |> Option.defaultWith (fun () -> FSharpFileSnapshot.CreateFromFileSystem path)) + ) + .AddProjectSnapshot( + (fun ((projectConfig, referencedProjects), sourceFiles) -> + ProjectSnapshot(projectConfig, referencedProjects, sourceFiles |> Seq.toList) + |> FSharpProjectSnapshot) + ) + + // In case this is an update, we should check for any existing project references that are not contained in the incoming compiler args and remove them + let existingReferences = depGraph.GetProjectReferencesOf projectIdentifier |> Set + + let referencesToRemove = existingReferences - projectReferences + let referencesToAdd = projectReferences - existingReferences + + for projectId in referencesToRemove do + depGraph.RemoveProjectReference(projectIdentifier, projectId) + + for projectId in referencesToAdd do + depGraph.AddProjectReference(projectIdentifier, projectId) + + // Check if any projects we know about depend on this project and add the references if they don't already exist + let dependentProjectIds = + depGraph.GetProjectsThatReference projectIdentifier.OutputFileName + + for dependentProjectId in dependentProjectIds do + depGraph.AddProjectReference(dependentProjectId, projectIdentifier) + + projectIdentifier) + + member this.AddOrUpdate(projectPath: string, outputPath, compilerArgs) = + + let directoryPath = Path.GetDirectoryName(projectPath) + + let fsharpFileExtensions = set [| ".fs"; ".fsi"; ".fsx" |] + + let isFSharpFile (file: string) = + Set.exists (fun (ext: string) -> file.EndsWith(ext, StringComparison.Ordinal)) fsharpFileExtensions + + let isReference: string -> bool = _.StartsWith("-r:") + + let referencesOnDisk = + compilerArgs |> Seq.filter isReference |> Seq.map _.Substring(3) + + let otherOptions = + compilerArgs + |> Seq.filter (not << isReference) + |> Seq.filter (not << isFSharpFile) + |> Seq.toList + + let sourceFiles = + compilerArgs + |> Seq.choose (fun (line: string) -> + if not (isFSharpFile line) then + None + else + Some(Path.Combine(directoryPath, line))) + + this.AddOrUpdate(projectPath, outputPath, sourceFiles, referencesOnDisk, otherOptions) + + member this.AddOrUpdate(projectFileName, outputFileName, sourceFiles, referencesOnDisk, otherOptions) = + + let projectConfig = + ProjectConfig(projectFileName, Some outputFileName, referencesOnDisk, otherOptions) + + this.AddOrUpdate(projectConfig, sourceFiles) diff --git a/src/FSharp.Compiler.LanguageServer/Common/LifecycleManager.fs b/src/FSharp.Compiler.LanguageServer/Common/LifecycleManager.fs index 14c6c4382ea..cd033fc3d2f 100644 --- a/src/FSharp.Compiler.LanguageServer/Common/LifecycleManager.fs +++ b/src/FSharp.Compiler.LanguageServer/Common/LifecycleManager.fs @@ -27,7 +27,7 @@ type FSharpLspServices(serviceCollection: IServiceCollection) as this = let serviceProvider = serviceCollection.BuildServiceProvider() interface ILspServices with - member this.GetRequiredService<'T>() : 'T = + member this.GetRequiredService<'T when 'T: not null>() : 'T = serviceProvider.GetRequiredService<'T>() member this.TryGetService(t) = serviceProvider.GetService(t) diff --git a/src/FSharp.Compiler.LanguageServer/FSharp.Compiler.LanguageServer.fsproj b/src/FSharp.Compiler.LanguageServer/FSharp.Compiler.LanguageServer.fsproj index 5a714ffd86e..bb98e60d79b 100644 --- a/src/FSharp.Compiler.LanguageServer/FSharp.Compiler.LanguageServer.fsproj +++ b/src/FSharp.Compiler.LanguageServer/FSharp.Compiler.LanguageServer.fsproj @@ -3,6 +3,7 @@ Exe net8.0 + true @@ -13,6 +14,10 @@ + + + + @@ -27,8 +32,11 @@ + + + diff --git a/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs index 70770760b13..71e6a148e3d 100644 --- a/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs +++ b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs @@ -30,7 +30,7 @@ type FSharpLanguageServer (jsonRpc: JsonRpc, logger: ILspLogger, ?initialWorkspace: FSharpWorkspace, ?addExtraHandlers: Action) = inherit AbstractLanguageServer(jsonRpc, logger) - let initialWorkspace = defaultArg initialWorkspace (FSharpWorkspace.Create []) + let initialWorkspace = defaultArg initialWorkspace (FSharpWorkspace()) do // This spins up the queue and ensure the LSP is ready to start receiving requests @@ -67,7 +67,7 @@ type FSharpLanguageServer lspServices :> ILspServices static member Create() = - FSharpLanguageServer.Create(FSharpWorkspace.Create Seq.empty, (fun _ -> ())) + FSharpLanguageServer.Create(FSharpWorkspace(), (fun _ -> ())) static member Create(initialWorkspace, addExtraHandlers: Action) = FSharpLanguageServer.Create(LspLogger System.Diagnostics.Trace.TraceInformation, initialWorkspace, addExtraHandlers) diff --git a/src/FSharp.Compiler.LanguageServer/Handlers/DocumentStateHandler.fs b/src/FSharp.Compiler.LanguageServer/Handlers/DocumentStateHandler.fs index 869d8a56146..e549b27a7c2 100644 --- a/src/FSharp.Compiler.LanguageServer/Handlers/DocumentStateHandler.fs +++ b/src/FSharp.Compiler.LanguageServer/Handlers/DocumentStateHandler.fs @@ -21,7 +21,7 @@ type DocumentStateHandler() = ) = let contextHolder = context.LspServices.GetRequiredService() - contextHolder.UpdateWorkspace _.OpenFile(request.TextDocument.Uri, request.TextDocument.Text) + contextHolder.UpdateWorkspace _.Files.Open(request.TextDocument.Uri, request.TextDocument.Text) Task.FromResult(SemanticTokensDeltaPartialResult()) @@ -35,7 +35,7 @@ type DocumentStateHandler() = ) = let contextHolder = context.LspServices.GetRequiredService() - contextHolder.UpdateWorkspace _.ChangeFile(request.TextDocument.Uri, request.ContentChanges.[0].Text) + contextHolder.UpdateWorkspace _.Files.Edit(request.TextDocument.Uri, request.ContentChanges.[0].Text) Task.FromResult(SemanticTokensDeltaPartialResult()) @@ -49,6 +49,6 @@ type DocumentStateHandler() = ) = let contextHolder = context.LspServices.GetRequiredService() - contextHolder.UpdateWorkspace _.CloseFile(request.TextDocument.Uri) + contextHolder.UpdateWorkspace _.Files.Close(request.TextDocument.Uri) Task.CompletedTask diff --git a/src/FSharp.Compiler.LanguageServer/Handlers/LanguageFeaturesHandler.fs b/src/FSharp.Compiler.LanguageServer/Handlers/LanguageFeaturesHandler.fs index 1609cdc06cc..d900fdfc3b8 100644 --- a/src/FSharp.Compiler.LanguageServer/Handlers/LanguageFeaturesHandler.fs +++ b/src/FSharp.Compiler.LanguageServer/Handlers/LanguageFeaturesHandler.fs @@ -19,4 +19,4 @@ type LanguageFeaturesHandler() = context: FSharpRequestContext, cancellationToken: CancellationToken ) = - Task.FromResult(new RelatedUnchangedDocumentDiagnosticReport()) \ No newline at end of file + Task.FromResult(new RelatedUnchangedDocumentDiagnosticReport()) diff --git a/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs b/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs index ace7d25343e..e7f0ecf07c7 100644 --- a/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs +++ b/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs @@ -7,6 +7,7 @@ namespace FSharp.VisualStudio.Extension; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.IO.Packaging; using System.IO.Pipelines; using System.Linq; using System.Threading; @@ -82,8 +83,8 @@ public ServerCapabilities OverrideServerCapabilities(ServerCapabilities value) Range = false }, HoverProvider = new HoverOptions() - { - WorkDoneProgress = true + { + WorkDoneProgress = true } }; return capabilities; @@ -101,7 +102,7 @@ public async Task HandleRequestAsync( FSharpRequestContext context, CancellationToken cancellationToken) { - var tokens = await context.GetSemanticTokensForFile(request!.TextDocument!.Uri).Please(cancellationToken); + var tokens = await context.Workspace.Query.GetSemanticTokensForFile(request!.TextDocument!.Uri).Please(cancellationToken); return new SemanticTokens { Data = tokens }; } @@ -117,15 +118,16 @@ internal class VsDiagnosticsHandler [LanguageServerEndpoint(VSInternalMethods.DocumentPullDiagnosticName)] public async Task HandleRequestAsync(VSInternalDocumentDiagnosticsParams request, FSharpRequestContext context, CancellationToken cancellationToken) { - var result = await context.GetDiagnosticsForFile(request!.TextDocument!.Uri).Please(cancellationToken); + var report = await context.Workspace.Query.GetDiagnosticsForFile(request!.TextDocument!.Uri).Please(cancellationToken); - var rep = new VSInternalDiagnosticReport + var vsReport = new VSInternalDiagnosticReport { - ResultId = "potato1", // Has to be present for diagnostic to show up - //Identifier = 69, + ResultId = report.ResultId, + //Identifier = 1, //Version = 1, + Diagnostics = - result.Select(d => + report.Diagnostics.Select(d => new Diagnostic { @@ -143,7 +145,7 @@ public async Task HandleRequestAsync(VSInternalDoc ).ToArray() }; - return [rep]; + return [vsReport]; } [LanguageServerEndpoint("textDocument/_vs_getProjectContexts")] @@ -171,6 +173,108 @@ public Task HandleRequestAsync(VSGetProjectContextsParams } +internal class SolutionObserver : IObserver> +{ + public void OnCompleted() + { + + } + + public void OnError(Exception error) + { + } + + public void OnNext(IQueryResults value) + { + Trace.TraceInformation("Solution was updated"); + } + +} + +internal class ProjectObserver(FSharpWorkspace workspace) : IObserver> +{ + private readonly FSharpWorkspace workspace = workspace; + + internal void ProcessProject(IProjectSnapshot project) + { + project.Id.TryGetValue("ProjectPath", out var projectPath); + + List<(string, string)> projectInfos = []; + + if (projectPath != null && projectPath.ToLower().EndsWith(".fsproj")) + { + var configs = project.ActiveConfigurations.ToList(); + + foreach (var config in configs) + { + if (config != null) + { + // Extract bin output path for each active config + var data = config.OutputGroups; + + string? outputPath = null; + foreach (var group in data) + { + if (group.Name == "Built") + { + foreach (var output in group.Outputs) + { + if (output.FinalOutputPath != null && (output.FinalOutputPath.ToLower().EndsWith(".dll") || output.FinalOutputPath.ToLower().EndsWith(".exe"))) + { + outputPath = output.FinalOutputPath; + break; + } + } + if (outputPath != null) + { + break; + } + } + } + + foreach (var ruleResults in config.RuleResults) + { + // XXX Idk why `.Where` does not work with these IAsyncQueryable type + if (ruleResults?.RuleName == "CompilerCommandLineArgs") + { + // XXX Not sure why there would be more than one item for this rule result + // Taking first one, ignoring the rest + var args = ruleResults?.Items?.FirstOrDefault()?.Name; + if (args != null && outputPath != null) projectInfos.Add((outputPath, args)); + } + } + } + } + + foreach (var projectInfo in projectInfos) + { + workspace.Projects.AddOrUpdate(projectPath, projectInfo.Item1, projectInfo.Item2.Split(';')); + } + + workspace.Debug_DumpMermaid("../../../../dep-graph.md"); + + + } + } + + public void OnNext(IQueryResults result) + { + foreach (var project in result) + { + this.ProcessProject(project); + } + } + + public void OnCompleted() + { + } + + public void OnError(Exception error) + { + } +} + + [VisualStudioContribution] internal class FSharpLanguageServerProvider : LanguageServerProvider { @@ -194,76 +298,37 @@ internal class FSharpLanguageServerProvider : LanguageServerProvider { var ws = this.Extensibility.Workspaces(); - IQueryResults? result = await ws.QueryProjectsAsync(project => project + var projectQuery = (IAsyncQueryable project) => project .With(p => p.ActiveConfigurations + .With(c => c.ConfigurationDimensions.With(d => d.Name).With(d => d.Value)) + .With(c => c.Properties.With(p => p.Name).With(p => p.Value)) + .With(c => c.OutputGroups.With(g => g.Name).With(g => g.Outputs.With(o => o.Name).With(o => o.FinalOutputPath).With(o => o.RootRelativeURL))) .With(c => c.RuleResultsByRuleName("CompilerCommandLineArgs") .With(r => r.RuleName) .With(r => r.Items))) - .With(p => new { p.ActiveConfigurations, p.Id, p.Guid }), cancellationToken); + .With(p => p.ProjectReferences + .With(r => r.ReferencedProjectPath) + .With(r => r.CanonicalName) + .With(r => r.Id) + .With(r => r.Name) + .With(r => r.ProjectGuid) + .With(r => r.ReferencedProjectId) + .With(r => r.ReferenceType)); + IQueryResults? result = await ws.QueryProjectsAsync(p => projectQuery(p).With(p => new { p.ActiveConfigurations, p.Id, p.Guid }), cancellationToken); + + var workspace = new FSharpWorkspace(); - List<(string, string)> projectsAndCommandLineArgs = []; foreach (var project in result) { - project.Id.TryGetValue("ProjectPath", out var projectPath); + var observer = new ProjectObserver(workspace); - List commandLineArgs = []; - if (projectPath != null) - { - // There can be multiple Active Configurations, e.g. one for net8.0 and one for net472 - // TODO For now taking any single one of them, but we might actually want to pick specific one - var config = project.ActiveConfigurations.FirstOrDefault(); - if (config != null) - { - foreach (var ruleResults in config.RuleResults) - { - // XXX Idk why `.Where` does not work with these IAsyncQuerable type - if (ruleResults?.RuleName == "CompilerCommandLineArgs") - { - // XXX Not sure why there would be more than one item for this rule result - // Taking first one, ignoring the rest - var args = ruleResults?.Items?.FirstOrDefault()?.Name; - if (args != null) commandLineArgs.Add(args); - } - } - } - if (commandLineArgs.Count > 0) - { - projectsAndCommandLineArgs.Add((projectPath, commandLineArgs[0])); - } - } + await projectQuery(project.AsQueryable()).SubscribeAsync(observer, CancellationToken.None); - try - { - this.ProcessProject(project); - } - catch (Exception ex) - { - Debug.WriteLine(ex); - } + // TODO: should we do this, or are we guaranteed it will get processed? + // observer.ProcessProject(project); } - FSharpWorkspace workspace; - - try - { - List snapshots = []; - foreach(var args in projectsAndCommandLineArgs) - { - var lines = args.Item2.Split(';'); // XXX Probably not robust enough - var path = args.Item1; - - string directoryPath = Path.GetDirectoryName(path) ?? throw new Exception("Directory path should not be null"); - var snapshot = FSharpProjectSnapshot.FromCommandLineArgs( - lines, directoryPath, Path.GetFileName(path)); - snapshots.Add(snapshot); - } - workspace = FSharpWorkspace.Create(snapshots); - } - catch - { - workspace = FSharpWorkspace.Create([]); - } var ((clientStream, serverStream), _server) = FSharpLanguageServer.Create(workspace, (serviceCollection) => { @@ -272,41 +337,25 @@ internal class FSharpLanguageServerProvider : LanguageServerProvider serviceCollection.AddSingleton(); }); - return new DuplexPipe( - PipeReader.Create(clientStream), - PipeWriter.Create(serverStream)); - } + var solutions = await ws.QuerySolutionAsync( + solution => solution.With(solution => solution.FileName), + cancellationToken); - private void ProcessProject(IProjectSnapshot project) - { - List>? files = project.Files.Please(); - var references = project.ProjectReferences.Please(); + var singleSolution = solutions.FirstOrDefault(); - var properties = project.Properties.Please(); - var id = project.Id; - - var configurationDimensions = project.ConfigurationDimensions.Please(); - var configurations = project.Configurations.Please(); - - foreach (var configuration in configurations) + if (singleSolution != null) { - this.ProcessConfiguration(configuration.Value); + var unsubscriber = await singleSolution + .AsQueryable() + .With(p => p.Projects.With(p => p.Files)) + .SubscribeAsync(new SolutionObserver(), CancellationToken.None); } - } - private void ProcessConfiguration(IProjectConfigurationSnapshot configuration) - { - var properties = configuration.Properties.Please(); - var packageReferences = configuration.PackageReferences.Please(); - var assemblyReferences = configuration.AssemblyReferences.Please(); - var refNames = assemblyReferences.Select(r => r.Value.Name).ToList(); - var dimensions = configuration.ConfigurationDimensions.Please(); - var outputGroups = configuration.OutputGroups.Please(); - var buildProperties = configuration.BuildProperties.Please(); - var buildPropDictionary = buildProperties.Select(p => (p.Value.Name, p.Value.Value)).ToList(); - return; - } + return new DuplexPipe( + PipeReader.Create(clientStream), + PipeWriter.Create(serverStream)); + } /// public override Task OnServerInitializationResultAsync(ServerInitializationResult serverInitializationResult, LanguageServerInitializationFailureInfo? initializationFailureInfo, CancellationToken cancellationToken) diff --git a/tests/FSharp.Compiler.LanguageServer.Tests/DependencyGraphTests.fs b/tests/FSharp.Compiler.LanguageServer.Tests/DependencyGraphTests.fs new file mode 100644 index 00000000000..5f6c378cfc8 --- /dev/null +++ b/tests/FSharp.Compiler.LanguageServer.Tests/DependencyGraphTests.fs @@ -0,0 +1,161 @@ +module DependencyGraphTests + +open FSharp.Compiler.LanguageServer.Common.DependencyGraph.Internal +open Xunit +open FSharp.Compiler.LanguageServer.Common.DependencyGraph + +[] +let ``Can add a node to the graph`` () = + let graph = DependencyGraph() + graph.AddOrUpdateNode(1, 1) |> ignore + Assert.Equal(1, graph.GetValue(1)) + +[] +let ``Can add a node with dependencies to the graph`` () = + let graph = DependencyGraph() + graph.AddOrUpdateNode(1, 1) + graph.AddOrUpdateNode(2, [ 1 ], (fun deps -> deps |> Seq.sum |> (+) 1)) + + graph.AddOrUpdateNode(3, [ 2 ], (fun deps -> deps |> Seq.sum |> (+) 1)) + |> ignore + + graph.AddOrUpdateNode(4, [ 1; 3 ], (fun deps -> deps |> Seq.sum |> (+) 1)) + |> ignore + + Assert.Equal(2, graph.GetValue(2)) + Assert.Equal(3, graph.GetValue(3)) + Assert.Equal(5, graph.GetValue(4)) + +[] +let ``Can update a value`` () = + let graph = DependencyGraph() + graph.AddOrUpdateNode(1, 1) + graph.AddOrUpdateNode(2, [ 1 ], (fun deps -> deps |> Seq.sum |> (+) 1)) + + graph.AddOrUpdateNode(3, [ 2 ], (fun deps -> deps |> Seq.sum |> (+) 1)) + |> ignore + + graph.AddOrUpdateNode(4, [ 1; 3 ], (fun deps -> deps |> Seq.sum |> (+) 1)) + |> ignore + + graph.AddOrUpdateNode(1, 2) |> ignore + + // Values were invalidated + Assert.Equal(None, graph.Debug.Nodes[2].Value) + Assert.Equal(None, graph.Debug.Nodes[3].Value) + Assert.Equal(None, graph.Debug.Nodes[4].Value) + + Assert.Equal(7, graph.GetValue(4)) + Assert.Equal(Some 3, graph.Debug.Nodes[2].Value) + Assert.Equal(Some 4, graph.Debug.Nodes[3].Value) + Assert.Equal(Some 7, graph.Debug.Nodes[4].Value) + +[] +let ``Dependencies are ordered`` () = + let graph = DependencyGraph() + let input = [ 1..100 ] + let ids = graph.AddList(seq { for x in input -> (x, [ x ]) }) + + graph.AddOrUpdateNode(101, ids, (fun deps -> deps |> Seq.collect id |> Seq.toList)) + |> ignore + + Assert.Equal(input, graph.GetValue(101)) + graph.AddOrUpdateNode(35, [ 42 ]) |> ignore + let expectedResult = input |> List.map (fun x -> if x = 35 then 42 else x) + Assert.Equal(expectedResult, graph.GetValue(101)) + +[] +let ``We can add a dependency between existing nodes`` () = + let graph = DependencyGraph() + graph.AddOrUpdateNode(1, [ 1 ]) + + graph.AddOrUpdateNode(2, [ 1 ], (fun deps -> deps |> Seq.concat |> Seq.toList)) + |> ignore + + graph.AddOrUpdateNode(3, [ 3 ]) |> ignore + Assert.Equal([ 1 ], graph.GetValue(2)) + graph.AddDependency(2, 3) + Assert.Equal([ 1; 3 ], graph.GetValue(2)) + +[] +let ``Can remove a node and update dependents`` () = + let graph = DependencyGraph() + graph.AddOrUpdateNode(1, 1) + graph.AddOrUpdateNode(2, [ 1 ], (fun deps -> deps |> Seq.sum |> (+) 1)) + + graph.AddOrUpdateNode(3, [ 2 ], (fun deps -> deps |> Seq.sum |> (+) 1)) + |> ignore + + graph.AddOrUpdateNode(4, [ 1; 3 ], (fun deps -> deps |> Seq.sum |> (+) 1)) + |> ignore + + // Check values before removal + Assert.Equal(2, graph.GetValue(2)) + Assert.Equal(3, graph.GetValue(3)) + Assert.Equal(5, graph.GetValue(4)) + + graph.RemoveNode(1) |> ignore + + // Check new values + Assert.Equal(1, graph.GetValue(2)) + Assert.Equal(2, graph.GetValue(3)) + Assert.Equal(3, graph.GetValue(4)) + +type MyDiscriminatedUnion = + | CaseA of int + | CaseB of string + +[] +let ``GraphBuilder with discriminated union`` () = + let graph = DependencyGraph() + let builder = GraphBuilder(graph, [ 1 ], (fun values -> values |> Seq.head), ()) + builder.Graph.AddOrUpdateNode(1, CaseA 1) + + builder.AddDependentNode( + 2, + (function + | CaseA x -> CaseB(string x) + | CaseB _ -> failwith "Unexpected case"), + (fun values -> values |> Seq.head) + ) + |> ignore + + Assert.Equal(CaseB "1", graph.GetValue(2)) + +[] +let ``GraphBuilder with chained AddDependentNode calls`` () = + let graph = DependencyGraph() + let builder = GraphBuilder(graph, [ 1 ], (fun values -> values |> Seq.head), ()) + builder.Graph.AddOrUpdateNode(1, CaseA 1) + + let builder2 = + builder.AddDependentNode( + 2, + (function + | CaseA x -> CaseB(string x) + | CaseB _ -> failwith "Unexpected case"), + (fun values -> values |> Seq.head) + ) + + builder2.AddDependentNode( + 3, + (function + | CaseB x -> CaseA(int x * 2) + | CaseA _ -> failwith "Unexpected case"), + (fun values -> values |> Seq.head) + ) + |> ignore + + Assert.Equal(CaseB "1", graph.GetValue(2)) + Assert.Equal(CaseA 2, graph.GetValue(3)) + + // Update the value of node 1 + builder.Graph.AddOrUpdateNode(1, CaseA 2) |> ignore + + // Values were invalidated + Assert.Equal(None, graph.Debug.Nodes[2].Value) + Assert.Equal(None, graph.Debug.Nodes[3].Value) + + // Check new values + Assert.Equal(CaseB "2", graph.GetValue(2)) + Assert.Equal(CaseA 4, graph.GetValue(3)) diff --git a/tests/FSharp.Compiler.LanguageServer.Tests/FSharp.Compiler.LanguageServer.Tests.fsproj b/tests/FSharp.Compiler.LanguageServer.Tests/FSharp.Compiler.LanguageServer.Tests.fsproj index dbffa2deb13..9570ca48441 100644 --- a/tests/FSharp.Compiler.LanguageServer.Tests/FSharp.Compiler.LanguageServer.Tests.fsproj +++ b/tests/FSharp.Compiler.LanguageServer.Tests/FSharp.Compiler.LanguageServer.Tests.fsproj @@ -15,6 +15,8 @@ + + diff --git a/tests/FSharp.Compiler.LanguageServer.Tests/FSharpWorkspaceTests.fs b/tests/FSharp.Compiler.LanguageServer.Tests/FSharpWorkspaceTests.fs new file mode 100644 index 00000000000..46492d09c06 --- /dev/null +++ b/tests/FSharp.Compiler.LanguageServer.Tests/FSharpWorkspaceTests.fs @@ -0,0 +1,300 @@ +module FSharpWorkspaceTests + +open System +open Xunit +open FSharp.Compiler.LanguageServer.Common +open FSharp.Compiler.CodeAnalysis.ProjectSnapshot +open TestFramework +open FSharp.Compiler.IO + +#nowarn "57" + +type ProjectConfig with + + static member Minimal(?name, ?outputPath, ?referencesOnDisk) = + let name = defaultArg name "test" + let projectFileName = $"{name}.fsproj" + let outputPath = defaultArg outputPath $"{name}.dll" + let referencesOnDisk = defaultArg referencesOnDisk [] + ProjectConfig(projectFileName, Some outputPath, referencesOnDisk, []) + +let getReferencedSnapshot (projectIdentifier: FSharpProjectIdentifier) (projectSnapshot: FSharpProjectSnapshot) = + projectSnapshot.ReferencedProjects + |> Seq.pick (function + | FSharpReference(x, snapshot) when x = projectIdentifier.OutputFileName -> Some snapshot + | _ -> None) + +let sourceFileOnDisk (content: string) = + let path = getTemporaryFileName () + ".fs" + FileSystem.OpenFileForWriteShim(path).Write(content) + Uri(path) + +let assertFileHasContent filePath expectedContent (projectSnapshot: FSharpProjectSnapshot) = + let fileSnapshot = + projectSnapshot.SourceFiles |> Seq.find (_.FileName >> (=) filePath) + + Assert.Equal(expectedContent, fileSnapshot.GetSource().Result.ToString()) + +[] +let ``Add project to workspace`` () = + let workspace = FSharpWorkspace() + let projectPath = "test.fsproj" + let outputPath = "test.dll" + let compilerArgs = [| "test.fs" |] + let projectIdentifier = workspace.Projects.AddOrUpdate(projectPath, outputPath, compilerArgs) + let projectSnapshot = workspace.Query.GetProjectSnapshot(projectIdentifier).Value + Assert.NotNull(projectSnapshot) + Assert.Equal(projectPath, projectSnapshot.ProjectFileName) + Assert.Equal(Some outputPath, projectSnapshot.OutputFileName) + Assert.Contains("test.fs", projectSnapshot.SourceFiles |> Seq.map (fun f -> f.FileName)) + +[] +let ``Open file in workspace`` () = + let workspace = FSharpWorkspace() + let fileUri = Uri("file:///test.fs") + let content = "let x = 1" + + let projectPath = "test.fsproj" + let outputPath = "test.dll" + let compilerArgs = [| fileUri.LocalPath |] + let _projectIdentifier = workspace.Projects.AddOrUpdate(projectPath, outputPath, compilerArgs) + + workspace.Files.Open(fileUri, content) + let projectSnapshot = workspace.Query.GetProjectSnapshotForFile(fileUri) + + // Retrieve the file snapshot from the project snapshot + let fileSnapshot = + projectSnapshot + |> Option.defaultWith (fun () -> failwith "Project snapshot not found") + |> _.SourceFiles + |> Seq.find (fun f -> f.FileName = fileUri.LocalPath) + + // Assert that the content of the file in the snapshot is correct + Assert.Equal(content, fileSnapshot.GetSource().Result.ToString()) + + let fileSnapshot = + projectSnapshot + |> Option.defaultWith (fun () -> failwith "Project snapshot not found") + |> _.SourceFiles + |> Seq.find (fun f -> f.FileName = fileUri.LocalPath) + + Assert.Equal(content, fileSnapshot.GetSource().Result.ToString()) + +[] +let ``Close file in workspace`` () = + let workspace = FSharpWorkspace() + + let contentOnDisk = "let x = 1" + let fileOnDisk = sourceFileOnDisk contentOnDisk + + let _projectIdentifier = + workspace.Projects.AddOrUpdate(ProjectConfig.Minimal(), [ fileOnDisk.LocalPath ]) + + workspace.Files.Open(fileOnDisk, contentOnDisk) + + let contentInMemory = "let x = 2" + workspace.Files.Edit(fileOnDisk, contentInMemory) + + let projectSnapshot = + workspace.Query.GetProjectSnapshotForFile(fileOnDisk) + |> Option.defaultWith (fun () -> failwith "Project snapshot not found") + + let fileSnapshot = + projectSnapshot.SourceFiles |> Seq.find (_.FileName >> (=) fileOnDisk.LocalPath) + + Assert.Equal(contentInMemory, fileSnapshot.GetSource().Result.ToString()) + + workspace.Files.Close(fileOnDisk) + + let projectSnapshot = + workspace.Query.GetProjectSnapshotForFile(fileOnDisk) + |> Option.defaultWith (fun () -> failwith "Project snapshot not found") + + let fileSnapshot = + projectSnapshot.SourceFiles |> Seq.find (_.FileName >> (=) fileOnDisk.LocalPath) + + Assert.Equal(contentOnDisk, fileSnapshot.GetSource().Result.ToString()) + +[] +let ``Change file in workspace`` () = + let workspace = FSharpWorkspace() + + let fileUri = Uri("file:///test.fs") + + let _projectIdentifier = + workspace.Projects.AddOrUpdate(ProjectConfig.Minimal(), [ fileUri.LocalPath ]) + + let initialContent = "let x = 2" + + workspace.Files.Open(fileUri, initialContent) + + let updatedContent = "let x = 3" + + workspace.Files.Edit(fileUri, updatedContent) + + let projectSnapshot = + workspace.Query.GetProjectSnapshotForFile(fileUri) + |> Option.defaultWith (fun () -> failwith "Project snapshot not found") + + let fileSnapshot = + projectSnapshot.SourceFiles |> Seq.find (_.FileName >> (=) fileUri.LocalPath) + + Assert.Equal(updatedContent, fileSnapshot.GetSource().Result.ToString()) + +[] +let ``Add multiple projects with references`` () = + let workspace = FSharpWorkspace() + let projectPath1 = "test1.fsproj" + let outputPath1 = "test1.dll" + let compilerArgs1 = [| "test1.fs" |] + + let projectIdentifier1 = + workspace.Projects.AddOrUpdate(projectPath1, outputPath1, compilerArgs1) + + let projectPath2 = "test2.fsproj" + let outputPath2 = "test2.dll" + let compilerArgs2 = [| "test2.fs"; "-r:test1.dll" |] + + let projectIdentifier2 = + workspace.Projects.AddOrUpdate(projectPath2, outputPath2, compilerArgs2) + + let projectSnapshot1 = workspace.Query.GetProjectSnapshot(projectIdentifier1).Value + let projectSnapshot2 = workspace.Query.GetProjectSnapshot(projectIdentifier2).Value + Assert.Contains("test1.fs", projectSnapshot1.SourceFiles |> Seq.map (fun f -> f.FileName)) + Assert.Contains("test2.fs", projectSnapshot2.SourceFiles |> Seq.map (fun f -> f.FileName)) + + Assert.Contains( + projectSnapshot1, + projectSnapshot2.ReferencedProjects + |> Seq.choose (function + | FSharpReferencedProjectSnapshot.FSharpReference(_, s) -> Some s + | _ -> None) + ) + +[] +let ``Propagate changes to snapshots`` () = + let workspace = FSharpWorkspace() + + let file1 = sourceFileOnDisk "let x = 1" + let pid1 = workspace.Projects.AddOrUpdate(ProjectConfig.Minimal("p1"), [ file1.LocalPath ]) + + let file2 = sourceFileOnDisk "let y = 2" + + let pid2 = + workspace.Projects.AddOrUpdate(ProjectConfig.Minimal("p2", referencesOnDisk = [ pid1.OutputFileName ]), [ file2.LocalPath ]) + + let file3 = sourceFileOnDisk "let z = 3" + + let pid3 = + workspace.Projects.AddOrUpdate(ProjectConfig.Minimal("p3", referencesOnDisk = [ pid2.OutputFileName ]), [ file3.LocalPath ]) + + let s3 = workspace.Query.GetProjectSnapshot(pid3).Value + + s3 + |> getReferencedSnapshot pid2 + |> getReferencedSnapshot pid1 + |> assertFileHasContent file1.LocalPath "let x = 1" + + let updatedContent = "let x = 2" + + workspace.Files.Edit(file1, updatedContent) + + let s3 = workspace.Query.GetProjectSnapshot(pid3).Value + + s3 + |> getReferencedSnapshot pid2 + |> getReferencedSnapshot pid1 + |> assertFileHasContent file1.LocalPath updatedContent + +[] +let ``Update project by adding a source file`` () = + let workspace = FSharpWorkspace() + let projectPath = "test.fsproj" + let outputPath = "test.dll" + let compilerArgs = [| "test.fs" |] + let projectIdentifier = workspace.Projects.AddOrUpdate(projectPath, outputPath, compilerArgs) + let newSourceFile = "newTest.fs" + let newCompilerArgs = [| "test.fs"; newSourceFile |] + workspace.Projects.AddOrUpdate(projectPath, outputPath, newCompilerArgs) |> ignore + let projectSnapshot = workspace.Query.GetProjectSnapshot(projectIdentifier).Value + Assert.NotNull(projectSnapshot) + Assert.Contains("test.fs", projectSnapshot.SourceFiles |> Seq.map (fun f -> f.FileName)) + Assert.Contains(newSourceFile, projectSnapshot.SourceFiles |> Seq.map (fun f -> f.FileName)) + +[] +let ``Update project by adding a reference`` () = + let workspace = FSharpWorkspace() + let projectPath1 = "test1.fsproj" + let outputPath1 = "test1.dll" + let compilerArgs1 = [| "test1.fs" |] + + let projectIdentifier1 = + workspace.Projects.AddOrUpdate(projectPath1, outputPath1, compilerArgs1) + + let projectPath2 = "test2.fsproj" + let outputPath2 = "test2.dll" + let compilerArgs2 = [| "test2.fs" |] + + let projectIdentifier2 = + workspace.Projects.AddOrUpdate(projectPath2, outputPath2, compilerArgs2) + + let newCompilerArgs2 = [| "test2.fs"; "-r:test1.dll" |] + workspace.Projects.AddOrUpdate(projectPath2, outputPath2, newCompilerArgs2) |> ignore + let projectSnapshot1 = workspace.Query.GetProjectSnapshot(projectIdentifier1).Value + let projectSnapshot2 = workspace.Query.GetProjectSnapshot(projectIdentifier2).Value + + Assert.Contains( + projectSnapshot1, + projectSnapshot2.ReferencedProjects + |> Seq.choose (function + | FSharpReferencedProjectSnapshot.FSharpReference(_, s) -> Some s + | _ -> None) + ) + +[] +let ``Create references in existing projects`` () = + let workspace = FSharpWorkspace() + let projectPath1 = "test1.fsproj" + let outputPath1 = "test1.dll" + let compilerArgs1 = [| "test1.fs" |] + + let projectIdentifier1 = + workspace.Projects.AddOrUpdate(projectPath1, outputPath1, compilerArgs1) + + let projectPath2 = "test2.fsproj" + let outputPath2 = "test2.dll" + let compilerArgs2 = [| "test2.fs" |] + + let projectIdentifier2 = + workspace.Projects.AddOrUpdate(projectPath2, outputPath2, compilerArgs2) + + let projectSnapshot1 = workspace.Query.GetProjectSnapshot(projectIdentifier1).Value + let projectSnapshot2 = workspace.Query.GetProjectSnapshot(projectIdentifier2).Value + + Assert.DoesNotContain( + projectSnapshot1, + projectSnapshot2.ReferencedProjects + |> Seq.choose (function + | FSharpReferencedProjectSnapshot.FSharpReference(_, s) -> Some s + | _ -> None) + ) + + let newCompilerArgs2 = [| "test2.fs"; "-r:test1.dll" |] + workspace.Projects.AddOrUpdate(projectPath2, outputPath2, newCompilerArgs2) |> ignore + let projectSnapshot1 = workspace.Query.GetProjectSnapshot(projectIdentifier1).Value + let projectSnapshot2 = workspace.Query.GetProjectSnapshot(projectIdentifier2).Value + + Assert.Contains( + projectSnapshot1, + projectSnapshot2.ReferencedProjects + |> Seq.choose (function + | FSharpReferencedProjectSnapshot.FSharpReference(_, s) -> Some s + | _ -> None) + ) + +[] +let ``Asking for an unknown project snapshot returns None`` () = + + let workspace = FSharpWorkspace() + + Assert.Equal(None, workspace.Query.GetProjectSnapshot(FSharpProjectIdentifier("hello", "world"))) diff --git a/tests/FSharp.Compiler.LanguageServer.Tests/LanguageServerTests.fs b/tests/FSharp.Compiler.LanguageServer.Tests/LanguageServerTests.fs index df5fc6f41df..f1f633a4c0d 100644 --- a/tests/FSharp.Compiler.LanguageServer.Tests/LanguageServerTests.fs +++ b/tests/FSharp.Compiler.LanguageServer.Tests/LanguageServerTests.fs @@ -11,42 +11,41 @@ open System.Diagnostics open Microsoft.VisualStudio.LanguageServer.Protocol open Nerdbank.Streams - [] let ``The server can process the initialization message`` () = - // Create a StringWriter to capture the output + // Create a StringWriter to capture the output let rpcTrace = new StringWriter() try - let struct (clientStream, _serverStream) = FullDuplexStream.CreatePair() + let struct (clientStream, _serverStream) = FullDuplexStream.CreatePair() - use formatter = new JsonMessageFormatter() + use formatter = new JsonMessageFormatter() - use messageHandler = new HeaderDelimitedMessageHandler(clientStream, clientStream, formatter) + use messageHandler = + new HeaderDelimitedMessageHandler(clientStream, clientStream, formatter) - use jsonRpc = new JsonRpc(messageHandler) - + use jsonRpc = new JsonRpc(messageHandler) - // Create a new TraceListener with the StringWriter - let listener = new TextWriterTraceListener(rpcTrace) + // Create a new TraceListener with the StringWriter + let listener = new TextWriterTraceListener(rpcTrace) - // Add the listener to the JsonRpc TraceSource - jsonRpc.TraceSource.Listeners.Add(listener) |> ignore + // Add the listener to the JsonRpc TraceSource + jsonRpc.TraceSource.Listeners.Add(listener) |> ignore - // Set the TraceLevel to Information to get all informational, warning and error messages - jsonRpc.TraceSource.Switch.Level <- SourceLevels.Information + // Set the TraceLevel to Information to get all informational, warning and error messages + jsonRpc.TraceSource.Switch.Level <- SourceLevels.Information - //jsonRpc.inv + //jsonRpc.inv - // Now all JsonRpc debug information will be written to the StringWriter + // Now all JsonRpc debug information will be written to the StringWriter - let log = ResizeArray() + let log = ResizeArray() - let _s = new FSharpLanguageServer(jsonRpc, (LspLogger log.Add)) + let _s = new FSharpLanguageServer(jsonRpc, (LspLogger log.Add)) - jsonRpc.StartListening() + jsonRpc.StartListening() //let initializeParams = InitializeParams( // ProcessId = System.Diagnostics.Process.GetCurrentProcess().Id, @@ -54,10 +53,7 @@ let ``The server can process the initialization message`` () = // InitializationOptions = None, // RootPath = "file:///c:/temp") - - - finally let _output = rpcTrace.ToString() - () \ No newline at end of file + () diff --git a/tests/FSharp.Compiler.LanguageServer.Tests/Program.fs b/tests/FSharp.Compiler.LanguageServer.Tests/Program.fs index 0695f84c683..80c6d842785 100644 --- a/tests/FSharp.Compiler.LanguageServer.Tests/Program.fs +++ b/tests/FSharp.Compiler.LanguageServer.Tests/Program.fs @@ -1 +1,3 @@ -module Program = let [] main _ = 0 +module Program = + [] + let main _ = 0 From 54930650a731bb790e79b0e70a4059c99f4686a7 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 13 Nov 2024 17:41:27 +0100 Subject: [PATCH 20/60] WIP: move workspace to FCS --- src/Compiler/FSharp.Compiler.Service.fsproj | 11 +- .../Service}/FSharpWorkspace.fs | 6 +- src/Compiler/Service/FSharpWorkspaceQuery.fs | 83 +++++++ .../Service}/FSharpWorkspaceState.fs | 13 +- .../Utilities}/DependencyGraph.fs | 4 +- .../Common/FSharpRequestContext.fs | 158 +++++++++++++ .../Common/FSharpWorkspaceQuery.fs | 212 ------------------ .../FSharp.Compiler.LanguageServer.fsproj | 7 +- .../FSharpLanguageServer.fs | 1 + .../FSharpLanguageServerProvider.cs | 3 +- 10 files changed, 271 insertions(+), 227 deletions(-) rename src/{FSharp.Compiler.LanguageServer/Common => Compiler/Service}/FSharpWorkspace.fs (91%) create mode 100644 src/Compiler/Service/FSharpWorkspaceQuery.fs rename src/{FSharp.Compiler.LanguageServer/Common => Compiler/Service}/FSharpWorkspaceState.fs (97%) rename src/{FSharp.Compiler.LanguageServer/Common => Compiler/Utilities}/DependencyGraph.fs (98%) delete mode 100644 src/FSharp.Compiler.LanguageServer/Common/FSharpWorkspaceQuery.fs diff --git a/src/Compiler/FSharp.Compiler.Service.fsproj b/src/Compiler/FSharp.Compiler.Service.fsproj index 6e75d920839..0a8762088fa 100644 --- a/src/Compiler/FSharp.Compiler.Service.fsproj +++ b/src/Compiler/FSharp.Compiler.Service.fsproj @@ -1,4 +1,4 @@ - + @@ -16,7 +16,7 @@ true $(DefineConstants);COMPILER true - @@ -16,7 +16,7 @@ true $(DefineConstants);COMPILER true - {depNumber}[{dep}]") + |> Seq.distinct) + |> String.concat "\n" + + $"```mermaid\n\ngraph LR\n\n{content}\n\n```" + + member _.OnWarning(f) = warningSubscribers.Add f |> ignore + + interface IDependencyGraph<'Id, 'Val> with + + member this.Debug_GetNodes(predicate) = self.Debug_GetNodes(predicate) + + member _.AddOrUpdateNode(id, value) = self.AddOrUpdateNode(id, value) + member _.AddList(nodes) = self.AddList(nodes) + + member _.AddOrUpdateNode(id, dependsOn, compute) = + self.AddOrUpdateNode(id, dependsOn, compute) + + member _.GetValue(id) = self.GetValue(id) + member _.GetDependenciesOf(id) = self.GetDependenciesOf(id) + member _.GetDependentsOf(id) = self.GetDependentsOf(id) + member _.AddDependency(node, dependsOn) = self.AddDependency(node, dependsOn) + + member _.RemoveDependency(node, noLongerDependsOn) = + self.RemoveDependency(node, noLongerDependsOn) + + member _.UpdateNode(id, update) = self.UpdateNode(id, update) + member _.RemoveNode(id) = self.RemoveNode(id) + + member _.OnWarning f = self.OnWarning f + + member _.Debug_RenderMermaid(x) = self.Debug_RenderMermaid(?mapping = x) + +/// This type can be used to chain together a series of dependent nodes when there is some kind of type hierarchy in the graph. +/// That is when 'T represents some subset of 'Val (e.g. a sub type or a case in DU). +/// It can also carry some state that is passed along the chain. +type GraphBuilder<'Id, 'Val, 'T, 'State when 'Id: equality> + (graph: IDependencyGraph<'Id, 'Val>, ids: 'Id seq, unwrap: 'Val seq -> 'T, state: 'State) = + + member _.Ids = ids + + member _.State = state + + member _.Graph = graph + + member _.AddDependentNode(id, compute, unwrapNext) = + graph.AddOrUpdateNode(id, ids, unwrap >> compute) + GraphBuilder(graph, Seq.singleton id, unwrapNext, state) + + member _.AddDependentNode(id, compute, unwrapNext, nextState) = + graph.AddOrUpdateNode(id, ids, unwrap >> compute) + GraphBuilder(graph, Seq.singleton id, unwrapNext, nextState) + +open Internal +open System.Runtime.CompilerServices + +type LockOperatedDependencyGraph<'Id, 'Val when 'Id: equality and 'Id: not null>() = + + let lockObj = System.Object() + let graph = DependencyGraph<_, _>() + + interface IThreadSafeDependencyGraph<'Id, 'Val> with + + member _.AddDependency(node, dependsOn) = + lock lockObj (fun () -> graph.AddDependency(node, dependsOn)) + + member _.AddList(nodes) = + lock lockObj (fun () -> graph.AddList(nodes)) + + member _.AddOrUpdateNode(id, value) = + lock lockObj (fun () -> graph.AddOrUpdateNode(id, value)) + + member _.AddOrUpdateNode(id, dependsOn, compute) = + lock lockObj (fun () -> graph.AddOrUpdateNode(id, dependsOn, compute)) + + member _.GetDependenciesOf(id) = + lock lockObj (fun () -> graph.GetDependenciesOf(id)) + + member _.GetDependentsOf(id) = + lock lockObj (fun () -> graph.GetDependentsOf(id)) + + member _.GetValue(id) = + lock lockObj (fun () -> graph.GetValue(id)) + + member _.UpdateNode(id, update) = + lock lockObj (fun () -> graph.UpdateNode(id, update)) + + member _.RemoveNode(id) = + lock lockObj (fun () -> graph.RemoveNode(id)) + + member _.RemoveDependency(node, noLongerDependsOn) = + lock lockObj (fun () -> graph.RemoveDependency(node, noLongerDependsOn)) + + member _.Transact(f) = lock lockObj (fun () -> f graph) + + member _.OnWarning(f) = + lock lockObj (fun () -> graph.OnWarning f) + + member _.Debug_GetNodes(predicate) = + lock lockObj (fun () -> graph.Debug_GetNodes(predicate)) + + member _.Debug_RenderMermaid(m) = + lock lockObj (fun () -> graph.Debug_RenderMermaid(?mapping = m)) + +[] +type GraphExtensions = + + [] + static member Unpack(node: 'NodeValue, unpacker) = + match unpacker node with + | Some value -> value + | None -> failwith $"Expected {unpacker} but got: {node}" + + [] + static member UnpackOne(dependencies: 'NodeValue seq, unpacker: 'NodeValue -> 'UnpackedDependency option) = + dependencies + |> Seq.tryExactlyOne + |> Option.bind unpacker + |> Option.defaultWith (fun () -> + failwith $"Expected exactly one dependency matching {unpacker} but got: %A{dependencies |> Seq.toArray}") + + [] + static member UnpackMany(dependencies: 'NodeValue seq, unpacker) = + let results = dependencies |> Seq.choose unpacker + + if dependencies |> Seq.length <> (results |> Seq.length) then + failwith $"Expected all dependencies to match {unpacker} but got: %A{dependencies |> Seq.toArray}" + + results + + [] + static member UnpackOneMany(dependencies: 'NodeValue seq, oneUnpacker, manyUnpacker) = + let mutable oneResult = None + let manyResult = new ResizeArray<_>() + let extras = new ResizeArray<_>() + + for dependency in dependencies do + match oneUnpacker dependency, manyUnpacker dependency with + | Some item, _ -> oneResult <- Some item + | None, Some item -> manyResult.Add item |> ignore + | None, None -> extras.Add dependency |> ignore + + match oneResult with + | None -> failwith $"Expected exactly one dependency matching {oneUnpacker} but didn't find any" + | Some head -> + if extras.Count > 0 then + failwith $"Found extra dependencies: %A{extras.ToArray()}" + + head, seq manyResult From a68d5a2b6662128b1a28d45d312d3417b5d9dca7 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 19 Nov 2024 14:44:24 +0100 Subject: [PATCH 25/60] nullness fix --- src/Compiler/Service/FSharpWorkspaceState.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler/Service/FSharpWorkspaceState.fs b/src/Compiler/Service/FSharpWorkspaceState.fs index 8b7de493921..15fa558fd3d 100644 --- a/src/Compiler/Service/FSharpWorkspaceState.fs +++ b/src/Compiler/Service/FSharpWorkspaceState.fs @@ -336,7 +336,7 @@ type FSharpWorkspaceProjects internal (depGraph: IThreadSafeDependencyGraph<_, _ member this.AddOrUpdate(projectPath: string, outputPath, compilerArgs) = - let directoryPath = Path.GetDirectoryName(projectPath) + let directoryPath = Path.GetDirectoryName(projectPath) |> Option.ofObj |> Option.defaultValue "" let fsharpFileExtensions = set [| ".fs"; ".fsi"; ".fsx" |] From 0c6940b33abab0ce11f9013d89813c6e144afdf6 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Tue, 19 Nov 2024 15:32:50 +0100 Subject: [PATCH 26/60] tests --- .../CompilerService/DependencyGraph.fs | 162 ++++++++++ .../CompilerService/FSharpWorkspace.fs | 301 ++++++++++++++++++ .../FSharp.Compiler.ComponentTests.fsproj | 3 + 3 files changed, 466 insertions(+) create mode 100644 tests/FSharp.Compiler.ComponentTests/CompilerService/DependencyGraph.fs create mode 100644 tests/FSharp.Compiler.ComponentTests/CompilerService/FSharpWorkspace.fs diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/DependencyGraph.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/DependencyGraph.fs new file mode 100644 index 00000000000..e7f3d83d0fa --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/DependencyGraph.fs @@ -0,0 +1,162 @@ +module CompilerService.DependencyGraph + +open Xunit +open Internal.Utilities.DependencyGraph.Internal +open Internal.Utilities.DependencyGraph + + +[] +let ``Can add a node to the graph`` () = + let graph = DependencyGraph() + graph.AddOrUpdateNode(1, 1) |> ignore + Assert.Equal(1, graph.GetValue(1)) + +[] +let ``Can add a node with dependencies to the graph`` () = + let graph = DependencyGraph() + graph.AddOrUpdateNode(1, 1) + graph.AddOrUpdateNode(2, [ 1 ], (fun deps -> deps |> Seq.sum |> (+) 1)) + + graph.AddOrUpdateNode(3, [ 2 ], (fun deps -> deps |> Seq.sum |> (+) 1)) + |> ignore + + graph.AddOrUpdateNode(4, [ 1; 3 ], (fun deps -> deps |> Seq.sum |> (+) 1)) + |> ignore + + Assert.Equal(2, graph.GetValue(2)) + Assert.Equal(3, graph.GetValue(3)) + Assert.Equal(5, graph.GetValue(4)) + +[] +let ``Can update a value`` () = + let graph = DependencyGraph() + graph.AddOrUpdateNode(1, 1) + graph.AddOrUpdateNode(2, [ 1 ], (fun deps -> deps |> Seq.sum |> (+) 1)) + + graph.AddOrUpdateNode(3, [ 2 ], (fun deps -> deps |> Seq.sum |> (+) 1)) + |> ignore + + graph.AddOrUpdateNode(4, [ 1; 3 ], (fun deps -> deps |> Seq.sum |> (+) 1)) + |> ignore + + graph.AddOrUpdateNode(1, 2) |> ignore + + // Values were invalidated + Assert.Equal(None, graph.Debug.Nodes[2].Value) + Assert.Equal(None, graph.Debug.Nodes[3].Value) + Assert.Equal(None, graph.Debug.Nodes[4].Value) + + Assert.Equal(7, graph.GetValue(4)) + Assert.Equal(Some 3, graph.Debug.Nodes[2].Value) + Assert.Equal(Some 4, graph.Debug.Nodes[3].Value) + Assert.Equal(Some 7, graph.Debug.Nodes[4].Value) + +[] +let ``Dependencies are ordered`` () = + let graph = DependencyGraph() + let input = [ 1..100 ] + let ids = graph.AddList(seq { for x in input -> (x, [ x ]) }) + + graph.AddOrUpdateNode(101, ids, (fun deps -> deps |> Seq.collect id |> Seq.toList)) + |> ignore + + Assert.Equal(input, graph.GetValue(101)) + graph.AddOrUpdateNode(35, [ 42 ]) |> ignore + let expectedResult = input |> List.map (fun x -> if x = 35 then 42 else x) + Assert.Equal(expectedResult, graph.GetValue(101)) + +[] +let ``We can add a dependency between existing nodes`` () = + let graph = DependencyGraph() + graph.AddOrUpdateNode(1, [ 1 ]) + + graph.AddOrUpdateNode(2, [ 1 ], (fun deps -> deps |> Seq.concat |> Seq.toList)) + |> ignore + + graph.AddOrUpdateNode(3, [ 3 ]) |> ignore + Assert.Equal([ 1 ], graph.GetValue(2)) + graph.AddDependency(2, 3) + Assert.Equal([ 1; 3 ], graph.GetValue(2)) + +[] +let ``Can remove a node and update dependents`` () = + let graph = DependencyGraph() + graph.AddOrUpdateNode(1, 1) + graph.AddOrUpdateNode(2, [ 1 ], (fun deps -> deps |> Seq.sum |> (+) 1)) + + graph.AddOrUpdateNode(3, [ 2 ], (fun deps -> deps |> Seq.sum |> (+) 1)) + |> ignore + + graph.AddOrUpdateNode(4, [ 1; 3 ], (fun deps -> deps |> Seq.sum |> (+) 1)) + |> ignore + + // Check values before removal + Assert.Equal(2, graph.GetValue(2)) + Assert.Equal(3, graph.GetValue(3)) + Assert.Equal(5, graph.GetValue(4)) + + graph.RemoveNode(1) |> ignore + + // Check new values + Assert.Equal(1, graph.GetValue(2)) + Assert.Equal(2, graph.GetValue(3)) + Assert.Equal(3, graph.GetValue(4)) + +type MyDiscriminatedUnion = + | CaseA of int + | CaseB of string + +[] +let ``GraphBuilder with discriminated union`` () = + let graph = DependencyGraph() + let builder = GraphBuilder(graph, [ 1 ], (fun values -> values |> Seq.head), ()) + builder.Graph.AddOrUpdateNode(1, CaseA 1) + + builder.AddDependentNode( + 2, + (function + | CaseA x -> CaseB(string x) + | CaseB _ -> failwith "Unexpected case"), + (fun values -> values |> Seq.head) + ) + |> ignore + + Assert.Equal(CaseB "1", graph.GetValue(2)) + +[] +let ``GraphBuilder with chained AddDependentNode calls`` () = + let graph = DependencyGraph() + let builder = GraphBuilder(graph, [ 1 ], (fun values -> values |> Seq.head), ()) + builder.Graph.AddOrUpdateNode(1, CaseA 1) + + let builder2 = + builder.AddDependentNode( + 2, + (function + | CaseA x -> CaseB(string x) + | CaseB _ -> failwith "Unexpected case"), + (fun values -> values |> Seq.head) + ) + + builder2.AddDependentNode( + 3, + (function + | CaseB x -> CaseA(int x * 2) + | CaseA _ -> failwith "Unexpected case"), + (fun values -> values |> Seq.head) + ) + |> ignore + + Assert.Equal(CaseB "1", graph.GetValue(2)) + Assert.Equal(CaseA 2, graph.GetValue(3)) + + // Update the value of node 1 + builder.Graph.AddOrUpdateNode(1, CaseA 2) |> ignore + + // Values were invalidated + Assert.Equal(None, graph.Debug.Nodes[2].Value) + Assert.Equal(None, graph.Debug.Nodes[3].Value) + + // Check new values + Assert.Equal(CaseB "2", graph.GetValue(2)) + Assert.Equal(CaseA 4, graph.GetValue(3)) diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/FSharpWorkspace.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/FSharpWorkspace.fs new file mode 100644 index 00000000000..fbae320131d --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/FSharpWorkspace.fs @@ -0,0 +1,301 @@ +module CompilerService.FSharpWorkspace + +open System +open Xunit + +open FSharp.Compiler.CodeAnalysis.ProjectSnapshot +open TestFramework +open FSharp.Compiler.IO +open FSharp.Compiler.CodeAnalysis.Workspace + +#nowarn "57" + +type ProjectConfig with + + static member Minimal(?name, ?outputPath, ?referencesOnDisk) = + let name = defaultArg name "test" + let projectFileName = $"{name}.fsproj" + let outputPath = defaultArg outputPath $"{name}.dll" + let referencesOnDisk = defaultArg referencesOnDisk [] + ProjectConfig(projectFileName, Some outputPath, referencesOnDisk, []) + +let getReferencedSnapshot (projectIdentifier: FSharpProjectIdentifier) (projectSnapshot: FSharpProjectSnapshot) = + projectSnapshot.ReferencedProjects + |> Seq.pick (function + | FSharpReference(x, snapshot) when x = projectIdentifier.OutputFileName -> Some snapshot + | _ -> None) + +let sourceFileOnDisk (content: string) = + let path = getTemporaryFileName () + ".fs" + FileSystem.OpenFileForWriteShim(path).Write(content) + Uri(path) + +let assertFileHasContent filePath expectedContent (projectSnapshot: FSharpProjectSnapshot) = + let fileSnapshot = + projectSnapshot.SourceFiles |> Seq.find (_.FileName >> (=) filePath) + + Assert.Equal(expectedContent, fileSnapshot.GetSource().Result.ToString()) + +[] +let ``Add project to workspace`` () = + let workspace = FSharpWorkspace() + let projectPath = "test.fsproj" + let outputPath = "test.dll" + let compilerArgs = [| "test.fs" |] + let projectIdentifier = workspace.Projects.AddOrUpdate(projectPath, outputPath, compilerArgs) + let projectSnapshot = workspace.Query.GetProjectSnapshot(projectIdentifier).Value + Assert.NotNull(projectSnapshot) + Assert.Equal(projectPath, projectSnapshot.ProjectFileName) + Assert.Equal(Some outputPath, projectSnapshot.OutputFileName) + Assert.Contains("test.fs", projectSnapshot.SourceFiles |> Seq.map (fun f -> f.FileName)) + +[] +let ``Open file in workspace`` () = + let workspace = FSharpWorkspace() + let fileUri = Uri("file:///test.fs") + let content = "let x = 1" + + let projectPath = "test.fsproj" + let outputPath = "test.dll" + let compilerArgs = [| fileUri.LocalPath |] + let _projectIdentifier = workspace.Projects.AddOrUpdate(projectPath, outputPath, compilerArgs) + + workspace.Files.Open(fileUri, content) + let projectSnapshot = workspace.Query.GetProjectSnapshotForFile(fileUri) + + // Retrieve the file snapshot from the project snapshot + let fileSnapshot = + projectSnapshot + |> Option.defaultWith (fun () -> failwith "Project snapshot not found") + |> _.SourceFiles + |> Seq.find (fun f -> f.FileName = fileUri.LocalPath) + + // Assert that the content of the file in the snapshot is correct + Assert.Equal(content, fileSnapshot.GetSource().Result.ToString()) + + let fileSnapshot = + projectSnapshot + |> Option.defaultWith (fun () -> failwith "Project snapshot not found") + |> _.SourceFiles + |> Seq.find (fun f -> f.FileName = fileUri.LocalPath) + + Assert.Equal(content, fileSnapshot.GetSource().Result.ToString()) + +[] +let ``Close file in workspace`` () = + let workspace = FSharpWorkspace() + + let contentOnDisk = "let x = 1" + let fileOnDisk = sourceFileOnDisk contentOnDisk + + let _projectIdentifier = + workspace.Projects.AddOrUpdate(ProjectConfig.Minimal(), [ fileOnDisk.LocalPath ]) + + workspace.Files.Open(fileOnDisk, contentOnDisk) + + let contentInMemory = "let x = 2" + workspace.Files.Edit(fileOnDisk, contentInMemory) + + let projectSnapshot = + workspace.Query.GetProjectSnapshotForFile(fileOnDisk) + |> Option.defaultWith (fun () -> failwith "Project snapshot not found") + + let fileSnapshot = + projectSnapshot.SourceFiles |> Seq.find (_.FileName >> (=) fileOnDisk.LocalPath) + + Assert.Equal(contentInMemory, fileSnapshot.GetSource().Result.ToString()) + + workspace.Files.Close(fileOnDisk) + + let projectSnapshot = + workspace.Query.GetProjectSnapshotForFile(fileOnDisk) + |> Option.defaultWith (fun () -> failwith "Project snapshot not found") + + let fileSnapshot = + projectSnapshot.SourceFiles |> Seq.find (_.FileName >> (=) fileOnDisk.LocalPath) + + Assert.Equal(contentOnDisk, fileSnapshot.GetSource().Result.ToString()) + +[] +let ``Change file in workspace`` () = + let workspace = FSharpWorkspace() + + let fileUri = Uri("file:///test.fs") + + let _projectIdentifier = + workspace.Projects.AddOrUpdate(ProjectConfig.Minimal(), [ fileUri.LocalPath ]) + + let initialContent = "let x = 2" + + workspace.Files.Open(fileUri, initialContent) + + let updatedContent = "let x = 3" + + workspace.Files.Edit(fileUri, updatedContent) + + let projectSnapshot = + workspace.Query.GetProjectSnapshotForFile(fileUri) + |> Option.defaultWith (fun () -> failwith "Project snapshot not found") + + let fileSnapshot = + projectSnapshot.SourceFiles |> Seq.find (_.FileName >> (=) fileUri.LocalPath) + + Assert.Equal(updatedContent, fileSnapshot.GetSource().Result.ToString()) + +[] +let ``Add multiple projects with references`` () = + let workspace = FSharpWorkspace() + let projectPath1 = "test1.fsproj" + let outputPath1 = "test1.dll" + let compilerArgs1 = [| "test1.fs" |] + + let projectIdentifier1 = + workspace.Projects.AddOrUpdate(projectPath1, outputPath1, compilerArgs1) + + let projectPath2 = "test2.fsproj" + let outputPath2 = "test2.dll" + let compilerArgs2 = [| "test2.fs"; "-r:test1.dll" |] + + let projectIdentifier2 = + workspace.Projects.AddOrUpdate(projectPath2, outputPath2, compilerArgs2) + + let projectSnapshot1 = workspace.Query.GetProjectSnapshot(projectIdentifier1).Value + let projectSnapshot2 = workspace.Query.GetProjectSnapshot(projectIdentifier2).Value + Assert.Contains("test1.fs", projectSnapshot1.SourceFiles |> Seq.map (fun f -> f.FileName)) + Assert.Contains("test2.fs", projectSnapshot2.SourceFiles |> Seq.map (fun f -> f.FileName)) + + Assert.Contains( + projectSnapshot1, + projectSnapshot2.ReferencedProjects + |> Seq.choose (function + | FSharpReferencedProjectSnapshot.FSharpReference(_, s) -> Some s + | _ -> None) + ) + +[] +let ``Propagate changes to snapshots`` () = + let workspace = FSharpWorkspace() + + let file1 = sourceFileOnDisk "let x = 1" + let pid1 = workspace.Projects.AddOrUpdate(ProjectConfig.Minimal("p1"), [ file1.LocalPath ]) + + let file2 = sourceFileOnDisk "let y = 2" + + let pid2 = + workspace.Projects.AddOrUpdate(ProjectConfig.Minimal("p2", referencesOnDisk = [ pid1.OutputFileName ]), [ file2.LocalPath ]) + + let file3 = sourceFileOnDisk "let z = 3" + + let pid3 = + workspace.Projects.AddOrUpdate(ProjectConfig.Minimal("p3", referencesOnDisk = [ pid2.OutputFileName ]), [ file3.LocalPath ]) + + let s3 = workspace.Query.GetProjectSnapshot(pid3).Value + + s3 + |> getReferencedSnapshot pid2 + |> getReferencedSnapshot pid1 + |> assertFileHasContent file1.LocalPath "let x = 1" + + let updatedContent = "let x = 2" + + workspace.Files.Edit(file1, updatedContent) + + let s3 = workspace.Query.GetProjectSnapshot(pid3).Value + + s3 + |> getReferencedSnapshot pid2 + |> getReferencedSnapshot pid1 + |> assertFileHasContent file1.LocalPath updatedContent + +[] +let ``Update project by adding a source file`` () = + let workspace = FSharpWorkspace() + let projectPath = "test.fsproj" + let outputPath = "test.dll" + let compilerArgs = [| "test.fs" |] + let projectIdentifier = workspace.Projects.AddOrUpdate(projectPath, outputPath, compilerArgs) + let newSourceFile = "newTest.fs" + let newCompilerArgs = [| "test.fs"; newSourceFile |] + workspace.Projects.AddOrUpdate(projectPath, outputPath, newCompilerArgs) |> ignore + let projectSnapshot = workspace.Query.GetProjectSnapshot(projectIdentifier).Value + Assert.NotNull(projectSnapshot) + Assert.Contains("test.fs", projectSnapshot.SourceFiles |> Seq.map (fun f -> f.FileName)) + Assert.Contains(newSourceFile, projectSnapshot.SourceFiles |> Seq.map (fun f -> f.FileName)) + +[] +let ``Update project by adding a reference`` () = + let workspace = FSharpWorkspace() + let projectPath1 = "test1.fsproj" + let outputPath1 = "test1.dll" + let compilerArgs1 = [| "test1.fs" |] + + let projectIdentifier1 = + workspace.Projects.AddOrUpdate(projectPath1, outputPath1, compilerArgs1) + + let projectPath2 = "test2.fsproj" + let outputPath2 = "test2.dll" + let compilerArgs2 = [| "test2.fs" |] + + let projectIdentifier2 = + workspace.Projects.AddOrUpdate(projectPath2, outputPath2, compilerArgs2) + + let newCompilerArgs2 = [| "test2.fs"; "-r:test1.dll" |] + workspace.Projects.AddOrUpdate(projectPath2, outputPath2, newCompilerArgs2) |> ignore + let projectSnapshot1 = workspace.Query.GetProjectSnapshot(projectIdentifier1).Value + let projectSnapshot2 = workspace.Query.GetProjectSnapshot(projectIdentifier2).Value + + Assert.Contains( + projectSnapshot1, + projectSnapshot2.ReferencedProjects + |> Seq.choose (function + | FSharpReferencedProjectSnapshot.FSharpReference(_, s) -> Some s + | _ -> None) + ) + +[] +let ``Create references in existing projects`` () = + let workspace = FSharpWorkspace() + let projectPath1 = "test1.fsproj" + let outputPath1 = "test1.dll" + let compilerArgs1 = [| "test1.fs" |] + + let projectIdentifier1 = + workspace.Projects.AddOrUpdate(projectPath1, outputPath1, compilerArgs1) + + let projectPath2 = "test2.fsproj" + let outputPath2 = "test2.dll" + let compilerArgs2 = [| "test2.fs" |] + + let projectIdentifier2 = + workspace.Projects.AddOrUpdate(projectPath2, outputPath2, compilerArgs2) + + let projectSnapshot1 = workspace.Query.GetProjectSnapshot(projectIdentifier1).Value + let projectSnapshot2 = workspace.Query.GetProjectSnapshot(projectIdentifier2).Value + + Assert.DoesNotContain( + projectSnapshot1, + projectSnapshot2.ReferencedProjects + |> Seq.choose (function + | FSharpReferencedProjectSnapshot.FSharpReference(_, s) -> Some s + | _ -> None) + ) + + let newCompilerArgs2 = [| "test2.fs"; "-r:test1.dll" |] + workspace.Projects.AddOrUpdate(projectPath2, outputPath2, newCompilerArgs2) |> ignore + let projectSnapshot1 = workspace.Query.GetProjectSnapshot(projectIdentifier1).Value + let projectSnapshot2 = workspace.Query.GetProjectSnapshot(projectIdentifier2).Value + + Assert.Contains( + projectSnapshot1, + projectSnapshot2.ReferencedProjects + |> Seq.choose (function + | FSharpReferencedProjectSnapshot.FSharpReference(_, s) -> Some s + | _ -> None) + ) + +[] +let ``Asking for an unknown project snapshot returns None`` () = + + let workspace = FSharpWorkspace() + + Assert.Equal(None, workspace.Query.GetProjectSnapshot(FSharpProjectIdentifier("hello", "world"))) diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index ffc935a2075..2754b601d5b 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -302,6 +302,8 @@ + + @@ -367,4 +369,5 @@ + From 2b04d3f0751a96ac9a8f2e4f4d0eaa002e7529a8 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Tue, 19 Nov 2024 15:51:47 +0100 Subject: [PATCH 27/60] Remove unused methods --- src/Compiler/Service/FSharpProjectSnapshot.fs | 63 ------------------- 1 file changed, 63 deletions(-) diff --git a/src/Compiler/Service/FSharpProjectSnapshot.fs b/src/Compiler/Service/FSharpProjectSnapshot.fs index 8b64a6be3a6..605e52afe35 100644 --- a/src/Compiler/Service/FSharpProjectSnapshot.fs +++ b/src/Compiler/Service/FSharpProjectSnapshot.fs @@ -747,69 +747,6 @@ and [] FSha FSharpProjectSnapshot.FromOptions(options, getFileSnapshot) - static member FromResponseFile(responseFile: FileInfo, projectFileName) = - if not responseFile.Exists then - failwith $"%s{responseFile.FullName} does not exist" - - let compilerArgs = File.ReadAllLines responseFile.FullName - - let directoryName: string = - match responseFile.DirectoryName with - | null -> failwith "Directory name of the response file is null" - | str -> str - - FSharpProjectSnapshot.FromCommandLineArgs(compilerArgs, directoryName, projectFileName) - - static member FromCommandLineArgs(compilerArgs: string array, directoryPath: string, projectFileName) = - let fsharpFileExtensions = set [| ".fs"; ".fsi"; ".fsx" |] - - let isFSharpFile (file: string) = - Set.exists (fun (ext: string) -> file.EndsWith(ext, StringComparison.Ordinal)) fsharpFileExtensions - - let isReference: string -> bool = _.StartsWith("-r:") - - let fsharpFiles = - compilerArgs - |> Array.choose (fun (line: string) -> - if not (isFSharpFile line) then - None - else - - let fullPath = Path.Combine(directoryPath, line) - if not (File.Exists fullPath) then None else Some fullPath) - |> Array.toList - - let referencesOnDisk = - compilerArgs |> Seq.filter isReference |> Seq.map _.Substring(3) |> Seq.toList - - let otherOptions = - compilerArgs - |> Seq.filter (not << isReference) - |> Seq.filter (not << isFSharpFile) - |> Seq.toList - - FSharpProjectSnapshot.Create( - projectFileName = projectFileName, - outputFileName = None, - projectId = None, - sourceFiles = (fsharpFiles |> List.map FSharpFileSnapshot.CreateFromFileSystem), - referencesOnDisk = - (referencesOnDisk - |> List.map (fun x -> - { - Path = x - LastModified = FileSystem.GetLastWriteTimeShim(x) - })), - otherOptions = otherOptions, - referencedProjects = [], - isIncompleteTypeCheckEnvironment = false, - useScriptResolutionRules = false, - loadTime = DateTime.Now, - unresolvedReferences = None, - originalLoadReferences = [], - stamp = None - ) - let rec internal snapshotToOptions (projectSnapshot: ProjectSnapshotBase<_>) = { ProjectFileName = projectSnapshot.ProjectFileName From b2ba2d24ef8b38d450085232df27561ce5ad7db1 Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Tue, 19 Nov 2024 16:17:49 +0100 Subject: [PATCH 28/60] f --- src/Compiler/Service/FSharpWorkspaceState.fs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Compiler/Service/FSharpWorkspaceState.fs b/src/Compiler/Service/FSharpWorkspaceState.fs index 15fa558fd3d..2227d03d478 100644 --- a/src/Compiler/Service/FSharpWorkspaceState.fs +++ b/src/Compiler/Service/FSharpWorkspaceState.fs @@ -336,7 +336,8 @@ type FSharpWorkspaceProjects internal (depGraph: IThreadSafeDependencyGraph<_, _ member this.AddOrUpdate(projectPath: string, outputPath, compilerArgs) = - let directoryPath = Path.GetDirectoryName(projectPath) |> Option.ofObj |> Option.defaultValue "" + let directoryPath = + Path.GetDirectoryName(projectPath) |> Option.ofObj |> Option.defaultValue "" let fsharpFileExtensions = set [| ".fs"; ".fsi"; ".fsx" |] From c19ff5e0242832ae0c4839aedd607f8b4857006f Mon Sep 17 00:00:00 2001 From: 0101 <0101@innit.cz> Date: Tue, 19 Nov 2024 16:39:44 +0100 Subject: [PATCH 29/60] Added nullness using file to fantomasignore --- .fantomasignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.fantomasignore b/.fantomasignore index d731fb6d9a5..157526b39c0 100644 --- a/.fantomasignore +++ b/.fantomasignore @@ -109,6 +109,7 @@ src/Compiler/Utilities/illib.fs src/Compiler/Utilities/NullnessShims.fs +src/Compiler/Utilities/DependencyGraph.fs src/Compiler/Utilities/LruCache.fsi src/Compiler/Utilities/LruCache.fs src/Compiler/Utilities/HashMultiMap.fsi From 9a590dc33dd021568c938e5f20eee99287cf851d Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 20 Nov 2024 12:23:03 +0100 Subject: [PATCH 30/60] Remove FSharpWorkspace signatures --- src/Compiler/FSharp.Compiler.Service.fsproj | 3 - src/Compiler/Service/FSharpWorkspace.fsi | 35 --- src/Compiler/Service/FSharpWorkspaceQuery.fsi | 42 ---- src/Compiler/Service/FSharpWorkspaceState.fsi | 201 ------------------ 4 files changed, 281 deletions(-) delete mode 100644 src/Compiler/Service/FSharpWorkspace.fsi delete mode 100644 src/Compiler/Service/FSharpWorkspaceQuery.fsi delete mode 100644 src/Compiler/Service/FSharpWorkspaceState.fsi diff --git a/src/Compiler/FSharp.Compiler.Service.fsproj b/src/Compiler/FSharp.Compiler.Service.fsproj index 29080b76529..b345d6ed1ca 100644 --- a/src/Compiler/FSharp.Compiler.Service.fsproj +++ b/src/Compiler/FSharp.Compiler.Service.fsproj @@ -542,11 +542,8 @@ - - - diff --git a/src/Compiler/Service/FSharpWorkspace.fsi b/src/Compiler/Service/FSharpWorkspace.fsi deleted file mode 100644 index ab06c1eea9f..00000000000 --- a/src/Compiler/Service/FSharpWorkspace.fsi +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. - -namespace FSharp.Compiler.CodeAnalysis.Workspace - -/// This type holds the current state of an F# workspace. It's mutable but thread-safe. -/// It accepts updates to the state and can be queried for information about the workspace. -/// -/// The state can be built up incrementally by adding projects with one of the `Projects.AddOrUpdate` overloads. -/// Updates to any project properties are done the same way. Each project is identified by its project file -/// path and output path or by `FSharpProjectIdentifier`. When the same project is added again, it will be -/// updated with the new information. -/// -/// Project references are discovered automatically as projects are added or updated. -/// -/// Updates to file contents are signaled through the `Files.Open`, `Files.Edit`, and `Files.Close` methods. -[] -type FSharpWorkspace = - - new: unit -> FSharpWorkspace - - new: checker: FSharp.Compiler.CodeAnalysis.FSharpChecker -> FSharpWorkspace - - member internal Debug_DumpMermaid: path: string -> unit - - /// The `FSharpChecker` instance used by this workspace. - member Checker: FSharp.Compiler.CodeAnalysis.FSharpChecker with get - - /// File management for this workspace - member Files: FSharpWorkspaceState.FSharpWorkspaceFiles with get - - /// Project management for this workspace - member Projects: FSharpWorkspaceState.FSharpWorkspaceProjects with get - - /// Use this to query the workspace for information - member Query: FSharpWorkspaceQuery.FSharpWorkspaceQuery with get diff --git a/src/Compiler/Service/FSharpWorkspaceQuery.fsi b/src/Compiler/Service/FSharpWorkspaceQuery.fsi deleted file mode 100644 index c4e7032d4ed..00000000000 --- a/src/Compiler/Service/FSharpWorkspaceQuery.fsi +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. - -module FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery - -open System -open System.Threading.Tasks - -open Internal.Utilities.DependencyGraph - -open FSharp.Compiler.CodeAnalysis -open FSharp.Compiler.CodeAnalysis.ProjectSnapshot -open FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState.WorkspaceGraphTypes -open FSharp.Compiler.Diagnostics -open FSharp.Compiler.EditorServices -open FSharp.Compiler.Text - -[] -type FSharpDiagnosticReport = - - internal new: diagnostics: FSharpDiagnostic array * resultId: int -> FSharpDiagnosticReport - - member Diagnostics: FSharpDiagnostic array with get - - /// The result ID of the diagnostics. This needs to be unique for each version of the document in order to be able to clear old diagnostics. - member ResultId: string with get - -[] -type FSharpWorkspaceQuery = - - internal new: - depGraph: IThreadSafeDependencyGraph * checker: FSharpChecker -> - FSharpWorkspaceQuery - - member GetDiagnosticsForFile: file: Uri -> Async - - member GetProjectSnapshot: projectIdentifier: FSharpProjectIdentifier -> FSharpProjectSnapshot option - - member GetProjectSnapshotForFile: file: Uri -> FSharpProjectSnapshot option - - member GetSemanticClassification: file: Uri -> Async - - member GetSource: file: Uri -> Task diff --git a/src/Compiler/Service/FSharpWorkspaceState.fsi b/src/Compiler/Service/FSharpWorkspaceState.fsi deleted file mode 100644 index 7dedd2b578c..00000000000 --- a/src/Compiler/Service/FSharpWorkspaceState.fsi +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. - -module FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState - -open System -open System.Runtime.CompilerServices - -open Internal.Utilities.DependencyGraph - -open FSharp.Compiler.CodeAnalysis.ProjectSnapshot - -/// Types for the workspace graph. These should not be accessed directly, rather through the -/// extension methods in `WorkspaceDependencyGraphExtensions`. -module internal WorkspaceGraphTypes = - - /// All project information except source files - type ProjectWithoutFiles = ProjectConfig * FSharpReferencedProjectSnapshot list - - [] - type WorkspaceNodeKey = - | SourceFile of filePath: string - | ReferenceOnDisk of filePath: string - - /// All project information except source files and (in-memory) project references - | ProjectConfig of FSharpProjectIdentifier - - /// All project information except source files - | ProjectWithoutFiles of FSharpProjectIdentifier - - /// Complete project information - | ProjectSnapshot of FSharpProjectIdentifier - - override ToString: unit -> string - - [] - type WorkspaceNodeValue = - | SourceFile of FSharpFileSnapshot - | ReferenceOnDisk of ReferenceOnDisk - - /// All project information except source files and (in-memory) project references - | ProjectConfig of ProjectConfig - - /// All project information except source files - | ProjectWithoutFiles of ProjectWithoutFiles - - /// Complete project information - | ProjectSnapshot of FSharpProjectSnapshot - - module WorkspaceNode = - - val projectConfig: value: WorkspaceNodeValue -> ProjectConfig option - - val projectSnapshot: value: WorkspaceNodeValue -> FSharpProjectSnapshot option - - val projectWithoutFiles: - value: WorkspaceNodeValue -> (ProjectConfig * FSharpReferencedProjectSnapshot list) option - - val sourceFile: value: WorkspaceNodeValue -> FSharpFileSnapshot option - - val referenceOnDisk: value: WorkspaceNodeValue -> ReferenceOnDisk option - - val projectConfigKey: value: WorkspaceNodeKey -> FSharpProjectIdentifier option - - val projectSnapshotKey: value: WorkspaceNodeKey -> FSharpProjectIdentifier option - - val projectWithoutFilesKey: value: WorkspaceNodeKey -> FSharpProjectIdentifier option - - val sourceFileKey: value: WorkspaceNodeKey -> string option - - val referenceOnDiskKey: value: WorkspaceNodeKey -> string option - -open WorkspaceGraphTypes - -[] -module internal WorkspaceDependencyGraphExtensions = - - /// This type adds extension methods to the dependency graph to constraint the types and type relations - /// that can be added to the graph. - /// - /// All unsafe operations that can throw at runtime, i.e. unpacking, are done here. - [] - type WorkspaceDependencyGraphTypeExtensions = - - [] - static member AddFiles: - this: IDependencyGraph * files: (string * FSharpFileSnapshot) seq -> - GraphBuilder - - [] - static member AddOrUpdateFile: - this: IDependencyGraph * file: string * snapshot: FSharpFileSnapshot -> - unit - - [] - static member AddProjectConfig: - this: GraphBuilder * - projectIdentifier: FSharpProjectIdentifier * - computeProjectConfig: (ReferenceOnDisk seq -> ProjectConfig) -> - GraphBuilder - - [] - static member AddProjectReference: - this: IDependencyGraph * - project: FSharpProjectIdentifier * - dependsOn: FSharpProjectIdentifier -> - unit - - [] - static member AddProjectSnapshot: - this: - GraphBuilder * - computeProjectSnapshot: (ProjectWithoutFiles * FSharpFileSnapshot seq -> FSharpProjectSnapshot) -> - unit - - [] - static member AddProjectWithoutFiles: - this: - GraphBuilder * - computeProjectWithoutFiles: (ProjectConfig * FSharpProjectSnapshot seq -> ProjectWithoutFiles) -> - GraphBuilder - - [] - static member AddReferencesOnDisk: - this: IDependencyGraph * references: ReferenceOnDisk seq -> - GraphBuilder - - [] - static member AddSourceFiles: - this: GraphBuilder * - sourceFiles: (string * #FSharpFileSnapshot) seq -> - GraphBuilder - - [] - static member GetProjectReferencesOf: - this: IDependencyGraph * project: FSharpProjectIdentifier -> - FSharpProjectIdentifier seq - - [] - static member GetProjectSnapshot: - this: IDependencyGraph * project: FSharpProjectIdentifier -> - FSharpProjectSnapshot - - [] - static member GetProjectsContaining: - this: IDependencyGraph * file: string -> FSharpProjectSnapshot seq - - [] - static member GetProjectsThatReference: - this: IDependencyGraph * dllPath: string -> FSharpProjectIdentifier seq - - [] - static member GetSourceFile: - this: IDependencyGraph * file: string -> FSharpFileSnapshot - - [] - static member RemoveProjectReference: - this: IDependencyGraph * - project: FSharpProjectIdentifier * - noLongerDependsOn: FSharpProjectIdentifier -> - unit - -/// Interface for managing files in an F# workspace. -[] -type FSharpWorkspaceFiles = - - internal new: depGraph: IThreadSafeDependencyGraph -> FSharpWorkspaceFiles - - /// Indicates that a file has been closed. Any changes that were not saved to disk are undone and any further reading - /// of the file's contents will be from the filesystem. - member Close: file: Uri -> unit - - /// Indicates that a file has been changed and now has the given content. If it wasn't previously open it is considered open now. - member Edit: file: Uri * content: string -> unit - - member internal GetFileContentIfOpen: path: string -> string option - - /// Indicates that a file has been opened and has the given content. Any updates to the file should be done through `Files.Edit`. - member Open: (Uri * string -> unit) with get - -/// Interface for managing with projects in an F# workspace. -[] -type FSharpWorkspaceProjects = - - internal new: - depGraph: IThreadSafeDependencyGraph * files: FSharpWorkspaceFiles -> - FSharpWorkspaceProjects - - /// Adds or updates an F# project in the workspace. Project is identified by the project file and output path or FSharpProjectIdentifier. - member AddOrUpdate: projectConfig: ProjectConfig * sourceFilePaths: string seq -> FSharpProjectIdentifier - - member AddOrUpdate: projectPath: string * outputPath: string * compilerArgs: string seq -> FSharpProjectIdentifier - - member AddOrUpdate: - projectFileName: string * - outputFileName: string * - sourceFiles: string seq * - referencesOnDisk: string seq * - otherOptions: string list -> - FSharpProjectIdentifier From 00d5cf239e2e580413eb77c14546e3f6e3afea26 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 20 Nov 2024 12:23:23 +0100 Subject: [PATCH 31/60] update baselines --- src/Compiler/Utilities/DependencyGraph.fs | 18 +---- .../CompilerService/DependencyGraph.fs | 16 ++-- ...ervice.SurfaceArea.netstandard20.debug.bsl | 77 ++++++++++++++++++- ...vice.SurfaceArea.netstandard20.release.bsl | 77 ++++++++++++++++++- 4 files changed, 162 insertions(+), 26 deletions(-) diff --git a/src/Compiler/Utilities/DependencyGraph.fs b/src/Compiler/Utilities/DependencyGraph.fs index 86d09252a14..55eb5e0d746 100644 --- a/src/Compiler/Utilities/DependencyGraph.fs +++ b/src/Compiler/Utilities/DependencyGraph.fs @@ -3,7 +3,7 @@ /// This Dependency Graph provides a way to maintain an up-to-date but lazy set of dependent values. /// When changes are applied to the graph (either vertices change value or edges change), no computation is performed. /// Only when a value is requested it is lazily computed and thereafter stored until invalidated by further changes. -module Internal.Utilities.DependencyGraph +module internal Internal.Utilities.DependencyGraph open System.Collections.Generic @@ -33,7 +33,6 @@ type IDependencyGraph<'Id, 'Val when 'Id: equality> = abstract member RemoveDependency: node: 'Id * noLongerDependsOn: 'Id -> unit abstract member UpdateNode: id: 'Id * update: ('Val -> 'Val) -> unit abstract member RemoveNode: id: 'Id -> unit - abstract member Debug_GetNodes: ('Id -> bool) -> DependencyNode<'Id, 'Val> seq abstract member Debug_RenderMermaid: ?mapping: ('Id -> 'Id) -> string abstract member OnWarning: (string -> unit) -> unit @@ -66,12 +65,7 @@ module Internal = nodes |> insert node.Id node invalidateDependents node.Id - member _.Debug = - {| - Nodes = nodes - Dependencies = dependencies - Dependents = dependents - |} + member _.Debug_Nodes = nodes member _.AddOrUpdateNode(id: 'Id, value: 'Val) = addNode @@ -199,9 +193,6 @@ module Internal = | false, _ -> () | false, _ -> () - member this.Debug_GetNodes(predicate: 'Id -> bool) : DependencyNode<'Id, 'Val> seq = - nodes.Values |> Seq.filter (fun node -> predicate node.Id) - member _.Debug_RenderMermaid(?mapping) = let mapping = defaultArg mapping id @@ -233,8 +224,6 @@ module Internal = interface IDependencyGraph<'Id, 'Val> with - member this.Debug_GetNodes(predicate) = self.Debug_GetNodes(predicate) - member _.AddOrUpdateNode(id, value) = self.AddOrUpdateNode(id, value) member _.AddList(nodes) = self.AddList(nodes) @@ -321,9 +310,6 @@ type LockOperatedDependencyGraph<'Id, 'Val when 'Id: equality and 'Id: not null> member _.OnWarning(f) = lock lockObj (fun () -> graph.OnWarning f) - member _.Debug_GetNodes(predicate) = - lock lockObj (fun () -> graph.Debug_GetNodes(predicate)) - member _.Debug_RenderMermaid(m) = lock lockObj (fun () -> graph.Debug_RenderMermaid(?mapping = m)) diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/DependencyGraph.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/DependencyGraph.fs index e7f3d83d0fa..514a8e3499f 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/DependencyGraph.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/DependencyGraph.fs @@ -42,14 +42,14 @@ let ``Can update a value`` () = graph.AddOrUpdateNode(1, 2) |> ignore // Values were invalidated - Assert.Equal(None, graph.Debug.Nodes[2].Value) - Assert.Equal(None, graph.Debug.Nodes[3].Value) - Assert.Equal(None, graph.Debug.Nodes[4].Value) + Assert.Equal(None, graph.Debug_Nodes[2].Value) + Assert.Equal(None, graph.Debug_Nodes[3].Value) + Assert.Equal(None, graph.Debug_Nodes[4].Value) Assert.Equal(7, graph.GetValue(4)) - Assert.Equal(Some 3, graph.Debug.Nodes[2].Value) - Assert.Equal(Some 4, graph.Debug.Nodes[3].Value) - Assert.Equal(Some 7, graph.Debug.Nodes[4].Value) + Assert.Equal(Some 3, graph.Debug_Nodes[2].Value) + Assert.Equal(Some 4, graph.Debug_Nodes[3].Value) + Assert.Equal(Some 7, graph.Debug_Nodes[4].Value) [] let ``Dependencies are ordered`` () = @@ -154,8 +154,8 @@ let ``GraphBuilder with chained AddDependentNode calls`` () = builder.Graph.AddOrUpdateNode(1, CaseA 2) |> ignore // Values were invalidated - Assert.Equal(None, graph.Debug.Nodes[2].Value) - Assert.Equal(None, graph.Debug.Nodes[3].Value) + Assert.Equal(None, graph.Debug_Nodes[2].Value) + Assert.Equal(None, graph.Debug_Nodes[3].Value) // Check new values Assert.Equal(CaseB "2", graph.GetValue(2)) diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl index 58788fc8fe0..b66bebd1cbc 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl @@ -2414,6 +2414,7 @@ FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: Boolean get_IsS FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: FSharpFileSnapshot Create(System.String, System.String, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceTextNew]]) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: FSharpFileSnapshot CreateFromDocumentSource(System.String, FSharp.Compiler.CodeAnalysis.DocumentSource) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: FSharpFileSnapshot CreateFromFileSystem(System.String) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: FSharpFileSnapshot CreateFromString(System.String, System.String) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: Int32 GetHashCode() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.String FileName FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.String GetFileName() @@ -2434,7 +2435,11 @@ FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 GetH FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 GetHashCode(System.Collections.IEqualityComparer) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 Tag FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 get_Tag() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String OutputFileName +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String ProjectFileName FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String ToString() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String get_OutputFileName() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String get_ProjectFileName() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String get_outputFileName() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String get_projectFileName() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String outputFileName @@ -2445,7 +2450,7 @@ FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Boolean get_ FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Boolean get_UseScriptResolutionRules() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectIdentifier Identifier FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectIdentifier get_Identifier() -FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot Create(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk], Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot], Boolean, Boolean, System.DateTime, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet], Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]], Microsoft.FSharp.Core.FSharpOption`1[System.Int64]) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot Create(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk], Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot], Boolean, Boolean, System.DateTime, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet], Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]], Microsoft.FSharp.Core.FSharpOption`1[System.Int64]) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot Replace(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot]) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot] SourceFiles FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot] get_SourceFiles() @@ -2464,7 +2469,9 @@ FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FS FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet] get_UnresolvedReferences() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.Int64] Stamp FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.Int64] get_Stamp() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.String] OutputFileName FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.String] ProjectId +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.String] get_OutputFileName() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.String] get_ProjectId() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: System.DateTime LoadTime FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: System.DateTime get_LoadTime() @@ -2496,6 +2503,8 @@ FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Bo FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Boolean get_IsFSharpReference() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Boolean get_IsILModuleReference() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Boolean get_IsPEReference() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Byte[] Version +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Byte[] get_Version() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+FSharpReference FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+ILModuleReference FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+PEReference @@ -2510,6 +2519,41 @@ FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: In FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: System.String OutputFile FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: System.String ToString() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: System.String get_OutputFile() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Boolean IsIncompleteTypeCheckEnvironment +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Boolean UseScriptResolutionRules +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Boolean get_IsIncompleteTypeCheckEnvironment() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Boolean get_UseScriptResolutionRules() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Byte[] Version +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Byte[] VersionForParsing +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Byte[] get_Version() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Byte[] get_VersionForParsing() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: FSharpProjectIdentifier Identifier +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: FSharpProjectIdentifier get_Identifier() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk] ReferencesOnDisk +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk] get_ReferencesOnDisk() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Collections.FSharpList`1[System.String] CommandLineOptions +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Collections.FSharpList`1[System.String] OtherOptions +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Collections.FSharpList`1[System.String] get_CommandLineOptions() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Collections.FSharpList`1[System.String] get_OtherOptions() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]] OriginalLoadReferences +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]] get_OriginalLoadReferences() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet] UnresolvedReferences +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet] get_UnresolvedReferences() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Core.FSharpOption`1[System.Int64] Stamp +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Core.FSharpOption`1[System.Int64] get_Stamp() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Core.FSharpOption`1[System.String] OutputFileName +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Core.FSharpOption`1[System.String] ProjectId +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Core.FSharpOption`1[System.String] get_OutputFileName() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Core.FSharpOption`1[System.String] get_ProjectId() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: System.DateTime LoadTime +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: System.DateTime get_LoadTime() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: System.String Label +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: System.String ProjectDirectory +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: System.String ProjectFileName +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: System.String get_Label() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: System.String get_ProjectDirectory() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: System.String get_ProjectFileName() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Void .ctor(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.String], System.Collections.Generic.IEnumerable`1[System.String], System.Collections.Generic.IEnumerable`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.DateTime], Microsoft.FSharp.Core.FSharpOption`1[System.Int64], Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Boolean Equals(ReferenceOnDisk) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Boolean Equals(ReferenceOnDisk, System.Collections.IEqualityComparer) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Boolean Equals(System.Object) @@ -2529,7 +2573,38 @@ FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.Proje FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot +FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspace: FSharp.Compiler.CodeAnalysis.FSharpChecker Checker +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspace: FSharp.Compiler.CodeAnalysis.FSharpChecker get_Checker() +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspace: FSharpWorkspaceFiles Files +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspace: FSharpWorkspaceFiles get_Files() +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspace: FSharpWorkspaceProjects Projects +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspace: FSharpWorkspaceProjects get_Projects() +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspace: FSharpWorkspaceQuery Query +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspace: FSharpWorkspaceQuery get_Query() +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspace: Void .ctor() +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspace: Void .ctor(FSharp.Compiler.CodeAnalysis.FSharpChecker) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpDiagnosticReport: FSharp.Compiler.Diagnostics.FSharpDiagnostic[] Diagnostics +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpDiagnosticReport: FSharp.Compiler.Diagnostics.FSharpDiagnostic[] get_Diagnostics() +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpDiagnosticReport: System.String ResultId +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpDiagnosticReport: System.String get_ResultId() +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpWorkspaceQuery: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpDiagnosticReport] GetDiagnosticsForFile(System.Uri) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpWorkspaceQuery: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.EditorServices.SemanticClassificationView]] GetSemanticClassification(System.Uri) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpWorkspaceQuery: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] GetProjectSnapshot(FSharpProjectIdentifier) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpWorkspaceQuery: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] GetProjectSnapshotForFile(System.Uri) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpWorkspaceQuery: System.Threading.Tasks.Task`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.ISourceTextNew]] GetSource(System.Uri) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery: FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpDiagnosticReport +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery: FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpWorkspaceQuery +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceFiles: Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.Uri,System.String],Microsoft.FSharp.Core.Unit] Open +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceFiles: Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.Uri,System.String],Microsoft.FSharp.Core.Unit] get_Open() +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceFiles: Void Close(System.Uri) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceFiles: Void Edit(System.Uri, System.String) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceProjects: FSharpProjectIdentifier AddOrUpdate(ProjectConfig, System.Collections.Generic.IEnumerable`1[System.String]) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceProjects: FSharpProjectIdentifier AddOrUpdate(System.String, System.String, System.Collections.Generic.IEnumerable`1[System.String]) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceProjects: FSharpProjectIdentifier AddOrUpdate(System.String, System.String, System.Collections.Generic.IEnumerable`1[System.String], System.Collections.Generic.IEnumerable`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[System.String]) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState: FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceFiles +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState: FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceProjects FSharp.Compiler.CompilerEnvironment: Boolean IsCheckerSupportedSubcategory(System.String) FSharp.Compiler.CompilerEnvironment: Boolean IsCompilable(System.String) FSharp.Compiler.CompilerEnvironment: Boolean IsScriptFile(System.String) diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl index 58788fc8fe0..b66bebd1cbc 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl @@ -2414,6 +2414,7 @@ FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: Boolean get_IsS FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: FSharpFileSnapshot Create(System.String, System.String, Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Threading.Tasks.Task`1[FSharp.Compiler.Text.ISourceTextNew]]) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: FSharpFileSnapshot CreateFromDocumentSource(System.String, FSharp.Compiler.CodeAnalysis.DocumentSource) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: FSharpFileSnapshot CreateFromFileSystem(System.String) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: FSharpFileSnapshot CreateFromString(System.String, System.String) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: Int32 GetHashCode() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.String FileName FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot: System.String GetFileName() @@ -2434,7 +2435,11 @@ FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 GetH FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 GetHashCode(System.Collections.IEqualityComparer) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 Tag FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: Int32 get_Tag() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String OutputFileName +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String ProjectFileName FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String ToString() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String get_OutputFileName() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String get_ProjectFileName() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String get_outputFileName() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String get_projectFileName() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier: System.String outputFileName @@ -2445,7 +2450,7 @@ FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Boolean get_ FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Boolean get_UseScriptResolutionRules() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectIdentifier Identifier FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectIdentifier get_Identifier() -FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot Create(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk], Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot], Boolean, Boolean, System.DateTime, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet], Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]], Microsoft.FSharp.Core.FSharpOption`1[System.Int64]) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot Create(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk], Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot], Boolean, Boolean, System.DateTime, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet], Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]], Microsoft.FSharp.Core.FSharpOption`1[System.Int64]) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot Replace(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot]) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot] SourceFiles FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot] get_SourceFiles() @@ -2464,7 +2469,9 @@ FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FS FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet] get_UnresolvedReferences() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.Int64] Stamp FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.Int64] get_Stamp() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.String] OutputFileName FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.String] ProjectId +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.String] get_OutputFileName() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.String] get_ProjectId() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: System.DateTime LoadTime FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: System.DateTime get_LoadTime() @@ -2496,6 +2503,8 @@ FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Bo FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Boolean get_IsFSharpReference() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Boolean get_IsILModuleReference() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Boolean get_IsPEReference() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Byte[] Version +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: Byte[] get_Version() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+FSharpReference FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+ILModuleReference FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot+PEReference @@ -2510,6 +2519,41 @@ FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: In FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: System.String OutputFile FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: System.String ToString() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot: System.String get_OutputFile() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Boolean IsIncompleteTypeCheckEnvironment +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Boolean UseScriptResolutionRules +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Boolean get_IsIncompleteTypeCheckEnvironment() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Boolean get_UseScriptResolutionRules() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Byte[] Version +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Byte[] VersionForParsing +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Byte[] get_Version() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Byte[] get_VersionForParsing() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: FSharpProjectIdentifier Identifier +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: FSharpProjectIdentifier get_Identifier() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk] ReferencesOnDisk +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk] get_ReferencesOnDisk() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Collections.FSharpList`1[System.String] CommandLineOptions +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Collections.FSharpList`1[System.String] OtherOptions +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Collections.FSharpList`1[System.String] get_CommandLineOptions() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Collections.FSharpList`1[System.String] get_OtherOptions() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]] OriginalLoadReferences +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]] get_OriginalLoadReferences() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet] UnresolvedReferences +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet] get_UnresolvedReferences() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Core.FSharpOption`1[System.Int64] Stamp +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Core.FSharpOption`1[System.Int64] get_Stamp() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Core.FSharpOption`1[System.String] OutputFileName +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Core.FSharpOption`1[System.String] ProjectId +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Core.FSharpOption`1[System.String] get_OutputFileName() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Microsoft.FSharp.Core.FSharpOption`1[System.String] get_ProjectId() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: System.DateTime LoadTime +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: System.DateTime get_LoadTime() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: System.String Label +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: System.String ProjectDirectory +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: System.String ProjectFileName +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: System.String get_Label() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: System.String get_ProjectDirectory() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: System.String get_ProjectFileName() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig: Void .ctor(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.String], System.Collections.Generic.IEnumerable`1[System.String], System.Collections.Generic.IEnumerable`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.Boolean], Microsoft.FSharp.Core.FSharpOption`1[System.DateTime], Microsoft.FSharp.Core.FSharpOption`1[System.Int64], Microsoft.FSharp.Core.FSharpOption`1[System.String]) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Boolean Equals(ReferenceOnDisk) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Boolean Equals(ReferenceOnDisk, System.Collections.IEqualityComparer) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk: Boolean Equals(System.Object) @@ -2529,7 +2573,38 @@ FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.Proje FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectIdentifier FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot +FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ProjectConfig FSharp.Compiler.CodeAnalysis.ProjectSnapshot: FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspace: FSharp.Compiler.CodeAnalysis.FSharpChecker Checker +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspace: FSharp.Compiler.CodeAnalysis.FSharpChecker get_Checker() +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspace: FSharpWorkspaceFiles Files +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspace: FSharpWorkspaceFiles get_Files() +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspace: FSharpWorkspaceProjects Projects +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspace: FSharpWorkspaceProjects get_Projects() +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspace: FSharpWorkspaceQuery Query +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspace: FSharpWorkspaceQuery get_Query() +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspace: Void .ctor() +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspace: Void .ctor(FSharp.Compiler.CodeAnalysis.FSharpChecker) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpDiagnosticReport: FSharp.Compiler.Diagnostics.FSharpDiagnostic[] Diagnostics +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpDiagnosticReport: FSharp.Compiler.Diagnostics.FSharpDiagnostic[] get_Diagnostics() +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpDiagnosticReport: System.String ResultId +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpDiagnosticReport: System.String get_ResultId() +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpWorkspaceQuery: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpDiagnosticReport] GetDiagnosticsForFile(System.Uri) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpWorkspaceQuery: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.EditorServices.SemanticClassificationView]] GetSemanticClassification(System.Uri) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpWorkspaceQuery: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] GetProjectSnapshot(FSharpProjectIdentifier) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpWorkspaceQuery: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] GetProjectSnapshotForFile(System.Uri) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpWorkspaceQuery: System.Threading.Tasks.Task`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.ISourceTextNew]] GetSource(System.Uri) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery: FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpDiagnosticReport +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery: FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpWorkspaceQuery +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceFiles: Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.Uri,System.String],Microsoft.FSharp.Core.Unit] Open +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceFiles: Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.Uri,System.String],Microsoft.FSharp.Core.Unit] get_Open() +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceFiles: Void Close(System.Uri) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceFiles: Void Edit(System.Uri, System.String) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceProjects: FSharpProjectIdentifier AddOrUpdate(ProjectConfig, System.Collections.Generic.IEnumerable`1[System.String]) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceProjects: FSharpProjectIdentifier AddOrUpdate(System.String, System.String, System.Collections.Generic.IEnumerable`1[System.String]) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceProjects: FSharpProjectIdentifier AddOrUpdate(System.String, System.String, System.Collections.Generic.IEnumerable`1[System.String], System.Collections.Generic.IEnumerable`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[System.String]) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState: FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceFiles +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState: FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceProjects FSharp.Compiler.CompilerEnvironment: Boolean IsCheckerSupportedSubcategory(System.String) FSharp.Compiler.CompilerEnvironment: Boolean IsCompilable(System.String) FSharp.Compiler.CompilerEnvironment: Boolean IsScriptFile(System.String) From aee81fa9f3badf515add23e1ea94b4b162d8a688 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 20 Nov 2024 12:23:03 +0100 Subject: [PATCH 32/60] Remove FSharpWorkspace signatures --- src/Compiler/FSharp.Compiler.Service.fsproj | 2 - src/Compiler/Service/FSharpWorkspace.fsi | 35 --- src/Compiler/Service/FSharpWorkspaceQuery.fsi | 42 ---- src/Compiler/Service/FSharpWorkspaceState.fsi | 201 ------------------ 4 files changed, 280 deletions(-) delete mode 100644 src/Compiler/Service/FSharpWorkspace.fsi delete mode 100644 src/Compiler/Service/FSharpWorkspaceQuery.fsi delete mode 100644 src/Compiler/Service/FSharpWorkspaceState.fsi diff --git a/src/Compiler/FSharp.Compiler.Service.fsproj b/src/Compiler/FSharp.Compiler.Service.fsproj index 3a9d0e6bc93..292744c2f9c 100644 --- a/src/Compiler/FSharp.Compiler.Service.fsproj +++ b/src/Compiler/FSharp.Compiler.Service.fsproj @@ -544,9 +544,7 @@ - - diff --git a/src/Compiler/Service/FSharpWorkspace.fsi b/src/Compiler/Service/FSharpWorkspace.fsi deleted file mode 100644 index ab06c1eea9f..00000000000 --- a/src/Compiler/Service/FSharpWorkspace.fsi +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. - -namespace FSharp.Compiler.CodeAnalysis.Workspace - -/// This type holds the current state of an F# workspace. It's mutable but thread-safe. -/// It accepts updates to the state and can be queried for information about the workspace. -/// -/// The state can be built up incrementally by adding projects with one of the `Projects.AddOrUpdate` overloads. -/// Updates to any project properties are done the same way. Each project is identified by its project file -/// path and output path or by `FSharpProjectIdentifier`. When the same project is added again, it will be -/// updated with the new information. -/// -/// Project references are discovered automatically as projects are added or updated. -/// -/// Updates to file contents are signaled through the `Files.Open`, `Files.Edit`, and `Files.Close` methods. -[] -type FSharpWorkspace = - - new: unit -> FSharpWorkspace - - new: checker: FSharp.Compiler.CodeAnalysis.FSharpChecker -> FSharpWorkspace - - member internal Debug_DumpMermaid: path: string -> unit - - /// The `FSharpChecker` instance used by this workspace. - member Checker: FSharp.Compiler.CodeAnalysis.FSharpChecker with get - - /// File management for this workspace - member Files: FSharpWorkspaceState.FSharpWorkspaceFiles with get - - /// Project management for this workspace - member Projects: FSharpWorkspaceState.FSharpWorkspaceProjects with get - - /// Use this to query the workspace for information - member Query: FSharpWorkspaceQuery.FSharpWorkspaceQuery with get diff --git a/src/Compiler/Service/FSharpWorkspaceQuery.fsi b/src/Compiler/Service/FSharpWorkspaceQuery.fsi deleted file mode 100644 index c4e7032d4ed..00000000000 --- a/src/Compiler/Service/FSharpWorkspaceQuery.fsi +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. - -module FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery - -open System -open System.Threading.Tasks - -open Internal.Utilities.DependencyGraph - -open FSharp.Compiler.CodeAnalysis -open FSharp.Compiler.CodeAnalysis.ProjectSnapshot -open FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState.WorkspaceGraphTypes -open FSharp.Compiler.Diagnostics -open FSharp.Compiler.EditorServices -open FSharp.Compiler.Text - -[] -type FSharpDiagnosticReport = - - internal new: diagnostics: FSharpDiagnostic array * resultId: int -> FSharpDiagnosticReport - - member Diagnostics: FSharpDiagnostic array with get - - /// The result ID of the diagnostics. This needs to be unique for each version of the document in order to be able to clear old diagnostics. - member ResultId: string with get - -[] -type FSharpWorkspaceQuery = - - internal new: - depGraph: IThreadSafeDependencyGraph * checker: FSharpChecker -> - FSharpWorkspaceQuery - - member GetDiagnosticsForFile: file: Uri -> Async - - member GetProjectSnapshot: projectIdentifier: FSharpProjectIdentifier -> FSharpProjectSnapshot option - - member GetProjectSnapshotForFile: file: Uri -> FSharpProjectSnapshot option - - member GetSemanticClassification: file: Uri -> Async - - member GetSource: file: Uri -> Task diff --git a/src/Compiler/Service/FSharpWorkspaceState.fsi b/src/Compiler/Service/FSharpWorkspaceState.fsi deleted file mode 100644 index 7dedd2b578c..00000000000 --- a/src/Compiler/Service/FSharpWorkspaceState.fsi +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. - -module FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState - -open System -open System.Runtime.CompilerServices - -open Internal.Utilities.DependencyGraph - -open FSharp.Compiler.CodeAnalysis.ProjectSnapshot - -/// Types for the workspace graph. These should not be accessed directly, rather through the -/// extension methods in `WorkspaceDependencyGraphExtensions`. -module internal WorkspaceGraphTypes = - - /// All project information except source files - type ProjectWithoutFiles = ProjectConfig * FSharpReferencedProjectSnapshot list - - [] - type WorkspaceNodeKey = - | SourceFile of filePath: string - | ReferenceOnDisk of filePath: string - - /// All project information except source files and (in-memory) project references - | ProjectConfig of FSharpProjectIdentifier - - /// All project information except source files - | ProjectWithoutFiles of FSharpProjectIdentifier - - /// Complete project information - | ProjectSnapshot of FSharpProjectIdentifier - - override ToString: unit -> string - - [] - type WorkspaceNodeValue = - | SourceFile of FSharpFileSnapshot - | ReferenceOnDisk of ReferenceOnDisk - - /// All project information except source files and (in-memory) project references - | ProjectConfig of ProjectConfig - - /// All project information except source files - | ProjectWithoutFiles of ProjectWithoutFiles - - /// Complete project information - | ProjectSnapshot of FSharpProjectSnapshot - - module WorkspaceNode = - - val projectConfig: value: WorkspaceNodeValue -> ProjectConfig option - - val projectSnapshot: value: WorkspaceNodeValue -> FSharpProjectSnapshot option - - val projectWithoutFiles: - value: WorkspaceNodeValue -> (ProjectConfig * FSharpReferencedProjectSnapshot list) option - - val sourceFile: value: WorkspaceNodeValue -> FSharpFileSnapshot option - - val referenceOnDisk: value: WorkspaceNodeValue -> ReferenceOnDisk option - - val projectConfigKey: value: WorkspaceNodeKey -> FSharpProjectIdentifier option - - val projectSnapshotKey: value: WorkspaceNodeKey -> FSharpProjectIdentifier option - - val projectWithoutFilesKey: value: WorkspaceNodeKey -> FSharpProjectIdentifier option - - val sourceFileKey: value: WorkspaceNodeKey -> string option - - val referenceOnDiskKey: value: WorkspaceNodeKey -> string option - -open WorkspaceGraphTypes - -[] -module internal WorkspaceDependencyGraphExtensions = - - /// This type adds extension methods to the dependency graph to constraint the types and type relations - /// that can be added to the graph. - /// - /// All unsafe operations that can throw at runtime, i.e. unpacking, are done here. - [] - type WorkspaceDependencyGraphTypeExtensions = - - [] - static member AddFiles: - this: IDependencyGraph * files: (string * FSharpFileSnapshot) seq -> - GraphBuilder - - [] - static member AddOrUpdateFile: - this: IDependencyGraph * file: string * snapshot: FSharpFileSnapshot -> - unit - - [] - static member AddProjectConfig: - this: GraphBuilder * - projectIdentifier: FSharpProjectIdentifier * - computeProjectConfig: (ReferenceOnDisk seq -> ProjectConfig) -> - GraphBuilder - - [] - static member AddProjectReference: - this: IDependencyGraph * - project: FSharpProjectIdentifier * - dependsOn: FSharpProjectIdentifier -> - unit - - [] - static member AddProjectSnapshot: - this: - GraphBuilder * - computeProjectSnapshot: (ProjectWithoutFiles * FSharpFileSnapshot seq -> FSharpProjectSnapshot) -> - unit - - [] - static member AddProjectWithoutFiles: - this: - GraphBuilder * - computeProjectWithoutFiles: (ProjectConfig * FSharpProjectSnapshot seq -> ProjectWithoutFiles) -> - GraphBuilder - - [] - static member AddReferencesOnDisk: - this: IDependencyGraph * references: ReferenceOnDisk seq -> - GraphBuilder - - [] - static member AddSourceFiles: - this: GraphBuilder * - sourceFiles: (string * #FSharpFileSnapshot) seq -> - GraphBuilder - - [] - static member GetProjectReferencesOf: - this: IDependencyGraph * project: FSharpProjectIdentifier -> - FSharpProjectIdentifier seq - - [] - static member GetProjectSnapshot: - this: IDependencyGraph * project: FSharpProjectIdentifier -> - FSharpProjectSnapshot - - [] - static member GetProjectsContaining: - this: IDependencyGraph * file: string -> FSharpProjectSnapshot seq - - [] - static member GetProjectsThatReference: - this: IDependencyGraph * dllPath: string -> FSharpProjectIdentifier seq - - [] - static member GetSourceFile: - this: IDependencyGraph * file: string -> FSharpFileSnapshot - - [] - static member RemoveProjectReference: - this: IDependencyGraph * - project: FSharpProjectIdentifier * - noLongerDependsOn: FSharpProjectIdentifier -> - unit - -/// Interface for managing files in an F# workspace. -[] -type FSharpWorkspaceFiles = - - internal new: depGraph: IThreadSafeDependencyGraph -> FSharpWorkspaceFiles - - /// Indicates that a file has been closed. Any changes that were not saved to disk are undone and any further reading - /// of the file's contents will be from the filesystem. - member Close: file: Uri -> unit - - /// Indicates that a file has been changed and now has the given content. If it wasn't previously open it is considered open now. - member Edit: file: Uri * content: string -> unit - - member internal GetFileContentIfOpen: path: string -> string option - - /// Indicates that a file has been opened and has the given content. Any updates to the file should be done through `Files.Edit`. - member Open: (Uri * string -> unit) with get - -/// Interface for managing with projects in an F# workspace. -[] -type FSharpWorkspaceProjects = - - internal new: - depGraph: IThreadSafeDependencyGraph * files: FSharpWorkspaceFiles -> - FSharpWorkspaceProjects - - /// Adds or updates an F# project in the workspace. Project is identified by the project file and output path or FSharpProjectIdentifier. - member AddOrUpdate: projectConfig: ProjectConfig * sourceFilePaths: string seq -> FSharpProjectIdentifier - - member AddOrUpdate: projectPath: string * outputPath: string * compilerArgs: string seq -> FSharpProjectIdentifier - - member AddOrUpdate: - projectFileName: string * - outputFileName: string * - sourceFiles: string seq * - referencesOnDisk: string seq * - otherOptions: string list -> - FSharpProjectIdentifier From 56fb7025a9830e658b3e9a305672a64ebabdbd6c Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 20 Nov 2024 12:51:12 +0100 Subject: [PATCH 33/60] merge --- src/Compiler/FSharp.Compiler.Service.fsproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Compiler/FSharp.Compiler.Service.fsproj b/src/Compiler/FSharp.Compiler.Service.fsproj index 292744c2f9c..13b343af2d4 100644 --- a/src/Compiler/FSharp.Compiler.Service.fsproj +++ b/src/Compiler/FSharp.Compiler.Service.fsproj @@ -546,7 +546,6 @@ - From 9507a851fbb8f49c4f1cb622dc590a1548455dec Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 20 Nov 2024 12:57:33 +0100 Subject: [PATCH 34/60] mergefix --- src/Compiler/FSharp.Compiler.Service.fsproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Compiler/FSharp.Compiler.Service.fsproj b/src/Compiler/FSharp.Compiler.Service.fsproj index 13b343af2d4..911429405e8 100644 --- a/src/Compiler/FSharp.Compiler.Service.fsproj +++ b/src/Compiler/FSharp.Compiler.Service.fsproj @@ -546,8 +546,6 @@ - - From 90b8bee44d052a64f426296c4535e56b2f0ac84a Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 20 Nov 2024 12:59:53 +0100 Subject: [PATCH 35/60] baselines --- ...ervice.SurfaceArea.netstandard20.debug.bsl | 78 ------------------- ...vice.SurfaceArea.netstandard20.release.bsl | 78 ------------------- 2 files changed, 156 deletions(-) diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl index aea7c4724ee..b66bebd1cbc 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl @@ -7,23 +7,6 @@ ! AssemblyReference: System.Reflection.Emit.ILGeneration ! AssemblyReference: System.Reflection.Metadata ! AssemblyReference: netstandard -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: j__TPar Dependencies -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: j__TPar get_Dependencies() -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: j__TPar Dependents -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: j__TPar get_Dependents() -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: j__TPar Nodes -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: j__TPar get_Nodes() -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: Boolean Equals(<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]) -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: Boolean Equals(<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar], System.Collections.IEqualityComparer) -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: Boolean Equals(System.Object) -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: Boolean Equals(System.Object, System.Collections.IEqualityComparer) -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: Int32 CompareTo(<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]) -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: Int32 CompareTo(System.Object) -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: Int32 CompareTo(System.Object, System.Collections.IComparer) -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: Int32 GetHashCode() -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: Int32 GetHashCode(System.Collections.IEqualityComparer) -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: System.String ToString() -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: Void .ctor(j__TPar, j__TPar, j__TPar) FSharp.Compiler.AbstractIL.IL+ILArgConvention+Tags: Int32 CDecl FSharp.Compiler.AbstractIL.IL+ILArgConvention+Tags: Int32 Default FSharp.Compiler.AbstractIL.IL+ILArgConvention+Tags: Int32 FastCall @@ -12349,67 +12332,6 @@ FSharp.Compiler.Xml.XmlDoc: System.String[] GetElaboratedXmlLines() FSharp.Compiler.Xml.XmlDoc: System.String[] UnprocessedLines FSharp.Compiler.Xml.XmlDoc: System.String[] get_UnprocessedLines() FSharp.Compiler.Xml.XmlDoc: Void .ctor(System.String[], FSharp.Compiler.Text.Range) -Internal.Utilities.DependencyGraph+DependencyNode`2[Identifier,Value]: Identifier Id -Internal.Utilities.DependencyGraph+DependencyNode`2[Identifier,Value]: Identifier get_Id() -Internal.Utilities.DependencyGraph+DependencyNode`2[Identifier,Value]: Microsoft.FSharp.Core.FSharpFunc`2[System.Collections.Generic.IEnumerable`1[Value],Value] Compute -Internal.Utilities.DependencyGraph+DependencyNode`2[Identifier,Value]: Microsoft.FSharp.Core.FSharpFunc`2[System.Collections.Generic.IEnumerable`1[Value],Value] get_Compute() -Internal.Utilities.DependencyGraph+DependencyNode`2[Identifier,Value]: Microsoft.FSharp.Core.FSharpOption`1[Value] Value -Internal.Utilities.DependencyGraph+DependencyNode`2[Identifier,Value]: Microsoft.FSharp.Core.FSharpOption`1[Value] get_Value() -Internal.Utilities.DependencyGraph+DependencyNode`2[Identifier,Value]: System.String ToString() -Internal.Utilities.DependencyGraph+DependencyNode`2[Identifier,Value]: Void .ctor(Identifier, Microsoft.FSharp.Core.FSharpOption`1[Value], Microsoft.FSharp.Core.FSharpFunc`2[System.Collections.Generic.IEnumerable`1[Value],Value]) -Internal.Utilities.DependencyGraph+GraphBuilder`4[Id,Val,T,State]: GraphBuilder`4 AddDependentNode[a,b](Id, Microsoft.FSharp.Core.FSharpFunc`2[T,Val], Microsoft.FSharp.Core.FSharpFunc`2[System.Collections.Generic.IEnumerable`1[Val],a], b) -Internal.Utilities.DependencyGraph+GraphBuilder`4[Id,Val,T,State]: GraphBuilder`4 AddDependentNode[a](Id, Microsoft.FSharp.Core.FSharpFunc`2[T,Val], Microsoft.FSharp.Core.FSharpFunc`2[System.Collections.Generic.IEnumerable`1[Val],a]) -Internal.Utilities.DependencyGraph+GraphBuilder`4[Id,Val,T,State]: IDependencyGraph`2 Graph -Internal.Utilities.DependencyGraph+GraphBuilder`4[Id,Val,T,State]: IDependencyGraph`2 get_Graph() -Internal.Utilities.DependencyGraph+GraphBuilder`4[Id,Val,T,State]: State State -Internal.Utilities.DependencyGraph+GraphBuilder`4[Id,Val,T,State]: State get_State() -Internal.Utilities.DependencyGraph+GraphBuilder`4[Id,Val,T,State]: System.Collections.Generic.IEnumerable`1[Id] Ids -Internal.Utilities.DependencyGraph+GraphBuilder`4[Id,Val,T,State]: System.Collections.Generic.IEnumerable`1[Id] get_Ids() -Internal.Utilities.DependencyGraph+GraphBuilder`4[Id,Val,T,State]: Void .ctor(IDependencyGraph`2, System.Collections.Generic.IEnumerable`1[Id], Microsoft.FSharp.Core.FSharpFunc`2[System.Collections.Generic.IEnumerable`1[Val],T], State) -Internal.Utilities.DependencyGraph+GraphExtensions: System.Collections.Generic.IEnumerable`1[a] UnpackMany[NodeValue,a](System.Collections.Generic.IEnumerable`1[NodeValue], Microsoft.FSharp.Core.FSharpFunc`2[NodeValue,Microsoft.FSharp.Core.FSharpOption`1[a]]) -Internal.Utilities.DependencyGraph+GraphExtensions: System.Tuple`2[a,System.Collections.Generic.IEnumerable`1[b]] UnpackOneMany[NodeValue,a,b](System.Collections.Generic.IEnumerable`1[NodeValue], Microsoft.FSharp.Core.FSharpFunc`2[NodeValue,Microsoft.FSharp.Core.FSharpOption`1[a]], Microsoft.FSharp.Core.FSharpFunc`2[NodeValue,Microsoft.FSharp.Core.FSharpOption`1[b]]) -Internal.Utilities.DependencyGraph+GraphExtensions: UnpackedDependency UnpackOne[NodeValue,UnpackedDependency](System.Collections.Generic.IEnumerable`1[NodeValue], Microsoft.FSharp.Core.FSharpFunc`2[NodeValue,Microsoft.FSharp.Core.FSharpOption`1[UnpackedDependency]]) -Internal.Utilities.DependencyGraph+GraphExtensions: a Unpack[NodeValue,a](NodeValue, Microsoft.FSharp.Core.FSharpFunc`2[NodeValue,Microsoft.FSharp.Core.FSharpOption`1[a]]) -Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val]: System.Collections.Generic.IEnumerable`1[Id] AddList(System.Collections.Generic.IEnumerable`1[System.Tuple`2[Id,Val]]) -Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val]: System.Collections.Generic.IEnumerable`1[Id] GetDependenciesOf(Id) -Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val]: System.Collections.Generic.IEnumerable`1[Id] GetDependentsOf(Id) -Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val]: System.Collections.Generic.IEnumerable`1[Internal.Utilities.DependencyGraph+DependencyNode`2[Id,Val]] Debug_GetNodes(Microsoft.FSharp.Core.FSharpFunc`2[Id,System.Boolean]) -Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val]: System.String Debug_RenderMermaid(Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[Id,Id]]) -Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val]: Val GetValue(Id) -Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val]: Void AddDependency(Id, Id) -Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val]: Void AddOrUpdateNode(Id, System.Collections.Generic.IEnumerable`1[Id], Microsoft.FSharp.Core.FSharpFunc`2[System.Collections.Generic.IEnumerable`1[Val],Val]) -Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val]: Void AddOrUpdateNode(Id, Val) -Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val]: Void OnWarning(Microsoft.FSharp.Core.FSharpFunc`2[System.String,Microsoft.FSharp.Core.Unit]) -Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val]: Void RemoveDependency(Id, Id) -Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val]: Void RemoveNode(Id) -Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val]: Void UpdateNode(Id, Microsoft.FSharp.Core.FSharpFunc`2[Val,Val]) -Internal.Utilities.DependencyGraph+IThreadSafeDependencyGraph`2[Id,Val]: a Transact[a](Microsoft.FSharp.Core.FSharpFunc`2[Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val],a]) -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: <>f__AnonymousType4056634344`3[System.Collections.Generic.Dictionary`2[Id,System.Collections.Generic.HashSet`1[Id]],System.Collections.Generic.Dictionary`2[Id,System.Collections.Generic.HashSet`1[Id]],System.Collections.Generic.Dictionary`2[Id,Internal.Utilities.DependencyGraph+DependencyNode`2[Id,Val]]] Debug -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: <>f__AnonymousType4056634344`3[System.Collections.Generic.Dictionary`2[Id,System.Collections.Generic.HashSet`1[Id]],System.Collections.Generic.Dictionary`2[Id,System.Collections.Generic.HashSet`1[Id]],System.Collections.Generic.Dictionary`2[Id,Internal.Utilities.DependencyGraph+DependencyNode`2[Id,Val]]] get_Debug() -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: Microsoft.FSharp.Collections.FSharpList`1[Id] AddList(System.Collections.Generic.IEnumerable`1[System.Tuple`2[Id,Val]]) -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: System.Collections.Generic.IEnumerable`1[Id] GetDependenciesOf(Id) -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: System.Collections.Generic.IEnumerable`1[Id] GetDependentsOf(Id) -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: System.Collections.Generic.IEnumerable`1[Internal.Utilities.DependencyGraph+DependencyNode`2[Id,Val]] Debug_GetNodes(Microsoft.FSharp.Core.FSharpFunc`2[Id,System.Boolean]) -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: System.String Debug_RenderMermaid(Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[Id,Id]]) -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: Val GetValue(Id) -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: Void .ctor() -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: Void AddDependency(Id, Id) -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: Void AddOrUpdateNode(Id, System.Collections.Generic.IEnumerable`1[Id], Microsoft.FSharp.Core.FSharpFunc`2[System.Collections.Generic.IEnumerable`1[Val],Val]) -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: Void AddOrUpdateNode(Id, Val) -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: Void OnWarning(Microsoft.FSharp.Core.FSharpFunc`2[System.String,Microsoft.FSharp.Core.Unit]) -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: Void RemoveDependency(Id, Id) -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: Void RemoveNode(Id) -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: Void UpdateNode(Id, Microsoft.FSharp.Core.FSharpFunc`2[Val,Val]) -Internal.Utilities.DependencyGraph+Internal: Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val] -Internal.Utilities.DependencyGraph+LockOperatedDependencyGraph`2[Id,Val]: Void .ctor() -Internal.Utilities.DependencyGraph: Internal.Utilities.DependencyGraph+DependencyNode`2[Identifier,Value] -Internal.Utilities.DependencyGraph: Internal.Utilities.DependencyGraph+GraphBuilder`4[Id,Val,T,State] -Internal.Utilities.DependencyGraph: Internal.Utilities.DependencyGraph+GraphExtensions -Internal.Utilities.DependencyGraph: Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val] -Internal.Utilities.DependencyGraph: Internal.Utilities.DependencyGraph+IThreadSafeDependencyGraph`2[Id,Val] -Internal.Utilities.DependencyGraph: Internal.Utilities.DependencyGraph+Internal -Internal.Utilities.DependencyGraph: Internal.Utilities.DependencyGraph+LockOperatedDependencyGraph`2[Id,Val] -Internal.Utilities.DependencyGraph: Void insert[a,b](a, b, System.Collections.Generic.Dictionary`2[a,b]) Internal.Utilities.Library.DelayInitArrayMap`3[T,TDictKey,TDictValue]: System.Collections.Generic.IDictionary`2[TDictKey,TDictValue] CreateDictionary(T[]) Internal.Utilities.Library.DelayInitArrayMap`3[T,TDictKey,TDictValue]: System.Collections.Generic.IDictionary`2[TDictKey,TDictValue] GetDictionary() Internal.Utilities.Library.DelayInitArrayMap`3[T,TDictKey,TDictValue]: T[] GetArray() diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl index aea7c4724ee..b66bebd1cbc 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl @@ -7,23 +7,6 @@ ! AssemblyReference: System.Reflection.Emit.ILGeneration ! AssemblyReference: System.Reflection.Metadata ! AssemblyReference: netstandard -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: j__TPar Dependencies -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: j__TPar get_Dependencies() -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: j__TPar Dependents -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: j__TPar get_Dependents() -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: j__TPar Nodes -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: j__TPar get_Nodes() -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: Boolean Equals(<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]) -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: Boolean Equals(<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar], System.Collections.IEqualityComparer) -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: Boolean Equals(System.Object) -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: Boolean Equals(System.Object, System.Collections.IEqualityComparer) -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: Int32 CompareTo(<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]) -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: Int32 CompareTo(System.Object) -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: Int32 CompareTo(System.Object, System.Collections.IComparer) -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: Int32 GetHashCode() -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: Int32 GetHashCode(System.Collections.IEqualityComparer) -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: System.String ToString() -<>f__AnonymousType4056634344`3[j__TPar,j__TPar,j__TPar]: Void .ctor(j__TPar, j__TPar, j__TPar) FSharp.Compiler.AbstractIL.IL+ILArgConvention+Tags: Int32 CDecl FSharp.Compiler.AbstractIL.IL+ILArgConvention+Tags: Int32 Default FSharp.Compiler.AbstractIL.IL+ILArgConvention+Tags: Int32 FastCall @@ -12349,67 +12332,6 @@ FSharp.Compiler.Xml.XmlDoc: System.String[] GetElaboratedXmlLines() FSharp.Compiler.Xml.XmlDoc: System.String[] UnprocessedLines FSharp.Compiler.Xml.XmlDoc: System.String[] get_UnprocessedLines() FSharp.Compiler.Xml.XmlDoc: Void .ctor(System.String[], FSharp.Compiler.Text.Range) -Internal.Utilities.DependencyGraph+DependencyNode`2[Identifier,Value]: Identifier Id -Internal.Utilities.DependencyGraph+DependencyNode`2[Identifier,Value]: Identifier get_Id() -Internal.Utilities.DependencyGraph+DependencyNode`2[Identifier,Value]: Microsoft.FSharp.Core.FSharpFunc`2[System.Collections.Generic.IEnumerable`1[Value],Value] Compute -Internal.Utilities.DependencyGraph+DependencyNode`2[Identifier,Value]: Microsoft.FSharp.Core.FSharpFunc`2[System.Collections.Generic.IEnumerable`1[Value],Value] get_Compute() -Internal.Utilities.DependencyGraph+DependencyNode`2[Identifier,Value]: Microsoft.FSharp.Core.FSharpOption`1[Value] Value -Internal.Utilities.DependencyGraph+DependencyNode`2[Identifier,Value]: Microsoft.FSharp.Core.FSharpOption`1[Value] get_Value() -Internal.Utilities.DependencyGraph+DependencyNode`2[Identifier,Value]: System.String ToString() -Internal.Utilities.DependencyGraph+DependencyNode`2[Identifier,Value]: Void .ctor(Identifier, Microsoft.FSharp.Core.FSharpOption`1[Value], Microsoft.FSharp.Core.FSharpFunc`2[System.Collections.Generic.IEnumerable`1[Value],Value]) -Internal.Utilities.DependencyGraph+GraphBuilder`4[Id,Val,T,State]: GraphBuilder`4 AddDependentNode[a,b](Id, Microsoft.FSharp.Core.FSharpFunc`2[T,Val], Microsoft.FSharp.Core.FSharpFunc`2[System.Collections.Generic.IEnumerable`1[Val],a], b) -Internal.Utilities.DependencyGraph+GraphBuilder`4[Id,Val,T,State]: GraphBuilder`4 AddDependentNode[a](Id, Microsoft.FSharp.Core.FSharpFunc`2[T,Val], Microsoft.FSharp.Core.FSharpFunc`2[System.Collections.Generic.IEnumerable`1[Val],a]) -Internal.Utilities.DependencyGraph+GraphBuilder`4[Id,Val,T,State]: IDependencyGraph`2 Graph -Internal.Utilities.DependencyGraph+GraphBuilder`4[Id,Val,T,State]: IDependencyGraph`2 get_Graph() -Internal.Utilities.DependencyGraph+GraphBuilder`4[Id,Val,T,State]: State State -Internal.Utilities.DependencyGraph+GraphBuilder`4[Id,Val,T,State]: State get_State() -Internal.Utilities.DependencyGraph+GraphBuilder`4[Id,Val,T,State]: System.Collections.Generic.IEnumerable`1[Id] Ids -Internal.Utilities.DependencyGraph+GraphBuilder`4[Id,Val,T,State]: System.Collections.Generic.IEnumerable`1[Id] get_Ids() -Internal.Utilities.DependencyGraph+GraphBuilder`4[Id,Val,T,State]: Void .ctor(IDependencyGraph`2, System.Collections.Generic.IEnumerable`1[Id], Microsoft.FSharp.Core.FSharpFunc`2[System.Collections.Generic.IEnumerable`1[Val],T], State) -Internal.Utilities.DependencyGraph+GraphExtensions: System.Collections.Generic.IEnumerable`1[a] UnpackMany[NodeValue,a](System.Collections.Generic.IEnumerable`1[NodeValue], Microsoft.FSharp.Core.FSharpFunc`2[NodeValue,Microsoft.FSharp.Core.FSharpOption`1[a]]) -Internal.Utilities.DependencyGraph+GraphExtensions: System.Tuple`2[a,System.Collections.Generic.IEnumerable`1[b]] UnpackOneMany[NodeValue,a,b](System.Collections.Generic.IEnumerable`1[NodeValue], Microsoft.FSharp.Core.FSharpFunc`2[NodeValue,Microsoft.FSharp.Core.FSharpOption`1[a]], Microsoft.FSharp.Core.FSharpFunc`2[NodeValue,Microsoft.FSharp.Core.FSharpOption`1[b]]) -Internal.Utilities.DependencyGraph+GraphExtensions: UnpackedDependency UnpackOne[NodeValue,UnpackedDependency](System.Collections.Generic.IEnumerable`1[NodeValue], Microsoft.FSharp.Core.FSharpFunc`2[NodeValue,Microsoft.FSharp.Core.FSharpOption`1[UnpackedDependency]]) -Internal.Utilities.DependencyGraph+GraphExtensions: a Unpack[NodeValue,a](NodeValue, Microsoft.FSharp.Core.FSharpFunc`2[NodeValue,Microsoft.FSharp.Core.FSharpOption`1[a]]) -Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val]: System.Collections.Generic.IEnumerable`1[Id] AddList(System.Collections.Generic.IEnumerable`1[System.Tuple`2[Id,Val]]) -Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val]: System.Collections.Generic.IEnumerable`1[Id] GetDependenciesOf(Id) -Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val]: System.Collections.Generic.IEnumerable`1[Id] GetDependentsOf(Id) -Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val]: System.Collections.Generic.IEnumerable`1[Internal.Utilities.DependencyGraph+DependencyNode`2[Id,Val]] Debug_GetNodes(Microsoft.FSharp.Core.FSharpFunc`2[Id,System.Boolean]) -Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val]: System.String Debug_RenderMermaid(Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[Id,Id]]) -Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val]: Val GetValue(Id) -Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val]: Void AddDependency(Id, Id) -Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val]: Void AddOrUpdateNode(Id, System.Collections.Generic.IEnumerable`1[Id], Microsoft.FSharp.Core.FSharpFunc`2[System.Collections.Generic.IEnumerable`1[Val],Val]) -Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val]: Void AddOrUpdateNode(Id, Val) -Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val]: Void OnWarning(Microsoft.FSharp.Core.FSharpFunc`2[System.String,Microsoft.FSharp.Core.Unit]) -Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val]: Void RemoveDependency(Id, Id) -Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val]: Void RemoveNode(Id) -Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val]: Void UpdateNode(Id, Microsoft.FSharp.Core.FSharpFunc`2[Val,Val]) -Internal.Utilities.DependencyGraph+IThreadSafeDependencyGraph`2[Id,Val]: a Transact[a](Microsoft.FSharp.Core.FSharpFunc`2[Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val],a]) -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: <>f__AnonymousType4056634344`3[System.Collections.Generic.Dictionary`2[Id,System.Collections.Generic.HashSet`1[Id]],System.Collections.Generic.Dictionary`2[Id,System.Collections.Generic.HashSet`1[Id]],System.Collections.Generic.Dictionary`2[Id,Internal.Utilities.DependencyGraph+DependencyNode`2[Id,Val]]] Debug -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: <>f__AnonymousType4056634344`3[System.Collections.Generic.Dictionary`2[Id,System.Collections.Generic.HashSet`1[Id]],System.Collections.Generic.Dictionary`2[Id,System.Collections.Generic.HashSet`1[Id]],System.Collections.Generic.Dictionary`2[Id,Internal.Utilities.DependencyGraph+DependencyNode`2[Id,Val]]] get_Debug() -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: Microsoft.FSharp.Collections.FSharpList`1[Id] AddList(System.Collections.Generic.IEnumerable`1[System.Tuple`2[Id,Val]]) -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: System.Collections.Generic.IEnumerable`1[Id] GetDependenciesOf(Id) -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: System.Collections.Generic.IEnumerable`1[Id] GetDependentsOf(Id) -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: System.Collections.Generic.IEnumerable`1[Internal.Utilities.DependencyGraph+DependencyNode`2[Id,Val]] Debug_GetNodes(Microsoft.FSharp.Core.FSharpFunc`2[Id,System.Boolean]) -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: System.String Debug_RenderMermaid(Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[Id,Id]]) -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: Val GetValue(Id) -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: Void .ctor() -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: Void AddDependency(Id, Id) -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: Void AddOrUpdateNode(Id, System.Collections.Generic.IEnumerable`1[Id], Microsoft.FSharp.Core.FSharpFunc`2[System.Collections.Generic.IEnumerable`1[Val],Val]) -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: Void AddOrUpdateNode(Id, Val) -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: Void OnWarning(Microsoft.FSharp.Core.FSharpFunc`2[System.String,Microsoft.FSharp.Core.Unit]) -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: Void RemoveDependency(Id, Id) -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: Void RemoveNode(Id) -Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val]: Void UpdateNode(Id, Microsoft.FSharp.Core.FSharpFunc`2[Val,Val]) -Internal.Utilities.DependencyGraph+Internal: Internal.Utilities.DependencyGraph+Internal+DependencyGraph`2[Id,Val] -Internal.Utilities.DependencyGraph+LockOperatedDependencyGraph`2[Id,Val]: Void .ctor() -Internal.Utilities.DependencyGraph: Internal.Utilities.DependencyGraph+DependencyNode`2[Identifier,Value] -Internal.Utilities.DependencyGraph: Internal.Utilities.DependencyGraph+GraphBuilder`4[Id,Val,T,State] -Internal.Utilities.DependencyGraph: Internal.Utilities.DependencyGraph+GraphExtensions -Internal.Utilities.DependencyGraph: Internal.Utilities.DependencyGraph+IDependencyGraph`2[Id,Val] -Internal.Utilities.DependencyGraph: Internal.Utilities.DependencyGraph+IThreadSafeDependencyGraph`2[Id,Val] -Internal.Utilities.DependencyGraph: Internal.Utilities.DependencyGraph+Internal -Internal.Utilities.DependencyGraph: Internal.Utilities.DependencyGraph+LockOperatedDependencyGraph`2[Id,Val] -Internal.Utilities.DependencyGraph: Void insert[a,b](a, b, System.Collections.Generic.Dictionary`2[a,b]) Internal.Utilities.Library.DelayInitArrayMap`3[T,TDictKey,TDictValue]: System.Collections.Generic.IDictionary`2[TDictKey,TDictValue] CreateDictionary(T[]) Internal.Utilities.Library.DelayInitArrayMap`3[T,TDictKey,TDictValue]: System.Collections.Generic.IDictionary`2[TDictKey,TDictValue] GetDictionary() Internal.Utilities.Library.DelayInitArrayMap`3[T,TDictKey,TDictValue]: T[] GetArray() From fe87acbc9966fee841007f9577233e07fa61c979 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Mon, 2 Dec 2024 10:54:17 +0100 Subject: [PATCH 36/60] Update fantomas ignore --- .fantomasignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.fantomasignore b/.fantomasignore index 157526b39c0..7ce9eee090d 100644 --- a/.fantomasignore +++ b/.fantomasignore @@ -122,6 +122,8 @@ src/Compiler/SyntaxTree/LexerStore.fs src/Compiler/Driver/GraphChecking/Graph.fsi src/Compiler/Driver/GraphChecking/Graph.fs +src/FSharp.Compiler.LanguageServer/Common/LifecycleManager.fs + # Fantomas limitations on implementation files (to investigate) src/Compiler/AbstractIL/ilwrite.fs From 8183316020863eb9268d97e515ab9c7a81fad0e0 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Mon, 2 Dec 2024 15:26:11 +0100 Subject: [PATCH 37/60] [LSP] phase 1 endpoints, dependency updates (#18087) --- VisualFSharp.sln | 15 ++ .../AssemblyCheck/SkipVerifyEmbeddedPdb.txt | 3 +- src/Compiler/Service/FSharpProjectSnapshot.fs | 1 + src/Compiler/Service/FSharpWorkspace.fs | 1 + src/Compiler/Service/FSharpWorkspaceQuery.fs | 2 + src/Compiler/Service/FSharpWorkspaceState.fs | 2 + .../Common/CapabilitiesManager.fs | 13 +- .../Common/FSharpRequestContext.fs | 23 ++- .../Common/LifecycleManager.fs | 24 ++- .../FSharp.Compiler.LanguageServer.fsproj | 18 +- .../FSharpLanguageServer.fs | 18 +- .../Handlers/DocumentStateHandler.fs | 8 +- .../Handlers/LanguageFeaturesHandler.fs | 42 +++- src/FSharp.Compiler.LanguageServer/Utils.fs | 63 ++++-- .../ExtensionEntrypoint.cs | 2 - .../FSharp.VisualStudio.Extension.csproj | 60 +++--- .../FSharpLanguageServerProvider.cs | 75 ++----- ...guageServerProtocol.Framework.Proxy.csproj | 19 ++ .../CompilerService/FSharpWorkspace.fs | 37 +--- ...Sharp.Compiler.LanguageServer.Tests.fsproj | 6 +- .../LanguageServerTests.fs | 59 ------ .../Protocol.fs | 193 ++++++++++++++++++ ...ervice.SurfaceArea.netstandard20.debug.bsl | 4 + ...vice.SurfaceArea.netstandard20.release.bsl | 4 + .../ProjectGeneration.fs | 89 +++++--- ...y_FSharp.Compiler.Service_Debug_net9.0.bsl | 2 +- ....Compiler.Service_Debug_netstandard2.0.bsl | 2 +- ...FSharp.Compiler.Service_Release_net9.0.bsl | 2 +- ...ompiler.Service_Release_netstandard2.0.bsl | 2 +- .../FSharp.Editor/Common/CancellableTasks.fs | 4 +- 30 files changed, 520 insertions(+), 273 deletions(-) create mode 100644 src/Microsoft.CommonLanguageServerProtocol.Framework.Proxy/Microsoft.CommonLanguageServerProtocol.Framework.Proxy.csproj delete mode 100644 tests/FSharp.Compiler.LanguageServer.Tests/LanguageServerTests.fs create mode 100644 tests/FSharp.Compiler.LanguageServer.Tests/Protocol.fs diff --git a/VisualFSharp.sln b/VisualFSharp.sln index a243905a780..df0b6fe31ce 100644 --- a/VisualFSharp.sln +++ b/VisualFSharp.sln @@ -192,12 +192,15 @@ EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "MicroPerf", "tests\benchmarks\CompiledCodeBenchmarks\MicroPerf\MicroPerf.fsproj", "{601CD5C1-EAFA-4AE3-8FB9-F667B5728213}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MicroPerfCSharp", "tests\benchmarks\CompiledCodeBenchmarks\MicroPerf\CS\MicroPerfCSharp.csproj", "{9F9DD315-37DA-4413-928E-1CFC6924B64F}" +EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Compiler.LanguageServer", "src\FSharp.Compiler.LanguageServer\FSharp.Compiler.LanguageServer.fsproj", "{D72F6593-DB2D-47AC-8E15-8DCE8527972E}" EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Compiler.LanguageServer.Tests", "tests\FSharp.Compiler.LanguageServer.Tests\FSharp.Compiler.LanguageServer.Tests.fsproj", "{1E83A6C8-FA4D-42BD-B4A5-B7F9AAD1B388}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FSharp.VisualStudio.Extension", "src\FSharp.VisualStudio.Extension\FSharp.VisualStudio.Extension.csproj", "{E1013576-6257-47BA-AAFB-F95B68DAB1FF}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CommonLanguageServerProtocol.Framework.Proxy", "src\Microsoft.CommonLanguageServerProtocol.Framework.Proxy\Microsoft.CommonLanguageServerProtocol.Framework.Proxy.csproj", "{FAFF15B5-F7C4-1CE5-9A18-2F6DC93E03ED}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1060,6 +1063,18 @@ Global {E1013576-6257-47BA-AAFB-F95B68DAB1FF}.Release|Any CPU.Build.0 = Release|Any CPU {E1013576-6257-47BA-AAFB-F95B68DAB1FF}.Release|x86.ActiveCfg = Release|Any CPU {E1013576-6257-47BA-AAFB-F95B68DAB1FF}.Release|x86.Build.0 = Release|Any CPU + {FAFF15B5-F7C4-1CE5-9A18-2F6DC93E03ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FAFF15B5-F7C4-1CE5-9A18-2F6DC93E03ED}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FAFF15B5-F7C4-1CE5-9A18-2F6DC93E03ED}.Debug|x86.ActiveCfg = Debug|Any CPU + {FAFF15B5-F7C4-1CE5-9A18-2F6DC93E03ED}.Debug|x86.Build.0 = Debug|Any CPU + {FAFF15B5-F7C4-1CE5-9A18-2F6DC93E03ED}.Proto|Any CPU.ActiveCfg = Debug|Any CPU + {FAFF15B5-F7C4-1CE5-9A18-2F6DC93E03ED}.Proto|Any CPU.Build.0 = Debug|Any CPU + {FAFF15B5-F7C4-1CE5-9A18-2F6DC93E03ED}.Proto|x86.ActiveCfg = Debug|Any CPU + {FAFF15B5-F7C4-1CE5-9A18-2F6DC93E03ED}.Proto|x86.Build.0 = Debug|Any CPU + {FAFF15B5-F7C4-1CE5-9A18-2F6DC93E03ED}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FAFF15B5-F7C4-1CE5-9A18-2F6DC93E03ED}.Release|Any CPU.Build.0 = Release|Any CPU + {FAFF15B5-F7C4-1CE5-9A18-2F6DC93E03ED}.Release|x86.ActiveCfg = Release|Any CPU + {FAFF15B5-F7C4-1CE5-9A18-2F6DC93E03ED}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/buildtools/AssemblyCheck/SkipVerifyEmbeddedPdb.txt b/buildtools/AssemblyCheck/SkipVerifyEmbeddedPdb.txt index 7af2a7f58cb..f7d25f2ca25 100644 --- a/buildtools/AssemblyCheck/SkipVerifyEmbeddedPdb.txt +++ b/buildtools/AssemblyCheck/SkipVerifyEmbeddedPdb.txt @@ -3,7 +3,8 @@ FSharp.Benchmarks.Common.dll FSharp.Compiler.Benchmarks.dll FSharp.Compiler.ComponentTests.dll FSharp.Test.Utilities.dll +FSharp.Compiler.LanguageServer.Tests.dll FSharp.Compiler.Private.Scripting.UnitTests.dll FSharp.Compiler.Service.Tests.dll FSharp.Core.UnitTests.dll -FSharpSuite.Tests.dll +FSharpSuite.Tests.dll \ No newline at end of file diff --git a/src/Compiler/Service/FSharpProjectSnapshot.fs b/src/Compiler/Service/FSharpProjectSnapshot.fs index 8b64a6be3a6..82f5e49804d 100644 --- a/src/Compiler/Service/FSharpProjectSnapshot.fs +++ b/src/Compiler/Service/FSharpProjectSnapshot.fs @@ -616,6 +616,7 @@ and [] FSha member _.OriginalLoadReferences = projectSnapshot.OriginalLoadReferences member _.Stamp = projectSnapshot.Stamp member _.OutputFileName = projectSnapshot.OutputFileName + member _.ProjectConfig = projectSnapshot.ProjectConfig static member Create ( diff --git a/src/Compiler/Service/FSharpWorkspace.fs b/src/Compiler/Service/FSharpWorkspace.fs index b250bce9c7b..ccde4b98fb1 100644 --- a/src/Compiler/Service/FSharpWorkspace.fs +++ b/src/Compiler/Service/FSharpWorkspace.fs @@ -22,6 +22,7 @@ open FSharpWorkspaceQuery /// Project references are discovered automatically as projects are added or updated. /// /// Updates to file contents are signaled through the `Files.Open`, `Files.Edit`, and `Files.Close` methods. +[] type FSharpWorkspace(checker: FSharpChecker) = let depGraph = LockOperatedDependencyGraph() :> IThreadSafeDependencyGraph<_, _> diff --git a/src/Compiler/Service/FSharpWorkspaceQuery.fs b/src/Compiler/Service/FSharpWorkspaceQuery.fs index 4a63655ebfd..b720608b27f 100644 --- a/src/Compiler/Service/FSharpWorkspaceQuery.fs +++ b/src/Compiler/Service/FSharpWorkspaceQuery.fs @@ -14,6 +14,7 @@ open FSharpWorkspaceState #nowarn "57" +[] type FSharpDiagnosticReport internal (diagnostics, resultId: int) = member _.Diagnostics = diagnostics @@ -21,6 +22,7 @@ type FSharpDiagnosticReport internal (diagnostics, resultId: int) = /// The result ID of the diagnostics. This needs to be unique for each version of the document in order to be able to clear old diagnostics. member _.ResultId = resultId.ToString() +[] type FSharpWorkspaceQuery internal (depGraph: IThreadSafeDependencyGraph<_, _>, checker: FSharpChecker) = let mutable resultIdCounter = 0 diff --git a/src/Compiler/Service/FSharpWorkspaceState.fs b/src/Compiler/Service/FSharpWorkspaceState.fs index 2227d03d478..cc3d3d8524d 100644 --- a/src/Compiler/Service/FSharpWorkspaceState.fs +++ b/src/Compiler/Service/FSharpWorkspaceState.fs @@ -228,6 +228,7 @@ module internal WorkspaceDependencyGraphExtensions = |> _.Unpack(WorkspaceNode.sourceFile) /// Interface for managing files in an F# workspace. +[] type FSharpWorkspaceFiles internal (depGraph: IThreadSafeDependencyGraph<_, _>) = /// Open files in the editor. @@ -255,6 +256,7 @@ type FSharpWorkspaceFiles internal (depGraph: IThreadSafeDependencyGraph<_, _>) | false, _ -> None /// Interface for managing with projects in an F# workspace. +[] type FSharpWorkspaceProjects internal (depGraph: IThreadSafeDependencyGraph<_, _>, files: FSharpWorkspaceFiles) = /// A map from project output path to project identifier. diff --git a/src/FSharp.Compiler.LanguageServer/Common/CapabilitiesManager.fs b/src/FSharp.Compiler.LanguageServer/Common/CapabilitiesManager.fs index 7632a80ce62..b8b50e95f33 100644 --- a/src/FSharp.Compiler.LanguageServer/Common/CapabilitiesManager.fs +++ b/src/FSharp.Compiler.LanguageServer/Common/CapabilitiesManager.fs @@ -15,8 +15,17 @@ type CapabilitiesManager(scOverrides: IServerCapabilitiesOverride seq) = TextDocumentSync = TextDocumentSyncOptions(OpenClose = true, Change = TextDocumentSyncKind.Full), DiagnosticOptions = DiagnosticOptions(WorkDoneProgress = true, InterFileDependencies = true, Identifier = "potato", WorkspaceDiagnostics = true), - CompletionProvider = CompletionOptions(TriggerCharacters = [| "."; " " |], ResolveProvider = true, WorkDoneProgress = true), - HoverProvider = SumType(HoverOptions(WorkDoneProgress = true)) + //CompletionProvider = CompletionOptions(TriggerCharacters = [| "."; " " |], ResolveProvider = true, WorkDoneProgress = true), + //HoverProvider = SumType(HoverOptions(WorkDoneProgress = true)) + SemanticTokensOptions = + SemanticTokensOptions( + Legend = + SemanticTokensLegend( + TokenTypes = (SemanticTokenTypes.AllTypes |> Seq.toArray), // XXX should be extended + TokenModifiers = (SemanticTokenModifiers.AllModifiers |> Seq.toArray) + ), + Range = false + ) ) interface IInitializeManager with diff --git a/src/FSharp.Compiler.LanguageServer/Common/FSharpRequestContext.fs b/src/FSharp.Compiler.LanguageServer/Common/FSharpRequestContext.fs index bef2d063d6e..85085a8bb8d 100644 --- a/src/FSharp.Compiler.LanguageServer/Common/FSharpRequestContext.fs +++ b/src/FSharp.Compiler.LanguageServer/Common/FSharpRequestContext.fs @@ -173,14 +173,15 @@ type ContextHolder(workspace, lspServices: ILspServices) = type FShapRequestContextFactory(lspServices: ILspServices) = - interface IRequestContextFactory with - - member _.CreateRequestContextAsync<'TRequestParam> - ( - queueItem: IQueueItem, - requestParam: 'TRequestParam, - cancellationToken: CancellationToken - ) = - lspServices.GetRequiredService() - |> _.GetContext() - |> Task.FromResult + inherit AbstractRequestContextFactory() + + override _.CreateRequestContextAsync<'TRequestParam> + ( + queueItem: IQueueItem, + methodHandler: IMethodHandler, + requestParam: 'TRequestParam, + cancellationToken: CancellationToken + ) = + lspServices.GetRequiredService() + |> _.GetContext() + |> Task.FromResult diff --git a/src/FSharp.Compiler.LanguageServer/Common/LifecycleManager.fs b/src/FSharp.Compiler.LanguageServer/Common/LifecycleManager.fs index cd033fc3d2f..13a97cf4a68 100644 --- a/src/FSharp.Compiler.LanguageServer/Common/LifecycleManager.fs +++ b/src/FSharp.Compiler.LanguageServer/Common/LifecycleManager.fs @@ -6,10 +6,12 @@ open StreamJsonRpc open System.Threading.Tasks open Microsoft.Extensions.DependencyInjection +#nowarn "3261" + type LspServiceLifeCycleManager() = interface ILifeCycleManager with - member this.ShutdownAsync(message: string) = + member _.ShutdownAsync(message: string) = task { try printfn "Shutting down" @@ -18,7 +20,7 @@ type LspServiceLifeCycleManager() = | :? ConnectionLostException -> () } - member this.ExitAsync() = Task.CompletedTask + member _.ExitAsync() = Task.CompletedTask type FSharpLspServices(serviceCollection: IServiceCollection) as this = @@ -27,15 +29,17 @@ type FSharpLspServices(serviceCollection: IServiceCollection) as this = let serviceProvider = serviceCollection.BuildServiceProvider() interface ILspServices with - member this.GetRequiredService<'T when 'T: not null>() : 'T = - serviceProvider.GetRequiredService<'T>() - - member this.TryGetService(t) = serviceProvider.GetService(t) + member _.GetRequiredService() = serviceProvider.GetRequiredService() - member this.GetRequiredServices() = serviceProvider.GetServices() + member _.GetService() = serviceProvider.GetService() - member this.GetRegisteredServices() = failwith "Not implemented" + member _.GetRequiredServices() = serviceProvider.GetServices() - member this.SupportsGetRegisteredServices() = false + member _.Dispose() = serviceProvider.Dispose() - member this.Dispose() = () + member _.TryGetService(``type``, service) = + match serviceProvider.GetService(``type``) with + | NonNull x -> + service <- x + true + | Null -> false diff --git a/src/FSharp.Compiler.LanguageServer/FSharp.Compiler.LanguageServer.fsproj b/src/FSharp.Compiler.LanguageServer/FSharp.Compiler.LanguageServer.fsproj index e2ea85b140c..1b5f7c99122 100644 --- a/src/FSharp.Compiler.LanguageServer/FSharp.Compiler.LanguageServer.fsproj +++ b/src/FSharp.Compiler.LanguageServer/FSharp.Compiler.LanguageServer.fsproj @@ -1,17 +1,17 @@ - + Exe net8.0 true + enable - - - - + + + @@ -32,20 +32,20 @@ + + - - - + + - \ No newline at end of file diff --git a/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs index 4af67d2100d..9d0d3ec4412 100644 --- a/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs +++ b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs @@ -15,6 +15,8 @@ open Nerdbank.Streams open System.Diagnostics open FSharp.Compiler.CodeAnalysis.Workspace +#nowarn "57" + [] module Stuff = [] @@ -29,7 +31,9 @@ type Extensions = type FSharpLanguageServer (jsonRpc: JsonRpc, logger: ILspLogger, ?initialWorkspace: FSharpWorkspace, ?addExtraHandlers: Action) = - inherit AbstractLanguageServer(jsonRpc, logger) + + // TODO: Switch to SystemTextJsonLanguageServer + inherit NewtonsoftLanguageServer(jsonRpc, Newtonsoft.Json.JsonSerializer.CreateDefault(), logger) let initialWorkspace = defaultArg initialWorkspace (FSharpWorkspace()) @@ -39,8 +43,6 @@ type FSharpLanguageServer member _.JsonRpc: JsonRpc = jsonRpc - member private this.GetBaseHandlerProvider() = base.GetHandlerProvider() - override this.ConstructLspServices() = let serviceCollection = new ServiceCollection() @@ -53,8 +55,7 @@ type FSharpLanguageServer .AddSingleton() .AddSingleton() .AddSingleton(logger) - .AddSingleton, FShapRequestContextFactory>() - .AddSingleton(fun _ -> this.GetBaseHandlerProvider()) + .AddSingleton, FShapRequestContextFactory>() .AddSingleton, CapabilitiesManager>() .AddSingleton(this) .AddSingleton(new LspServiceLifeCycleManager()) @@ -68,7 +69,10 @@ type FSharpLanguageServer lspServices :> ILspServices static member Create() = - FSharpLanguageServer.Create(FSharpWorkspace(), (fun _ -> ())) + FSharpLanguageServer.Create(FSharpWorkspace()) + + static member Create(initialWorkspace) = + FSharpLanguageServer.Create(initialWorkspace, (fun _ -> ())) static member Create(initialWorkspace, addExtraHandlers: Action) = FSharpLanguageServer.Create(LspLogger System.Diagnostics.Trace.TraceInformation, initialWorkspace, addExtraHandlers) @@ -89,7 +93,7 @@ type FSharpLanguageServer jsonRpc.TraceSource.Listeners.Add(listener) |> ignore - jsonRpc.TraceSource.Switch.Level <- SourceLevels.Information + jsonRpc.TraceSource.Switch.Level <- SourceLevels.All let server = new FSharpLanguageServer(jsonRpc, logger, initialWorkspace, ?addExtraHandlers = addExtraHandlers) diff --git a/src/FSharp.Compiler.LanguageServer/Handlers/DocumentStateHandler.fs b/src/FSharp.Compiler.LanguageServer/Handlers/DocumentStateHandler.fs index e549b27a7c2..76135d84fe7 100644 --- a/src/FSharp.Compiler.LanguageServer/Handlers/DocumentStateHandler.fs +++ b/src/FSharp.Compiler.LanguageServer/Handlers/DocumentStateHandler.fs @@ -7,12 +7,14 @@ open FSharp.Compiler.LanguageServer.Common open System.Threading open System.Threading.Tasks +#nowarn "57" + type DocumentStateHandler() = interface IMethodHandler with member _.MutatesSolutionState = true interface IRequestHandler with - [] + [] member _.HandleRequestAsync ( request: DidOpenTextDocumentParams, @@ -26,7 +28,7 @@ type DocumentStateHandler() = Task.FromResult(SemanticTokensDeltaPartialResult()) interface IRequestHandler with - [] + [] member _.HandleRequestAsync ( request: DidChangeTextDocumentParams, @@ -40,7 +42,7 @@ type DocumentStateHandler() = Task.FromResult(SemanticTokensDeltaPartialResult()) interface INotificationHandler with - [] + [] member _.HandleNotificationAsync ( request: DidCloseTextDocumentParams, diff --git a/src/FSharp.Compiler.LanguageServer/Handlers/LanguageFeaturesHandler.fs b/src/FSharp.Compiler.LanguageServer/Handlers/LanguageFeaturesHandler.fs index d900fdfc3b8..a144cf1832b 100644 --- a/src/FSharp.Compiler.LanguageServer/Handlers/LanguageFeaturesHandler.fs +++ b/src/FSharp.Compiler.LanguageServer/Handlers/LanguageFeaturesHandler.fs @@ -2,21 +2,57 @@ open Microsoft.CommonLanguageServerProtocol.Framework open Microsoft.VisualStudio.LanguageServer.Protocol +open Microsoft.VisualStudio.FSharp.Editor.CancellableTasks open FSharp.Compiler.LanguageServer.Common +open FSharp.Compiler.LanguageServer open System.Threading.Tasks open System.Threading +open System.Collections.Generic +open Microsoft.VisualStudio.FSharp.Editor + +#nowarn "57" type LanguageFeaturesHandler() = interface IMethodHandler with member _.MutatesSolutionState = false - // TODO: this is not getting called interface IRequestHandler, FSharpRequestContext> with - [] + [] member _.HandleRequestAsync ( request: DocumentDiagnosticParams, context: FSharpRequestContext, cancellationToken: CancellationToken ) = - Task.FromResult(new RelatedUnchangedDocumentDiagnosticReport()) + cancellableTask { + + let! fsharpDiagnosticReport = context.Workspace.Query.GetDiagnosticsForFile request.TextDocument.Uri + + let report = + FullDocumentDiagnosticReport( + Items = (fsharpDiagnosticReport.Diagnostics |> Array.map (_.ToLspDiagnostic())), + ResultId = fsharpDiagnosticReport.ResultId + ) + + let relatedDocuments = Dictionary() + + relatedDocuments.Add( + request.TextDocument.Uri, + SumType report + ) + + return + SumType( + RelatedFullDocumentDiagnosticReport(RelatedDocuments = relatedDocuments) + ) + } + |> CancellableTask.start cancellationToken + + interface IRequestHandler with + [] + member _.HandleRequestAsync(request: SemanticTokensParams, context: FSharpRequestContext, cancellationToken: CancellationToken) = + cancellableTask { + let! tokens = context.GetSemanticTokensForFile(request.TextDocument.Uri) + return SemanticTokens(Data = tokens) + } + |> CancellableTask.start cancellationToken diff --git a/src/FSharp.Compiler.LanguageServer/Utils.fs b/src/FSharp.Compiler.LanguageServer/Utils.fs index abae720654e..2f19328a25f 100644 --- a/src/FSharp.Compiler.LanguageServer/Utils.fs +++ b/src/FSharp.Compiler.LanguageServer/Utils.fs @@ -1,25 +1,54 @@ -[] -module FSharp.Compiler.LanguageServer.Utils +namespace FSharp.Compiler.LanguageServer open Microsoft.CommonLanguageServerProtocol.Framework +open Microsoft.VisualStudio.LanguageServer.Protocol + +open FSharp.Compiler.Diagnostics +open System.Runtime.CompilerServices + +[] +module Utils = + + type LspRange = Microsoft.VisualStudio.LanguageServer.Protocol.Range + + let LspLogger (output: string -> unit) = + { new ILspLogger with + member this.LogEndContext(message: string, ``params``: obj array) : unit = + output $"EndContext :: {message} %A{``params``}" + + member this.LogError(message: string, ``params``: obj array) : unit = + output $"ERROR :: {message} %A{``params``}" + + member this.LogException(``exception``: exn, message: string, ``params``: obj array) : unit = + output $"EXCEPTION :: %A{``exception``} {message} %A{``params``}" + + member this.LogInformation(message: string, ``params``: obj array) : unit = + output $"INFO :: {message} %A{``params``}" -let LspLogger (output: string -> unit) = - { new ILspLogger with - member this.LogEndContext(message: string, ``params``: obj array) : unit = - output $"EndContext :: {message} %A{``params``}" + member this.LogStartContext(message: string, ``params``: obj array) : unit = + output $"StartContext :: {message} %A{``params``}" - member this.LogError(message: string, ``params``: obj array) : unit = - output $"ERROR :: {message} %A{``params``}" + member this.LogWarning(message: string, ``params``: obj array) : unit = + output $"WARNING :: {message} %A{``params``}" + } - member this.LogException(``exception``: exn, message: string, ``params``: obj array) : unit = - output $"EXCEPTION :: %A{``exception``} {message} %A{``params``}" + type FSharp.Compiler.Text.Range with - member this.LogInformation(message: string, ``params``: obj array) : unit = - output $"INFO :: {message} %A{``params``}" + member this.ToLspRange() = + LspRange( + Start = Position(Line = this.StartLine - 1, Character = this.StartColumn), + End = Position(Line = this.EndLine - 1, Character = this.EndColumn) + ) - member this.LogStartContext(message: string, ``params``: obj array) : unit = - output $"StartContext :: {message} %A{``params``}" +[] +type FSharpDiagnosticExtensions = - member this.LogWarning(message: string, ``params``: obj array) : unit = - output $"WARNING :: {message} %A{``params``}" - } + [] + static member ToLspDiagnostic(this: FSharpDiagnostic) = + Diagnostic( + Range = this.Range.ToLspRange(), + Severity = DiagnosticSeverity.Error, + Message = $"LSP: {this.Message}", + //Source = "Intellisense", + Code = SumType this.ErrorNumberText + ) diff --git a/src/FSharp.VisualStudio.Extension/ExtensionEntrypoint.cs b/src/FSharp.VisualStudio.Extension/ExtensionEntrypoint.cs index 7957511b29e..8fbaf39dc70 100644 --- a/src/FSharp.VisualStudio.Extension/ExtensionEntrypoint.cs +++ b/src/FSharp.VisualStudio.Extension/ExtensionEntrypoint.cs @@ -11,8 +11,6 @@ internal class ExtensionEntrypoint : Extension { - - /// public override ExtensionConfiguration ExtensionConfiguration => new() { diff --git a/src/FSharp.VisualStudio.Extension/FSharp.VisualStudio.Extension.csproj b/src/FSharp.VisualStudio.Extension/FSharp.VisualStudio.Extension.csproj index bef467713a0..1c946fbe71c 100644 --- a/src/FSharp.VisualStudio.Extension/FSharp.VisualStudio.Extension.csproj +++ b/src/FSharp.VisualStudio.Extension/FSharp.VisualStudio.Extension.csproj @@ -1,33 +1,33 @@  - - net8.0-windows - enable - 12 - en-US - - - - - - - - - - - - - - - - - - - - - - false - - - + + net8.0-windows + enable + 12 + en-US + + + + + + + + + + + + + + + + + + + + + + false + + + diff --git a/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs b/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs index 93bb5b39729..429570e917c 100644 --- a/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs +++ b/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs @@ -6,8 +6,6 @@ namespace FSharp.VisualStudio.Extension; using System; using System.Collections.Generic; using System.Diagnostics; -using System.IO; -using System.IO.Packaging; using System.IO.Pipelines; using System.Linq; using System.Threading; @@ -25,9 +23,6 @@ namespace FSharp.VisualStudio.Extension; using Microsoft.VisualStudio.ProjectSystem.Query; using Microsoft.VisualStudio.RpcContracts.LanguageServerProvider; using Nerdbank.Streams; -using Newtonsoft.Json.Linq; -using StreamJsonRpc; -using static FSharp.Compiler.CodeAnalysis.ProjectSnapshot; /// #pragma warning disable VSEXTPREVIEW_LSP // Type is for evaluation purposes only and is subject to change or removal in future updates. @@ -74,82 +69,48 @@ public ServerCapabilities OverrideServerCapabilities(ServerCapabilities value) { Legend = new() { - TokenTypes = [ ..SemanticTokenTypes.AllTypes], // XXX should be extended - TokenModifiers = [ ..SemanticTokenModifiers.AllModifiers] + TokenTypes = [.. SemanticTokenTypes.AllTypes], // XXX should be extended + TokenModifiers = [.. SemanticTokenModifiers.AllModifiers] }, Full = new SemanticTokensFullOptions() { Delta = false }, Range = false - }, - HoverProvider = new HoverOptions() - { - WorkDoneProgress = true } + //, + //HoverProvider = new HoverOptions() + //{ + // WorkDoneProgress = true + //} }; return capabilities; } } -internal class SemanticTokensHandler - : IRequestHandler -{ - public bool MutatesSolutionState => false; - - [LanguageServerEndpoint("textDocument/semanticTokens/full")] - public async Task HandleRequestAsync( - SemanticTokensParams request, - FSharpRequestContext context, - CancellationToken cancellationToken) - { - var tokens = await context.GetSemanticTokensForFile(request!.TextDocument!.Uri); - - return new SemanticTokens { Data = tokens }; - } -} - - internal class VsDiagnosticsHandler - : IRequestHandler, + : IRequestHandler, IRequestHandler { public bool MutatesSolutionState => false; - [LanguageServerEndpoint(VSInternalMethods.DocumentPullDiagnosticName)] - public async Task HandleRequestAsync(VSInternalDocumentDiagnosticsParams request, FSharpRequestContext context, CancellationToken cancellationToken) + [LanguageServerEndpoint(VSInternalMethods.DocumentPullDiagnosticName, LanguageServerConstants.DefaultLanguageName)] + public async Task HandleRequestAsync(VSInternalDiagnosticParams request, FSharpRequestContext context, CancellationToken cancellationToken) { - var report = await context.Workspace.Query.GetDiagnosticsForFile(request!.TextDocument!.Uri).Please(cancellationToken); + var report = await context.Workspace.Query.GetDiagnosticsForFile(request!.TextDocument!.Uri).Please(cancellationToken); - var vsReport = new VSInternalDiagnosticReport + var vsReport = new VSInternalDiagnosticReport { ResultId = report.ResultId, //Identifier = 1, //Version = 1, - - Diagnostics = - report.Diagnostics.Select(d => - - new Diagnostic - { - Range = new Microsoft.VisualStudio.LanguageServer.Protocol.Range - { - // F# uses 1-based indexing for lines, need to adjust - Start = new Position { Line = d.StartLine-1, Character = d.StartColumn }, - End = new Position { Line = d.EndLine-1, Character = d.EndColumn } - }, - Severity = DiagnosticSeverity.Error, - Message = $"LSP: {d.Message}", - //Source = "Intellisense", - Code = d.ErrorNumberText - } - ).ToArray() + Diagnostics = [.. report.Diagnostics.Select(FSharpDiagnosticExtensions.ToLspDiagnostic)] }; return [vsReport]; } - [LanguageServerEndpoint("textDocument/_vs_getProjectContexts")] + [LanguageServerEndpoint("textDocument/_vs_getProjectContexts", LanguageServerConstants.DefaultLanguageName)] public Task HandleRequestAsync(VSGetProjectContextsParams request, FSharpRequestContext context, CancellationToken cancellationToken) { return Task.FromResult(new VSProjectContextList() @@ -331,11 +292,10 @@ internal class FSharpLanguageServerProvider : LanguageServerProvider } - var ((clientStream, serverStream), _server) = FSharpLanguageServer.Create(workspace, (serviceCollection) => + var ((inputStream, outputStream), _server) = FSharpLanguageServer.Create(workspace, (serviceCollection) => { serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); }); var solutions = await ws.QuerySolutionAsync( @@ -352,10 +312,9 @@ internal class FSharpLanguageServerProvider : LanguageServerProvider .SubscribeAsync(new SolutionObserver(), CancellationToken.None); } - return new DuplexPipe( - PipeReader.Create(clientStream), - PipeWriter.Create(serverStream)); + PipeReader.Create(inputStream), + PipeWriter.Create(outputStream)); } /// diff --git a/src/Microsoft.CommonLanguageServerProtocol.Framework.Proxy/Microsoft.CommonLanguageServerProtocol.Framework.Proxy.csproj b/src/Microsoft.CommonLanguageServerProtocol.Framework.Proxy/Microsoft.CommonLanguageServerProtocol.Framework.Proxy.csproj new file mode 100644 index 00000000000..97ac5ccaebe --- /dev/null +++ b/src/Microsoft.CommonLanguageServerProtocol.Framework.Proxy/Microsoft.CommonLanguageServerProtocol.Framework.Proxy.csproj @@ -0,0 +1,19 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/FSharpWorkspace.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/FSharpWorkspace.fs index fbae320131d..8da785b92d8 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/FSharpWorkspace.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/FSharpWorkspace.fs @@ -7,35 +7,10 @@ open FSharp.Compiler.CodeAnalysis.ProjectSnapshot open TestFramework open FSharp.Compiler.IO open FSharp.Compiler.CodeAnalysis.Workspace +open FSharp.Test.ProjectGeneration.WorkspaceHelpers #nowarn "57" -type ProjectConfig with - - static member Minimal(?name, ?outputPath, ?referencesOnDisk) = - let name = defaultArg name "test" - let projectFileName = $"{name}.fsproj" - let outputPath = defaultArg outputPath $"{name}.dll" - let referencesOnDisk = defaultArg referencesOnDisk [] - ProjectConfig(projectFileName, Some outputPath, referencesOnDisk, []) - -let getReferencedSnapshot (projectIdentifier: FSharpProjectIdentifier) (projectSnapshot: FSharpProjectSnapshot) = - projectSnapshot.ReferencedProjects - |> Seq.pick (function - | FSharpReference(x, snapshot) when x = projectIdentifier.OutputFileName -> Some snapshot - | _ -> None) - -let sourceFileOnDisk (content: string) = - let path = getTemporaryFileName () + ".fs" - FileSystem.OpenFileForWriteShim(path).Write(content) - Uri(path) - -let assertFileHasContent filePath expectedContent (projectSnapshot: FSharpProjectSnapshot) = - let fileSnapshot = - projectSnapshot.SourceFiles |> Seq.find (_.FileName >> (=) filePath) - - Assert.Equal(expectedContent, fileSnapshot.GetSource().Result.ToString()) - [] let ``Add project to workspace`` () = let workspace = FSharpWorkspace() @@ -89,7 +64,7 @@ let ``Close file in workspace`` () = let fileOnDisk = sourceFileOnDisk contentOnDisk let _projectIdentifier = - workspace.Projects.AddOrUpdate(ProjectConfig.Minimal(), [ fileOnDisk.LocalPath ]) + workspace.Projects.AddOrUpdate(ProjectConfig.Empty(), [ fileOnDisk.LocalPath ]) workspace.Files.Open(fileOnDisk, contentOnDisk) @@ -123,7 +98,7 @@ let ``Change file in workspace`` () = let fileUri = Uri("file:///test.fs") let _projectIdentifier = - workspace.Projects.AddOrUpdate(ProjectConfig.Minimal(), [ fileUri.LocalPath ]) + workspace.Projects.AddOrUpdate(ProjectConfig.Empty(), [ fileUri.LocalPath ]) let initialContent = "let x = 2" @@ -177,17 +152,17 @@ let ``Propagate changes to snapshots`` () = let workspace = FSharpWorkspace() let file1 = sourceFileOnDisk "let x = 1" - let pid1 = workspace.Projects.AddOrUpdate(ProjectConfig.Minimal("p1"), [ file1.LocalPath ]) + let pid1 = workspace.Projects.AddOrUpdate(ProjectConfig.Empty("p1"), [ file1.LocalPath ]) let file2 = sourceFileOnDisk "let y = 2" let pid2 = - workspace.Projects.AddOrUpdate(ProjectConfig.Minimal("p2", referencesOnDisk = [ pid1.OutputFileName ]), [ file2.LocalPath ]) + workspace.Projects.AddOrUpdate(ProjectConfig.Empty("p2", referencesOnDisk = [ pid1.OutputFileName ]), [ file2.LocalPath ]) let file3 = sourceFileOnDisk "let z = 3" let pid3 = - workspace.Projects.AddOrUpdate(ProjectConfig.Minimal("p3", referencesOnDisk = [ pid2.OutputFileName ]), [ file3.LocalPath ]) + workspace.Projects.AddOrUpdate(ProjectConfig.Empty("p3", referencesOnDisk = [ pid2.OutputFileName ]), [ file3.LocalPath ]) let s3 = workspace.Query.GetProjectSnapshot(pid3).Value diff --git a/tests/FSharp.Compiler.LanguageServer.Tests/FSharp.Compiler.LanguageServer.Tests.fsproj b/tests/FSharp.Compiler.LanguageServer.Tests/FSharp.Compiler.LanguageServer.Tests.fsproj index dbffa2deb13..046357aafe6 100644 --- a/tests/FSharp.Compiler.LanguageServer.Tests/FSharp.Compiler.LanguageServer.Tests.fsproj +++ b/tests/FSharp.Compiler.LanguageServer.Tests/FSharp.Compiler.LanguageServer.Tests.fsproj @@ -15,7 +15,7 @@ - + @@ -37,6 +37,10 @@ + + + + diff --git a/tests/FSharp.Compiler.LanguageServer.Tests/LanguageServerTests.fs b/tests/FSharp.Compiler.LanguageServer.Tests/LanguageServerTests.fs deleted file mode 100644 index f1f633a4c0d..00000000000 --- a/tests/FSharp.Compiler.LanguageServer.Tests/LanguageServerTests.fs +++ /dev/null @@ -1,59 +0,0 @@ -module LanguageServerTests - -open System -open Xunit - -open FSharp.Compiler.LanguageServer -open StreamJsonRpc -open System.IO -open System.Diagnostics - -open Microsoft.VisualStudio.LanguageServer.Protocol -open Nerdbank.Streams - -[] -let ``The server can process the initialization message`` () = - - // Create a StringWriter to capture the output - let rpcTrace = new StringWriter() - - try - - let struct (clientStream, _serverStream) = FullDuplexStream.CreatePair() - - use formatter = new JsonMessageFormatter() - - use messageHandler = - new HeaderDelimitedMessageHandler(clientStream, clientStream, formatter) - - use jsonRpc = new JsonRpc(messageHandler) - - // Create a new TraceListener with the StringWriter - let listener = new TextWriterTraceListener(rpcTrace) - - // Add the listener to the JsonRpc TraceSource - jsonRpc.TraceSource.Listeners.Add(listener) |> ignore - - // Set the TraceLevel to Information to get all informational, warning and error messages - jsonRpc.TraceSource.Switch.Level <- SourceLevels.Information - - //jsonRpc.inv - - // Now all JsonRpc debug information will be written to the StringWriter - - let log = ResizeArray() - - let _s = new FSharpLanguageServer(jsonRpc, (LspLogger log.Add)) - - jsonRpc.StartListening() - - //let initializeParams = InitializeParams( - // ProcessId = System.Diagnostics.Process.GetCurrentProcess().Id, - // RootUri = Uri("file:///c:/temp"), - // InitializationOptions = None, - // RootPath = "file:///c:/temp") - - finally - - let _output = rpcTrace.ToString() - () diff --git a/tests/FSharp.Compiler.LanguageServer.Tests/Protocol.fs b/tests/FSharp.Compiler.LanguageServer.Tests/Protocol.fs new file mode 100644 index 00000000000..9072fbfb460 --- /dev/null +++ b/tests/FSharp.Compiler.LanguageServer.Tests/Protocol.fs @@ -0,0 +1,193 @@ +module LanguageServer.Protocol + +open System +open Xunit + +open FSharp.Compiler.LanguageServer +open StreamJsonRpc +open System.IO +open System.Diagnostics + +open Microsoft.VisualStudio.LanguageServer.Protocol +open Nerdbank.Streams + +open FSharp.Test.ProjectGeneration.WorkspaceHelpers +open FSharp.Compiler.CodeAnalysis.Workspace +open FSharp.Compiler.CodeAnalysis.ProjectSnapshot + +#nowarn "57" + +type TestRpcClient(jsonRpc, rpcTrace, workspace, initializeResult: InitializeResult) = + + member val JsonRpc = jsonRpc + member val RpcTraceWriter = rpcTrace + member val Workspace = workspace + member val Capabilities = initializeResult.Capabilities + + member _.RpcTrace = rpcTrace.ToString() + +let initializeLanguageServer (workspace) = + + let workspace = defaultArg workspace (FSharpWorkspace()) + + // Create a StringWriter to capture the output + let rpcTrace = new StringWriter() + + let (inputStream, outputStream), server = FSharpLanguageServer.Create(workspace) + + let formatter = new JsonMessageFormatter() + + let messageHandler = + new HeaderDelimitedMessageHandler(inputStream, outputStream, formatter) + + let jsonRpc = new JsonRpc(messageHandler) + + // Create a new TraceListener with the StringWriter + let listener = new TextWriterTraceListener(rpcTrace) + + // Add the listener to the JsonRpc TraceSource + server.JsonRpc.TraceSource.Listeners.Add(listener) |> ignore + + // Set the TraceLevel to Information to get all informational, warning and error messages + server.JsonRpc.TraceSource.Switch.Level <- SourceLevels.All + + let initializeParams = + InitializeParams( + ProcessId = System.Diagnostics.Process.GetCurrentProcess().Id, + RootUri = Uri("file:///c:/temp"), + InitializationOptions = None + ) + + jsonRpc.StartListening() + + task { + let! response = jsonRpc.InvokeAsync("initialize", initializeParams) + return TestRpcClient(jsonRpc, rpcTrace, workspace, response) + } + +[] +let ``The server can process the initialization message`` () = + task { + + let! client = initializeLanguageServer None + Assert.NotNull(client.Capabilities) + + } + +/// Initialize workspace, open a document, get diagnostics, edit the document, get diagnostics, close the document +[] +let ``Basic server workflow`` () = + task { + + let! client = initializeLanguageServer None + + let workspace = client.Workspace + + let contentOnDisk = "let x = 1" + let fileOnDisk = sourceFileOnDisk contentOnDisk + + let _projectIdentifier = + workspace.Projects.AddOrUpdate(ProjectConfig.Create(), [ fileOnDisk.LocalPath ]) + + do! + client.JsonRpc.NotifyAsync( + Methods.TextDocumentDidOpenName, + DidOpenTextDocumentParams( + TextDocument = TextDocumentItem(Uri = fileOnDisk, LanguageId = "F#", Version = 1, Text = contentOnDisk) + ) + ) + + let! diagnosticsResponse = + client.JsonRpc.InvokeAsync>( + Methods.TextDocumentDiagnosticName, + DocumentDiagnosticParams(TextDocument = TextDocumentIdentifier(Uri = fileOnDisk)) + ) + + Assert.Equal( + 0, + (diagnosticsResponse.First.RelatedDocuments + |> Seq.head + |> _.Value.First.Items.Length) + ) + + let contentEdit = $"{contentOnDisk}\nx <- 2" + + do! + client.JsonRpc.NotifyAsync( + Methods.TextDocumentDidChangeName, + DidChangeTextDocumentParams( + TextDocument = VersionedTextDocumentIdentifier(Uri = fileOnDisk, Version = 2), + ContentChanges = [| TextDocumentContentChangeEvent(Text = contentEdit) |] + ) + ) + + let! diagnosticsResponse = + client.JsonRpc.InvokeAsync>( + Methods.TextDocumentDiagnosticName, + DocumentDiagnosticParams(TextDocument = TextDocumentIdentifier(Uri = fileOnDisk)) + ) + + let diagnostics = + diagnosticsResponse.First.RelatedDocuments |> Seq.head |> _.Value.First.Items + + Assert.Equal(1, diagnostics.Length) + Assert.Contains("This value is not mutable", diagnostics[0].Message) + + do! + client.JsonRpc.NotifyAsync( + Methods.TextDocumentDidCloseName, + DidCloseTextDocumentParams(TextDocument = TextDocumentIdentifier(Uri = fileOnDisk)) + ) + + let! diagnosticsResponse = + client.JsonRpc.InvokeAsync>( + Methods.TextDocumentDiagnosticName, + DocumentDiagnosticParams(TextDocument = TextDocumentIdentifier(Uri = fileOnDisk)) + ) + + // We didn't save the file, so it should be again read from disk and have no diagnostics + Assert.Equal( + 0, + (diagnosticsResponse.First.RelatedDocuments + |> Seq.head + |> _.Value.First.Items.Length) + ) + } + +[] +let ``Full semantic tokens`` () = + + task { + let! client = initializeLanguageServer None + let workspace = client.Workspace + let contentOnDisk = "let x = 1" + let fileOnDisk = sourceFileOnDisk contentOnDisk + let _projectIdentifier = + workspace.Projects.AddOrUpdate(ProjectConfig.Create(), [ fileOnDisk.LocalPath ]) + do! + client.JsonRpc.NotifyAsync( + Methods.TextDocumentDidOpenName, + DidOpenTextDocumentParams( + TextDocument = TextDocumentItem(Uri = fileOnDisk, LanguageId = "F#", Version = 1, Text = contentOnDisk) + ) + ) + let! semanticTokensResponse = + client.JsonRpc.InvokeAsync( + Methods.TextDocumentSemanticTokensFullName, + SemanticTokensParams(TextDocument = TextDocumentIdentifier(Uri = fileOnDisk)) + ) + + let expected = [| 0; 0; 0; 1; 0; 0; 0; 3; 15; 0; 0; 4; 1; 17; 0; 0; 4; 1; 19; 0 |] + + Assert.Equal(expected, semanticTokensResponse.Data) + } + +[] +let ``Shutdown and exit`` () = + task { + let! client = initializeLanguageServer None + + let! _respone = client.JsonRpc.InvokeAsync<_>(Methods.ShutdownName) + + do! client.JsonRpc.NotifyAsync(Methods.ExitName) + } diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl index b66bebd1cbc..25e8adafbe9 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl @@ -2451,6 +2451,8 @@ FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Boolean get_ FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectIdentifier Identifier FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectIdentifier get_Identifier() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot Create(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk], Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot], Boolean, Boolean, System.DateTime, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet], Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]], Microsoft.FSharp.Core.FSharpOption`1[System.Int64]) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot FromCommandLineArgs(System.String[], System.String, System.String) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot FromResponseFile(System.IO.FileInfo, System.String) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot Replace(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot]) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot] SourceFiles FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot] get_SourceFiles() @@ -2473,6 +2475,8 @@ FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FS FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.String] ProjectId FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.String] get_OutputFileName() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.String] get_ProjectId() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: ProjectConfig ProjectConfig +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: ProjectConfig get_ProjectConfig() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: System.DateTime LoadTime FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: System.DateTime get_LoadTime() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: System.String Label diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl index b66bebd1cbc..25e8adafbe9 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl @@ -2451,6 +2451,8 @@ FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Boolean get_ FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectIdentifier Identifier FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectIdentifier get_Identifier() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot Create(System.String, Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Core.FSharpOption`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+ReferenceOnDisk], Microsoft.FSharp.Collections.FSharpList`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpReferencedProjectSnapshot], Boolean, Boolean, System.DateTime, Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpUnresolvedReferencesSet], Microsoft.FSharp.Collections.FSharpList`1[System.Tuple`3[FSharp.Compiler.Text.Range,System.String,System.String]], Microsoft.FSharp.Core.FSharpOption`1[System.Int64]) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot FromCommandLineArgs(System.String[], System.String, System.String) +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot FromResponseFile(System.IO.FileInfo, System.String) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: FSharpProjectSnapshot Replace(Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot]) FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot] SourceFiles FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpFileSnapshot] get_SourceFiles() @@ -2473,6 +2475,8 @@ FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FS FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.String] ProjectId FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.String] get_OutputFileName() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: Microsoft.FSharp.Core.FSharpOption`1[System.String] get_ProjectId() +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: ProjectConfig ProjectConfig +FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: ProjectConfig get_ProjectConfig() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: System.DateTime LoadTime FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: System.DateTime get_LoadTime() FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot: System.String Label diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 197788b0e2e..2e5e5fd8961 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -35,6 +35,8 @@ open FSharp.Test.Utilities open OpenTelemetry open OpenTelemetry.Resources open OpenTelemetry.Trace +open TestFramework +open FSharp.Compiler.IO #nowarn "57" // Experimental feature use @@ -94,7 +96,7 @@ module ReferenceHelpers = { Name = matches.Groups.[1].Value Version = version - Path = DirectoryInfo(Path.Combine(matches.Groups[3].Value, version)) }) + Path = DirectoryInfo(Path.Combine(matches.Groups[3].Value, version)) }) |> Seq.toList) let getFrameworkReference (reference: Reference) = @@ -263,7 +265,7 @@ type SyntheticProject = static member Create(name: string, [] sourceFiles: SyntheticSourceFile[]) = { SyntheticProject.Create(name) with SourceFiles = sourceFiles |> List.ofArray } - + static member CreateForScript(scriptFile: SyntheticSourceFile) = { SyntheticProject.Create() with SourceFiles = [scriptFile]; UseScriptResolutionRules = true } @@ -350,7 +352,7 @@ type SyntheticProject = OriginalLoadReferences = [] Stamp = None } - + OptionsCache.GetOrAdd(key, factory).Value @@ -484,14 +486,14 @@ let private writeFile (p: SyntheticProject) (f: SyntheticSourceFile) = let mkSyntheticProjectForResponseFile (responseFile: FileInfo) : SyntheticProject = if not responseFile.Exists then failwith $"%s{responseFile.FullName} does not exist" - + let compilerArgs = File.ReadAllLines responseFile.FullName let fsharpFileExtensions = set [| ".fs" ; ".fsi" ; ".fsx" |] let isFSharpFile (file : string) = Set.exists (fun (ext : string) -> file.EndsWith (ext, StringComparison.Ordinal)) fsharpFileExtensions - + let fsharpFiles = compilerArgs |> Array.choose (fun (line : string) -> @@ -516,7 +518,7 @@ let mkSyntheticProjectForResponseFile (responseFile: FileInfo) : SyntheticProjec implementationFiles |> List.map (fun implPath -> let id = - let fileNameWithoutExtension = Path.GetFileNameWithoutExtension implPath + let fileNameWithoutExtension = Path.GetFileNameWithoutExtension implPath let directoryOfFile = FileInfo(implPath).DirectoryName let relativeUri = Uri(responseFile.FullName).MakeRelativeUri(Uri(directoryOfFile)) let relativeFolderPath = Uri.UnescapeDataString(relativeUri.ToString()).Replace('/', Path.DirectorySeparatorChar) @@ -535,10 +537,10 @@ let mkSyntheticProjectForResponseFile (responseFile: FileInfo) : SyntheticProjec Source = File.ReadAllText implPath ExtraSource = "" EntryPoint = false - IsPhysicalFile = true + IsPhysicalFile = true } ) - + let otherOptions = compilerArgs |> Array.filter (fun line -> not (isFSharpFile line)) @@ -769,13 +771,13 @@ module ProjectOperations = |> Seq.toArray Assert.Equal<(string * int * int * int)[]>(expected |> Seq.sort |> Seq.toArray, actual) - + let expectNone x = if Option.isSome x then failwith "expected None, but was Some" - + let expectSome x = if Option.isNone x then failwith "expected Some, but was None" - + let rec saveProject (p: SyntheticProject) generateSignatureFiles checker = async { Directory.CreateDirectory(p.ProjectDir) |> ignore @@ -808,19 +810,19 @@ module ProjectOperations = let rec absorbAutoGeneratedSignatures checker (p: SyntheticProject) = async { do! saveProject p true checker - let files = [ + let files = [ for file in p.SourceFiles do if file.SignatureFile = AutoGenerated then let text = file |> getSignatureFilePath p |> File.ReadAllText { file with SignatureFile = Custom text } - else file + else file ] - let! projects = - p.DependsOn + let! projects = + p.DependsOn |> Seq.map (absorbAutoGeneratedSignatures checker) |> Async.Sequential - return - { p with + return + { p with SourceFiles = files AutoAddModules = false DependsOn = projects |> Array.toList } @@ -936,7 +938,7 @@ let SaveAndCheckProject project checker isExistingProject = Cursor = None } } -type MoveFileDirection = Up | Down +type MoveFileDirection = Up | Down type ProjectWorkflowBuilder ( @@ -1361,7 +1363,7 @@ type ProjectWorkflowBuilder if ex.IsSome then raise ex.Value return ctx } - + [] member this.TryGetRecentCheckResults(workflow: Async, fileId: string, expected) = async { @@ -1372,15 +1374,15 @@ type ProjectWorkflowBuilder let! snapshot = FSharpProjectSnapshot.FromOptions(options, getFileSnapshot ctx.Project) let r = checker.TryGetRecentCheckResultsForFile(fileName, snapshot) expected r - + match r with | Some(parseFileResults, checkFileResults) -> - let signature = getSignature(parseFileResults, FSharpCheckFileAnswer.Succeeded(checkFileResults)) + let signature = getSignature(parseFileResults, FSharpCheckFileAnswer.Succeeded(checkFileResults)) match ctx.Signatures.TryFind(fileId) with | Some priorSignature -> Assert.Equal(priorSignature, signature) | None -> () | None -> () - + return ctx } @@ -1423,7 +1425,7 @@ type SyntheticProject with projectDir ++ node.Attributes["Include"].InnerText ] |> List.partition (fun path -> path.EndsWith ".fsi") let signatureFiles = set signatureFiles - + let parseReferences refType = [ for node in fsproj.DocumentElement.SelectNodes($"//{refType}") do { Name = node.Attributes["Include"].InnerText @@ -1449,3 +1451,44 @@ type SyntheticProject with OtherOptions = [ for w in nowarns do $"--nowarn:{w}" ] } + + +module WorkspaceHelpers = + + type ProjectConfig with + + /// Creates an empty project configuration with optional parameters for name, output path, and references on disk. + static member Empty(?name, ?outputPath, ?referencesOnDisk) = + let name = defaultArg name "test" + let projectFileName = $"{name}.fsproj" + let outputPath = defaultArg outputPath $"{name}.dll" + let referencesOnDisk = defaultArg referencesOnDisk [] + ProjectConfig(projectFileName, Some outputPath, referencesOnDisk, []) + + static member Create(?name) = + let name = defaultArg name "test" + + let snapshot, _ = + CompilerAssertHelpers.checker.GetProjectSnapshotFromScript(name, SourceTextNew.ofString "", assumeDotNetFramework = false) + |> Async.RunSynchronously + snapshot.ProjectConfig + + /// Retrieves the referenced project snapshot from the given project snapshot based on the project identifier. + let getReferencedSnapshot (projectIdentifier: FSharpProjectIdentifier) (projectSnapshot: FSharpProjectSnapshot) = + projectSnapshot.ReferencedProjects + |> Seq.pick (function + | FSharpReference(x, snapshot) when x = projectIdentifier.OutputFileName -> Some snapshot + | _ -> None) + + /// Creates a temporary source file on disk with the given content and returns its URI. + let sourceFileOnDisk (content: string) = + let path = getTemporaryFileName () + ".fs" + FileSystem.OpenFileForWriteShim(path).Write(content) + Uri(path) + + /// Asserts that the file at the given path within the project snapshot has the expected content. + let assertFileHasContent filePath expectedContent (projectSnapshot: FSharpProjectSnapshot) = + let fileSnapshot = + projectSnapshot.SourceFiles |> Seq.find (_.FileName >> (=) filePath) + + Assert.Equal(expectedContent, fileSnapshot.GetSource().Result.ToString()) diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl index d59c7d2adda..331b8bb361a 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl @@ -21,7 +21,7 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x00000082][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x0000008B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+MagicAssemblyResolution::ResolveAssemblyCore([FSharp.Compiler.Service]Internal.Utilities.Library.CompilationThreadToken, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, [FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, [FSharp.Compiler.Service]FSharp.Compiler.CompilerImports+TcImports, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompiler, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiConsoleOutput, string)][offset 0x00000015][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3510-791::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001E5][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3510-792::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001E5][found Char] Unexpected type on the stack. [IL]: Error [UnmanagedPointer]: : FSharp.Compiler.Interactive.Shell+Utilities+pointerToNativeInt@110::Invoke(object)][offset 0x00000007] Unmanaged pointers are not a verifiable type. [IL]: Error [StackUnexpected]: : .$FSharpCheckerResults+dataTipOfReferences@2205::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000084][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-504::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack. diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl index be76953ef7d..16f3dd27fb0 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl @@ -28,7 +28,7 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x0000008B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+FsiStdinSyphon::GetLine(string, int32)][offset 0x00000039][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+MagicAssemblyResolution::ResolveAssemblyCore([FSharp.Compiler.Service]Internal.Utilities.Library.CompilationThreadToken, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, [FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, [FSharp.Compiler.Service]FSharp.Compiler.CompilerImports+TcImports, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompiler, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiConsoleOutput, string)][offset 0x00000015][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3510-791::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001E5][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3510-792::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001E5][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+FsiInteractionProcessor::CompletionsForPartialLID([FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompilerState, string)][offset 0x0000001B][found Char] Unexpected type on the stack. [IL]: Error [UnmanagedPointer]: : FSharp.Compiler.Interactive.Shell+Utilities+pointerToNativeInt@110::Invoke(object)][offset 0x00000007] Unmanaged pointers are not a verifiable type. [IL]: Error [StackUnexpected]: : .$FSharpCheckerResults+dataTipOfReferences@2205::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000084][found Char] Unexpected type on the stack. diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl index 1c6248ec60a..82567e7b827 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl @@ -21,7 +21,7 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x00000082][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x0000008B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+MagicAssemblyResolution::ResolveAssemblyCore([FSharp.Compiler.Service]Internal.Utilities.Library.CompilationThreadToken, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, [FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, [FSharp.Compiler.Service]FSharp.Compiler.CompilerImports+TcImports, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompiler, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiConsoleOutput, string)][offset 0x00000015][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3510-831::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001C7][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3510-832::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001C7][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$FSharpCheckerResults+GetReferenceResolutionStructuredToolTipText@2205::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000076][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-525::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-525::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000003B][found Char] Unexpected type on the stack. diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl index fc795ecde1b..266e340248d 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl @@ -28,7 +28,7 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x0000008B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+FsiStdinSyphon::GetLine(string, int32)][offset 0x00000032][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+MagicAssemblyResolution::ResolveAssemblyCore([FSharp.Compiler.Service]Internal.Utilities.Library.CompilationThreadToken, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, [FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, [FSharp.Compiler.Service]FSharp.Compiler.CompilerImports+TcImports, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompiler, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiConsoleOutput, string)][offset 0x00000015][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3510-831::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001C7][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3510-832::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001C7][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+FsiInteractionProcessor::CompletionsForPartialLID([FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompilerState, string)][offset 0x00000024][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$FSharpCheckerResults+GetReferenceResolutionStructuredToolTipText@2205::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000076][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.AssemblyContent+traverseMemberFunctionAndValues@176::Invoke([FSharp.Compiler.Service]FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue)][offset 0x0000002B][found Char] Unexpected type on the stack. diff --git a/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs b/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs index 78a28eb0de2..4299936d05a 100644 --- a/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs +++ b/vsintegration/src/FSharp.Editor/Common/CancellableTasks.fs @@ -336,7 +336,7 @@ module CancellableTasks = else let mutable awaiter = sm.ResumptionDynamicInfo.ResumptionData - :?> ICriticalNotifyCompletion + :?> ICriticalNotifyCompletion | null assert not (isNull awaiter) sm.Data.MethodBuilder.AwaitUnsafeOnCompleted(&awaiter, &sm) @@ -386,7 +386,7 @@ module CancellableTasks = (MoveNextMethodImpl<_>(fun sm -> //-- RESUMABLE CODE START __resumeAt sm.ResumptionPoint - let mutable __stack_exn: Exception = null + let mutable __stack_exn: Exception | null = null try let __stack_code_fin = code.Invoke(&sm) From 091a26027b641bc216db9cae5b9ca1d8f8aa490d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Pokorn=C3=BD?= Date: Wed, 4 Dec 2024 13:28:54 +0100 Subject: [PATCH 38/60] update ilverify baselines --- .../ILVerify/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl | 2 +- .../ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl | 2 +- .../ilverify_FSharp.Compiler.Service_Release_net9.0.bsl | 2 +- .../ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl index 4df538c11d4..b5cf7e8da0b 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl @@ -21,7 +21,7 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x00000082][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x0000008B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+MagicAssemblyResolution::ResolveAssemblyCore([FSharp.Compiler.Service]Internal.Utilities.Library.CompilationThreadToken, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, [FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, [FSharp.Compiler.Service]FSharp.Compiler.CompilerImports+TcImports, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompiler, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiConsoleOutput, string)][offset 0x00000015][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3510-787::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001E5][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3510-788::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001E5][found Char] Unexpected type on the stack. [IL]: Error [UnmanagedPointer]: : FSharp.Compiler.Interactive.Shell+Utilities+pointerToNativeInt@110::Invoke(object)][offset 0x00000007] Unmanaged pointers are not a verifiable type. [IL]: Error [StackUnexpected]: : .$FSharpCheckerResults+dataTipOfReferences@2205::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000084][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-500::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack. diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl index 14796a8c2f1..60f6b9d27d2 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl @@ -28,7 +28,7 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x0000008B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+FsiStdinSyphon::GetLine(string, int32)][offset 0x00000039][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+MagicAssemblyResolution::ResolveAssemblyCore([FSharp.Compiler.Service]Internal.Utilities.Library.CompilationThreadToken, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, [FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, [FSharp.Compiler.Service]FSharp.Compiler.CompilerImports+TcImports, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompiler, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiConsoleOutput, string)][offset 0x00000015][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3510-787::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001E5][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3510-788::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001E5][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+FsiInteractionProcessor::CompletionsForPartialLID([FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompilerState, string)][offset 0x0000001B][found Char] Unexpected type on the stack. [IL]: Error [UnmanagedPointer]: : FSharp.Compiler.Interactive.Shell+Utilities+pointerToNativeInt@110::Invoke(object)][offset 0x00000007] Unmanaged pointers are not a verifiable type. [IL]: Error [StackUnexpected]: : .$FSharpCheckerResults+dataTipOfReferences@2205::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000084][found Char] Unexpected type on the stack. diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl index 170cf9c2b2b..a930478a45e 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl @@ -21,7 +21,7 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x00000082][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x0000008B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+MagicAssemblyResolution::ResolveAssemblyCore([FSharp.Compiler.Service]Internal.Utilities.Library.CompilationThreadToken, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, [FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, [FSharp.Compiler.Service]FSharp.Compiler.CompilerImports+TcImports, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompiler, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiConsoleOutput, string)][offset 0x00000015][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3510-829::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001C7][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3510-830::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001C7][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$FSharpCheckerResults+GetReferenceResolutionStructuredToolTipText@2205::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000076][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-523::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-523::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000003B][found Char] Unexpected type on the stack. diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl index 183ffc579fd..6e58910d95a 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl @@ -28,7 +28,7 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x0000008B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+FsiStdinSyphon::GetLine(string, int32)][offset 0x00000032][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+MagicAssemblyResolution::ResolveAssemblyCore([FSharp.Compiler.Service]Internal.Utilities.Library.CompilationThreadToken, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, [FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, [FSharp.Compiler.Service]FSharp.Compiler.CompilerImports+TcImports, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompiler, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiConsoleOutput, string)][offset 0x00000015][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3510-829::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001C7][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3510-830::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001C7][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+FsiInteractionProcessor::CompletionsForPartialLID([FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompilerState, string)][offset 0x00000024][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$FSharpCheckerResults+GetReferenceResolutionStructuredToolTipText@2205::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000076][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.AssemblyContent+traverseMemberFunctionAndValues@176::Invoke([FSharp.Compiler.Service]FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue)][offset 0x0000002B][found Char] Unexpected type on the stack. From e01bc94888efc225981127bd8284b07fc4568986 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Tue, 17 Dec 2024 09:33:47 +0100 Subject: [PATCH 39/60] Some LSP updates (#18141) - Added some activity tracing - Disabled using assembly on disk based on modified time --- src/Compiler/Facilities/AsyncMemoize.fs | 4 +- src/Compiler/Service/FSharpWorkspace.fs | 9 +- src/Compiler/Service/FSharpWorkspaceQuery.fs | 50 ++++- src/Compiler/Service/FSharpWorkspaceState.fs | 116 +++++++++-- src/Compiler/Service/TransparentCompiler.fs | 19 +- src/Compiler/Utilities/lib.fs | 10 +- src/Compiler/Utilities/lib.fsi | 4 + src/FSharp.Compiler.LanguageServer/Utils.fs | 36 ++++ .../FSharp.VisualStudio.Extension.csproj | 20 +- .../FSharpLanguageServerProvider.cs | 46 ++++- .../CompilerService/FSharpWorkspace.fs | 194 ++++++++++++++++-- .../FSharpChecker/CommonWorkflows.fs | 8 +- ...ervice.SurfaceArea.netstandard20.debug.bsl | 8 +- ...vice.SurfaceArea.netstandard20.release.bsl | 8 +- .../ProjectGeneration.fs | 69 ++++++- ...y_FSharp.Compiler.Service_Debug_net9.0.bsl | 2 +- ....Compiler.Service_Debug_netstandard2.0.bsl | 4 +- ...FSharp.Compiler.Service_Release_net9.0.bsl | 2 +- ...ompiler.Service_Release_netstandard2.0.bsl | 80 ++++---- 19 files changed, 571 insertions(+), 118 deletions(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index 2e45d131a28..0cb7b926736 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -113,7 +113,7 @@ module internal Utils = /// Return file name with one directory above it let shortPath (path: string) = - let dirPath = !! Path.GetDirectoryName(path) + let dirPath = Path.GetDirectoryName(path) |> Option.ofObj |> Option.defaultValue "" let dir = dirPath.Split Path.DirectorySeparatorChar @@ -243,7 +243,7 @@ type internal AsyncMemoize<'TKey, 'TVersion, 'TValue when 'TKey: equality and 'T |> Option.map countHit |> Option.defaultWith cacheSetNewJob - async { + async { let otherVersions, job = lock cache getOrAdd log Requested key diff --git a/src/Compiler/Service/FSharpWorkspace.fs b/src/Compiler/Service/FSharpWorkspace.fs index ccde4b98fb1..0b557386d07 100644 --- a/src/Compiler/Service/FSharpWorkspace.fs +++ b/src/Compiler/Service/FSharpWorkspace.fs @@ -46,14 +46,7 @@ type FSharpWorkspace(checker: FSharpChecker) = ) ) - member internal this.Debug_DumpMermaid(path) = - let content = - depGraph.Debug_RenderMermaid (function - // Collapse all reference on disk nodes into one. Otherwise the graph is too big to render. - | WorkspaceGraphTypes.WorkspaceNodeKey.ReferenceOnDisk _ -> WorkspaceGraphTypes.WorkspaceNodeKey.ReferenceOnDisk "..." - | x -> x) - - File.WriteAllText(__SOURCE_DIRECTORY__ + path, content) + member internal _.DepGraph = depGraph /// The `FSharpChecker` instance used by this workspace. member _.Checker = checker diff --git a/src/Compiler/Service/FSharpWorkspaceQuery.fs b/src/Compiler/Service/FSharpWorkspaceQuery.fs index b720608b27f..39c87094928 100644 --- a/src/Compiler/Service/FSharpWorkspaceQuery.fs +++ b/src/Compiler/Service/FSharpWorkspaceQuery.fs @@ -5,12 +5,15 @@ module FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery open System open System.Collections.Generic +open FSharp.Compiler.Diagnostics open System.Threading open FSharp.Compiler.CodeAnalysis open Internal.Utilities.DependencyGraph +open Internal.Utilities.Library.Extras open FSharpWorkspaceState +open Internal.Utilities.Library #nowarn "57" @@ -32,13 +35,20 @@ type FSharpWorkspaceQuery internal (depGraph: IThreadSafeDependencyGraph<_, _>, // in order to be able to clear previous diagnostics let getDiagnosticResultId () = Interlocked.Increment(&resultIdCounter) + member internal _.Checker = checker + member _.GetProjectSnapshot projectIdentifier = + use _ = + Activity.start "GetProjectSnapshot" [ Activity.Tags.project, projectIdentifier.ToString() |> (!!) ] + try depGraph.GetProjectSnapshot projectIdentifier |> Some with :? KeyNotFoundException -> None member _.GetProjectSnapshotForFile(file: Uri) = + use _ = + Activity.start "GetProjectSnapshotForFile" [ Activity.Tags.fileName, file.LocalPath ] depGraph.GetProjectsContaining file.LocalPath @@ -47,28 +57,48 @@ type FSharpWorkspaceQuery internal (depGraph: IThreadSafeDependencyGraph<_, _>, // Otherwise we have to keep track of which project/configuration is active |> Seq.tryHead // For now just get the first one - // TODO: split to parse and check diagnostics - member this.GetDiagnosticsForFile(file: Uri) = + member this.GetParseAndCheckResultsForFile(file: Uri) = async { - let! diagnostics = + use _ = + Activity.start "GetParseAndCheckResultsForFile" [ Activity.Tags.fileName, file.LocalPath ] + + return! this.GetProjectSnapshotForFile file |> Option.map (fun snapshot -> async { - let! parseResult, checkFileAnswer = - checker.ParseAndCheckFileInProject(file.LocalPath, snapshot, "LSP Get diagnostics") + let! parseResult, checkFileAnswer = checker.ParseAndCheckFileInProject(file.LocalPath, snapshot) return match checkFileAnswer with - | FSharpCheckFileAnswer.Succeeded result -> result.Diagnostics - | FSharpCheckFileAnswer.Aborted -> parseResult.Diagnostics + | FSharpCheckFileAnswer.Succeeded result -> Some parseResult, Some result + | FSharpCheckFileAnswer.Aborted -> Some parseResult, None }) - |> Option.defaultValue (async.Return [||]) + |> Option.defaultValue (async.Return(None, None)) - return FSharpDiagnosticReport(diagnostics, getDiagnosticResultId ()) } - member this.GetSemanticClassification(file) = + member this.GetCheckResultsForFile(file) = + this.GetParseAndCheckResultsForFile file |> Async.map snd + + // TODO: split to parse and check diagnostics + member this.GetDiagnosticsForFile(file: Uri) = + use _ = + Activity.start "GetDiagnosticsForFile" [ Activity.Tags.fileName, file.LocalPath ] + + this.GetParseAndCheckResultsForFile file + |> Async.map (fun results -> + let diagnostics = + match results with + | _, Some checkResult -> checkResult.Diagnostics + | Some parseResult, _ -> parseResult.Diagnostics + | _ -> [||] + + FSharpDiagnosticReport(diagnostics, getDiagnosticResultId ())) + + member this.GetSemanticClassification(file: Uri) = + use _ = + Activity.start "GetSemanticClassification" [ Activity.Tags.fileName, file.LocalPath ] this.GetProjectSnapshotForFile file |> Option.map (fun snapshot -> diff --git a/src/Compiler/Service/FSharpWorkspaceState.fs b/src/Compiler/Service/FSharpWorkspaceState.fs index cc3d3d8524d..deda0a607b7 100644 --- a/src/Compiler/Service/FSharpWorkspaceState.fs +++ b/src/Compiler/Service/FSharpWorkspaceState.fs @@ -9,8 +9,10 @@ open System.Runtime.CompilerServices open System.Collections.Concurrent open FSharp.Compiler.CodeAnalysis.ProjectSnapshot +open FSharp.Compiler.Diagnostics open Internal.Utilities.Collections open Internal.Utilities.DependencyGraph +open Internal.Utilities.Library #nowarn "57" @@ -206,9 +208,7 @@ module internal WorkspaceDependencyGraphExtensions = [] static member GetProjectReferencesOf(this: IDependencyGraph<_, _>, project) = this.GetDependenciesOf(WorkspaceNodeKey.ProjectWithoutFiles project) - |> Seq.choose (function - | WorkspaceNodeKey.ProjectSnapshot projectId -> Some projectId - | _ -> None) + |> Seq.choose WorkspaceNode.projectSnapshotKey [] static member GetProjectsThatReference(this: IDependencyGraph<_, _>, dllPath) = @@ -224,8 +224,26 @@ module internal WorkspaceDependencyGraphExtensions = [] static member GetSourceFile(this: IDependencyGraph<_, _>, file) = - this.GetValue(WorkspaceNodeKey.SourceFile file) - |> _.Unpack(WorkspaceNode.sourceFile) + this.GetValue(WorkspaceNodeKey.SourceFile file).Unpack(WorkspaceNode.sourceFile) + + [] + static member GetFilesOf(this: IDependencyGraph<_, _>, project) = + this.GetDependenciesOf(WorkspaceNodeKey.ProjectSnapshot project) + |> Seq.map this.GetValue + |> Seq.choose WorkspaceNode.sourceFile + + [] + static member ReplaceSourceFiles(this: IDependencyGraph<_, _>, project, sourceFiles) = + let projectId = WorkspaceNodeKey.ProjectSnapshot project + + this.GetDependenciesOf(WorkspaceNodeKey.ProjectSnapshot project) + |> Seq.where (WorkspaceNode.sourceFileKey >> _.IsSome) + |> Seq.iter (fun fileId -> this.RemoveDependency(projectId, noLongerDependsOn = fileId)) + + sourceFiles + |> Seq.map (fun (file, snapshot) -> WorkspaceNodeKey.SourceFile file, WorkspaceNodeValue.SourceFile snapshot) + |> this.AddList + |> Seq.iter (fun fileId -> this.AddDependency(WorkspaceNodeKey.ProjectSnapshot project, dependsOn = fileId)) /// Interface for managing files in an F# workspace. [] @@ -235,21 +253,33 @@ type FSharpWorkspaceFiles internal (depGraph: IThreadSafeDependencyGraph<_, _>) let openFiles = ConcurrentDictionary() /// Indicates that a file has been opened and has the given content. Any updates to the file should be done through `Files.Edit`. - member this.Open = this.Edit + member _.Open(file: Uri, content) = + use _ = Activity.start "Files.Open" [ Activity.Tags.fileName, file.LocalPath ] + + openFiles.AddOrUpdate(file.LocalPath, content, (fun _ _ -> content)) |> ignore + depGraph.AddOrUpdateFile(file.LocalPath, FSharpFileSnapshot.CreateFromString(file.LocalPath, content)) /// Indicates that a file has been changed and now has the given content. If it wasn't previously open it is considered open now. member _.Edit(file: Uri, content) = + use _ = Activity.start "Files.Edit" [ Activity.Tags.fileName, file.LocalPath ] + openFiles.AddOrUpdate(file.LocalPath, content, (fun _ _ -> content)) |> ignore depGraph.AddOrUpdateFile(file.LocalPath, FSharpFileSnapshot.CreateFromString(file.LocalPath, content)) /// Indicates that a file has been closed. Any changes that were not saved to disk are undone and any further reading /// of the file's contents will be from the filesystem. member _.Close(file: Uri) = + use _ = Activity.start "Files.Close" [ Activity.Tags.fileName, file.LocalPath ] + openFiles.TryRemove(file.LocalPath) |> ignore // The file may have had changes that weren't saved to disk and are therefore undone by closing it. depGraph.AddOrUpdateFile(file.LocalPath, FSharpFileSnapshot.CreateFromFileSystem(file.LocalPath)) + /// Returns file paths for all source files of the given project. + member _.OfProject(projectIdentifier: FSharpProjectIdentifier) = + depGraph.GetFilesOf projectIdentifier |> Seq.map _.FileName + member internal _.GetFileContentIfOpen(path: string) = match openFiles.TryGetValue(path) with | true, content -> Some content @@ -262,8 +292,34 @@ type FSharpWorkspaceProjects internal (depGraph: IThreadSafeDependencyGraph<_, _ /// A map from project output path to project identifier. let outputPathMap = ConcurrentDictionary() + let createFileSnapshot path = + files.GetFileContentIfOpen path + |> Option.map (fun content -> FSharpFileSnapshot.CreateFromString(path, content)) + |> Option.defaultWith (fun () -> FSharpFileSnapshot.CreateFromFileSystem path) + + member val internal Debug_DumpGraphOnEveryChange: string option = None with get, set + + member internal _.files = files + + member internal this.Debug_DumpMermaid(path) = + let content = + depGraph.Debug_RenderMermaid (function + // Collapse all reference on disk nodes into one. Otherwise the graph is too big to render. + | WorkspaceGraphTypes.WorkspaceNodeKey.ReferenceOnDisk _ -> WorkspaceGraphTypes.WorkspaceNodeKey.ReferenceOnDisk "..." + | x -> x) + + File.WriteAllText(path, content) + /// Adds or updates an F# project in the workspace. Project is identified by the project file and output path or FSharpProjectIdentifier. - member _.AddOrUpdate(projectConfig: ProjectConfig, sourceFilePaths: string seq) = + member this.AddOrUpdate(projectConfig: ProjectConfig, sourceFilePaths: string seq) = + + use _ = + Activity.start + "Projects.AddOrUpdate" + [ + Activity.Tags.project, projectConfig.Identifier.ToString() + "sourceFiles", sourceFilePaths |> String.concat "; " + ] let projectIdentifier = projectConfig.Identifier @@ -301,14 +357,7 @@ type FSharpWorkspaceProjects internal (depGraph: IThreadSafeDependencyGraph<_, _ projectConfig, referencedProjects) ) - .AddSourceFiles( - sourceFilePaths - |> Seq.map (fun path -> - path, - files.GetFileContentIfOpen path - |> Option.map (fun content -> FSharpFileSnapshot.CreateFromString(path, content)) - |> Option.defaultWith (fun () -> FSharpFileSnapshot.CreateFromFileSystem path)) - ) + .AddSourceFiles(sourceFilePaths |> Seq.map (fun path -> path, createFileSnapshot path)) .AddProjectSnapshot( (fun ((projectConfig, referencedProjects), sourceFiles) -> ProjectSnapshot(projectConfig, referencedProjects, sourceFiles |> Seq.toList) @@ -334,8 +383,13 @@ type FSharpWorkspaceProjects internal (depGraph: IThreadSafeDependencyGraph<_, _ for dependentProjectId in dependentProjectIds do depGraph.AddProjectReference(dependentProjectId, projectIdentifier) + this.Debug_DumpGraphOnEveryChange |> Option.iter (this.Debug_DumpMermaid) + projectIdentifier) + member this.AddOrUpdate(projectConfig, sourceFilePaths: Uri seq) = + this.AddOrUpdate(projectConfig, sourceFilePaths |> Seq.map _.LocalPath) + member this.AddOrUpdate(projectPath: string, outputPath, compilerArgs) = let directoryPath = @@ -344,7 +398,9 @@ type FSharpWorkspaceProjects internal (depGraph: IThreadSafeDependencyGraph<_, _ let fsharpFileExtensions = set [| ".fs"; ".fsi"; ".fsx" |] let isFSharpFile (file: string) = - Set.exists (fun (ext: string) -> file.EndsWith(ext, StringComparison.Ordinal)) fsharpFileExtensions + file.Length > 0 + && file[0] <> '-' + && Set.exists (fun (ext: string) -> file.EndsWith(ext, StringComparison.Ordinal)) fsharpFileExtensions let isReference: string -> bool = _.StartsWith("-r:") @@ -373,3 +429,31 @@ type FSharpWorkspaceProjects internal (depGraph: IThreadSafeDependencyGraph<_, _ ProjectConfig(projectFileName, Some outputFileName, referencesOnDisk, otherOptions) this.AddOrUpdate(projectConfig, sourceFiles) + + member this.Update(projectIdentifier, newSourceFiles) = + + let newSourceFiles = newSourceFiles |> Seq.cache + + use _ = + Activity.start + "Projects.Update" + [ + Activity.Tags.project, !! projectIdentifier.ToString() + "sourceFiles", newSourceFiles |> String.concat "; " + ] + + depGraph.Transact(fun depGraph -> + + let existingSourceFiles = + depGraph.GetFilesOf projectIdentifier |> Seq.map (fun f -> f.FileName, f) |> Map + + let newFilesWithSnapshots = + newSourceFiles + |> Seq.map (fun path -> + path, + existingSourceFiles.TryFind path + |> Option.defaultWith (fun () -> createFileSnapshot path)) + + depGraph.ReplaceSourceFiles(projectIdentifier, newFilesWithSnapshots) + + this.Debug_DumpGraphOnEveryChange |> Option.iter (this.Debug_DumpMermaid)) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index f5ec26102d3..3839b4d6b2b 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -1692,6 +1692,13 @@ type internal TransparentCompiler projectSnapshot.SignatureKey, async { + use _ = + Activity.start + "ComputeProjectExtras" + [| + Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName |> (!!) + |] + let! results, finalInfo = ComputeParseAndCheckAllFilesInProject bootstrapInfo projectSnapshot let assemblyName = bootstrapInfo.AssemblyName @@ -1784,6 +1791,12 @@ type internal TransparentCompiler caches.AssemblyData.Get( projectSnapshot.SignatureKey, async { + use _ = + Activity.start + "ComputeAssemblyData" + [| + Activity.Tags.project, projectSnapshot.ProjectFileName |> Path.GetFileName |> (!!) + |] try @@ -1799,9 +1812,9 @@ type internal TransparentCompiler // - and then another change has to be made (to any file buffer) - so that recheck is triggered and we get here again // Until that sequence happens the project will be used from disk (if available). // To get around it we probably need to detect changes made in the editor and record a timestamp for them. - let shouldUseOnDisk = - availableOnDiskModifiedTime - |> Option.exists (fun t -> t >= projectSnapshot.GetLastModifiedTimeOnDisk()) + let shouldUseOnDisk = false + //availableOnDiskModifiedTime + //|> Option.exists (fun t -> t >= projectSnapshot.GetLastModifiedTimeOnDisk()) let name = projectSnapshot.ProjectFileName |> Path.GetFileNameWithoutExtension diff --git a/src/Compiler/Utilities/lib.fs b/src/Compiler/Utilities/lib.fs index 921b0a6dba3..214c53b4d61 100755 --- a/src/Compiler/Utilities/lib.fs +++ b/src/Compiler/Utilities/lib.fs @@ -24,7 +24,7 @@ let isEnvVarSet s = let GetEnvInteger e dflt = match Environment.GetEnvironmentVariable(e) with null -> dflt | t -> try int t with _ -> dflt -let dispose (x: IDisposable MaybeNull) = +let dispose (x: IDisposable MaybeNull) = match x with | Null -> () | NonNull x -> x.Dispose() @@ -449,4 +449,10 @@ module ListParallel = |> ArrayParallel.map f |> Array.toList - \ No newline at end of file +[] +module Async = + let map f a = + async { + let! a = a + return f a + } diff --git a/src/Compiler/Utilities/lib.fsi b/src/Compiler/Utilities/lib.fsi index cbdb893c5b8..2e9c495af53 100644 --- a/src/Compiler/Utilities/lib.fsi +++ b/src/Compiler/Utilities/lib.fsi @@ -291,3 +291,7 @@ module ListParallel = val map: ('T -> 'U) -> 'T list -> 'U list //val inline mapi: (int -> 'T -> 'U) -> 'T list -> 'U list + +[] +module Async = + val map: ('T -> 'U) -> Async<'T> -> Async<'U> diff --git a/src/FSharp.Compiler.LanguageServer/Utils.fs b/src/FSharp.Compiler.LanguageServer/Utils.fs index 2f19328a25f..c5283b6fb76 100644 --- a/src/FSharp.Compiler.LanguageServer/Utils.fs +++ b/src/FSharp.Compiler.LanguageServer/Utils.fs @@ -3,6 +3,8 @@ namespace FSharp.Compiler.LanguageServer open Microsoft.CommonLanguageServerProtocol.Framework open Microsoft.VisualStudio.LanguageServer.Protocol +open System.Diagnostics + open FSharp.Compiler.Diagnostics open System.Runtime.CompilerServices @@ -52,3 +54,37 @@ type FSharpDiagnosticExtensions = //Source = "Intellisense", Code = SumType this.ErrorNumberText ) + +module Activity = + let listen (filter) logMsg = + let indent (activity: Activity) = + let rec loop (activity: Activity) n = + match activity.Parent with + | Null -> n + | NonNull parent -> loop (parent) (n + 1) + + String.replicate (loop activity 0) " " + + let collectTags (activity: Activity) = + [ for tag in activity.Tags -> $"{tag.Key}: %A{tag.Value}" ] + |> String.concat ", " + + let listener = + new ActivityListener( + ShouldListenTo = (fun source -> source.Name = FSharp.Compiler.Diagnostics.ActivityNames.FscSourceName), + Sample = + (fun context -> + if filter context.Name then + ActivitySamplingResult.AllDataAndRecorded + else + ActivitySamplingResult.None), + ActivityStarted = (fun a -> logMsg $"{indent a}{a.OperationName} {collectTags a}") + ) + + ActivitySource.AddActivityListener(listener) + + let listenToAll () = + listen (fun _ -> true) Trace.TraceInformation + + let listenToSome () = + listen (fun x -> not <| x.Contains "StackGuard") Trace.TraceInformation diff --git a/src/FSharp.VisualStudio.Extension/FSharp.VisualStudio.Extension.csproj b/src/FSharp.VisualStudio.Extension/FSharp.VisualStudio.Extension.csproj index 1c946fbe71c..db3a0a846f3 100644 --- a/src/FSharp.VisualStudio.Extension/FSharp.VisualStudio.Extension.csproj +++ b/src/FSharp.VisualStudio.Extension/FSharp.VisualStudio.Extension.csproj @@ -9,11 +9,20 @@ - - - - - + + + + + + @@ -21,7 +30,6 @@ - diff --git a/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs b/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs index 429570e917c..f31309920b4 100644 --- a/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs +++ b/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs @@ -6,16 +6,19 @@ namespace FSharp.VisualStudio.Extension; using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.IO.Pipelines; using System.Linq; using System.Threading; using System.Threading.Tasks; using FSharp.Compiler.CodeAnalysis.Workspace; +using FSharp.Compiler.Diagnostics; using FSharp.Compiler.LanguageServer; using FSharp.Compiler.LanguageServer.Common; using Microsoft.CommonLanguageServerProtocol.Framework; using Microsoft.Extensions.DependencyInjection; +using Microsoft.FSharp.Core; using Microsoft.VisualStudio.Extensibility; using Microsoft.VisualStudio.Extensibility.Editor; using Microsoft.VisualStudio.Extensibility.LanguageServer; @@ -213,8 +216,11 @@ internal void ProcessProject(IProjectSnapshot project) workspace.Projects.AddOrUpdate(projectPath, projectInfo.Item1, projectInfo.Item2.Split(';')); } - workspace.Debug_DumpMermaid("../../../../dep-graph.md"); + //var graphPath = Path.Combine(Path.GetDirectoryName(projectPath) ?? ".", "..", "depGraph.md"); + //workspace.projects.Debug_DumpGraphOnEveryChange = FSharpOption.Some(graphPath); + + //Trace.TraceInformation($"Auto-saving workspace graph to {graphPath}"); } } @@ -258,6 +264,42 @@ internal class FSharpLanguageServerProvider : LanguageServerProvider /// public override async Task CreateServerConnectionAsync(CancellationToken cancellationToken) { + var activitySourceName = "fsc"; + + FSharp.Compiler.LanguageServer.Activity.listenToSome(); + + //const string vsMajorVersion = "17.0"; + + //var settings = OpenTelemetryExporterSettingsBuilder + // .CreateVSDefault(vsMajorVersion) + // .Build(); + + //try + //{ + // var tracerProvider = Sdk.CreateTracerProviderBuilder() + // .AddVisualStudioDefaultTraceExporter(settings) + // //.AddConsoleExporter() + // .AddOtlpExporter() + // .Build(); + //} + //catch (Exception e) + //{ + // Trace.TraceError($"Failed to create OpenTelemetry tracer provider: {e}"); + //} + + + var activitySource = new ActivitySource(activitySourceName); + var activity = activitySource.CreateActivity("CreateServerConnectionAsync", ActivityKind.Internal); + + if (activity != null) + { + activity.Start(); + } + else + { + Trace.TraceWarning("Failed to start OpenTelemetry activity, there are no listeners"); + } + var ws = this.Extensibility.Workspaces(); var projectQuery = (IAsyncQueryable project) => project @@ -315,7 +357,7 @@ internal class FSharpLanguageServerProvider : LanguageServerProvider return new DuplexPipe( PipeReader.Create(inputStream), PipeWriter.Create(outputStream)); - } + } /// public override Task OnServerInitializationResultAsync(ServerInitializationResult serverInitializationResult, LanguageServerInitializationFailureInfo? initializationFailureInfo, CancellationToken cancellationToken) diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/FSharpWorkspace.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/FSharpWorkspace.fs index 8da785b92d8..a111dd6da5c 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/FSharpWorkspace.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/FSharpWorkspace.fs @@ -1,19 +1,61 @@ module CompilerService.FSharpWorkspace open System + open Xunit open FSharp.Compiler.CodeAnalysis.ProjectSnapshot -open TestFramework -open FSharp.Compiler.IO open FSharp.Compiler.CodeAnalysis.Workspace +open FSharp.Compiler.Diagnostics open FSharp.Test.ProjectGeneration.WorkspaceHelpers +open OpenTelemetry +open OpenTelemetry.Resources +open OpenTelemetry.Trace +open OpenTelemetry.Exporter +open System.IO #nowarn "57" +// System.Diagnostics.DiagnosticSource seems to be missing in NET FW. Might investigate this later +#if !NETFRAMEWORK + +type FilteredJaegerExporter(predicate) = + + inherit SimpleActivityExportProcessor(new JaegerExporter(new JaegerExporterOptions())) + + override _.OnEnd(activity: System.Diagnostics.Activity) = + if predicate activity then + base.OnEnd activity + +/// Wrapper for FSharpWorkspace to use in tests. Provides OpenTelemetry tracing. +type TestingWorkspace(testName) as _this = + inherit FSharpWorkspace() + + let debugGraphPath = __SOURCE_DIRECTORY__ ++ $"{testName}.md" + + let tracerProvider = + Sdk + .CreateTracerProviderBuilder() + .AddSource("fsc") + .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(serviceName="F#", serviceVersion = "1")) + .AddProcessor(new FilteredJaegerExporter(_.DisplayName >> (<>) "DiagnosticsLogger.StackGuard.Guard")) + .Build() + + let activity = Activity.start $"Test FSharpWorkspace {testName}" [] + + do _this.Projects.Debug_DumpGraphOnEveryChange <- Some debugGraphPath + + member _.DebugGraphPath = debugGraphPath + + interface IDisposable with + member _.Dispose() = + activity.Dispose() + tracerProvider.ForceFlush() |> ignore + tracerProvider.Dispose() + [] let ``Add project to workspace`` () = - let workspace = FSharpWorkspace() + use workspace = new TestingWorkspace("Add project to workspace") let projectPath = "test.fsproj" let outputPath = "test.dll" let compilerArgs = [| "test.fs" |] @@ -26,7 +68,7 @@ let ``Add project to workspace`` () = [] let ``Open file in workspace`` () = - let workspace = FSharpWorkspace() + use workspace = new TestingWorkspace("Open file in workspace") let fileUri = Uri("file:///test.fs") let content = "let x = 1" @@ -58,7 +100,7 @@ let ``Open file in workspace`` () = [] let ``Close file in workspace`` () = - let workspace = FSharpWorkspace() + use workspace = new TestingWorkspace("Close file in workspace") let contentOnDisk = "let x = 1" let fileOnDisk = sourceFileOnDisk contentOnDisk @@ -93,7 +135,7 @@ let ``Close file in workspace`` () = [] let ``Change file in workspace`` () = - let workspace = FSharpWorkspace() + use workspace = new TestingWorkspace("Change file in workspace") let fileUri = Uri("file:///test.fs") @@ -119,7 +161,7 @@ let ``Change file in workspace`` () = [] let ``Add multiple projects with references`` () = - let workspace = FSharpWorkspace() + use workspace = new TestingWorkspace("Add multiple projects with references") let projectPath1 = "test1.fsproj" let outputPath1 = "test1.dll" let compilerArgs1 = [| "test1.fs" |] @@ -149,7 +191,7 @@ let ``Add multiple projects with references`` () = [] let ``Propagate changes to snapshots`` () = - let workspace = FSharpWorkspace() + use workspace = new TestingWorkspace("Propagate changes to snapshots") let file1 = sourceFileOnDisk "let x = 1" let pid1 = workspace.Projects.AddOrUpdate(ProjectConfig.Empty("p1"), [ file1.LocalPath ]) @@ -183,8 +225,8 @@ let ``Propagate changes to snapshots`` () = |> assertFileHasContent file1.LocalPath updatedContent [] -let ``Update project by adding a source file`` () = - let workspace = FSharpWorkspace() +let ``AddOrUpdate project by adding a source file`` () = + use workspace = new TestingWorkspace("Update project by adding a source file") let projectPath = "test.fsproj" let outputPath = "test.dll" let compilerArgs = [| "test.fs" |] @@ -197,9 +239,36 @@ let ``Update project by adding a source file`` () = Assert.Contains("test.fs", projectSnapshot.SourceFiles |> Seq.map (fun f -> f.FileName)) Assert.Contains(newSourceFile, projectSnapshot.SourceFiles |> Seq.map (fun f -> f.FileName)) +[] +let ``Update project by adding a source file`` () = + use workspace = new TestingWorkspace("Update project by adding a source file") + let projectPath = "test.fsproj" + let outputPath = "test.dll" + let compilerArgs = [| "test.fs" |] + let projectIdentifier = workspace.Projects.AddOrUpdate(projectPath, outputPath, compilerArgs) + let newSourceFile = "newTest.fs" + let newSourceFiles = [| "test.fs"; newSourceFile |] + workspace.Projects.Update(projectIdentifier, newSourceFiles) |> ignore + let projectSnapshot = workspace.Query.GetProjectSnapshot(projectIdentifier).Value + Assert.NotNull(projectSnapshot) + Assert.Contains("test.fs", projectSnapshot.SourceFiles |> Seq.map (fun f -> f.FileName)) + Assert.Contains(newSourceFile, projectSnapshot.SourceFiles |> Seq.map (fun f -> f.FileName)) + +[] +let ``Update project by removing a source file`` () = + use workspace = new TestingWorkspace("Update project by removing a source file") + let projectPath = "test.fsproj" + let outputPath = "test.dll" + let compilerArgs = [| "test.fs"; "newTest.fs" |] + let projectIdentifier = workspace.Projects.AddOrUpdate(projectPath, outputPath, compilerArgs) + let newCompilerArgs = [| "test.fs" |] + workspace.Projects.AddOrUpdate(projectPath, outputPath, newCompilerArgs) |> ignore + let files = workspace.Files.OfProject(projectIdentifier) |> Seq.toArray + Assert.Equal([| "test.fs" |], files) + [] let ``Update project by adding a reference`` () = - let workspace = FSharpWorkspace() + use workspace = new TestingWorkspace("Update project by adding a reference") let projectPath1 = "test1.fsproj" let outputPath1 = "test1.dll" let compilerArgs1 = [| "test1.fs" |] @@ -229,7 +298,7 @@ let ``Update project by adding a reference`` () = [] let ``Create references in existing projects`` () = - let workspace = FSharpWorkspace() + use workspace = new TestingWorkspace("Create references in existing projects") let projectPath1 = "test1.fsproj" let outputPath1 = "test1.dll" let compilerArgs1 = [| "test1.fs" |] @@ -271,6 +340,105 @@ let ``Create references in existing projects`` () = [] let ``Asking for an unknown project snapshot returns None`` () = - let workspace = FSharpWorkspace() + use workspace = new TestingWorkspace("Asking for an unknown project snapshot returns None") Assert.Equal(None, workspace.Query.GetProjectSnapshot(FSharpProjectIdentifier("hello", "world"))) + + +[] +let ``Works with signature files`` () = + task { + + use workspace = new TestingWorkspace("Works with signature files") + + let projectConfig = ProjectConfig.Create() + + let sourceFileUri = projectConfig.FileUri "test.fs" + + let source = "let x = 1" + + let projectIdentifier = workspace.Projects.AddOrUpdate(projectConfig, [ sourceFileUri ]) + + workspace.Files.Open(sourceFileUri, source) + + let! signatureUri, _signatureSource = workspace.AddSignatureFile(projectIdentifier, sourceFileUri, writeToDisk=false) + + let! diag = workspace.Query.GetDiagnosticsForFile(signatureUri) + + Assert.Equal(0, diag.Diagnostics.Length) + + workspace.Files.Edit(signatureUri, "module Test\n\nval x: potato") + + let! diag = workspace.Query.GetDiagnosticsForFile(signatureUri) + + Assert.Equal(1, diag.Diagnostics.Length) + Assert.Equal("The type 'potato' is not defined.", diag.Diagnostics[0].Message) + + workspace.Files.Edit(signatureUri, "module Test\n\nval y: int") + + let! diag = workspace.Query.GetDiagnosticsForFile(sourceFileUri) + + Assert.Equal(1, diag.Diagnostics.Length) + Assert.Equal("Module 'Test' requires a value 'y'", diag.Diagnostics[0].Message) + } + + +let reposDir = __SOURCE_DIRECTORY__ ++ ".." ++ ".." ++ ".." ++ ".." +let giraffeDir = reposDir ++ "Giraffe" ++ "src" ++ "Giraffe" |> Path.GetFullPath +let giraffeTestsDir = reposDir ++ "Giraffe" ++ "tests" ++ "Giraffe.Tests" |> Path.GetFullPath +let giraffeSampleDir = reposDir ++ "Giraffe" ++ "samples" ++ "EndpointRoutingApp" ++ "EndpointRoutingApp" |> Path.GetFullPath +let giraffeSignaturesDir = reposDir ++ "giraffe-signatures" ++ "src" ++ "Giraffe" |> Path.GetFullPath +let giraffeSignaturesTestsDir = reposDir ++ "giraffe-signatures" ++ "tests" ++ "Giraffe.Tests" |> Path.GetFullPath +let giraffeSignaturesSampleDir = reposDir ++ "giraffe-signatures" ++ "samples" ++ "EndpointRoutingApp" ++ "EndpointRoutingApp" |> Path.GetFullPath + +type GiraffeFactAttribute() = + inherit Xunit.FactAttribute() + do + if not (Directory.Exists giraffeDir) then + do base.Skip <- $"Giraffe not found ({giraffeDir}). You can get it here: https://github.com/giraffe-fsharp/Giraffe" + if not (Directory.Exists giraffeSignaturesDir) then + do base.Skip <- $"Giraffe (with signatures) not found ({giraffeSignaturesDir}). You can get it here: https://github.com/nojaf/Giraffe/tree/signatures" + + +[] +let ``Giraffe signature test`` () = + task { + use workspace = new TestingWorkspace("Giraffe signature test") + + let responseFileName = "compilerArgs.rsp" + + let _identifiers = + [ + giraffeSignaturesDir + giraffeSignaturesTestsDir + giraffeSignaturesSampleDir ] + |> Seq.map (fun dir -> + let projectName = Path.GetFileName dir + let dllName = $"{projectName}.dll" + let responseFile = dir ++ responseFileName + let outputFile = dir ++ "bin" ++ "Debug" ++ "net6.0" ++ dllName + let projectFile = dir ++ projectName + ".fsproj" + let compilerArgs = File.ReadAllLines responseFile + workspace.Projects.AddOrUpdate(projectFile, outputFile, compilerArgs) + ) + |> Seq.toList + + let _ = workspace.Files.OpenFromDisk(giraffeSignaturesSampleDir ++ "Program.fs") + + let! diag = workspace.Query.GetDiagnosticsForFile(Uri(giraffeSignaturesSampleDir ++ "Program.fs")) + Assert.Equal(0, diag.Diagnostics.Length) + + let middlewareFsiSource = workspace.Files.OpenFromDisk(giraffeSignaturesDir ++ "Middleware.fsi") + let middlewareFsiNewSource = middlewareFsiSource.Replace("static member AddGiraffe:", "static member AddGiraffe2:") + + let! diag = workspace.Query.GetDiagnosticsForFile(Uri(giraffeSignaturesDir ++ "Middleware.fsi")) + Assert.Equal(0, diag.Diagnostics.Length) + + workspace.Files.Edit(Uri(giraffeSignaturesDir ++ "Middleware.fsi"), middlewareFsiNewSource) + + let! diag = workspace.Query.GetDiagnosticsForFile(Uri(giraffeSignaturesSampleDir ++ "Program.fs")) + Assert.Equal(1, diag.Diagnostics.Length) + Assert.Equal("The type 'IServiceCollection' does not define the field, constructor or member 'AddGiraffe'.", diag.Diagnostics[0].Message) + } + +#endif \ No newline at end of file diff --git a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs index 4b0163a9f4b..c50ee0ce940 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs +++ b/tests/FSharp.Compiler.ComponentTests/FSharpChecker/CommonWorkflows.fs @@ -172,10 +172,10 @@ let ``We don't lose subsequent diagnostics when there's error in one file`` () = let project = { SyntheticProject.Create( { sourceFile "First" [] with - Source = """module AbstractBaseClass.File1 - + Source = """module AbstractBaseClass.File1 + let foo x = () - + a""" }, { sourceFile "Second" [] with Source = """module AbstractBaseClass.File2 @@ -189,7 +189,7 @@ let ``We don't lose subsequent diagnostics when there's error in one file`` () = abstract P: int""" }) with AutoAddModules = false SkipInitialCheck = true } - + project.Workflow { checkFile "First" (expectErrorCodes ["FS0039"]) checkFile "Second" (expectErrorCodes ["FS0054"; "FS0365"]) diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl index 25e8adafbe9..f7cb133ef1e 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl @@ -2594,19 +2594,23 @@ FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpDiagnosticRepo FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpDiagnosticReport: System.String ResultId FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpDiagnosticReport: System.String get_ResultId() FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpWorkspaceQuery: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpDiagnosticReport] GetDiagnosticsForFile(System.Uri) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpWorkspaceQuery: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults]] GetCheckResultsForFile(System.Uri) FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpWorkspaceQuery: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.EditorServices.SemanticClassificationView]] GetSemanticClassification(System.Uri) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpWorkspaceQuery: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults],Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults]]] GetParseAndCheckResultsForFile(System.Uri) FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpWorkspaceQuery: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] GetProjectSnapshot(FSharpProjectIdentifier) FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpWorkspaceQuery: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] GetProjectSnapshotForFile(System.Uri) FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpWorkspaceQuery: System.Threading.Tasks.Task`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.ISourceTextNew]] GetSource(System.Uri) FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery: FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpDiagnosticReport FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery: FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpWorkspaceQuery -FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceFiles: Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.Uri,System.String],Microsoft.FSharp.Core.Unit] Open -FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceFiles: Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.Uri,System.String],Microsoft.FSharp.Core.Unit] get_Open() +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceFiles: System.Collections.Generic.IEnumerable`1[System.String] OfProject(FSharpProjectIdentifier) FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceFiles: Void Close(System.Uri) FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceFiles: Void Edit(System.Uri, System.String) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceFiles: Void Open(System.Uri, System.String) FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceProjects: FSharpProjectIdentifier AddOrUpdate(ProjectConfig, System.Collections.Generic.IEnumerable`1[System.String]) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceProjects: FSharpProjectIdentifier AddOrUpdate(ProjectConfig, System.Collections.Generic.IEnumerable`1[System.Uri]) FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceProjects: FSharpProjectIdentifier AddOrUpdate(System.String, System.String, System.Collections.Generic.IEnumerable`1[System.String]) FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceProjects: FSharpProjectIdentifier AddOrUpdate(System.String, System.String, System.Collections.Generic.IEnumerable`1[System.String], System.Collections.Generic.IEnumerable`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[System.String]) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceProjects: Void Update(FSharpProjectIdentifier, System.Collections.Generic.IEnumerable`1[System.String]) FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState: FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceFiles FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState: FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceProjects FSharp.Compiler.CompilerEnvironment: Boolean IsCheckerSupportedSubcategory(System.String) diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl index 25e8adafbe9..f7cb133ef1e 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl @@ -2594,19 +2594,23 @@ FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpDiagnosticRepo FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpDiagnosticReport: System.String ResultId FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpDiagnosticReport: System.String get_ResultId() FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpWorkspaceQuery: Microsoft.FSharp.Control.FSharpAsync`1[FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpDiagnosticReport] GetDiagnosticsForFile(System.Uri) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpWorkspaceQuery: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults]] GetCheckResultsForFile(System.Uri) FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpWorkspaceQuery: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.EditorServices.SemanticClassificationView]] GetSemanticClassification(System.Uri) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpWorkspaceQuery: Microsoft.FSharp.Control.FSharpAsync`1[System.Tuple`2[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpParseFileResults],Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.FSharpCheckFileResults]]] GetParseAndCheckResultsForFile(System.Uri) FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpWorkspaceQuery: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] GetProjectSnapshot(FSharpProjectIdentifier) FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpWorkspaceQuery: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.CodeAnalysis.ProjectSnapshot+FSharpProjectSnapshot] GetProjectSnapshotForFile(System.Uri) FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpWorkspaceQuery: System.Threading.Tasks.Task`1[Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.Text.ISourceTextNew]] GetSource(System.Uri) FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery: FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpDiagnosticReport FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery: FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery+FSharpWorkspaceQuery -FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceFiles: Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.Uri,System.String],Microsoft.FSharp.Core.Unit] Open -FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceFiles: Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.Uri,System.String],Microsoft.FSharp.Core.Unit] get_Open() +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceFiles: System.Collections.Generic.IEnumerable`1[System.String] OfProject(FSharpProjectIdentifier) FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceFiles: Void Close(System.Uri) FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceFiles: Void Edit(System.Uri, System.String) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceFiles: Void Open(System.Uri, System.String) FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceProjects: FSharpProjectIdentifier AddOrUpdate(ProjectConfig, System.Collections.Generic.IEnumerable`1[System.String]) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceProjects: FSharpProjectIdentifier AddOrUpdate(ProjectConfig, System.Collections.Generic.IEnumerable`1[System.Uri]) FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceProjects: FSharpProjectIdentifier AddOrUpdate(System.String, System.String, System.Collections.Generic.IEnumerable`1[System.String]) FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceProjects: FSharpProjectIdentifier AddOrUpdate(System.String, System.String, System.Collections.Generic.IEnumerable`1[System.String], System.Collections.Generic.IEnumerable`1[System.String], Microsoft.FSharp.Collections.FSharpList`1[System.String]) +FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceProjects: Void Update(FSharpProjectIdentifier, System.Collections.Generic.IEnumerable`1[System.String]) FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState: FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceFiles FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState: FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState+FSharpWorkspaceProjects FSharp.Compiler.CompilerEnvironment: Boolean IsCheckerSupportedSubcategory(System.String) diff --git a/tests/FSharp.Test.Utilities/ProjectGeneration.fs b/tests/FSharp.Test.Utilities/ProjectGeneration.fs index 2e5e5fd8961..3efe9560999 100644 --- a/tests/FSharp.Test.Utilities/ProjectGeneration.fs +++ b/tests/FSharp.Test.Utilities/ProjectGeneration.fs @@ -24,6 +24,7 @@ open System.Text.RegularExpressions open System.Threading.Tasks open System.Xml +open Internal.Utilities.Library.Extras open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.CodeAnalysis.ProjectSnapshot open FSharp.Compiler.Diagnostics @@ -37,6 +38,9 @@ open OpenTelemetry.Resources open OpenTelemetry.Trace open TestFramework open FSharp.Compiler.IO +open FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceState +open FSharp.Compiler.CodeAnalysis.Workspace.FSharpWorkspaceQuery +open FSharp.Compiler.CodeAnalysis.Workspace #nowarn "57" // Experimental feature use @@ -1455,24 +1459,81 @@ type SyntheticProject with module WorkspaceHelpers = + let createTestProjectDirName () = + let projectDirectoryName = $"FSharp-Test-Project-{Guid.NewGuid().ToString()[..8]}" + Path.GetTempPath() ++ projectDirectoryName + type ProjectConfig with /// Creates an empty project configuration with optional parameters for name, output path, and references on disk. static member Empty(?name, ?outputPath, ?referencesOnDisk) = + let directory = createTestProjectDirName() let name = defaultArg name "test" - let projectFileName = $"{name}.fsproj" - let outputPath = defaultArg outputPath $"{name}.dll" + let projectFileName = directory ++ $"{name}.fsproj" + let outputPath = defaultArg outputPath (directory ++ $"{name}.dll") let referencesOnDisk = defaultArg referencesOnDisk [] ProjectConfig(projectFileName, Some outputPath, referencesOnDisk, []) static member Create(?name) = let name = defaultArg name "test" - + let fullPath = createTestProjectDirName() ++ $"{name}.fsproj" let snapshot, _ = - CompilerAssertHelpers.checker.GetProjectSnapshotFromScript(name, SourceTextNew.ofString "", assumeDotNetFramework = false) + CompilerAssertHelpers.checker.GetProjectSnapshotFromScript(fullPath, SourceTextNew.ofString "", assumeDotNetFramework = false) |> Async.RunSynchronously snapshot.ProjectConfig + /// Returns URI of a file in the project directory. + member this.FileUri(fileName) = + this.ProjectDirectory ++ fileName |> Uri + + type FSharpWorkspaceQuery with + + member this.GetSignature(sourceFile: Uri) = + use _ = Activity.start "FSharpWorkspace.GetSignature" [ Activity.Tags.fileName, sourceFile.LocalPath ] + this.GetCheckResultsForFile(sourceFile) + |> Async.map (Option.bind(_.GenerateSignature())) + + type FSharpWorkspaceProjects with + + member projects.AddFileBefore(projectIdentifier, newFile: Uri, addBefore: Uri) = + use _ = Activity.start "FSharpWorkspace.AddFileBefore" [ Activity.Tags.project, projectIdentifier.ToString(); "newFile", newFile.LocalPath; "addBefore", addBefore.LocalPath ] + + let existingFiles = projects.files.OfProject projectIdentifier + let newFiles = seq { + for file in existingFiles do + if file = addBefore.LocalPath then + newFile.LocalPath + file + } + projects.Update(projectIdentifier, newFiles) + + type FSharpWorkspaceFiles with + + /// Opens the file from a given path in the Workspace using content read from disk. + member this.OpenFromDisk(path) = + let content = FileSystem.OpenFileForReadShim(path).ReadAllText() + this.Open(Uri path, content) + content + + type FSharpWorkspace with + + member this.AddSignatureFile(projectIdentifier, sourceFile: Uri, ?writeToDisk) = + use _ = Activity.start "FSharpWorkspace.AddSignatureFile" [ Activity.Tags.project, projectIdentifier.ToString(); "sourceFile", sourceFile.LocalPath ] + + let writeToDisk = defaultArg writeToDisk true + async { + match! this.Query.GetSignature sourceFile with + | None -> return failwith $"Couldn't get signature for {sourceFile}" + | Some signature -> + let signatureFileUri = Uri $"{sourceFile.LocalPath}i" + if writeToDisk then + FileSystem.OpenFileForWriteShim(signatureFileUri.LocalPath).Write(signature) + else + this.Files.Open(signatureFileUri, signature.ToString()) + this.Projects.AddFileBefore(projectIdentifier, newFile=signatureFileUri, addBefore=sourceFile) + return signatureFileUri, signature + } + /// Retrieves the referenced project snapshot from the given project snapshot based on the project identifier. let getReferencedSnapshot (projectIdentifier: FSharpProjectIdentifier) (projectSnapshot: FSharpProjectSnapshot) = projectSnapshot.ReferencedProjects diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl index 31c4fa6c2ca..eee492cd26e 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl @@ -21,7 +21,7 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x00000082][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x0000008B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+MagicAssemblyResolution::ResolveAssemblyCore([FSharp.Compiler.Service]Internal.Utilities.Library.CompilationThreadToken, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, [FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, [FSharp.Compiler.Service]FSharp.Compiler.CompilerImports+TcImports, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompiler, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiConsoleOutput, string)][offset 0x00000015][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3510-789::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001E5][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3510-791::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001E5][found Char] Unexpected type on the stack. [IL]: Error [UnmanagedPointer]: : FSharp.Compiler.Interactive.Shell+Utilities+pointerToNativeInt@110::Invoke(object)][offset 0x00000007] Unmanaged pointers are not a verifiable type. [IL]: Error [StackUnexpected]: : .$FSharpCheckerResults+dataTipOfReferences@2205::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000084][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-501::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack. diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl index 965ceab6c76..b64945808cc 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl @@ -28,7 +28,7 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x0000008B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+FsiStdinSyphon::GetLine(string, int32)][offset 0x00000039][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+MagicAssemblyResolution::ResolveAssemblyCore([FSharp.Compiler.Service]Internal.Utilities.Library.CompilationThreadToken, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, [FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, [FSharp.Compiler.Service]FSharp.Compiler.CompilerImports+TcImports, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompiler, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiConsoleOutput, string)][offset 0x00000015][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3510-789::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001E5][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3510-791::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001E5][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+FsiInteractionProcessor::CompletionsForPartialLID([FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompilerState, string)][offset 0x0000001B][found Char] Unexpected type on the stack. [IL]: Error [UnmanagedPointer]: : FSharp.Compiler.Interactive.Shell+Utilities+pointerToNativeInt@110::Invoke(object)][offset 0x00000007] Unmanaged pointers are not a verifiable type. [IL]: Error [StackUnexpected]: : .$FSharpCheckerResults+dataTipOfReferences@2205::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000084][found Char] Unexpected type on the stack. @@ -80,7 +80,7 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.IL::parseILVersion(string)][offset 0x0000000B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.IL::parseILVersion(string)][offset 0x00000021][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.IL+parseNamed@5242::Invoke([FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1>, int32, int32)][offset 0x00000087][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : Internal.Utilities.Collections.Utils::shortPath(string)][offset 0x00000015][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : Internal.Utilities.Collections.Utils::shortPath(string)][offset 0x00000048][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : Internal.Utilities.FSharpEnvironment+probePathForDotnetHost@321::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000028][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.SimulatedMSBuildReferenceResolver+Pipe #6 input at line 68@68::FSharp.Compiler.CodeAnalysis.ILegacyReferenceResolver.Resolve([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyResolutionEnvironment, [S.P.CoreLib]System.Tuple`2[], string, [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, string, string, [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, string, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>>)][offset 0x0000034D][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$FSharp.Compiler.DiagnosticsLogger::.cctor()][offset 0x000000CD][found Char] Unexpected type on the stack. diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl index a930478a45e..1604aea3da7 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl @@ -21,7 +21,7 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x00000082][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x0000008B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+MagicAssemblyResolution::ResolveAssemblyCore([FSharp.Compiler.Service]Internal.Utilities.Library.CompilationThreadToken, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, [FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, [FSharp.Compiler.Service]FSharp.Compiler.CompilerImports+TcImports, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompiler, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiConsoleOutput, string)][offset 0x00000015][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3510-830::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001C7][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3510-831::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001C7][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$FSharpCheckerResults+GetReferenceResolutionStructuredToolTipText@2205::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000076][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-523::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-523::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000003B][found Char] Unexpected type on the stack. diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl index c849a700611..ed03e733c2a 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl @@ -15,45 +15,45 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.Symbols.FSharpEntity::TryGetFullDisplayName()][offset 0x00000024][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Symbols.FSharpEntity::TryGetFullCompiledName()][offset 0x00000024][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue::TryGetFullCompiledOperatorNameIdents()][offset 0x00000060][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue+fullName@2490-3::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000030][found Char] Unexpected type on the stack. [IL]: Error [ReturnPtrToStack]: : FSharp.Compiler.CodeAnalysis.ItemKeyStore::ReadKeyString([System.Reflection.Metadata]System.Reflection.Metadata.BlobReader&)][offset 0x00000023] Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. [IL]: Error [ReturnPtrToStack]: : FSharp.Compiler.CodeAnalysis.ItemKeyStore::ReadFirstKeyString()][offset 0x00000064] Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.ItemKeyStoreBuilder::writeRange([FSharp.Compiler.Service]FSharp.Compiler.Text.Range)][offset 0x00000017][found address of '[FSharp.Compiler.Service]FSharp.Compiler.Text.Range'][expected Native Int] Unexpected type on the stack. [IL]: Error [ExpectedNumericType]: : FSharp.Compiler.EditorServices.SemanticClassificationKeyStoreBuilder::WriteAll([FSharp.Compiler.Service]FSharp.Compiler.EditorServices.SemanticClassificationItem[])][offset 0x0000001C][found address of '[FSharp.Compiler.Service]FSharp.Compiler.EditorServices.SemanticClassificationItem'] Expected numeric type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Tokenization.FSharpLineTokenizer+clo@921-612::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Tokenization.FSharpLineTokenizer+clo@921-612::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000003B][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Tokenization.FSharpLineTokenizer+clo@921-612::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000064][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Tokenization.FSharpLineTokenizer+clo@921-612::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000006D][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Tokenization.FSharpLineTokenizer+clo@921-612::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000076][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.Parent::FormatEntityFullName([FSharp.Compiler.Service]FSharp.Compiler.Symbols.FSharpEntity)][offset 0x00000052][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.TypeCheckInfo+GetReferenceResolutionStructuredToolTipText@2205::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x0000007C][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.Parent::FormatEntityFullName([FSharp.Compiler.Service]FSharp.Compiler.Symbols.FSharpEntity)][offset 0x0000003F][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.FSharpChecker::.ctor([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, int32, bool, bool, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.FSharpOption`1>>, bool, bool, bool, bool, [FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+ParallelReferenceResolution, bool, [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1>>>, bool, [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1)][offset 0x000000A2][found ref 'object'][expected ref '[FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.IBackgroundCompiler'] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.FSharpChecker::TokenizeFile(string)][offset 0x0000000B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x0000005C][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x00000065][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x00000082][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x0000008B][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+FsiStdinSyphon::GetLine(string, int32)][offset 0x00000039][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+FsiStdinSyphon::GetLine(string, int32)][offset 0x00000032][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+MagicAssemblyResolution::ResolveAssemblyCore([FSharp.Compiler.Service]Internal.Utilities.Library.CompilationThreadToken, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, [FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, [FSharp.Compiler.Service]FSharp.Compiler.CompilerImports+TcImports, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompiler, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiConsoleOutput, string)][offset 0x00000015][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+FsiStdinLexerProvider+clo@3510-968::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001C7][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3510-831::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001C7][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+FsiInteractionProcessor::CompletionsForPartialLID([FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompilerState, string)][offset 0x00000024][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.AssemblyContent+traverseMemberFunctionAndValues@176::Invoke([FSharp.Compiler.Service]FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue)][offset 0x0000004C][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$FSharpCheckerResults+GetReferenceResolutionStructuredToolTipText@2205::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000076][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.AssemblyContent+traverseMemberFunctionAndValues@176::Invoke([FSharp.Compiler.Service]FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue)][offset 0x0000002B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.AssemblyContent+traverseEntity@218::GenerateNext([S.P.CoreLib]System.Collections.Generic.IEnumerable`1&)][offset 0x000000BB][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.ParsedInput+visitor@1423-12::VisitExpr([FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>, [FSharp.Compiler.Service]FSharp.Compiler.Syntax.SynExpr)][offset 0x00000620][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.ParsedInput+visitor@1423-11::VisitExpr([FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>, [FSharp.Compiler.Service]FSharp.Compiler.Syntax.SynExpr)][offset 0x00000620][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-523::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-523::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000003B][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-523::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000064][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-523::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000006D][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@921-523::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000076][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$Symbols+fullName@2490-3::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000030][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Driver+ProcessCommandLineFlags@301-1::Invoke(string)][offset 0x0000000B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Driver+ProcessCommandLineFlags@301-1::Invoke(string)][offset 0x00000014][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.CreateILModule+MainModuleBuilder::ConvertProductVersionToILVersionInfo(string)][offset 0x00000017][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.CreateILModule+MainModuleBuilder::ConvertProductVersionToILVersionInfo(string)][offset 0x00000010][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.StaticLinking+TypeForwarding::followTypeForwardForILTypeRef([FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.IL+ILTypeRef)][offset 0x00000010][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerOptions::getCompilerOption([FSharp.Compiler.Service]FSharp.Compiler.CompilerOptions+CompilerOption, [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1)][offset 0x000000A7][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerOptions::parseOption@266(string)][offset 0x0000000B][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerOptions::getOptionArgList@306([FSharp.Compiler.Service]FSharp.Compiler.CompilerOptions+CompilerOption, string)][offset 0x00000049][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerOptions::getOptionArgList@306([FSharp.Compiler.Service]FSharp.Compiler.CompilerOptions+CompilerOption, string)][offset 0x00000052][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerOptions::getSwitch@324(string)][offset 0x0000000B][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerOptions::attempt@372([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2, [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, string, string, string, string, [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1)][offset 0x00000A99][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerOptions::processArg@332([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2, [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1)][offset 0x0000003E][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerOptions::AddPathMapping([FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, string)][offset 0x0000000B][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerOptions::subSystemVersionSwitch$cont@656([FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, string, [FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x0000000B][found Char] Unexpected type on the stack. [IL]: Error [StackUnderflow]: : FSharp.Compiler.CompilerOptions::DoWithColor([System.Console]System.ConsoleColor, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2)][offset 0x0000005E] Stack underflow. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerOptions+parseOption@269::Invoke(string)][offset 0x0000000B][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerOptions+getOptionArgList@307::Invoke([FSharp.Compiler.Service]FSharp.Compiler.CompilerOptions+CompilerOption, string)][offset 0x00000049][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerOptions+getOptionArgList@307::Invoke([FSharp.Compiler.Service]FSharp.Compiler.CompilerOptions+CompilerOption, string)][offset 0x00000052][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerOptions+getSwitch@325::Invoke(string)][offset 0x0000000B][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerOptions+attempt@373::Invoke([FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1)][offset 0x00000CA9][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerOptions+processArg@333::Invoke([FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1)][offset 0x0000003F][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerOptions+subSystemVersionSwitch@656::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000010][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerOptions+ResponseFile+parseLine@239::Invoke(string)][offset 0x00000026][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.ParseAndCheckInputs+CheckMultipleInputsUsingGraphMode@1865::Invoke(int32)][offset 0x00000031][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.ParseAndCheckInputs+CheckMultipleInputsUsingGraphMode@1865::Invoke(int32)][offset 0x0000003A][found Char] Unexpected type on the stack. @@ -64,19 +64,19 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.IlxGen::HashRangeSorted([S.P.CoreLib]System.Collections.Generic.IDictionary`2>)][offset 0x00000011][found ref '[FSharp.Compiler.Service]FSharp.Compiler.IlxGen+HashRangeSorted@1850-1'][expected ref '[FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,int32>'] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.IlxGen::HashRangeSorted([S.P.CoreLib]System.Collections.Generic.IDictionary`2>)][offset 0x00000012][found ref '[FSharp.Compiler.Service]FSharp.Compiler.IlxGen+HashRangeSorted@1850'][expected ref '[FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,T0>'] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.PatternMatchCompilation::isProblematicClause([FSharp.Compiler.Service]FSharp.Compiler.PatternMatchCompilation+MatchClause)][offset 0x00000040][found Byte] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.PatternMatchCompilation::staticInitialization@()][offset 0x0000000B][found Boolean] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$FSharp.Compiler.PatternMatchCompilation::.cctor()][offset 0x0000000B][found Boolean] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.NicePrint+TastDefinitionPrinting+meths@2092-3::Invoke([FSharp.Compiler.Service]FSharp.Compiler.Infos+MethInfo)][offset 0x000000B3][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.NicePrint+PrintUtilities::layoutXmlDoc([FSharp.Compiler.Service]FSharp.Compiler.TypedTreeOps+DisplayEnv, bool, [FSharp.Compiler.Service]FSharp.Compiler.Xml.XmlDoc, [FSharp.Compiler.Service]FSharp.Compiler.Text.Layout)][offset 0x00000034][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.TypeProviders::ValidateNamespaceName(string, [FSharp.Compiler.Service]FSharp.Compiler.Tainted`1, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, string)][offset 0x00000074][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.TypeProviders::ValidateExpectedName([FSharp.Compiler.Service]FSharp.Compiler.Text.Range, string[], string, [FSharp.Compiler.Service]FSharp.Compiler.Tainted`1)][offset 0x000000AC][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.TypeProviders::ValidateExpectedName([FSharp.Compiler.Service]FSharp.Compiler.Text.Range, string[], string, [FSharp.Compiler.Service]FSharp.Compiler.Tainted`1)][offset 0x000000A8][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Lexhelp::stringBufferAsString([FSharp.Compiler.Service]FSharp.Compiler.IO.ByteBuffer)][offset 0x0000008E][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Syntax.PrettyNaming::SplitNamesForILPath(string)][offset 0x0000004B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Syntax.PrettyNaming::SplitNamesForILPath(string)][offset 0x00000054][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Syntax.PrettyNaming::staticInitialization@()][offset 0x00001182][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Syntax.PrettyNaming::staticInitialization@()][offset 0x0000118B][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryWriter::writeILMetadataAndCode(bool, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.IL+ILVersionInfo, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.IL+ILGlobals, bool, bool, bool, [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1, [S.P.CoreLib]System.Collections.Generic.IEnumerable`1, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.IL+ILModuleDef, int32, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2)][offset 0x00000B8C][found Byte] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryWriter::writeILMetadataAndCode(bool, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.IL+ILVersionInfo, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.IL+ILGlobals, bool, bool, bool, [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1, [S.P.CoreLib]System.Collections.Generic.IEnumerable`1, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.IL+ILModuleDef, int32, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2)][offset 0x0000050D][found Boolean] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILPdbWriter+PortablePdbGenerator::serializeDocumentName(string)][offset 0x00000073][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$FSharp.Compiler.Syntax.PrettyNaming::.cctor()][offset 0x00001182][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$FSharp.Compiler.Syntax.PrettyNaming::.cctor()][offset 0x0000118B][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryWriter::writeILMetadataAndCode(bool, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.IL+ILVersionInfo, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.IL+ILGlobals, bool, bool, bool, [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1, [S.P.CoreLib]System.Collections.Generic.IEnumerable`1, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.IL+ILModuleDef, int32, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2)][offset 0x00000B21][found Byte] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryWriter::writeILMetadataAndCode(bool, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.IL+ILVersionInfo, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.IL+ILGlobals, bool, bool, bool, [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1, [S.P.CoreLib]System.Collections.Generic.IEnumerable`1, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.IL+ILModuleDef, int32, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2)][offset 0x000004E2][found Boolean] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILPdbWriter+PortablePdbGenerator::serializeDocumentName(string)][offset 0x00000071][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILPdbWriter+pushShadowedLocals@959::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x000001C0][found Byte] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader::seekReadTypeDefRowUncached([FSharp.Core]Microsoft.FSharp.Core.FSharpRef`1>, int32)][offset 0x00000080][found Byte] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader::seekReadTypeDefRowUncached([FSharp.Core]Microsoft.FSharp.Core.FSharpRef`1>, int32)][offset 0x000000A1][found Byte] Unexpected type on the stack. @@ -95,34 +95,34 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader::seekReadNestedRowUncached([FSharp.Core]Microsoft.FSharp.Core.FSharpRef`1>, int32)][offset 0x00000038][found Byte] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader::seekReadNestedRowUncached([FSharp.Core]Microsoft.FSharp.Core.FSharpRef`1>, int32)][offset 0x00000058][found Byte] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader::seekReadGenericParamConstraintIdx([FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+ILMetadataReader, [FSharp.Compiler.Service]FSharp.Compiler.IO.ReadOnlyByteMemory, int32)][offset 0x00000025][found Byte] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader::openMetadataReader(string, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+BinaryFile, int32, [S.P.CoreLib]System.Tuple`8,bool,bool,bool,bool,bool,System.Tuple`5,bool,int32,int32,int32>>, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+PEReader, [FSharp.Compiler.Service]FSharp.Compiler.IO.ReadOnlyByteMemory, [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1, bool)][offset 0x000006BD][found Boolean] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader::rowKindSize$cont@4423(bool, bool, bool, bool[], bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, [FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x000000E5][found Byte] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader::openMetadataReader(string, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+BinaryFile, int32, [S.P.CoreLib]System.Tuple`8,bool,bool,bool,bool,bool,System.Tuple`5,bool,int32,int32,int32>>, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+PEReader, [FSharp.Compiler.Service]FSharp.Compiler.IO.ReadOnlyByteMemory, [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1, bool)][offset 0x000006BF][found Boolean] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader+seekReadInterfaceImpls@2238-3::Invoke(int32)][offset 0x0000002F][found Byte] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader+seekReadGenericParamConstraints@2303-2::Invoke(int32)][offset 0x0000002F][found Byte] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader+enclIdx@2332-2::Invoke(int32)][offset 0x0000002F][found Byte] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader+seekReadMethodImpls@3100-4::Invoke(int32)][offset 0x0000002F][found Byte] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader+seekReadEvents@3180-3::Invoke(int32)][offset 0x0000002F][found Byte] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader+seekReadProperties@3250-3::Invoke(int32)][offset 0x0000002F][found Byte] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader+rowKindSize@4423-1::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x000000FD][found Byte] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.NativeRes+VersionHelper::TryParse(string, bool, uint16, bool, [S.P.CoreLib]System.Version&)][offset 0x00000026][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.IL::parseILVersion(string)][offset 0x0000000B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.IL::parseILVersion(string)][offset 0x00000021][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.IL+parseNamed@5242::Invoke([FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1>, int32, int32)][offset 0x0000008D][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : Internal.Utilities.Collections.Utils::shortPath(string)][offset 0x00000016][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : Internal.Utilities.FSharpEnvironment+probePathForDotnetHost@321::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x0000002A][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.SimulatedMSBuildReferenceResolver+SimulatedMSBuildResolver@68::FSharp.Compiler.CodeAnalysis.ILegacyReferenceResolver.Resolve([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyResolutionEnvironment, [S.P.CoreLib]System.Tuple`2[], string, [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, string, string, [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, string, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>>)][offset 0x000002F8][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.DiagnosticsLogger::staticInitialization@()][offset 0x00000066][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.IL::parseNamed@5241(uint8[], [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1>, int32, int32)][offset 0x0000007E][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : Internal.Utilities.Collections.Utils::shortPath(string)][offset 0x0000003A][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : Internal.Utilities.FSharpEnvironment::probePathForDotnetHost@320([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x0000002A][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.SimulatedMSBuildReferenceResolver+SimulatedMSBuildResolver@68::FSharp.Compiler.CodeAnalysis.ILegacyReferenceResolver.Resolve([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyResolutionEnvironment, [S.P.CoreLib]System.Tuple`2[], string, [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, string, string, [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, string, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>>)][offset 0x000002F5][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$FSharp.Compiler.DiagnosticsLogger::.cctor()][offset 0x000000B6][found Char] Unexpected type on the stack. [IL]: Error [CallVirtOnValueType]: : FSharp.Compiler.Text.RangeModule+comparer@543::System.Collections.Generic.IEqualityComparer.GetHashCode([FSharp.Compiler.Service]FSharp.Compiler.Text.Range)][offset 0x00000002] Callvirt on a value type method. [IL]: Error [StackUnexpected]: : Internal.Utilities.PathMapModule::applyDir([FSharp.Compiler.Service]Internal.Utilities.PathMap, string)][offset 0x00000035][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : Internal.Utilities.PathMapModule::applyDir([FSharp.Compiler.Service]Internal.Utilities.PathMap, string)][offset 0x00000041][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : Internal.Utilities.XmlAdapters::staticInitialization@()][offset 0x0000000A][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : Internal.Utilities.XmlAdapters::staticInitialization@()][offset 0x00000013][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : Internal.Utilities.XmlAdapters::staticInitialization@()][offset 0x0000001C][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : Internal.Utilities.XmlAdapters::staticInitialization@()][offset 0x00000025][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : Internal.Utilities.XmlAdapters::staticInitialization@()][offset 0x0000002E][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$Internal.Utilities.XmlAdapters::.cctor()][offset 0x0000000A][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$Internal.Utilities.XmlAdapters::.cctor()][offset 0x00000013][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$Internal.Utilities.XmlAdapters::.cctor()][offset 0x0000001C][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$Internal.Utilities.XmlAdapters::.cctor()][offset 0x00000025][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$Internal.Utilities.XmlAdapters::.cctor()][offset 0x0000002E][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.IO.FileSystemUtils::trimQuotes(string)][offset 0x0000000B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.IO.FileSystemUtils::trimQuotes(string)][offset 0x00000014][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : Internal.Utilities.Library.String::lowerCaseFirstChar(string)][offset 0x0000003A][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : Internal.Utilities.Library.Array+loop@276-3::Invoke(int32)][offset 0x00000012][found Byte] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : Internal.Utilities.Library.Array::loop@275-3(bool[], int32)][offset 0x00000008][found Byte] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : Microsoft.FSharp.Collections.ArrayModule+Parallel::Choose([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>, !!0[])][offset 0x00000081][found Byte] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : Microsoft.FSharp.Collections.ArrayModule+Parallel::Filter([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2, !!0[])][offset 0x00000029][found Byte] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : Microsoft.FSharp.Collections.ArrayModule+Parallel::Partition([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2, !!0[])][offset 0x00000038][found Byte] Unexpected type on the stack. From 511de908d79830878eb6367bed2cabee1bb016c3 Mon Sep 17 00:00:00 2001 From: Petr Pokorny Date: Wed, 18 Dec 2024 18:15:40 +0100 Subject: [PATCH 40/60] wip --- .../Common/CapabilitiesManager.fs | 38 ++++++++----- .../FSharp.Compiler.LanguageServer.fsproj | 1 + .../FSharpLanguageServer.fs | 10 +++- .../FSharpLanguageServerConfig.fs | 14 +++++ .../ExtensionEntrypoint.cs | 2 + .../FSharp.VisualStudio.Extension.csproj | 8 +-- .../FSharpExtensionSettings.cs | 55 +++++++++++++++++++ .../FSharpLanguageServerProvider.cs | 54 +++++++++++++++--- .../Classification/ClassificationService.fs | 13 +++++ .../Diagnostics/DocumentDiagnosticAnalyzer.fs | 7 ++- .../LanguageService/WorkspaceExtensions.fs | 42 ++++++++++++++ 11 files changed, 213 insertions(+), 31 deletions(-) create mode 100644 src/FSharp.Compiler.LanguageServer/FSharpLanguageServerConfig.fs create mode 100644 src/FSharp.VisualStudio.Extension/FSharpExtensionSettings.cs diff --git a/src/FSharp.Compiler.LanguageServer/Common/CapabilitiesManager.fs b/src/FSharp.Compiler.LanguageServer/Common/CapabilitiesManager.fs index b8b50e95f33..00b5e9c2bb3 100644 --- a/src/FSharp.Compiler.LanguageServer/Common/CapabilitiesManager.fs +++ b/src/FSharp.Compiler.LanguageServer/Common/CapabilitiesManager.fs @@ -2,43 +2,53 @@ open Microsoft.VisualStudio.LanguageServer.Protocol open Microsoft.CommonLanguageServerProtocol.Framework +open FSharp.Compiler.LanguageServer type IServerCapabilitiesOverride = - abstract member OverrideServerCapabilities: ServerCapabilities -> ServerCapabilities + abstract member OverrideServerCapabilities: FSharpLanguageServerConfig * ServerCapabilities * ClientCapabilities -> ServerCapabilities -type CapabilitiesManager(scOverrides: IServerCapabilitiesOverride seq) = +type CapabilitiesManager(config: FSharpLanguageServerConfig, scOverrides: IServerCapabilitiesOverride seq) = let mutable initializeParams = None - let defaultCapabilities = + let getInitializeParams() = + match initializeParams with + | Some params' -> params' + | None -> failwith "InitializeParams is null" + + let addIf (enabled: bool) (capability: 'a) = + if enabled then capability |> withNull else null + + let defaultCapabilities (clientCapabilities: ClientCapabilities) = + // TODO: don't register if dynamic registraion is supported ServerCapabilities( TextDocumentSync = TextDocumentSyncOptions(OpenClose = true, Change = TextDocumentSyncKind.Full), DiagnosticOptions = - DiagnosticOptions(WorkDoneProgress = true, InterFileDependencies = true, Identifier = "potato", WorkspaceDiagnostics = true), + addIf config.EnabledFeatures.Diagnostics (DiagnosticOptions(WorkDoneProgress = true, InterFileDependencies = true, Identifier = "potato", WorkspaceDiagnostics = true)), //CompletionProvider = CompletionOptions(TriggerCharacters = [| "."; " " |], ResolveProvider = true, WorkDoneProgress = true), //HoverProvider = SumType(HoverOptions(WorkDoneProgress = true)) SemanticTokensOptions = - SemanticTokensOptions( + addIf config.EnabledFeatures.SemanticHighlighting + + (SemanticTokensOptions( Legend = SemanticTokensLegend( TokenTypes = (SemanticTokenTypes.AllTypes |> Seq.toArray), // XXX should be extended TokenModifiers = (SemanticTokenModifiers.AllModifiers |> Seq.toArray) ), Range = false - ) + )) ) interface IInitializeManager with member this.SetInitializeParams(request) = initializeParams <- Some request + member this.GetInitializeParams() = getInitializeParams() + member this.GetInitializeResult() = + let clientCapabilities = getInitializeParams().Capabilities let serverCapabilities = - (defaultCapabilities, scOverrides) - ||> Seq.fold (fun acc (x: IServerCapabilitiesOverride) -> x.OverrideServerCapabilities acc) - - InitializeResult(Capabilities = serverCapabilities) + (defaultCapabilities clientCapabilities, scOverrides) + ||> Seq.fold (fun acc (x: IServerCapabilitiesOverride) -> x.OverrideServerCapabilities(config, acc, clientCapabilities)) - member this.GetInitializeParams() = - match initializeParams with - | Some params' -> params' - | None -> failwith "InitializeParams is null" + InitializeResult(Capabilities = serverCapabilities) \ No newline at end of file diff --git a/src/FSharp.Compiler.LanguageServer/FSharp.Compiler.LanguageServer.fsproj b/src/FSharp.Compiler.LanguageServer/FSharp.Compiler.LanguageServer.fsproj index 1b5f7c99122..d6ebfc1c6f6 100644 --- a/src/FSharp.Compiler.LanguageServer/FSharp.Compiler.LanguageServer.fsproj +++ b/src/FSharp.Compiler.LanguageServer/FSharp.Compiler.LanguageServer.fsproj @@ -34,6 +34,7 @@ + diff --git a/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs index 9d0d3ec4412..d5e234526bf 100644 --- a/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs +++ b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs @@ -30,11 +30,12 @@ type Extensions = Async.StartAsTask(this, cancellationToken = ct) type FSharpLanguageServer - (jsonRpc: JsonRpc, logger: ILspLogger, ?initialWorkspace: FSharpWorkspace, ?addExtraHandlers: Action) = + (jsonRpc: JsonRpc, logger: ILspLogger, ?initialWorkspace: FSharpWorkspace, ?addExtraHandlers: Action, ?config: FSharpLanguageServerConfig) = // TODO: Switch to SystemTextJsonLanguageServer inherit NewtonsoftLanguageServer(jsonRpc, Newtonsoft.Json.JsonSerializer.CreateDefault(), logger) + let config = defaultArg config FSharpLanguageServerConfig.Default let initialWorkspace = defaultArg initialWorkspace (FSharpWorkspace()) do @@ -77,7 +78,10 @@ type FSharpLanguageServer static member Create(initialWorkspace, addExtraHandlers: Action) = FSharpLanguageServer.Create(LspLogger System.Diagnostics.Trace.TraceInformation, initialWorkspace, addExtraHandlers) - static member Create(logger: ILspLogger, initialWorkspace, ?addExtraHandlers: Action) = + static member Create(initialWorkspace, config: FSharpLanguageServerConfig, addExtraHandlers: Action) = + FSharpLanguageServer.Create(LspLogger System.Diagnostics.Trace.TraceInformation, initialWorkspace, addExtraHandlers, config) + + static member Create(logger: ILspLogger, initialWorkspace, ?addExtraHandlers: Action, ?config: FSharpLanguageServerConfig) = let struct (clientStream, serverStream) = FullDuplexStream.CreatePair() @@ -96,7 +100,7 @@ type FSharpLanguageServer jsonRpc.TraceSource.Switch.Level <- SourceLevels.All let server = - new FSharpLanguageServer(jsonRpc, logger, initialWorkspace, ?addExtraHandlers = addExtraHandlers) + new FSharpLanguageServer(jsonRpc, logger, initialWorkspace, ?addExtraHandlers = addExtraHandlers, ?config = config) jsonRpc.StartListening() diff --git a/src/FSharp.Compiler.LanguageServer/FSharpLanguageServerConfig.fs b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServerConfig.fs new file mode 100644 index 00000000000..15e8466b0ed --- /dev/null +++ b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServerConfig.fs @@ -0,0 +1,14 @@ +namespace FSharp.Compiler.LanguageServer + +type FSharpLanguageServerFeatures = + { + Diagnostics: bool + SemanticHighlighting: bool + } + static member Default = { Diagnostics = true; SemanticHighlighting = true } + +type FSharpLanguageServerConfig = + { + EnabledFeatures: FSharpLanguageServerFeatures + } + static member Default = { EnabledFeatures = FSharpLanguageServerFeatures.Default } diff --git a/src/FSharp.VisualStudio.Extension/ExtensionEntrypoint.cs b/src/FSharp.VisualStudio.Extension/ExtensionEntrypoint.cs index 8fbaf39dc70..ba47320b925 100644 --- a/src/FSharp.VisualStudio.Extension/ExtensionEntrypoint.cs +++ b/src/FSharp.VisualStudio.Extension/ExtensionEntrypoint.cs @@ -2,6 +2,8 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.VisualStudio.Extensibility; +using System.Threading; +using System; using Extension = Microsoft.VisualStudio.Extensibility.Extension; /// diff --git a/src/FSharp.VisualStudio.Extension/FSharp.VisualStudio.Extension.csproj b/src/FSharp.VisualStudio.Extension/FSharp.VisualStudio.Extension.csproj index db3a0a846f3..fae776deb73 100644 --- a/src/FSharp.VisualStudio.Extension/FSharp.VisualStudio.Extension.csproj +++ b/src/FSharp.VisualStudio.Extension/FSharp.VisualStudio.Extension.csproj @@ -9,10 +9,10 @@ - - - - + + + + + + + + + + + + diff --git a/eng/Versions.props b/eng/Versions.props index bacdd4e3233..97afcbb5f76 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -94,7 +94,7 @@ 17.10.40152 17.10.526-pre-g1b474069f5 17.10.41 - 17.11.0-preview-24178-03 + 17.12.6 $(RoslynVersion) $(RoslynVersion) diff --git a/global.json b/global.json index 598d8b7367b..c4d9f0a9ee5 100644 --- a/global.json +++ b/global.json @@ -11,7 +11,7 @@ "Microsoft.VisualStudio.Component.FSharp" ] }, - "xcopy-msbuild": "17.12.0" + "xcopy-msbuild": "17.13.0" }, "native-tools": { "perl": "5.38.2.2" From e9af46c4cdcd4fd9444c44ce60355686efb27758 Mon Sep 17 00:00:00 2001 From: Adam Boniecki <20281641+abonie@users.noreply.github.com> Date: Wed, 9 Apr 2025 14:47:47 +0200 Subject: [PATCH 47/60] Run fantomas --- src/Compiler/Service/FSharpWorkspaceState.fs | 2 +- .../Handlers/DocumentStateHandler.fs | 21 ++++++------------- .../Handlers/LanguageFeaturesHandler.fs | 13 ++++++------ 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/src/Compiler/Service/FSharpWorkspaceState.fs b/src/Compiler/Service/FSharpWorkspaceState.fs index 6accec4751e..ff112a7977c 100644 --- a/src/Compiler/Service/FSharpWorkspaceState.fs +++ b/src/Compiler/Service/FSharpWorkspaceState.fs @@ -430,7 +430,7 @@ type FSharpWorkspaceProjects internal (depGraph: IThreadSafeDependencyGraph<_, _ Activity.start "Projects.Update" [ - Activity.Tags.project, !! projectIdentifier.ToString() + Activity.Tags.project, !!projectIdentifier.ToString() "sourceFiles", newSourceFiles |> String.concat "; " ] diff --git a/src/FSharp.Compiler.LanguageServer/Handlers/DocumentStateHandler.fs b/src/FSharp.Compiler.LanguageServer/Handlers/DocumentStateHandler.fs index 7cee1203d38..b7f281ed9e8 100644 --- a/src/FSharp.Compiler.LanguageServer/Handlers/DocumentStateHandler.fs +++ b/src/FSharp.Compiler.LanguageServer/Handlers/DocumentStateHandler.fs @@ -16,11 +16,8 @@ type DocumentStateHandler() = interface IRequestHandler with [] member _.HandleRequestAsync - ( - request: DidOpenTextDocumentParams, - context: FSharpRequestContext, - _cancellationToken: CancellationToken - ) = + (request: DidOpenTextDocumentParams, context: FSharpRequestContext, _cancellationToken: CancellationToken) + = let contextHolder = context.LspServices.GetRequiredService() contextHolder.UpdateWorkspace _.Files.Open(request.TextDocument.Uri, request.TextDocument.Text) @@ -30,11 +27,8 @@ type DocumentStateHandler() = interface IRequestHandler with [] member _.HandleRequestAsync - ( - request: DidChangeTextDocumentParams, - context: FSharpRequestContext, - _cancellationToken: CancellationToken - ) = + (request: DidChangeTextDocumentParams, context: FSharpRequestContext, _cancellationToken: CancellationToken) + = let contextHolder = context.LspServices.GetRequiredService() contextHolder.UpdateWorkspace _.Files.Edit(request.TextDocument.Uri, request.ContentChanges.[0].Text) @@ -44,11 +38,8 @@ type DocumentStateHandler() = interface INotificationHandler with [] member _.HandleNotificationAsync - ( - request: DidCloseTextDocumentParams, - context: FSharpRequestContext, - _cancellationToken: CancellationToken - ) = + (request: DidCloseTextDocumentParams, context: FSharpRequestContext, _cancellationToken: CancellationToken) + = let contextHolder = context.LspServices.GetRequiredService() contextHolder.UpdateWorkspace _.Files.Close(request.TextDocument.Uri) diff --git a/src/FSharp.Compiler.LanguageServer/Handlers/LanguageFeaturesHandler.fs b/src/FSharp.Compiler.LanguageServer/Handlers/LanguageFeaturesHandler.fs index a144cf1832b..474758924d0 100644 --- a/src/FSharp.Compiler.LanguageServer/Handlers/LanguageFeaturesHandler.fs +++ b/src/FSharp.Compiler.LanguageServer/Handlers/LanguageFeaturesHandler.fs @@ -16,14 +16,15 @@ type LanguageFeaturesHandler() = interface IMethodHandler with member _.MutatesSolutionState = false - interface IRequestHandler, FSharpRequestContext> with + interface IRequestHandler< + DocumentDiagnosticParams, + SumType, + FSharpRequestContext + > with [] member _.HandleRequestAsync - ( - request: DocumentDiagnosticParams, - context: FSharpRequestContext, - cancellationToken: CancellationToken - ) = + (request: DocumentDiagnosticParams, context: FSharpRequestContext, cancellationToken: CancellationToken) + = cancellableTask { let! fsharpDiagnosticReport = context.Workspace.Query.GetDiagnosticsForFile request.TextDocument.Uri From 0030459ae75354d67b47eb45f847753154446086 Mon Sep 17 00:00:00 2001 From: Adam Boniecki <20281641+abonie@users.noreply.github.com> Date: Thu, 10 Apr 2025 16:52:39 +0200 Subject: [PATCH 48/60] Revert change in AsyncMemoize --- src/Compiler/Facilities/AsyncMemoize.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler/Facilities/AsyncMemoize.fs b/src/Compiler/Facilities/AsyncMemoize.fs index ac52eaf8607..e776fe0aae0 100644 --- a/src/Compiler/Facilities/AsyncMemoize.fs +++ b/src/Compiler/Facilities/AsyncMemoize.fs @@ -114,7 +114,7 @@ module internal Utils = /// Return file name with one directory above it let shortPath (path: string) = - let dirPath = !!Path.GetDirectoryName(path) + let dirPath = Path.GetDirectoryName(path) |> Option.ofObj |> Option.defaultValue "" let dir = dirPath.Split Path.DirectorySeparatorChar From c785f3857df6b69bbdb1b96215b170b6b86f70be Mon Sep 17 00:00:00 2001 From: Adam Boniecki <20281641+abonie@users.noreply.github.com> Date: Fri, 11 Apr 2025 09:44:27 +0200 Subject: [PATCH 49/60] Adjust ilverify bsl --- .../ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl | 2 +- .../ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl index 525eb905b0f..a2e883eaf2d 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl @@ -80,7 +80,7 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.IL::parseILVersion(string)][offset 0x0000000B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.IL::parseILVersion(string)][offset 0x00000021][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.IL+parseNamed@5311::Invoke([FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1>, int32, int32)][offset 0x00000087][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : Internal.Utilities.Collections.Utils::shortPath(string)][offset 0x00000015][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : Internal.Utilities.Collections.Utils::shortPath(string)][offset 0x00000048][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : Internal.Utilities.FSharpEnvironment+probePathForDotnetHost@316::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000028][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.SimulatedMSBuildReferenceResolver+Pipe #6 input at line 68@68::FSharp.Compiler.CodeAnalysis.ILegacyReferenceResolver.Resolve([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyResolutionEnvironment, [S.P.CoreLib]System.Tuple`2[], string, [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, string, string, [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, string, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>>)][offset 0x0000034D][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$FSharp.Compiler.DiagnosticsLogger::.cctor()][offset 0x000000CD][found Char] Unexpected type on the stack. diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl index 93edb0b96df..57710d181f0 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl @@ -107,7 +107,7 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.IL::parseILVersion(string)][offset 0x0000000B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.IL::parseILVersion(string)][offset 0x00000021][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.IL::parseNamed@5310(uint8[], [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1>, int32, int32)][offset 0x0000007E][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : Internal.Utilities.Collections.Utils::shortPath(string)][offset 0x00000016][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : Internal.Utilities.Collections.Utils::shortPath(string)][offset 0x0000003A][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : Internal.Utilities.FSharpEnvironment::probePathForDotnetHost@315([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x0000002A][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.SimulatedMSBuildReferenceResolver+SimulatedMSBuildResolver@68::FSharp.Compiler.CodeAnalysis.ILegacyReferenceResolver.Resolve([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyResolutionEnvironment, [S.P.CoreLib]System.Tuple`2[], string, [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, string, string, [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, string, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>>)][offset 0x000002F5][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$FSharp.Compiler.DiagnosticsLogger::.cctor()][offset 0x000000B6][found Char] Unexpected type on the stack. From 79533b8e2c99f93127d0ede295c660fa486b7f19 Mon Sep 17 00:00:00 2001 From: Adam Boniecki <20281641+abonie@users.noreply.github.com> Date: Wed, 30 Apr 2025 17:09:11 +0200 Subject: [PATCH 50/60] Underscore unused variable --- .../Common/CapabilitiesManager.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FSharp.Compiler.LanguageServer/Common/CapabilitiesManager.fs b/src/FSharp.Compiler.LanguageServer/Common/CapabilitiesManager.fs index 23d81ff7403..556a1d96edc 100644 --- a/src/FSharp.Compiler.LanguageServer/Common/CapabilitiesManager.fs +++ b/src/FSharp.Compiler.LanguageServer/Common/CapabilitiesManager.fs @@ -19,7 +19,7 @@ type CapabilitiesManager(config: FSharpLanguageServerConfig, scOverrides: IServe let addIf (enabled: bool) (capability: 'a) = if enabled then capability |> withNull else null - let defaultCapabilities (clientCapabilities: ClientCapabilities) = + let defaultCapabilities (_clientCapabilities: ClientCapabilities) = // TODO: don't register if dynamic registraion is supported ServerCapabilities( TextDocumentSync = TextDocumentSyncOptions(OpenClose = true, Change = TextDocumentSyncKind.Full), From 13b59093ce663d78ea9043ba7619a64cbf7c9748 Mon Sep 17 00:00:00 2001 From: Adam Boniecki <20281641+abonie@users.noreply.github.com> Date: Wed, 30 Apr 2025 17:23:49 +0200 Subject: [PATCH 51/60] Run fantomas --- .../FSharpLanguageServer.fs | 8 +- .../Classification/ClassificationService.fs | 288 +++++++++--------- .../Diagnostics/DocumentDiagnosticAnalyzer.fs | 10 +- .../LanguageService/WorkspaceExtensions.fs | 36 ++- 4 files changed, 176 insertions(+), 166 deletions(-) diff --git a/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs index c5329d3cdbb..3aadb29b2b7 100644 --- a/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs +++ b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs @@ -89,12 +89,8 @@ type FSharpLanguageServer FSharpLanguageServer.Create(LspLogger System.Diagnostics.Trace.TraceInformation, initialWorkspace, addExtraHandlers, config) static member Create - ( - logger: ILspLogger, - initialWorkspace, - ?addExtraHandlers: Action, - ?config: FSharpLanguageServerConfig - ) = + (logger: ILspLogger, initialWorkspace, ?addExtraHandlers: Action, ?config: FSharpLanguageServerConfig) + = let struct (clientStream, serverStream) = FullDuplexStream.CreatePair() diff --git a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs index e9331986d5a..e6e07761a75 100644 --- a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs +++ b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs @@ -152,164 +152,156 @@ type internal FSharpClassificationService [] () = member _.AddLexicalClassifications(_: SourceText, _: TextSpan, _: List, _: CancellationToken) = () member _.AddSyntacticClassificationsAsync - ( - document: Document, - textSpan: TextSpan, - result: List, - cancellationToken: CancellationToken - ) = + (document: Document, textSpan: TextSpan, result: List, cancellationToken: CancellationToken) + = if not (document |> shouldProduceClassification) then System.Threading.Tasks.Task.CompletedTask - else - - cancellableTask { - use _logBlock = Logger.LogBlock(LogEditorFunctionId.Classification_Syntactic) - - let! cancellationToken = CancellableTask.getCancellationToken () - - let defines, langVersion, strictIndentation = document.GetFsharpParsingOptions() - - let! sourceText = document.GetTextAsync(cancellationToken) - - // For closed documents, only get classification for the text within the span. - // This may be inaccurate for multi-line tokens such as string literals, but this is ok for now - // as it's better than having to tokenize a big part of a file which in return will allocate a lot and hurt find all references performance. - let isOpenDocument = document.Project.Solution.Workspace.IsDocumentOpen document.Id - - let eventProps: (string * obj) array = - [| - "context.document.project.id", document.Project.Id.Id.ToString() - "context.document.id", document.Id.Id.ToString() - "isOpenDocument", isOpenDocument - "textSpanLength", textSpan.Length - |] - - use _eventDuration = - TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSyntacticClassifications, eventProps) - - if not isOpenDocument then - let classifiedSpans = - getLexicalClassifications (document.FilePath, defines, sourceText, textSpan, cancellationToken) - - result.AddRange(classifiedSpans) - else - Tokenizer.classifySpans ( - document.Id, - sourceText, - textSpan, - Some(document.FilePath), - defines, - Some langVersion, - strictIndentation, - result, - cancellationToken - ) - } - |> CancellableTask.startAsTask cancellationToken + else + + cancellableTask { + use _logBlock = Logger.LogBlock(LogEditorFunctionId.Classification_Syntactic) + + let! cancellationToken = CancellableTask.getCancellationToken () + + let defines, langVersion, strictIndentation = document.GetFsharpParsingOptions() + + let! sourceText = document.GetTextAsync(cancellationToken) + + // For closed documents, only get classification for the text within the span. + // This may be inaccurate for multi-line tokens such as string literals, but this is ok for now + // as it's better than having to tokenize a big part of a file which in return will allocate a lot and hurt find all references performance. + let isOpenDocument = document.Project.Solution.Workspace.IsDocumentOpen document.Id + + let eventProps: (string * obj) array = + [| + "context.document.project.id", document.Project.Id.Id.ToString() + "context.document.id", document.Id.Id.ToString() + "isOpenDocument", isOpenDocument + "textSpanLength", textSpan.Length + |] + + use _eventDuration = + TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSyntacticClassifications, eventProps) + + if not isOpenDocument then + let classifiedSpans = + getLexicalClassifications (document.FilePath, defines, sourceText, textSpan, cancellationToken) + + result.AddRange(classifiedSpans) + else + Tokenizer.classifySpans ( + document.Id, + sourceText, + textSpan, + Some(document.FilePath), + defines, + Some langVersion, + strictIndentation, + result, + cancellationToken + ) + } + |> CancellableTask.startAsTask cancellationToken member _.AddSemanticClassificationsAsync - ( - document: Document, - textSpan: TextSpan, - result: List, - cancellationToken: CancellationToken - ) = + (document: Document, textSpan: TextSpan, result: List, cancellationToken: CancellationToken) + = if not (document |> shouldProduceClassification) then System.Threading.Tasks.Task.CompletedTask - else - - cancellableTask { - use _logBlock = Logger.LogBlock(LogEditorFunctionId.Classification_Semantic) - - let! sourceText = document.GetTextAsync(cancellationToken) - - // If we are trying to get semantic classification for a document that is not open, get the results from the background and cache it. - // We do this for find all references when it is populating results. - // We cache it temporarily so we do not have to continuously call into the checker and perform a background operation. - let isOpenDocument = document.Project.Solution.Workspace.IsDocumentOpen document.Id - - if not isOpenDocument then - match! unopenedDocumentsSemanticClassificationCache.TryGetValueAsync document with - | ValueSome classificationDataLookup -> - let eventProps: (string * obj) array = - [| - "context.document.project.id", document.Project.Id.Id.ToString() - "context.document.id", document.Id.Id.ToString() - "isOpenDocument", isOpenDocument - "textSpanLength", textSpan.Length - "cacheHit", true - |] - - use _eventDuration = - TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSemanticClassifications, eventProps) - - addSemanticClassificationByLookup sourceText textSpan classificationDataLookup result - | ValueNone -> - let eventProps: (string * obj) array = - [| - "context.document.project.id", document.Project.Id.Id.ToString() - "context.document.id", document.Id.Id.ToString() - "isOpenDocument", isOpenDocument - "textSpanLength", textSpan.Length - "cacheHit", false - |] - - use _eventDuration = - TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSemanticClassifications, eventProps) - - let! classificationData = document.GetFSharpSemanticClassificationAsync(nameof (FSharpClassificationService)) - - let classificationDataLookup = toSemanticClassificationLookup classificationData - do! unopenedDocumentsSemanticClassificationCache.SetAsync(document, classificationDataLookup) - addSemanticClassificationByLookup sourceText textSpan classificationDataLookup result - else - - match! openedDocumentsSemanticClassificationCache.TryGetValueAsync document with - | ValueSome classificationDataLookup -> - let eventProps: (string * obj) array = - [| - "context.document.project.id", document.Project.Id.Id.ToString() - "context.document.id", document.Id.Id.ToString() - "isOpenDocument", isOpenDocument - "textSpanLength", textSpan.Length - "cacheHit", true - |] - - use _eventDuration = - TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSemanticClassifications, eventProps) - - addSemanticClassificationByLookup sourceText textSpan classificationDataLookup result - | ValueNone -> - - let eventProps: (string * obj) array = - [| - "context.document.project.id", document.Project.Id.Id.ToString() - "context.document.id", document.Id.Id.ToString() - "isOpenDocument", isOpenDocument - "textSpanLength", textSpan.Length - "cacheHit", false - |] - - use _eventDuration = - TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSemanticClassifications, eventProps) - - let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync(nameof (IFSharpClassificationService)) - - let targetRange = - RoslynHelpers.TextSpanToFSharpRange(document.FilePath, textSpan, sourceText) - - let classificationData = checkResults.GetSemanticClassification(Some targetRange) - - if classificationData.Length > 0 then - let classificationDataLookup = itemToSemanticClassificationLookup classificationData + else + + cancellableTask { + use _logBlock = Logger.LogBlock(LogEditorFunctionId.Classification_Semantic) + + let! sourceText = document.GetTextAsync(cancellationToken) + + // If we are trying to get semantic classification for a document that is not open, get the results from the background and cache it. + // We do this for find all references when it is populating results. + // We cache it temporarily so we do not have to continuously call into the checker and perform a background operation. + let isOpenDocument = document.Project.Solution.Workspace.IsDocumentOpen document.Id + + if not isOpenDocument then + match! unopenedDocumentsSemanticClassificationCache.TryGetValueAsync document with + | ValueSome classificationDataLookup -> + let eventProps: (string * obj) array = + [| + "context.document.project.id", document.Project.Id.Id.ToString() + "context.document.id", document.Id.Id.ToString() + "isOpenDocument", isOpenDocument + "textSpanLength", textSpan.Length + "cacheHit", true + |] + + use _eventDuration = + TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSemanticClassifications, eventProps) + + addSemanticClassificationByLookup sourceText textSpan classificationDataLookup result + | ValueNone -> + let eventProps: (string * obj) array = + [| + "context.document.project.id", document.Project.Id.Id.ToString() + "context.document.id", document.Id.Id.ToString() + "isOpenDocument", isOpenDocument + "textSpanLength", textSpan.Length + "cacheHit", false + |] + + use _eventDuration = + TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSemanticClassifications, eventProps) + + let! classificationData = document.GetFSharpSemanticClassificationAsync(nameof (FSharpClassificationService)) + + let classificationDataLookup = toSemanticClassificationLookup classificationData do! unopenedDocumentsSemanticClassificationCache.SetAsync(document, classificationDataLookup) + addSemanticClassificationByLookup sourceText textSpan classificationDataLookup result + else - addSemanticClassification sourceText textSpan classificationData result - } - |> CancellableTask.ifCanceledReturn () - |> CancellableTask.startAsTask cancellationToken + match! openedDocumentsSemanticClassificationCache.TryGetValueAsync document with + | ValueSome classificationDataLookup -> + let eventProps: (string * obj) array = + [| + "context.document.project.id", document.Project.Id.Id.ToString() + "context.document.id", document.Id.Id.ToString() + "isOpenDocument", isOpenDocument + "textSpanLength", textSpan.Length + "cacheHit", true + |] + + use _eventDuration = + TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSemanticClassifications, eventProps) + + addSemanticClassificationByLookup sourceText textSpan classificationDataLookup result + | ValueNone -> + + let eventProps: (string * obj) array = + [| + "context.document.project.id", document.Project.Id.Id.ToString() + "context.document.id", document.Id.Id.ToString() + "isOpenDocument", isOpenDocument + "textSpanLength", textSpan.Length + "cacheHit", false + |] + + use _eventDuration = + TelemetryReporter.ReportSingleEventWithDuration(TelemetryEvents.AddSemanticClassifications, eventProps) + + let! _, checkResults = document.GetFSharpParseAndCheckResultsAsync(nameof (IFSharpClassificationService)) + + let targetRange = + RoslynHelpers.TextSpanToFSharpRange(document.FilePath, textSpan, sourceText) + + let classificationData = checkResults.GetSemanticClassification(Some targetRange) + + if classificationData.Length > 0 then + let classificationDataLookup = itemToSemanticClassificationLookup classificationData + do! unopenedDocumentsSemanticClassificationCache.SetAsync(document, classificationDataLookup) + + addSemanticClassification sourceText textSpan classificationData result + } + |> CancellableTask.ifCanceledReturn () + |> CancellableTask.startAsTask cancellationToken // Do not perform classification if we don't have project options (#defines matter) member _.AdjustStaleClassification(_: SourceText, classifiedSpan: ClassifiedSpan) : ClassifiedSpan = classifiedSpan diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs index ab1da64c1d8..b7f7ed3d4cf 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs @@ -151,14 +151,20 @@ type internal FSharpDocumentDiagnosticAnalyzer [] () = interface IFSharpDocumentDiagnosticAnalyzer with member _.AnalyzeSyntaxAsync(document: Document, cancellationToken: CancellationToken) : Task> = - if document.Project.IsFSharpMetadata || (not (document |> shouldProduceDiagnostics)) then + if + document.Project.IsFSharpMetadata + || (not (document |> shouldProduceDiagnostics)) + then Task.FromResult ImmutableArray.Empty else FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(document, DiagnosticsType.Syntax) |> CancellableTask.start cancellationToken member _.AnalyzeSemanticsAsync(document: Document, cancellationToken: CancellationToken) : Task> = - if document.Project.IsFSharpMiscellaneousOrMetadata && not document.IsFSharpScript || (not (document |> shouldProduceDiagnostics)) then + if + document.Project.IsFSharpMiscellaneousOrMetadata && not document.IsFSharpScript + || (not (document |> shouldProduceDiagnostics)) + then Task.FromResult ImmutableArray.Empty else FSharpDocumentDiagnosticAnalyzer.GetDiagnostics(document, DiagnosticsType.Semantic) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index ba6f1a1fa7a..cf5b9fd42ac 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -35,14 +35,21 @@ module internal ProjectCache = module internal SolutionConfigCache = - type FSharpExtensionConfig = { - GetDiagnosticsFrom: string - GetSemanticHighlightingFrom: string - } with + type FSharpExtensionConfig = + { + GetDiagnosticsFrom: string + GetSemanticHighlightingFrom: string + } + static member Old = "old" static member Lsp = "lsp" static member Both = "both" - static member Default = { GetDiagnosticsFrom = FSharpExtensionConfig.Both; GetSemanticHighlightingFrom = FSharpExtensionConfig.Both } + + static member Default = + { + GetDiagnosticsFrom = FSharpExtensionConfig.Both + GetSemanticHighlightingFrom = FSharpExtensionConfig.Both + } member this.ShouldProduceDiagnostics() = Set.contains this.GetDiagnosticsFrom (set [ FSharpExtensionConfig.Old; FSharpExtensionConfig.Both ]) @@ -51,11 +58,14 @@ module internal SolutionConfigCache = Set.contains this.GetSemanticHighlightingFrom (set [ FSharpExtensionConfig.Old; FSharpExtensionConfig.Both ]) let readFSharpExtensionConfig (solutionPath: string) = - let configFilePath = Path.Combine(solutionPath, "extensibility.settings.VisualStudio.json") + let configFilePath = + Path.Combine(solutionPath, "extensibility.settings.VisualStudio.json") + if File.Exists configFilePath then try let json = File.ReadAllText configFilePath let jObject = JObject.Parse json + { GetDiagnosticsFrom = jObject["fsharp.getDiagnosticsFrom"].ToString().ToLower() GetSemanticHighlightingFrom = jObject["fsharp.getSemanticHighlightingFrom"].ToString().ToLower() @@ -64,11 +74,13 @@ module internal SolutionConfigCache = System.Diagnostics.Trace.TraceError($"Error reading FSharpExtensionConfig from {configFilePath}", ex) FSharpExtensionConfig.Default else - System.Diagnostics.Trace.TraceInformation($"extensibility.settings.VisualStudio.json not found in {solutionPath}. Using default config.") + System.Diagnostics.Trace.TraceInformation( + $"extensibility.settings.VisualStudio.json not found in {solutionPath}. Using default config." + ) + FSharpExtensionConfig.Default - let ExtensionConfig = - ConditionalWeakTable() + let ExtensionConfig = ConditionalWeakTable() type Solution with @@ -77,7 +89,11 @@ type Solution with this.Workspace.Services.GetRequiredService() member internal this.GetFSharpExtensionConfig() = - SolutionConfigCache.ExtensionConfig.GetValue(this, ConditionalWeakTable<_, _>.CreateValueCallback(fun _ -> SolutionConfigCache.readFSharpExtensionConfig(Path.GetDirectoryName this.FilePath))) + SolutionConfigCache.ExtensionConfig.GetValue( + this, + ConditionalWeakTable<_, _>.CreateValueCallback(fun _ -> + SolutionConfigCache.readFSharpExtensionConfig (Path.GetDirectoryName this.FilePath)) + ) module internal FSharpProjectSnapshotSerialization = From 26d98a54b28718c4f3ea09404ad1d3969dad1298 Mon Sep 17 00:00:00 2001 From: Adam Boniecki <20281641+abonie@users.noreply.github.com> Date: Tue, 13 May 2025 13:29:29 +0200 Subject: [PATCH 52/60] Add Editor project to sln --- VSFSharpExtension.sln | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/VSFSharpExtension.sln b/VSFSharpExtension.sln index 3a246468d30..a4e19ff2fae 100644 --- a/VSFSharpExtension.sln +++ b/VSFSharpExtension.sln @@ -15,6 +15,8 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.Core", "src\FSharp.C EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharp.DependencyManager.Nuget", "src\FSharp.DependencyManager.Nuget\FSharp.DependencyManager.Nuget.fsproj", "{860808CF-D092-4511-B011-6205B654031D}" EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.Editor", "vsintegration\src\FSharp.Editor\FSharp.Editor.fsproj", "{3D4A95CB-1563-CD9A-3949-FE01922CD5BD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -53,6 +55,12 @@ Global {860808CF-D092-4511-B011-6205B654031D}.Proto|Any CPU.Build.0 = Debug|Any CPU {860808CF-D092-4511-B011-6205B654031D}.Release|Any CPU.ActiveCfg = Release|Any CPU {860808CF-D092-4511-B011-6205B654031D}.Release|Any CPU.Build.0 = Release|Any CPU + {3D4A95CB-1563-CD9A-3949-FE01922CD5BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3D4A95CB-1563-CD9A-3949-FE01922CD5BD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3D4A95CB-1563-CD9A-3949-FE01922CD5BD}.Proto|Any CPU.ActiveCfg = Debug|Any CPU + {3D4A95CB-1563-CD9A-3949-FE01922CD5BD}.Proto|Any CPU.Build.0 = Debug|Any CPU + {3D4A95CB-1563-CD9A-3949-FE01922CD5BD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3D4A95CB-1563-CD9A-3949-FE01922CD5BD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 7c1b9627e98b17578bf70feecec5d772c5c5a275 Mon Sep 17 00:00:00 2001 From: Adam Boniecki <20281641+abonie@users.noreply.github.com> Date: Fri, 16 May 2025 09:08:39 +0200 Subject: [PATCH 53/60] Handle null argument in reading extension config (#18558) --- .../LanguageService/WorkspaceExtensions.fs | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs index cf5b9fd42ac..518bf88bc74 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -58,27 +58,31 @@ module internal SolutionConfigCache = Set.contains this.GetSemanticHighlightingFrom (set [ FSharpExtensionConfig.Old; FSharpExtensionConfig.Both ]) let readFSharpExtensionConfig (solutionPath: string) = - let configFilePath = - Path.Combine(solutionPath, "extensibility.settings.VisualStudio.json") + if String.IsNullOrEmpty(solutionPath) then + System.Diagnostics.Trace.TraceWarning("Solution path is null or empty. Using default config.") + FSharpExtensionConfig.Default + else + let configFilePath = + Path.Combine(solutionPath, "extensibility.settings.VisualStudio.json") - if File.Exists configFilePath then - try - let json = File.ReadAllText configFilePath - let jObject = JObject.Parse json + if File.Exists configFilePath then + try + let json = File.ReadAllText configFilePath + let jObject = JObject.Parse json - { - GetDiagnosticsFrom = jObject["fsharp.getDiagnosticsFrom"].ToString().ToLower() - GetSemanticHighlightingFrom = jObject["fsharp.getSemanticHighlightingFrom"].ToString().ToLower() - } - with ex -> - System.Diagnostics.Trace.TraceError($"Error reading FSharpExtensionConfig from {configFilePath}", ex) - FSharpExtensionConfig.Default - else - System.Diagnostics.Trace.TraceInformation( - $"extensibility.settings.VisualStudio.json not found in {solutionPath}. Using default config." - ) + { + GetDiagnosticsFrom = jObject["fsharp.getDiagnosticsFrom"].ToString().ToLower() + GetSemanticHighlightingFrom = jObject["fsharp.getSemanticHighlightingFrom"].ToString().ToLower() + } + with ex -> + System.Diagnostics.Trace.TraceError($"Error reading FSharpExtensionConfig from {configFilePath}", ex) + FSharpExtensionConfig.Default + else + System.Diagnostics.Trace.TraceInformation( + $"extensibility.settings.VisualStudio.json not found in {solutionPath}. Using default config." + ) - FSharpExtensionConfig.Default + FSharpExtensionConfig.Default let ExtensionConfig = ConditionalWeakTable() From 12213b3c1765f168ba06447802e9ee9247622713 Mon Sep 17 00:00:00 2001 From: Adam Boniecki <20281641+abonie@users.noreply.github.com> Date: Thu, 29 May 2025 14:53:41 +0200 Subject: [PATCH 54/60] Fix merge from main Add a null check to FSharpWorkspace --- .../CompilerService/FSharpWorkspace.fs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/FSharpWorkspace.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/FSharpWorkspace.fs index fd3aa628ab5..519d9215c37 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/FSharpWorkspace.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/FSharpWorkspace.fs @@ -49,7 +49,10 @@ type TestingWorkspace(testName) as _this = interface IDisposable with member _.Dispose() = - activity.Dispose() + if not (isNull activity) then + activity.Dispose() + else + () //tracerProvider.ForceFlush() |> ignore //tracerProvider.Dispose() From 8711bb92bfdcabd43a4648269af5e0ea69bd6a65 Mon Sep 17 00:00:00 2001 From: Adam Boniecki <20281641+abonie@users.noreply.github.com> Date: Thu, 29 May 2025 15:41:10 +0200 Subject: [PATCH 55/60] Fix ilverify baseline --- ...verify_FSharp.Compiler.Service_Debug_net9.0.bsl | 14 +++++++------- ...Sharp.Compiler.Service_Debug_netstandard2.0.bsl | 14 +++++++------- ...rify_FSharp.Compiler.Service_Release_net9.0.bsl | 2 +- ...arp.Compiler.Service_Release_netstandard2.0.bsl | 2 +- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl index 7b028fc821e..a85997220f0 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_net9.0.bsl @@ -21,14 +21,14 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x00000082][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x0000008B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+MagicAssemblyResolution::ResolveAssemblyCore([FSharp.Compiler.Service]Internal.Utilities.Library.CompilationThreadToken, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, [FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, [FSharp.Compiler.Service]FSharp.Compiler.CompilerImports+TcImports, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompiler, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiConsoleOutput, string)][offset 0x00000015][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3494-807::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001E5][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3494-813::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001E5][found Char] Unexpected type on the stack. [IL]: Error [UnmanagedPointer]: : FSharp.Compiler.Interactive.Shell+Utilities+pointerToNativeInt@106::Invoke(object)][offset 0x00000007] Unmanaged pointers are not a verifiable type. [IL]: Error [StackUnexpected]: : .$FSharpCheckerResults+dataTipOfReferences@2225::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000084][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@924-509::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@924-509::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000003B][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@924-509::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000082][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@924-509::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000008B][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@924-509::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000094][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@924-515::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@924-515::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000003B][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@924-515::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000082][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@924-515::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000008B][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@924-515::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000094][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.StaticLinking+TypeForwarding::followTypeForwardForILTypeRef([FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.IL+ILTypeRef)][offset 0x00000010][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerOptions::getCompilerOption([FSharp.Compiler.Service]FSharp.Compiler.CompilerOptions+CompilerOption, [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1)][offset 0x000000E6][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CompilerOptions::AddPathMapping([FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, string)][offset 0x0000000B][found Char] Unexpected type on the stack. @@ -54,7 +54,7 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILPdbWriter+PortablePdbGenerator::serializeDocumentName(string)][offset 0x00000090][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILPdbWriter+pushShadowedLocals@959::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000232][found Byte] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader::seekReadUntaggedIdx([FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.BinaryConstants+TableName, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+ILMetadataReader, [FSharp.Compiler.Service]FSharp.Compiler.IO.ReadOnlyByteMemory, int32&)][offset 0x0000000D][found Byte] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader::openMetadataReader(string, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+BinaryFile, int32, [S.P.CoreLib]System.Tuple`8,bool,bool,bool,bool,bool,System.Tuple`5,bool,int32,int32,int32>>, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+PEReader, [FSharp.Compiler.Service]FSharp.Compiler.IO.ReadOnlyByteMemory, [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1, bool)][offset 0x000007A1][found Boolean] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader::openMetadataReader(string, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+BinaryFile, int32, [S.P.CoreLib]System.Tuple`8,bool,bool,bool,bool,bool,System.Tuple`5,bool,int32,int32,int32>>, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+PEReader, [FSharp.Compiler.Service]FSharp.Compiler.IO.ReadOnlyByteMemory, [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1, bool)][offset 0x000007A3][found Boolean] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader+rowKindSize@4445::Invoke([FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+RowKind)][offset 0x00000128][found Byte] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.IL::parseILVersion(string)][offset 0x0000000B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.IL::parseILVersion(string)][offset 0x00000021][found Char] Unexpected type on the stack. diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl index 2338b087b2d..97e484e95ba 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Debug_netstandard2.0.bsl @@ -28,18 +28,18 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x0000008B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+FsiStdinSyphon::GetLine(string, int32)][offset 0x00000039][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+MagicAssemblyResolution::ResolveAssemblyCore([FSharp.Compiler.Service]Internal.Utilities.Library.CompilationThreadToken, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, [FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, [FSharp.Compiler.Service]FSharp.Compiler.CompilerImports+TcImports, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompiler, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiConsoleOutput, string)][offset 0x00000015][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3494-807::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001E5][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3494-813::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001E5][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+FsiInteractionProcessor::CompletionsForPartialLID([FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompilerState, string)][offset 0x0000001B][found Char] Unexpected type on the stack. [IL]: Error [UnmanagedPointer]: : FSharp.Compiler.Interactive.Shell+Utilities+pointerToNativeInt@106::Invoke(object)][offset 0x00000007] Unmanaged pointers are not a verifiable type. [IL]: Error [StackUnexpected]: : .$FSharpCheckerResults+dataTipOfReferences@2225::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000084][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.AssemblyContent+traverseMemberFunctionAndValues@176::Invoke([FSharp.Compiler.Service]FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue)][offset 0x00000059][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.AssemblyContent+traverseEntity@218::GenerateNext([S.P.CoreLib]System.Collections.Generic.IEnumerable`1&)][offset 0x000000DA][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.ParsedInput+visitor@1431-6::VisitExpr([FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>, [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2>, [FSharp.Compiler.Service]FSharp.Compiler.Syntax.SynExpr)][offset 0x00000605][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@924-509::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@924-509::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000003B][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@924-509::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000082][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@924-509::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000008B][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@924-509::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000094][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@924-515::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@924-515::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000003B][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@924-515::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000082][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@924-515::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000008B][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@924-515::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000094][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$Symbols+fullName@2496-1::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000015][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CreateILModule+MainModuleBuilder::ConvertProductVersionToILVersionInfo(string)][offset 0x00000011][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.StaticLinking+TypeForwarding::followTypeForwardForILTypeRef([FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.IL+ILTypeRef)][offset 0x00000010][found Char] Unexpected type on the stack. @@ -74,7 +74,7 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILPdbWriter+PortablePdbGenerator::serializeDocumentName(string)][offset 0x00000090][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILPdbWriter+pushShadowedLocals@959::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000232][found Byte] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader::seekReadUntaggedIdx([FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.BinaryConstants+TableName, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+ILMetadataReader, [FSharp.Compiler.Service]FSharp.Compiler.IO.ReadOnlyByteMemory, int32&)][offset 0x0000000D][found Byte] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader::openMetadataReader(string, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+BinaryFile, int32, [S.P.CoreLib]System.Tuple`8,bool,bool,bool,bool,bool,System.Tuple`5,bool,int32,int32,int32>>, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+PEReader, [FSharp.Compiler.Service]FSharp.Compiler.IO.ReadOnlyByteMemory, [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1, bool)][offset 0x000007A1][found Boolean] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader::openMetadataReader(string, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+BinaryFile, int32, [S.P.CoreLib]System.Tuple`8,bool,bool,bool,bool,bool,System.Tuple`5,bool,int32,int32,int32>>, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+PEReader, [FSharp.Compiler.Service]FSharp.Compiler.IO.ReadOnlyByteMemory, [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1, bool)][offset 0x000007A3][found Boolean] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader+rowKindSize@4445::Invoke([FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+RowKind)][offset 0x00000128][found Byte] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.NativeRes+VersionHelper::TryParse(string, bool, uint16, bool, [S.P.CoreLib]System.Version&)][offset 0x0000003D][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.IL::parseILVersion(string)][offset 0x0000000B][found Char] Unexpected type on the stack. diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl index 85719ed70c8..98aacd8aed4 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl @@ -75,7 +75,7 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader::seekReadNestedRowUncached([FSharp.Core]Microsoft.FSharp.Core.FSharpRef`1>, int32)][offset 0x00000058][found Byte] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader::seekReadGenericParamConstraintIdx([FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+ILMetadataReader, [FSharp.Compiler.Service]FSharp.Compiler.IO.ReadOnlyByteMemory, int32)][offset 0x00000025][found Byte] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader::rowKindSize$cont@4446(bool, bool, bool, bool[], bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, [FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x000000E5][found Byte] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader::openMetadataReader(string, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+BinaryFile, int32, [S.P.CoreLib]System.Tuple`8,bool,bool,bool,bool,bool,System.Tuple`5,bool,int32,int32,int32>>, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+PEReader, [FSharp.Compiler.Service]FSharp.Compiler.IO.ReadOnlyByteMemory, [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1, bool)][offset 0x000006BF][found Boolean] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader::openMetadataReader(string, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+BinaryFile, int32, [S.P.CoreLib]System.Tuple`8,bool,bool,bool,bool,bool,System.Tuple`5,bool,int32,int32,int32>>, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+PEReader, [FSharp.Compiler.Service]FSharp.Compiler.IO.ReadOnlyByteMemory, [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1, bool)][offset 0x000006B6][found Boolean] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader+seekReadInterfaceImpls@2263-3::Invoke(int32)][offset 0x0000002F][found Byte] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader+seekReadGenericParamConstraints@2328-2::Invoke(int32)][offset 0x0000002F][found Byte] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader+enclIdx@2357-2::Invoke(int32)][offset 0x0000002F][found Byte] Unexpected type on the stack. diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl index 8468fc0e10a..720ff501a19 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl @@ -96,7 +96,7 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader::seekReadNestedRowUncached([FSharp.Core]Microsoft.FSharp.Core.FSharpRef`1>, int32)][offset 0x00000058][found Byte] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader::seekReadGenericParamConstraintIdx([FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+ILMetadataReader, [FSharp.Compiler.Service]FSharp.Compiler.IO.ReadOnlyByteMemory, int32)][offset 0x00000025][found Byte] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader::rowKindSize$cont@4446(bool, bool, bool, bool[], bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, bool, [FSharp.Core]Microsoft.FSharp.Collections.FSharpList`1, [FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x000000E5][found Byte] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader::openMetadataReader(string, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+BinaryFile, int32, [S.P.CoreLib]System.Tuple`8,bool,bool,bool,bool,bool,System.Tuple`5,bool,int32,int32,int32>>, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+PEReader, [FSharp.Compiler.Service]FSharp.Compiler.IO.ReadOnlyByteMemory, [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1, bool)][offset 0x000006BF][found Boolean] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader::openMetadataReader(string, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+BinaryFile, int32, [S.P.CoreLib]System.Tuple`8,bool,bool,bool,bool,bool,System.Tuple`5,bool,int32,int32,int32>>, [FSharp.Compiler.Service]FSharp.Compiler.AbstractIL.ILBinaryReader+PEReader, [FSharp.Compiler.Service]FSharp.Compiler.IO.ReadOnlyByteMemory, [FSharp.Core]Microsoft.FSharp.Core.FSharpOption`1, bool)][offset 0x000006B6][found Boolean] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader+seekReadInterfaceImpls@2263-3::Invoke(int32)][offset 0x0000002F][found Byte] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader+seekReadGenericParamConstraints@2328-2::Invoke(int32)][offset 0x0000002F][found Byte] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.AbstractIL.ILBinaryReader+enclIdx@2357-2::Invoke(int32)][offset 0x0000002F][found Byte] Unexpected type on the stack. From 18aa65039a014564f21b94c617afad124a096378 Mon Sep 17 00:00:00 2001 From: Adam Boniecki <20281641+abonie@users.noreply.github.com> Date: Fri, 30 May 2025 16:14:05 +0200 Subject: [PATCH 56/60] Update tests/FSharp.Compiler.ComponentTests/CompilerService/FSharpWorkspace.fs Co-authored-by: Tomas Grosup --- .../CompilerService/FSharpWorkspace.fs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/CompilerService/FSharpWorkspace.fs b/tests/FSharp.Compiler.ComponentTests/CompilerService/FSharpWorkspace.fs index 519d9215c37..6fd5062d35e 100644 --- a/tests/FSharp.Compiler.ComponentTests/CompilerService/FSharpWorkspace.fs +++ b/tests/FSharp.Compiler.ComponentTests/CompilerService/FSharpWorkspace.fs @@ -49,10 +49,7 @@ type TestingWorkspace(testName) as _this = interface IDisposable with member _.Dispose() = - if not (isNull activity) then - activity.Dispose() - else - () + use _ = activity in () //tracerProvider.ForceFlush() |> ignore //tracerProvider.Dispose() From 04de05357612ae6147b77e8b693f12dabf9a5c32 Mon Sep 17 00:00:00 2001 From: Adam Boniecki <20281641+abonie@users.noreply.github.com> Date: Thu, 5 Jun 2025 15:30:37 +0200 Subject: [PATCH 57/60] Update TransparentCompiler.fs Go back to trying to use GetLastModifiedTimeOnDisk to decide if file should be read from disk. As the TODO comment explains, it is flawed in that in doesn't always switch to in-memory mode when possible, but that's still better than ignoring changes made outside the editor --- src/Compiler/Service/TransparentCompiler.fs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index a1acf0cea22..020c81e280f 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -1880,11 +1880,11 @@ type internal TransparentCompiler try - //let availableOnDiskModifiedTime = - // if FileSystem.FileExistsShim fileName then - // Some <| FileSystem.GetLastWriteTimeShim fileName - // else - // None + let availableOnDiskModifiedTime = + if FileSystem.FileExistsShim fileName then + Some <| FileSystem.GetLastWriteTimeShim fileName + else + None // TODO: This kinda works, but the problem is that in order to switch a project to "in-memory" mode // - some file needs to be edited (this triggers a re-check, but LastModifiedTimeOnDisk won't change) @@ -1893,8 +1893,8 @@ type internal TransparentCompiler // Until that sequence happens the project will be used from disk (if available). // To get around it we probably need to detect changes made in the editor and record a timestamp for them. let shouldUseOnDisk = false - //availableOnDiskModifiedTime - //|> Option.exists (fun t -> t >= projectSnapshot.GetLastModifiedTimeOnDisk()) + availableOnDiskModifiedTime + |> Option.exists (fun t -> t >= projectSnapshot.GetLastModifiedTimeOnDisk()) let name = projectSnapshot.ProjectFileName |> Path.GetFileNameWithoutExtension From 6ac0aaee3ea40446859b6fb2c99ad65719aa6512 Mon Sep 17 00:00:00 2001 From: Adam Boniecki <20281641+abonie@users.noreply.github.com> Date: Thu, 5 Jun 2025 15:31:50 +0200 Subject: [PATCH 58/60] Update src/Compiler/Service/TransparentCompiler.fs --- src/Compiler/Service/TransparentCompiler.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 020c81e280f..4338c0deec6 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -1892,7 +1892,7 @@ type internal TransparentCompiler // - and then another change has to be made (to any file buffer) - so that recheck is triggered and we get here again // Until that sequence happens the project will be used from disk (if available). // To get around it we probably need to detect changes made in the editor and record a timestamp for them. - let shouldUseOnDisk = false + let shouldUseOnDisk = availableOnDiskModifiedTime |> Option.exists (fun t -> t >= projectSnapshot.GetLastModifiedTimeOnDisk()) From 01a3f0c8526275a75fba558995b5109475d308bb Mon Sep 17 00:00:00 2001 From: Adam Boniecki <20281641+abonie@users.noreply.github.com> Date: Thu, 5 Jun 2025 15:32:39 +0200 Subject: [PATCH 59/60] Update src/Compiler/Service/TransparentCompiler.fs --- src/Compiler/Service/TransparentCompiler.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler/Service/TransparentCompiler.fs b/src/Compiler/Service/TransparentCompiler.fs index 4338c0deec6..0c3f01d4a3a 100644 --- a/src/Compiler/Service/TransparentCompiler.fs +++ b/src/Compiler/Service/TransparentCompiler.fs @@ -1865,7 +1865,7 @@ type internal TransparentCompiler } ) - let ComputeAssemblyData (projectSnapshot: ProjectSnapshot) _fileName = + let ComputeAssemblyData (projectSnapshot: ProjectSnapshot) fileName = caches.AssemblyData.Get( projectSnapshot.SignatureKey, async { From ad115075afe5a33869a0c55185be99ffe431b53e Mon Sep 17 00:00:00 2001 From: Adam Boniecki <20281641+abonie@users.noreply.github.com> Date: Thu, 5 Jun 2025 18:15:35 +0200 Subject: [PATCH 60/60] Fix baselines --- .../ilverify_FSharp.Compiler.Service_Release_net9.0.bsl | 2 +- .../ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl index 24d1bc972d0..e6e4e6c9d6a 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_net9.0.bsl @@ -21,7 +21,7 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x00000082][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x0000008B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+MagicAssemblyResolution::ResolveAssemblyCore([FSharp.Compiler.Service]Internal.Utilities.Library.CompilationThreadToken, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, [FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, [FSharp.Compiler.Service]FSharp.Compiler.CompilerImports+TcImports, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompiler, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiConsoleOutput, string)][offset 0x00000015][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3494-850::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001C7][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3494-851::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001C7][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$FSharpCheckerResults+GetReferenceResolutionStructuredToolTipText@2225::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000076][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@924-530::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x00000032][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$ServiceLexing+clo@924-530::Invoke([FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2,Microsoft.FSharp.Core.Unit>)][offset 0x0000003B][found Char] Unexpected type on the stack. diff --git a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl index a5602f8173f..1273c54bca1 100644 --- a/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl +++ b/tests/ILVerify/ilverify_FSharp.Compiler.Service_Release_netstandard2.0.bsl @@ -28,7 +28,7 @@ [IL]: Error [StackUnexpected]: : FSharp.Compiler.CodeAnalysis.Hosted.CompilerHelpers::fscCompile([FSharp.Compiler.Service]FSharp.Compiler.CodeAnalysis.LegacyReferenceResolver, string, string[])][offset 0x0000008B][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+FsiStdinSyphon::GetLine(string, int32)][offset 0x00000032][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+MagicAssemblyResolution::ResolveAssemblyCore([FSharp.Compiler.Service]Internal.Utilities.Library.CompilationThreadToken, [FSharp.Compiler.Service]FSharp.Compiler.Text.Range, [FSharp.Compiler.Service]FSharp.Compiler.CompilerConfig+TcConfigBuilder, [FSharp.Compiler.Service]FSharp.Compiler.CompilerImports+TcImports, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompiler, [FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiConsoleOutput, string)][offset 0x00000015][found Char] Unexpected type on the stack. -[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3494-850::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001C7][found Char] Unexpected type on the stack. +[IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+clo@3494-851::Invoke([S.P.CoreLib]System.Tuple`3)][offset 0x000001C7][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.Interactive.Shell+FsiInteractionProcessor::CompletionsForPartialLID([FSharp.Compiler.Service]FSharp.Compiler.Interactive.Shell+FsiDynamicCompilerState, string)][offset 0x00000024][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : .$FSharpCheckerResults+GetReferenceResolutionStructuredToolTipText@2225::Invoke([FSharp.Core]Microsoft.FSharp.Core.Unit)][offset 0x00000076][found Char] Unexpected type on the stack. [IL]: Error [StackUnexpected]: : FSharp.Compiler.EditorServices.AssemblyContent+traverseMemberFunctionAndValues@176::Invoke([FSharp.Compiler.Service]FSharp.Compiler.Symbols.FSharpMemberOrFunctionOrValue)][offset 0x0000002B][found Char] Unexpected type on the stack.