Skip to content

Commit 8871484

Browse files
authored
Merge pull request #18219 from 0101/extension-config
Extension config
2 parents 7ac0a54 + 13b5909 commit 8871484

11 files changed

+399
-168
lines changed

src/FSharp.Compiler.LanguageServer/Common/CapabilitiesManager.fs

+38-19
Original file line numberDiff line numberDiff line change
@@ -2,43 +2,62 @@
22

33
open Microsoft.VisualStudio.LanguageServer.Protocol
44
open Microsoft.CommonLanguageServerProtocol.Framework
5+
open FSharp.Compiler.LanguageServer
56

67
type IServerCapabilitiesOverride =
7-
abstract member OverrideServerCapabilities: ServerCapabilities -> ServerCapabilities
8+
abstract member OverrideServerCapabilities: FSharpLanguageServerConfig * ServerCapabilities * ClientCapabilities -> ServerCapabilities
89

9-
type CapabilitiesManager(scOverrides: IServerCapabilitiesOverride seq) =
10+
type CapabilitiesManager(config: FSharpLanguageServerConfig, scOverrides: IServerCapabilitiesOverride seq) =
1011

1112
let mutable initializeParams = None
1213

13-
let defaultCapabilities =
14+
let getInitializeParams () =
15+
match initializeParams with
16+
| Some params' -> params'
17+
| None -> failwith "InitializeParams is null"
18+
19+
let addIf (enabled: bool) (capability: 'a) =
20+
if enabled then capability |> withNull else null
21+
22+
let defaultCapabilities (_clientCapabilities: ClientCapabilities) =
23+
// TODO: don't register if dynamic registraion is supported
1424
ServerCapabilities(
1525
TextDocumentSync = TextDocumentSyncOptions(OpenClose = true, Change = TextDocumentSyncKind.Full),
1626
DiagnosticOptions =
17-
DiagnosticOptions(WorkDoneProgress = true, InterFileDependencies = true, Identifier = "potato", WorkspaceDiagnostics = true),
27+
addIf
28+
config.EnabledFeatures.Diagnostics
29+
(DiagnosticOptions(
30+
WorkDoneProgress = true,
31+
InterFileDependencies = true,
32+
Identifier = "potato",
33+
WorkspaceDiagnostics = true
34+
)),
1835
//CompletionProvider = CompletionOptions(TriggerCharacters = [| "."; " " |], ResolveProvider = true, WorkDoneProgress = true),
1936
//HoverProvider = SumType<bool, HoverOptions>(HoverOptions(WorkDoneProgress = true))
2037
SemanticTokensOptions =
21-
SemanticTokensOptions(
22-
Legend =
23-
SemanticTokensLegend(
24-
TokenTypes = (SemanticTokenTypes.AllTypes |> Seq.toArray), // XXX should be extended
25-
TokenModifiers = (SemanticTokenModifiers.AllModifiers |> Seq.toArray)
26-
),
27-
Range = false
28-
)
38+
addIf
39+
config.EnabledFeatures.SemanticHighlighting
40+
41+
(SemanticTokensOptions(
42+
Legend =
43+
SemanticTokensLegend(
44+
TokenTypes = (SemanticTokenTypes.AllTypes |> Seq.toArray), // XXX should be extended
45+
TokenModifiers = (SemanticTokenModifiers.AllModifiers |> Seq.toArray)
46+
),
47+
Range = false
48+
))
2949
)
3050

3151
interface IInitializeManager<InitializeParams, InitializeResult> with
3252
member this.SetInitializeParams(request) = initializeParams <- Some request
3353

54+
member this.GetInitializeParams() = getInitializeParams ()
55+
3456
member this.GetInitializeResult() =
57+
let clientCapabilities = getInitializeParams().Capabilities
58+
3559
let serverCapabilities =
36-
(defaultCapabilities, scOverrides)
37-
||> Seq.fold (fun acc (x: IServerCapabilitiesOverride) -> x.OverrideServerCapabilities acc)
60+
(defaultCapabilities clientCapabilities, scOverrides)
61+
||> Seq.fold (fun acc (x: IServerCapabilitiesOverride) -> x.OverrideServerCapabilities(config, acc, clientCapabilities))
3862

3963
InitializeResult(Capabilities = serverCapabilities)
40-
41-
member this.GetInitializeParams() =
42-
match initializeParams with
43-
| Some params' -> params'
44-
| None -> failwith "InitializeParams is null"

src/FSharp.Compiler.LanguageServer/FSharp.Compiler.LanguageServer.fsproj

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
<ItemGroup>
3535
<Compile Include="..\..\vsintegration\src\FSharp.Editor\Common\CancellableTasks.fs" />
3636
<Compile Include="Utils.fs" />
37+
<Compile Include="FSharpLanguageServerConfig.fs" />
3738
<Compile Include="Common\LifecycleManager.fs" />
3839
<Compile Include="Common\CapabilitiesManager.fs" />
3940
<Compile Include="Common\FSharpRequestContext.fs" />

src/FSharp.Compiler.LanguageServer/FSharpLanguageServer.fs

+16-3
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,18 @@ type Extensions =
3030
Async.StartAsTask(this, cancellationToken = ct)
3131

3232
type FSharpLanguageServer
33-
(jsonRpc: JsonRpc, logger: ILspLogger, ?initialWorkspace: FSharpWorkspace, ?addExtraHandlers: Action<IServiceCollection>) =
33+
(
34+
jsonRpc: JsonRpc,
35+
logger: ILspLogger,
36+
?initialWorkspace: FSharpWorkspace,
37+
?addExtraHandlers: Action<IServiceCollection>,
38+
?config: FSharpLanguageServerConfig
39+
) =
3440

3541
// TODO: Switch to SystemTextJsonLanguageServer
3642
inherit NewtonsoftLanguageServer<FSharpRequestContext>(jsonRpc, Newtonsoft.Json.JsonSerializer.CreateDefault(), logger)
3743

44+
let config = defaultArg config FSharpLanguageServerConfig.Default
3845
let initialWorkspace = defaultArg initialWorkspace (FSharpWorkspace())
3946

4047
do
@@ -50,6 +57,7 @@ type FSharpLanguageServer
5057
serviceCollection
5158
.AddSingleton(initialWorkspace)
5259
.AddSingleton<ContextHolder>()
60+
.AddSingleton<FSharpLanguageServerConfig>(config)
5361
.AddSingleton<IMethodHandler, InitializeHandler<InitializeParams, InitializeResult, FSharpRequestContext>>()
5462
.AddSingleton<IMethodHandler, InitializedHandler<InitializedParams, FSharpRequestContext>>()
5563
.AddSingleton<IMethodHandler, DocumentStateHandler>()
@@ -77,7 +85,12 @@ type FSharpLanguageServer
7785
static member Create(initialWorkspace, addExtraHandlers: Action<IServiceCollection>) =
7886
FSharpLanguageServer.Create(LspLogger System.Diagnostics.Trace.TraceInformation, initialWorkspace, addExtraHandlers)
7987

80-
static member Create(logger: ILspLogger, initialWorkspace, ?addExtraHandlers: Action<IServiceCollection>) =
88+
static member Create(initialWorkspace, config: FSharpLanguageServerConfig, addExtraHandlers: Action<IServiceCollection>) =
89+
FSharpLanguageServer.Create(LspLogger System.Diagnostics.Trace.TraceInformation, initialWorkspace, addExtraHandlers, config)
90+
91+
static member Create
92+
(logger: ILspLogger, initialWorkspace, ?addExtraHandlers: Action<IServiceCollection>, ?config: FSharpLanguageServerConfig)
93+
=
8194

8295
let struct (clientStream, serverStream) = FullDuplexStream.CreatePair()
8396

@@ -96,7 +109,7 @@ type FSharpLanguageServer
96109
jsonRpc.TraceSource.Switch.Level <- SourceLevels.All
97110

98111
let server =
99-
new FSharpLanguageServer(jsonRpc, logger, initialWorkspace, ?addExtraHandlers = addExtraHandlers)
112+
new FSharpLanguageServer(jsonRpc, logger, initialWorkspace, ?addExtraHandlers = addExtraHandlers, ?config = config)
100113

101114
jsonRpc.StartListening()
102115

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
namespace FSharp.Compiler.LanguageServer
2+
3+
type FSharpLanguageServerFeatures =
4+
{
5+
Diagnostics: bool
6+
SemanticHighlighting: bool
7+
}
8+
9+
static member Default =
10+
{
11+
Diagnostics = true
12+
SemanticHighlighting = true
13+
}
14+
15+
type FSharpLanguageServerConfig =
16+
{
17+
EnabledFeatures: FSharpLanguageServerFeatures
18+
}
19+
20+
static member Default =
21+
{
22+
EnabledFeatures = FSharpLanguageServerFeatures.Default
23+
}

src/FSharp.VisualStudio.Extension/ExtensionEntrypoint.cs

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
using Microsoft.Extensions.DependencyInjection;
44
using Microsoft.VisualStudio.Extensibility;
5+
using System.Threading;
6+
using System;
57
using Extension = Microsoft.VisualStudio.Extensibility.Extension;
68

79
/// <summary>

src/FSharp.VisualStudio.Extension/FSharp.VisualStudio.Extension.csproj

+4-4
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99
</PropertyGroup>
1010

1111
<ItemGroup>
12-
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Sdk" Version="17.13.39351" />
13-
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Build" Version="17.13.39351" />
14-
<PackageReference Include="Microsoft.VisualStudio.LanguageServer.Protocol.Internal" Version="17.13.7-preview" />
15-
<PackageReference Include="Microsoft.VisualStudio.ProjectSystem.Query" Version="17.13.53-pre-g2e2e7c24c7" />
12+
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Sdk" Version="17.13.39620" />
13+
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Build" Version="17.13.39620" />
14+
<PackageReference Include="Microsoft.VisualStudio.LanguageServer.Protocol.Internal" Version="17.13.9" />
15+
<PackageReference Include="Microsoft.VisualStudio.ProjectSystem.Query" Version="17.13.66" />
1616
<PackageReference Include="Microsoft.VisualStudio.Threading" Version="17.13.2" />
1717
<!--<PackageReference Include="Microsoft.VisualStudio.OpenTelemetry.ClientExtensions" Version="0.1.718-beta" />
1818
<PackageReference Include="Microsoft.VisualStudio.OpenTelemetry.Collector" Version="0.1.718-beta" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using Microsoft.VisualStudio.Extensibility;
2+
using Microsoft.VisualStudio.Extensibility.Settings;
3+
4+
namespace FSharp.VisualStudio.Extension
5+
{
6+
#pragma warning disable VSEXTPREVIEW_SETTINGS // The settings API is currently in preview and marked as experimental
7+
8+
internal static class FSharpExtensionSettings
9+
{
10+
public const string OLD = "old";
11+
public const string LSP = "lsp";
12+
public const string BOTH = "both";
13+
public const string UNSET = "unset";
14+
15+
public static EnumSettingEntry[] ExtensionChoice { get; } =
16+
[
17+
new(OLD, "%FSharpSettings.Old%"),
18+
new(LSP, "%FSharpSettings.LSP%"),
19+
new(BOTH, "%FSharpSettings.Both%"),
20+
new(UNSET, "%FSharpSettings.Unset%"),
21+
];
22+
23+
24+
[VisualStudioContribution]
25+
public static SettingCategory FSharpCategory { get; } = new("fsharp", "%F#%");
26+
27+
[VisualStudioContribution]
28+
public static Setting.Enum GetDiagnosticsFrom { get; } = new(
29+
"getDiagnosticsFrom",
30+
"%FSharpSettings.GetDiagnosticsFrom%",
31+
FSharpCategory,
32+
ExtensionChoice,
33+
defaultValue: UNSET)
34+
{
35+
Description = "%Which extension should be used to provide diagnostics%",
36+
};
37+
38+
[VisualStudioContribution]
39+
public static Setting.Enum GetSemanticHighlightingFrom { get; } = new(
40+
"getSemanticHighlightingFrom",
41+
"%FSharpSettings.GetSemanticHighlightingFrom%",
42+
FSharpCategory,
43+
ExtensionChoice,
44+
defaultValue: UNSET)
45+
{
46+
Description = "%Which extension should be used to provide semantic highlighting%",
47+
};
48+
49+
public static Setting<string>[] AllStringSettings { get; } =
50+
[
51+
GetDiagnosticsFrom,
52+
GetSemanticHighlightingFrom,
53+
];
54+
}
55+
}

src/FSharp.VisualStudio.Extension/FSharpLanguageServerProvider.cs

+46-8
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ namespace FSharp.VisualStudio.Extension;
2222
using Microsoft.VisualStudio.Extensibility;
2323
using Microsoft.VisualStudio.Extensibility.Editor;
2424
using Microsoft.VisualStudio.Extensibility.LanguageServer;
25+
using Microsoft.VisualStudio.Extensibility.Settings;
2526
using Microsoft.VisualStudio.LanguageServer.Protocol;
2627
using Microsoft.VisualStudio.ProjectSystem.Query;
2728
using Microsoft.VisualStudio.RpcContracts.LanguageServerProvider;
@@ -41,14 +42,17 @@ internal static class Extensions
4142

4243
internal class VsServerCapabilitiesOverride : IServerCapabilitiesOverride
4344
{
44-
public ServerCapabilities OverrideServerCapabilities(ServerCapabilities value)
45+
public ServerCapabilities OverrideServerCapabilities(FSharpLanguageServerConfig config, ServerCapabilities value, ClientCapabilities clientCapabilities)
4546
{
4647
var capabilities = new VSInternalServerCapabilities
4748
{
4849
TextDocumentSync = value.TextDocumentSync,
4950
SupportsDiagnosticRequests = true,
5051
ProjectContextProvider = true,
51-
DiagnosticProvider = new()
52+
DiagnosticProvider =
53+
config.EnabledFeatures.Diagnostics ?
54+
55+
new()
5256
{
5357
SupportsMultipleContextsDiagnostics = true,
5458
DiagnosticKinds = [
@@ -67,8 +71,8 @@ public ServerCapabilities OverrideServerCapabilities(ServerCapabilities value)
6771
//new(PullDiagnosticCategories.DocumentAnalyzerSyntax),
6872
//new(PullDiagnosticCategories.DocumentAnalyzerSemantic),
6973
]
70-
},
71-
SemanticTokensOptions = new()
74+
} : null,
75+
SemanticTokensOptions = config.EnabledFeatures.SemanticHighlighting ? new()
7276
{
7377
Legend = new()
7478
{
@@ -80,7 +84,7 @@ public ServerCapabilities OverrideServerCapabilities(ServerCapabilities value)
8084
Delta = false
8185
},
8286
Range = false
83-
}
87+
} : null,
8488
//,
8589
//HoverProvider = new HoverOptions()
8690
//{
@@ -268,6 +272,41 @@ internal class FSharpLanguageServerProvider : LanguageServerProvider
268272

269273
FSharp.Compiler.LanguageServer.Activity.listenToSome();
270274

275+
#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.
276+
277+
// Write default settings unless they're overridden. Otherwise users can't even find which settings exist.
278+
279+
var settingsReadResult = await this.Extensibility.Settings().ReadEffectiveValuesAsync(FSharpExtensionSettings.AllStringSettings, cancellationToken);
280+
281+
var settingValues = FSharpExtensionSettings.AllStringSettings.Select(
282+
setting => (setting, settingsReadResult.ValueOrDefault(setting, defaultValue: FSharpExtensionSettings.UNSET)));
283+
284+
foreach (var (setting, value) in settingValues.Where(x => x.Item2 == FSharpExtensionSettings.UNSET))
285+
{
286+
await this.Extensibility.Settings().WriteAsync(batch =>
287+
batch.WriteSetting(setting, FSharpExtensionSettings.BOTH), "write default settings", cancellationToken);
288+
}
289+
290+
var enabled = new[] { FSharpExtensionSettings.LSP, FSharpExtensionSettings.BOTH };
291+
292+
var serverConfig = new FSharpLanguageServerConfig(
293+
new FSharpLanguageServerFeatures(
294+
diagnostics: enabled.Contains(settingsReadResult.ValueOrDefault(FSharpExtensionSettings.GetDiagnosticsFrom, defaultValue: FSharpExtensionSettings.BOTH)),
295+
semanticHighlighting: enabled.Contains(settingsReadResult.ValueOrDefault(FSharpExtensionSettings.GetSemanticHighlightingFrom, defaultValue: FSharpExtensionSettings.BOTH))
296+
));
297+
298+
var disposeToEndSubscription =
299+
this.Extensibility.Settings().SubscribeAsync(
300+
[FSharpExtensionSettings.FSharpCategory],
301+
cancellationToken,
302+
changeHandler: result =>
303+
{
304+
Trace.TraceInformation($"Settings update", result);
305+
});
306+
307+
#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.
308+
309+
271310
//const string vsMajorVersion = "17.0";
272311

273312
//var settings = OpenTelemetryExporterSettingsBuilder
@@ -333,8 +372,7 @@ internal class FSharpLanguageServerProvider : LanguageServerProvider
333372
// observer.ProcessProject(project);
334373
}
335374

336-
337-
var ((inputStream, outputStream), _server) = FSharpLanguageServer.Create(workspace, (serviceCollection) =>
375+
var ((inputStream, outputStream), _server) = FSharpLanguageServer.Create(workspace, serverConfig, (serviceCollection) =>
338376
{
339377
serviceCollection.AddSingleton<IServerCapabilitiesOverride, VsServerCapabilitiesOverride>();
340378
serviceCollection.AddSingleton<IMethodHandler, VsDiagnosticsHandler>();
@@ -357,7 +395,7 @@ internal class FSharpLanguageServerProvider : LanguageServerProvider
357395
return new DuplexPipe(
358396
PipeReader.Create(inputStream),
359397
PipeWriter.Create(outputStream));
360-
}
398+
}
361399

362400
/// <inheritdoc/>
363401
public override Task OnServerInitializationResultAsync(ServerInitializationResult serverInitializationResult, LanguageServerInitializationFailureInfo? initializationFailureInfo, CancellationToken cancellationToken)

0 commit comments

Comments
 (0)