Skip to content

Commit 9beb586

Browse files
committed
fix(language-server): support LSP clients that only support workspace/configuration.
See OmniSharp/csharp-language-server-protocol#1101 for the upstream bug that necessitates this. We can revert to unscoped configuration when that is resolved.
1 parent 62fa42f commit 9beb586

File tree

10 files changed

+81
-44
lines changed

10 files changed

+81
-44
lines changed

src/language-server/Contextive.LanguageServer.Tests/CompletionTests.fs

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ let completionTests =
3030
$"Given {fileName} contextive, in document {text} at position {position} respond with expected completion list " {
3131
let config =
3232
[ Workspace.optionsBuilder <| Path.Combine("fixtures", "completion_tests")
33-
ConfigurationSection.contextivePathOptionsBuilder $"{fileName}.yml" ]
33+
ConfigurationSection.contextivePathBuilder $"{fileName}.yml" ]
3434

3535
use! client = TestClient(config) |> init
3636

src/language-server/Contextive.LanguageServer.Tests/ConfigurationTests.fs

+18-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ let definitionsTests =
1313
[ testAsync "Can receive configuration value" {
1414
let config =
1515
[ Workspace.optionsBuilder <| Path.Combine("fixtures", "completion_tests")
16-
ConfigurationSection.contextivePathOptionsBuilder "one.yml" ]
16+
ConfigurationSection.contextivePathBuilder "one.yml" ]
1717

1818
use! client = TestClient(config) |> init
1919

@@ -29,17 +29,32 @@ let definitionsTests =
2929

3030
let config =
3131
[ Workspace.optionsBuilder <| Path.Combine("fixtures", "completion_tests")
32-
ConfigurationSection.contextivePathLoaderOptionsBuilder pathLoader ]
32+
ConfigurationSection.contextivePathLoaderBuilder pathLoader ]
3333

3434
use! client = TestClient(config) |> init
3535

3636
path <- "two.yml"
37-
ConfigurationSection.didChange client path
37+
ConfigurationSection.didChangePath client path
3838

3939
let! labels = Completion.getCompletionLabels client
4040

4141
test <@ (labels, Fixtures.Two.expectedCompletionLabels) ||> Seq.compareWith compare = 0 @>
4242

4343
}
4444

45+
testAsync "Can handle client that doesn't support didChangeConfiguration" {
46+
let mutable path = "one.yml"
47+
48+
let config =
49+
[ Workspace.optionsBuilder <| Path.Combine("fixtures", "completion_tests")
50+
ConfigurationSection.contextivePathBuilder path ]
51+
52+
use! client = TestClient(config) |> init
53+
54+
let! labels = Completion.getCompletionLabels client
55+
56+
test <@ (labels, Fixtures.One.expectedCompletionLabels) ||> Seq.compareWith compare = 0 @>
57+
58+
}
59+
4560
]

src/language-server/Contextive.LanguageServer.Tests/DefinitionsTests.fs

+3-3
Original file line numberDiff line numberDiff line change
@@ -163,21 +163,21 @@ let definitionsTests =
163163

164164
let config =
165165
[ Workspace.optionsBuilder <| Path.Combine("fixtures", "completion_tests")
166-
ConfigurationSection.contextivePathLoaderOptionsBuilder pathLoader ]
166+
ConfigurationSection.contextivePathLoaderBuilder pathLoader ]
167167

168168
use! client = TestClient(config) |> init
169169

170170
let! termsWhenValidAtStart = Completion.getCompletionLabels client
171171
test <@ (termsWhenValidAtStart, Fixtures.One.expectedCompletionLabels) ||> compareList = 0 @>
172172

173173
path <- $"{fileName}.yml"
174-
ConfigurationSection.didChange client path
174+
ConfigurationSection.didChangePath client path
175175

176176
let! termsWhenInvalid = Completion.getCompletionLabels client
177177
test <@ Seq.length termsWhenInvalid = 0 @>
178178

179179
path <- validPath
180-
ConfigurationSection.didChange client path
180+
ConfigurationSection.didChangePath client path
181181

182182
let! termsWhenValidAtEnd = Completion.getCompletionLabels client
183183
test <@ (termsWhenValidAtEnd, Fixtures.One.expectedCompletionLabels) ||> compareList = 0 @>

src/language-server/Contextive.LanguageServer.Tests/Helpers/ConfigurationSection.fs

+39-16
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ open OmniSharp.Extensions.LanguageServer.Protocol.Client
99
open OmniSharp.Extensions.LanguageServer.Protocol.Workspace
1010
open System.Threading.Tasks
1111

12-
let jTokenFromMap values =
12+
let private jTokenFromMap values =
1313
let configValue = JObject()
1414

1515
values
@@ -38,27 +38,50 @@ let private createHandler section configValuesLoader =
3838
else
3939
Task.FromResult(configSectionResultFromMap <| Map [])
4040

41-
let optionsBuilder section configValuesLoader (b: LanguageClientOptions) =
41+
let configurationHandlerBuilder section configValuesLoader (b: LanguageClientOptions) =
4242
let handler = createHandler section configValuesLoader
43-
43+
b.OnConfiguration(handler) |> ignore
4444
b
45-
.WithCapability(Capabilities.DidChangeConfigurationCapability(DynamicRegistration = true))
46-
.OnConfiguration(handler)
45+
46+
let private didChangeConfigurationBuilder (b: LanguageClientOptions) =
47+
b.WithCapability(Capabilities.DidChangeConfigurationCapability(DynamicRegistration = true))
4748
|> ignore
4849

4950
b
5051

51-
type PathLoader = unit -> obj
52-
53-
let mapLoader (pathLoader: PathLoader) () = Map[("path", pathLoader ())]
54-
55-
let contextivePathLoaderOptionsBuilder (pathLoader: PathLoader) =
56-
optionsBuilder "contextive" <| mapLoader pathLoader
57-
58-
let contextivePathOptionsBuilder path =
59-
contextivePathLoaderOptionsBuilder (fun () -> path)
60-
61-
let didChange (client: ILanguageClient) path =
52+
type ValueLoader = unit -> obj
53+
54+
let private mapLoader (key: string) (valueLoader: ValueLoader) () = Map[(key, valueLoader ())]
55+
56+
let private mapPathLoader = mapLoader "path"
57+
58+
let private contextivePathConfigurationHandlerBuilder (pathLoader: ValueLoader) =
59+
configurationHandlerBuilder "contextive" <| mapPathLoader pathLoader
60+
61+
/// <summary>
62+
/// <para>Use when you have a setting value that needs to change during the test.</para>
63+
/// <para>The test client will support `workspace/configuration` AND `workspace/didChangeConfiguration`.</para>
64+
/// <para>When a `workspace/configuration` request is received, the `pathLoader` function will be invoked to get the current value.</para>
65+
/// <para>To trigger a `workspace/didChangeConfiguration` notification, use <see cref="didChangePath">didChangePath</see>.</para>
66+
/// </summary>
67+
let contextivePathLoaderBuilder (pathLoader: ValueLoader) =
68+
contextivePathConfigurationHandlerBuilder pathLoader
69+
>> didChangeConfigurationBuilder
70+
71+
/// <summary>
72+
/// <para>Use when you have a fixed setting value.</para>
73+
/// <para>The test client will ONLY support `workspace/configuration` and will always supply this fixed value</para>
74+
/// <para>Using <see cref="didChangePath">didChangePath</see> will have no impact as the server will not support the `workspace/didChangeConfiguration` notification</para>
75+
/// </summary>
76+
let contextivePathBuilder path =
77+
contextivePathConfigurationHandlerBuilder (fun () -> path)
78+
79+
/// <summary>
80+
/// <para>Trigger a `workspace/didChangeConfiguration` notification with the new value.</para>
81+
/// <para>This will only work if the TestClient was created with the <see cref="contextivePathLoaderBuilder">contextivePathLoaderBuilder</see>.</para>
82+
/// <para>Ensure this method is invoked with the same path value as will be returned from the `pathLoader` registered with the TestClient.</para>
83+
/// </summary>
84+
let didChangePath (client: ILanguageClient) path =
6285
let setting = jTokenFromMap <| Map[("path", path)]
6386
let configSection = jTokenFromMap <| Map[("contextive", setting)]
6487

src/language-server/Contextive.LanguageServer.Tests/HoverTests.fs

+3-3
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ let hoverTests =
3737
$"Given definitions file '{fileName}' and file contents '{multiLineToSingleLine text}', server responds to hover request at Position {position} with '{expectedTerm}'" {
3838
let config =
3939
[ Workspace.optionsBuilder <| Path.Combine("fixtures", "completion_tests")
40-
ConfigurationSection.contextivePathOptionsBuilder $"{fileName}.yml" ]
40+
ConfigurationSection.contextivePathBuilder $"{fileName}.yml" ]
4141

4242
use! client = TestClient(config) |> init
4343

@@ -89,7 +89,7 @@ let hoverTests =
8989

9090
let config =
9191
[ Workspace.optionsBuilder <| Path.Combine("fixtures", "completion_tests")
92-
ConfigurationSection.contextivePathOptionsBuilder $"{fileName}.yml" ]
92+
ConfigurationSection.contextivePathBuilder $"{fileName}.yml" ]
9393

9494
use! client = TestClient(config) |> init
9595

@@ -129,7 +129,7 @@ let hoverTests =
129129

130130
let config =
131131
[ Workspace.optionsBuilder <| Path.Combine("fixtures", "completion_tests")
132-
ConfigurationSection.contextivePathOptionsBuilder $"{fileName}.yml" ]
132+
ConfigurationSection.contextivePathBuilder $"{fileName}.yml" ]
133133

134134
use! client = TestClient(config) |> init
135135

src/language-server/Contextive.LanguageServer.Tests/InitializationTests.fs

+4-8
Original file line numberDiff line numberDiff line change
@@ -28,38 +28,34 @@ let initializationTests =
2828

2929
let config =
3030
[ Workspace.optionsBuilder ""
31-
ConfigurationSection.contextivePathOptionsBuilder pathValue ]
31+
ConfigurationSection.contextivePathBuilder pathValue ]
3232

3333
let! (client, reply) = TestClient(config) |> initAndWaitForReply
3434

3535
test <@ client.ClientSettings.Capabilities.Workspace.Configuration.IsSupported @>
36-
test <@ client.ClientSettings.Capabilities.Workspace.DidChangeConfiguration.IsSupported @>
3736

3837
test <@ (defaultArg reply "").Contains(pathValue) @>
3938
}
4039

4140
testAsync "Server loads contextive file from absolute location without workspace" {
4241
let pathValue = Guid.NewGuid().ToString()
4342

44-
let config =
45-
[ ConfigurationSection.contextivePathOptionsBuilder $"/tmp/{pathValue}" ]
43+
let config = [ ConfigurationSection.contextivePathBuilder $"/tmp/{pathValue}" ]
4644

4745
let! (client, reply) = TestClient(config) |> initAndWaitForReply
4846

4947
test <@ client.ClientSettings.Capabilities.Workspace.Configuration.IsSupported @>
50-
test <@ client.ClientSettings.Capabilities.Workspace.DidChangeConfiguration.IsSupported @>
5148

5249
test <@ (defaultArg reply "").Contains(pathValue) @>
5350
}
5451

5552
testAsync "Server does NOT load contextive file from relative location without workspace" {
5653
let pathValue = Guid.NewGuid().ToString()
57-
let config = [ ConfigurationSection.contextivePathOptionsBuilder pathValue ]
54+
let config = [ ConfigurationSection.contextivePathBuilder pathValue ]
5855

5956
let! (client, reply) = TestClientWithCustomInitWait(config, Some pathValue) |> initAndWaitForReply
6057

6158
test <@ client.ClientSettings.Capabilities.Workspace.Configuration.IsSupported @>
62-
test <@ client.ClientSettings.Capabilities.Workspace.DidChangeConfiguration.IsSupported @>
6359

6460
test
6561
<@
@@ -71,7 +67,7 @@ let initializationTests =
7167
testAsync "Server loads contextive file from default location when no configuration supplied" {
7268
let config =
7369
[ Workspace.optionsBuilder ""
74-
ConfigurationSection.optionsBuilder "dummySection" (fun () -> Map []) ]
70+
ConfigurationSection.configurationHandlerBuilder "dummySection" (fun () -> Map []) ]
7571

7672
let defaultPath = ".contextive/definitions.yml"
7773

src/language-server/Contextive.LanguageServer.Tests/SurveyTests.fs

+3-3
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ let initializationTests =
4848
let config =
4949
[ showMessageRequestHandlerBuilder <| handler messageAwaiter null
5050
Workspace.optionsBuilder ""
51-
ConfigurationSection.contextivePathOptionsBuilder pathValue ]
51+
ConfigurationSection.contextivePathBuilder pathValue ]
5252

5353
let! (client, reply) = TestClient(config) |> initAndWaitForReply
5454

@@ -75,7 +75,7 @@ let initializationTests =
7575
let config =
7676
[ showMessageRequestHandlerBuilder <| handler messageAwaiter null
7777
Workspace.optionsBuilder ""
78-
ConfigurationSection.contextivePathOptionsBuilder pathValue ]
78+
ConfigurationSection.contextivePathBuilder pathValue ]
7979

8080
let! (client, reply) = TestClient(config) |> initAndWaitForReply
8181

@@ -102,7 +102,7 @@ let initializationTests =
102102
[ showMessageRequestHandlerBuilder <| handler messageAwaiter response
103103
showDocumentRequestHandlerBuilder <| handler showDocAwaiter showDocResponse
104104
Workspace.optionsBuilder ""
105-
ConfigurationSection.contextivePathOptionsBuilder pathValue ]
105+
ConfigurationSection.contextivePathBuilder pathValue ]
106106

107107
let! (client, reply) = TestClient(config) |> initAndWaitForReply
108108

src/language-server/Contextive.LanguageServer.Tests/TextDocumentTests.fs

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ let textDocumentTests =
5252
testAsync "Server supports full sync" {
5353
let config =
5454
[ Workspace.optionsBuilder <| Path.Combine("fixtures", "completion_tests")
55-
ConfigurationSection.contextivePathOptionsBuilder $"one.yml" ]
55+
ConfigurationSection.contextivePathBuilder $"one.yml" ]
5656

5757
use! client = TestClient(config) |> init
5858
test <@ client.ServerSettings.Capabilities.TextDocumentSync.Options.Change = TextDocumentSyncKind.Full @>

src/language-server/Contextive.LanguageServer.Tests/WatchedFilesTests.fs

+5-5
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ let watchedFileTests =
3636

3737
let config =
3838
[ Workspace.optionsBuilder <| Path.Combine("fixtures", "completion_tests")
39-
ConfigurationSection.contextivePathOptionsBuilder $"one.yml"
39+
ConfigurationSection.contextivePathBuilder $"one.yml"
4040
WatchedFiles.optionsBuilder registrationAwaiter ]
4141

4242
let! client = TestClient(config) |> init
@@ -62,7 +62,7 @@ let watchedFileTests =
6262

6363
let config =
6464
[ Workspace.optionsBuilder <| Path.Combine("fixtures", "completion_tests")
65-
ConfigurationSection.contextivePathLoaderOptionsBuilder pathLoader
65+
ConfigurationSection.contextivePathLoaderBuilder pathLoader
6666
WatchedFiles.optionsBuilder registrationAwaiter ]
6767

6868
let! client = TestClient(config) |> init
@@ -72,7 +72,7 @@ let watchedFileTests =
7272
ConditionAwaiter.clear registrationAwaiter 500
7373

7474
definitionsFile <- newDefinitionsFile
75-
ConfigurationSection.didChange client definitionsFile
75+
ConfigurationSection.didChangePath client definitionsFile
7676

7777
let! secondRegistrationMsg =
7878
ConditionAwaiter.waitFor
@@ -113,7 +113,7 @@ let watchedFileTests =
113113

114114
let config =
115115
[ Workspace.optionsBuilder relativePath
116-
ConfigurationSection.contextivePathOptionsBuilder definitionsFile ]
116+
ConfigurationSection.contextivePathBuilder definitionsFile ]
117117

118118
let! client = TestClient(config) |> init
119119

@@ -153,7 +153,7 @@ let watchedFileTests =
153153

154154
let config =
155155
[ Workspace.optionsBuilder relativePath
156-
ConfigurationSection.contextivePathOptionsBuilder definitionsFile ]
156+
ConfigurationSection.contextivePathBuilder definitionsFile ]
157157

158158
let! client = TestClient(config) |> init
159159

src/language-server/Contextive.LanguageServer/Server.fs

+4-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ let private getConfig (s: ILanguageServer) section key =
3030
Log.Logger.Information $"Getting {section} {key} config..."
3131

3232
let! config =
33-
s.Configuration.GetConfiguration(ConfigurationItem(Section = configSection))
33+
// We need to use `GetScopedConfiguration` because of https://github.com/OmniSharp/csharp-language-server-protocol/issues/1101
34+
// We can revert to `GetConfiguration` when that bug is fixed.
35+
s.Configuration.GetScopedConfiguration("file:///", System.Threading.CancellationToken.None)
36+
//s.Configuration.GetConfiguration(ConfigurationItem(Section = configSection))
3437
|> Async.AwaitTask
3538

3639
let configValue = config.GetSection(section).Item(key)

0 commit comments

Comments
 (0)