diff --git a/src/FSharp.Compiler.LanguageServer/Common/CapabilitiesManager.fs b/src/FSharp.Compiler.LanguageServer/Common/CapabilitiesManager.fs index b8b50e95f33..23d81ff7403 100644 --- a/src/FSharp.Compiler.LanguageServer/Common/CapabilitiesManager.fs +++ b/src/FSharp.Compiler.LanguageServer/Common/CapabilitiesManager.fs @@ -2,43 +2,62 @@ 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<bool, HoverOptions>(HoverOptions(WorkDoneProgress = true)) SemanticTokensOptions = - SemanticTokensOptions( - Legend = - SemanticTokensLegend( - TokenTypes = (SemanticTokenTypes.AllTypes |> Seq.toArray), // XXX should be extended - TokenModifiers = (SemanticTokenModifiers.AllModifiers |> Seq.toArray) - ), - Range = false - ) + 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<InitializeParams, InitializeResult> 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) + (defaultCapabilities clientCapabilities, scOverrides) + ||> Seq.fold (fun acc (x: IServerCapabilitiesOverride) -> x.OverrideServerCapabilities(config, acc, clientCapabilities)) InitializeResult(Capabilities = serverCapabilities) - - member this.GetInitializeParams() = - match initializeParams with - | Some params' -> params' - | None -> failwith "InitializeParams is null" 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 @@ <ItemGroup> <Compile Include="..\..\vsintegration\src\FSharp.Editor\Common\CancellableTasks.fs" /> <Compile Include="Utils.fs" /> + <Compile Include="FSharpLanguageServerConfig.fs" /> <Compile Include="Common\LifecycleManager.fs" /> <Compile Include="Common\CapabilitiesManager.fs" /> <Compile Include="Common\FSharpRequestContext.fs" /> diff --git a/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs index 9d0d3ec4412..c5329d3cdbb 100644 --- a/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs +++ b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs @@ -30,11 +30,18 @@ type Extensions = Async.StartAsTask(this, cancellationToken = ct) type FSharpLanguageServer - (jsonRpc: JsonRpc, logger: ILspLogger, ?initialWorkspace: FSharpWorkspace, ?addExtraHandlers: Action<IServiceCollection>) = + ( + jsonRpc: JsonRpc, + logger: ILspLogger, + ?initialWorkspace: FSharpWorkspace, + ?addExtraHandlers: Action<IServiceCollection>, + ?config: FSharpLanguageServerConfig + ) = // TODO: Switch to SystemTextJsonLanguageServer inherit NewtonsoftLanguageServer<FSharpRequestContext>(jsonRpc, Newtonsoft.Json.JsonSerializer.CreateDefault(), logger) + let config = defaultArg config FSharpLanguageServerConfig.Default let initialWorkspace = defaultArg initialWorkspace (FSharpWorkspace()) do @@ -50,6 +57,7 @@ type FSharpLanguageServer serviceCollection .AddSingleton(initialWorkspace) .AddSingleton<ContextHolder>() + .AddSingleton<FSharpLanguageServerConfig>(config) .AddSingleton<IMethodHandler, InitializeHandler<InitializeParams, InitializeResult, FSharpRequestContext>>() .AddSingleton<IMethodHandler, InitializedHandler<InitializedParams, FSharpRequestContext>>() .AddSingleton<IMethodHandler, DocumentStateHandler>() @@ -77,7 +85,16 @@ type FSharpLanguageServer static member Create(initialWorkspace, addExtraHandlers: Action<IServiceCollection>) = FSharpLanguageServer.Create(LspLogger System.Diagnostics.Trace.TraceInformation, initialWorkspace, addExtraHandlers) - static member Create(logger: ILspLogger, initialWorkspace, ?addExtraHandlers: Action<IServiceCollection>) = + static member Create(initialWorkspace, config: FSharpLanguageServerConfig, addExtraHandlers: Action<IServiceCollection>) = + FSharpLanguageServer.Create(LspLogger System.Diagnostics.Trace.TraceInformation, initialWorkspace, addExtraHandlers, config) + + static member Create + ( + logger: ILspLogger, + initialWorkspace, + ?addExtraHandlers: Action<IServiceCollection>, + ?config: FSharpLanguageServerConfig + ) = let struct (clientStream, serverStream) = FullDuplexStream.CreatePair() @@ -96,7 +113,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..a1acd2b88da --- /dev/null +++ b/src/FSharp.Compiler.LanguageServer/FSharpLanguageServerConfig.fs @@ -0,0 +1,23 @@ +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; /// <summary> 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 @@ </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.VisualStudio.Extensibility.Sdk" Version="17.13.39351" /> - <PackageReference Include="Microsoft.VisualStudio.Extensibility.Build" Version="17.13.39351" /> - <PackageReference Include="Microsoft.VisualStudio.LanguageServer.Protocol.Internal" Version="17.13.7-preview" /> - <PackageReference Include="Microsoft.VisualStudio.ProjectSystem.Query" Version="17.13.53-pre-g2e2e7c24c7" /> + <PackageReference Include="Microsoft.VisualStudio.Extensibility.Sdk" Version="17.13.39620" /> + <PackageReference Include="Microsoft.VisualStudio.Extensibility.Build" Version="17.13.39620" /> + <PackageReference Include="Microsoft.VisualStudio.LanguageServer.Protocol.Internal" Version="17.13.9" /> + <PackageReference Include="Microsoft.VisualStudio.ProjectSystem.Query" Version="17.13.66" /> <PackageReference Include="Microsoft.VisualStudio.Threading" Version="17.13.2" /> <!--<PackageReference Include="Microsoft.VisualStudio.OpenTelemetry.ClientExtensions" Version="0.1.718-beta" /> <PackageReference Include="Microsoft.VisualStudio.OpenTelemetry.Collector" Version="0.1.718-beta" /> diff --git a/src/FSharp.VisualStudio.Extension/FSharpExtensionSettings.cs b/src/FSharp.VisualStudio.Extension/FSharpExtensionSettings.cs new file mode 100644 index 00000000000..edc3e7adcff --- /dev/null +++ b/src/FSharp.VisualStudio.Extension/FSharpExtensionSettings.cs @@ -0,0 +1,55 @@ +using Microsoft.VisualStudio.Extensibility; +using Microsoft.VisualStudio.Extensibility.Settings; + +namespace FSharp.VisualStudio.Extension +{ +#pragma warning disable VSEXTPREVIEW_SETTINGS // The settings API is currently in preview and marked as experimental + + internal static class FSharpExtensionSettings + { + public const string OLD = "old"; + public const string LSP = "lsp"; + public const string BOTH = "both"; + public const string UNSET = "unset"; + + public static EnumSettingEntry[] ExtensionChoice { get; } = + [ + new(OLD, "%FSharpSettings.Old%"), + new(LSP, "%FSharpSettings.LSP%"), + new(BOTH, "%FSharpSettings.Both%"), + new(UNSET, "%FSharpSettings.Unset%"), + ]; + + + [VisualStudioContribution] + public static SettingCategory FSharpCategory { get; } = new("fsharp", "%F#%"); + + [VisualStudioContribution] + public static Setting.Enum GetDiagnosticsFrom { get; } = new( + "getDiagnosticsFrom", + "%FSharpSettings.GetDiagnosticsFrom%", + FSharpCategory, + ExtensionChoice, + defaultValue: UNSET) + { + Description = "%Which extension should be used to provide diagnostics%", + }; + + [VisualStudioContribution] + public static Setting.Enum GetSemanticHighlightingFrom { get; } = new( + "getSemanticHighlightingFrom", + "%FSharpSettings.GetSemanticHighlightingFrom%", + FSharpCategory, + ExtensionChoice, + defaultValue: UNSET) + { + Description = "%Which extension should be used to provide semantic highlighting%", + }; + + public static Setting<string>[] AllStringSettings { get; } = + [ + GetDiagnosticsFrom, + GetSemanticHighlightingFrom, + ]; + } +} diff --git a/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs b/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs index f31309920b4..c4f90a7ca1d 100644 --- a/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs +++ b/src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs @@ -22,6 +22,7 @@ namespace FSharp.VisualStudio.Extension; using Microsoft.VisualStudio.Extensibility; using Microsoft.VisualStudio.Extensibility.Editor; using Microsoft.VisualStudio.Extensibility.LanguageServer; +using Microsoft.VisualStudio.Extensibility.Settings; using Microsoft.VisualStudio.LanguageServer.Protocol; using Microsoft.VisualStudio.ProjectSystem.Query; using Microsoft.VisualStudio.RpcContracts.LanguageServerProvider; @@ -41,14 +42,17 @@ internal static class Extensions internal class VsServerCapabilitiesOverride : IServerCapabilitiesOverride { - public ServerCapabilities OverrideServerCapabilities(ServerCapabilities value) + public ServerCapabilities OverrideServerCapabilities(FSharpLanguageServerConfig config, ServerCapabilities value, ClientCapabilities clientCapabilities) { var capabilities = new VSInternalServerCapabilities { TextDocumentSync = value.TextDocumentSync, SupportsDiagnosticRequests = true, ProjectContextProvider = true, - DiagnosticProvider = new() + DiagnosticProvider = + config.EnabledFeatures.Diagnostics ? + + new() { SupportsMultipleContextsDiagnostics = true, DiagnosticKinds = [ @@ -67,8 +71,8 @@ public ServerCapabilities OverrideServerCapabilities(ServerCapabilities value) //new(PullDiagnosticCategories.DocumentAnalyzerSyntax), //new(PullDiagnosticCategories.DocumentAnalyzerSemantic), ] - }, - SemanticTokensOptions = new() + } : null, + SemanticTokensOptions = config.EnabledFeatures.SemanticHighlighting ? new() { Legend = new() { @@ -80,7 +84,7 @@ public ServerCapabilities OverrideServerCapabilities(ServerCapabilities value) Delta = false }, Range = false - } + } : null, //, //HoverProvider = new HoverOptions() //{ @@ -268,6 +272,41 @@ internal class FSharpLanguageServerProvider : LanguageServerProvider FSharp.Compiler.LanguageServer.Activity.listenToSome(); +#pragma warning disable VSEXTPREVIEW_SETTINGS // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + + // Write default settings unless they're overridden. Otherwise users can't even find which settings exist. + + var settingsReadResult = await this.Extensibility.Settings().ReadEffectiveValuesAsync(FSharpExtensionSettings.AllStringSettings, cancellationToken); + + var settingValues = FSharpExtensionSettings.AllStringSettings.Select( + setting => (setting, settingsReadResult.ValueOrDefault(setting, defaultValue: FSharpExtensionSettings.UNSET))); + + foreach (var (setting, value) in settingValues.Where(x => x.Item2 == FSharpExtensionSettings.UNSET)) + { + await this.Extensibility.Settings().WriteAsync(batch => + batch.WriteSetting(setting, FSharpExtensionSettings.BOTH), "write default settings", cancellationToken); + } + + var enabled = new[] { FSharpExtensionSettings.LSP, FSharpExtensionSettings.BOTH }; + + var serverConfig = new FSharpLanguageServerConfig( + new FSharpLanguageServerFeatures( + diagnostics: enabled.Contains(settingsReadResult.ValueOrDefault(FSharpExtensionSettings.GetDiagnosticsFrom, defaultValue: FSharpExtensionSettings.BOTH)), + semanticHighlighting: enabled.Contains(settingsReadResult.ValueOrDefault(FSharpExtensionSettings.GetSemanticHighlightingFrom, defaultValue: FSharpExtensionSettings.BOTH)) + )); + + var disposeToEndSubscription = + this.Extensibility.Settings().SubscribeAsync( + [FSharpExtensionSettings.FSharpCategory], + cancellationToken, + changeHandler: result => + { + Trace.TraceInformation($"Settings update", result); + }); + +#pragma warning restore VSEXTPREVIEW_SETTINGS // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed. + + //const string vsMajorVersion = "17.0"; //var settings = OpenTelemetryExporterSettingsBuilder @@ -333,8 +372,7 @@ internal class FSharpLanguageServerProvider : LanguageServerProvider // observer.ProcessProject(project); } - - var ((inputStream, outputStream), _server) = FSharpLanguageServer.Create(workspace, (serviceCollection) => + var ((inputStream, outputStream), _server) = FSharpLanguageServer.Create(workspace, serverConfig, (serviceCollection) => { serviceCollection.AddSingleton<IServerCapabilitiesOverride, VsServerCapabilitiesOverride>(); serviceCollection.AddSingleton<IMethodHandler, VsDiagnosticsHandler>(); @@ -357,7 +395,7 @@ internal class FSharpLanguageServerProvider : LanguageServerProvider return new DuplexPipe( PipeReader.Create(inputStream), PipeWriter.Create(outputStream)); - } + } /// <inheritdoc/> public override Task OnServerInitializationResultAsync(ServerInitializationResult serverInitializationResult, LanguageServerInitializationFailureInfo? initializationFailureInfo, CancellationToken cancellationToken) diff --git a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs index afdb990489b..e9331986d5a 100644 --- a/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs +++ b/vsintegration/src/FSharp.Editor/Classification/ClassificationService.fs @@ -32,6 +32,9 @@ type SemanticClassificationLookup = IReadOnlyDictionary<int, ResizeArray<Semanti [<Export(typeof<IFSharpClassificationService>)>] type internal FSharpClassificationService [<ImportingConstructor>] () = + static let shouldProduceClassification (document: Document) = + document.Project.Solution.GetFSharpExtensionConfig().ShouldProduceSemanticHighlighting() + static let getLexicalClassifications (filePath: string, defines, text: SourceText, textSpan: TextSpan, ct: CancellationToken) = let text = text.GetSubText(textSpan) @@ -155,6 +158,11 @@ type internal FSharpClassificationService [<ImportingConstructor>] () = result: List<ClassifiedSpan>, cancellationToken: CancellationToken ) = + + if not (document |> shouldProduceClassification) then + System.Threading.Tasks.Task.CompletedTask + else + cancellableTask { use _logBlock = Logger.LogBlock(LogEditorFunctionId.Classification_Syntactic) @@ -207,6 +215,11 @@ type internal FSharpClassificationService [<ImportingConstructor>] () = result: List<ClassifiedSpan>, cancellationToken: CancellationToken ) = + + if not (document |> shouldProduceClassification) then + System.Threading.Tasks.Task.CompletedTask + else + cancellableTask { use _logBlock = Logger.LogBlock(LogEditorFunctionId.Classification_Semantic) diff --git a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs index 36eb5bc7a0e..ab1da64c1d8 100644 --- a/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs +++ b/vsintegration/src/FSharp.Editor/Diagnostics/DocumentDiagnosticAnalyzer.fs @@ -24,6 +24,9 @@ type internal DiagnosticsType = [<Export(typeof<IFSharpDocumentDiagnosticAnalyzer>)>] type internal FSharpDocumentDiagnosticAnalyzer [<ImportingConstructor>] () = + let shouldProduceDiagnostics (document: Document) = + document.Project.Solution.GetFSharpExtensionConfig().ShouldProduceDiagnostics() + static let diagnosticEqualityComparer = { new IEqualityComparer<FSharpDiagnostic> with @@ -148,14 +151,14 @@ type internal FSharpDocumentDiagnosticAnalyzer [<ImportingConstructor>] () = interface IFSharpDocumentDiagnosticAnalyzer with member _.AnalyzeSyntaxAsync(document: Document, cancellationToken: CancellationToken) : Task<ImmutableArray<Diagnostic>> = - if document.Project.IsFSharpMetadata 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<ImmutableArray<Diagnostic>> = - if document.Project.IsFSharpMiscellaneousOrMetadata && not document.IsFSharpScript 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 6c3b04f1844..fa53d6b5127 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/WorkspaceExtensions.fs @@ -15,6 +15,8 @@ open FSharp.Compiler.BuildGraph open CancellableTasks +open System.IO + open Internal.Utilities.Collections open Newtonsoft.Json open Newtonsoft.Json.Linq @@ -31,12 +33,52 @@ module internal ProjectCache = let Projects = ConditionalWeakTable<Project, FSharpChecker * FSharpProjectOptionsManager * FSharpParsingOptions * FSharpProjectOptions>() +module internal SolutionConfigCache = + + type FSharpExtensionConfig = { + GetDiagnosticsFrom: string + GetSemanticHighlightingFrom: string + } with + static member Old = "old" + static member Lsp = "lsp" + static member Both = "both" + static member Default = { GetDiagnosticsFrom = FSharpExtensionConfig.Both; GetSemanticHighlightingFrom = FSharpExtensionConfig.Both } + + member this.ShouldProduceDiagnostics() = + Set.contains this.GetDiagnosticsFrom (set [ FSharpExtensionConfig.Old; FSharpExtensionConfig.Both ]) + + member this.ShouldProduceSemanticHighlighting() = + Set.contains this.GetSemanticHighlightingFrom (set [ FSharpExtensionConfig.Old; FSharpExtensionConfig.Both ]) + + let readFSharpExtensionConfig (solutionPath: string) = + 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() + } + 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 + + let ExtensionConfig = + ConditionalWeakTable<Solution, FSharpExtensionConfig>() + type Solution with /// Get the instance of IFSharpWorkspaceService. member internal this.GetFSharpWorkspaceService() = this.Workspace.Services.GetRequiredService<IFSharpWorkspaceService>() + member internal this.GetFSharpExtensionConfig() = + SolutionConfigCache.ExtensionConfig.GetValue(this, ConditionalWeakTable<_, _>.CreateValueCallback(fun _ -> SolutionConfigCache.readFSharpExtensionConfig(Path.GetDirectoryName this.FilePath))) + module internal FSharpProjectSnapshotSerialization = let serializeFileSnapshot (snapshot: FSharpFileSnapshot) =