@@ -61,7 +61,6 @@ defmodule ElixirLS.LanguageServer.Server do
61
61
# Tracks source files that are currently open in the editor
62
62
source_files: % { } ,
63
63
awaiting_contracts: [ ] ,
64
- supports_dynamic: false ,
65
64
mix_project?: false ,
66
65
mix_env: nil ,
67
66
mix_target: nil ,
@@ -91,6 +90,8 @@ defmodule ElixirLS.LanguageServer.Server do
91
90
".sface"
92
91
]
93
92
93
+ @ default_config_timeout 3
94
+
94
95
## Client API
95
96
96
97
def start_link ( name \\ nil ) do
@@ -235,21 +236,18 @@ defmodule ElixirLS.LanguageServer.Server do
235
236
end
236
237
237
238
@ impl GenServer
238
- def handle_info ( :default_config , state = % __MODULE__ { } ) do
239
- state =
240
- case state do
241
- % { settings: nil } ->
242
- Logger . warning (
243
- "Did not receive workspace/didChangeConfiguration notification after 5 seconds. " <>
244
- "Using default settings."
245
- )
246
-
247
- set_settings ( state , % { } )
239
+ def handle_info ( :default_config , state = % __MODULE__ { settings: nil } ) do
240
+ Logger . warning (
241
+ "Did not receive workspace/didChangeConfiguration notification after #{ @ default_config_timeout } seconds. " <>
242
+ "The server will use default config."
243
+ )
248
244
249
- _ ->
250
- state
251
- end
245
+ state = set_settings ( state , % { } )
246
+ { :noreply , state }
247
+ end
252
248
249
+ def handle_info ( :default_config , state = % __MODULE__ { } ) do
250
+ # we got workspace/didChangeConfiguration in time, nothing to do here
253
251
{ :noreply , state }
254
252
end
255
253
@@ -293,11 +291,67 @@ defmodule ElixirLS.LanguageServer.Server do
293
291
## Helpers
294
292
295
293
defp handle_notification ( notification ( "initialized" ) , state = % __MODULE__ { } ) do
296
- # If we don't receive workspace/didChangeConfiguration for 5 seconds, use default settings
297
- Process . send_after ( self ( ) , :default_config , 5000 )
294
+ # according to https://github.com/microsoft/language-server-protocol/issues/567#issuecomment-1060605611
295
+ # this is the best point to pull configuration
298
296
299
- if state . supports_dynamic do
300
- add_watched_extensions ( state , @ default_watched_extensions )
297
+ supports_get_configuration =
298
+ get_in ( state . client_capabilities , [
299
+ "workspace" ,
300
+ "configuration"
301
+ ] )
302
+
303
+ state =
304
+ if supports_get_configuration do
305
+ response = JsonRpc . get_configuration_request ( state . root_uri , "elixirLS" )
306
+
307
+ case response do
308
+ { :ok , [ result ] } when is_map ( result ) ->
309
+ Logger . info (
310
+ "Received client configuration via workspace/configuration\n #{ inspect ( result ) } "
311
+ )
312
+
313
+ set_settings ( state , result )
314
+
315
+ other ->
316
+ Logger . error ( "Cannot get client configuration: #{ inspect ( other ) } " )
317
+ state
318
+ end
319
+ else
320
+ Logger . info ( "Client does not support workspace/configuration request" )
321
+ state
322
+ end
323
+
324
+ unless state . settings do
325
+ # We still don't have the configuration. Let's wait for workspace/didChangeConfiguration
326
+ Process . send_after ( self ( ) , :default_config , @ default_config_timeout * 1000 )
327
+ end
328
+
329
+ supports_dynamic_configuration_change_registration =
330
+ get_in ( state . client_capabilities , [
331
+ "workspace" ,
332
+ "didChangeConfiguration" ,
333
+ "dynamicRegistration"
334
+ ] )
335
+
336
+ if supports_dynamic_configuration_change_registration do
337
+ Logger . info ( "Registering for workspace/didChangeConfiguration notifications" )
338
+ listen_for_configuration_changes ( state . server_instance_id )
339
+ else
340
+ Logger . info ( "Client does not support workspace/didChangeConfiguration dynamic registration" )
341
+ end
342
+
343
+ supports_dynamic_file_watcher_registration =
344
+ get_in ( state . client_capabilities , [
345
+ "workspace" ,
346
+ "didChangeWatchedFiles" ,
347
+ "dynamicRegistration"
348
+ ] )
349
+
350
+ if supports_dynamic_file_watcher_registration do
351
+ Logger . info ( "Registering for workspace/didChangeWatchedFiles notifications" )
352
+ add_watched_extensions ( state . server_instance_id , @ default_watched_extensions )
353
+ else
354
+ Logger . info ( "Client does not support workspace/didChangeWatchedFiles dynamic registration" )
301
355
end
302
356
303
357
state
@@ -321,6 +375,10 @@ defmodule ElixirLS.LanguageServer.Server do
321
375
# the `projectDir` or `mixEnv` settings. If the settings don't match the format expected, leave
322
376
# settings unchanged or set default settings if this is the first request.
323
377
defp handle_notification ( did_change_configuration ( changed_settings ) , state = % __MODULE__ { } ) do
378
+ Logger . info (
379
+ "Received client configuration via workspace/didChangeConfiguration:\n #{ inspect ( changed_settings ) } "
380
+ )
381
+
324
382
prev_settings = state . settings || % { }
325
383
326
384
new_settings =
@@ -569,19 +627,10 @@ defmodule ElixirLS.LanguageServer.Server do
569
627
state
570
628
end
571
629
572
- # Explicitly request file watchers from the client if supported
573
- supports_dynamic =
574
- get_in ( client_capabilities , [
575
- "textDocument" ,
576
- "codeAction" ,
577
- "dynamicRegistration"
578
- ] )
579
-
580
630
state = % {
581
631
state
582
632
| client_capabilities: client_capabilities ,
583
- server_instance_id: server_instance_id ,
584
- supports_dynamic: supports_dynamic
633
+ server_instance_id: server_instance_id
585
634
}
586
635
587
636
{ :ok ,
@@ -1161,7 +1210,8 @@ defmodule ElixirLS.LanguageServer.Server do
1161
1210
|> set_mix_target ( mix_target )
1162
1211
|> set_project_dir ( project_dir )
1163
1212
|> set_dialyzer_enabled ( enable_dialyzer )
1164
- |> add_watched_extensions ( additional_watched_extensions )
1213
+
1214
+ add_watched_extensions ( state . server_instance_id , additional_watched_extensions )
1165
1215
1166
1216
maybe_rebuild ( state )
1167
1217
state = create_gitignore ( state )
@@ -1173,25 +1223,42 @@ defmodule ElixirLS.LanguageServer.Server do
1173
1223
trigger_build ( % { state | settings: settings } )
1174
1224
end
1175
1225
1176
- defp add_watched_extensions ( state = % __MODULE__ { } , [ ] ) do
1177
- state
1226
+ defp add_watched_extensions ( _server_instance_id , [ ] ) do
1227
+ :ok
1178
1228
end
1179
1229
1180
- defp add_watched_extensions ( state = % __MODULE__ { } , exts ) when is_list ( exts ) do
1230
+ defp add_watched_extensions ( server_instance_id , exts ) when is_list ( exts ) do
1181
1231
case JsonRpc . register_capability_request (
1232
+ server_instance_id ,
1182
1233
"workspace/didChangeWatchedFiles" ,
1183
1234
% {
1184
1235
"watchers" => Enum . map ( exts , & % { "globPattern" => "**/*" <> & 1 } )
1185
1236
}
1186
1237
) do
1187
1238
{ :ok , nil } ->
1188
- :ok
1239
+ Logger . info ( "client/registerCapability succeeded" )
1189
1240
1190
1241
other ->
1191
1242
Logger . error ( "client/registerCapability returned: #{ inspect ( other ) } " )
1192
1243
end
1244
+ end
1193
1245
1194
- state
1246
+ defp listen_for_configuration_changes ( server_instance_id ) do
1247
+ # the schema is not documented in official LSP docs
1248
+ # using https://github.com/microsoft/vscode-languageserver-node/blob/7792b0b21c994cc9bebc3117eeb652a22e2d9e1f/protocol/src/common/protocol.ts#L1504C18-L1504C59
1249
+ case JsonRpc . register_capability_request (
1250
+ server_instance_id ,
1251
+ "workspace/didChangeConfiguration" ,
1252
+ % {
1253
+ "section" => "elixirLS"
1254
+ }
1255
+ ) do
1256
+ { :ok , nil } ->
1257
+ Logger . info ( "client/registerCapability succeeded" )
1258
+
1259
+ other ->
1260
+ Logger . error ( "client/registerCapability returned: #{ inspect ( other ) } " )
1261
+ end
1195
1262
end
1196
1263
1197
1264
defp set_dialyzer_enabled ( state = % __MODULE__ { } , enable_dialyzer ) do
@@ -1221,6 +1288,7 @@ defmodule ElixirLS.LanguageServer.Server do
1221
1288
:warning ,
1222
1289
"Environment variables change detected. ElixirLS will restart"
1223
1290
)
1291
+
1224
1292
# sleep so the client has time to show the message
1225
1293
Process . sleep ( 5000 )
1226
1294
ElixirLS.LanguageServer . restart ( )
0 commit comments