From 2719e586fcbbe63d6652605d2e3aeee22274e511 Mon Sep 17 00:00:00 2001 From: Florian Fischer Date: Sat, 14 Jan 2023 16:21:22 +0100 Subject: support configuration and initializationOptions per language server Send language server settings after initialization via a workspace/didChangeConfiguration notification. Implement a workspace/configuration handler using the values stored in the language server settings. Send the init_options as initializationOptions in the initialize parameters. --- README.md | 25 +++++++++++++++------- init.lua | 59 +++++++++++++++++++++++++++++++++++++++++---------- lspc.lua | 5 ++++- supported-servers.lua | 8 ++++++- 4 files changed, 76 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 290df0e..cd53100 100644 --- a/README.md +++ b/README.md @@ -138,21 +138,30 @@ you have to create a language server configuration and insert it into the `ls_ma table. Please have a look at #2 and share your configuration with everyone else. -A language server configuration is a Lua table containing a `name` field which is -used to manage the language server. -And a `cmd` field which is used to start the language server. +A language server configuration is a Lua table containing a at least a `name` field +which is used to manage the language server and a `cmd` field which is used to +start the language server. **Note:** the language server must communicate with vis-lspc via stdio. Your language server probably supports stdio but maybe requires a [special command line flag](https://microsoft.github.io/language-server-protocol/specifications/specification-current/#implementationConsiderations). -**Example:** The language server configuration for clangd and its entries in `ls_map` +Additional field are: - -- clangd language server configuration - local clangd = {name = 'clangd', cmd = 'clangd'} +* `settings` - a table of arbitrary possibly nested data. It is sent in a `workspace/didChangeConfiguration` to the language server after initialization. It is also used to lookup configuration for the `workspace/configuratio` method call. +* `init_options` - table of arbitrary possibly nested data. It is send to the server as `initializationOptions` in the parameters of the `initialize` method call. - -- map of known language servers per syntax - lspc.ls_map = {cpp = clangd, ansi_c = clangd} +**Example:** The language server configuration entry in the `ls_map` for lua-language-server + +```lua +ls_map.lua = { + name = 'lua-language-server', + cmd = 'lua-language-server', + settings = { + Lua = {diagnostics = {globals = {'vis'}}, telemetry = {enable = false}}, + }, +}, +``` Language servers configured in vis-lspc can be found in `supported_servers.lua`. diff --git a/init.lua b/init.lua index c85df1e..c20786d 100644 --- a/init.lua +++ b/init.lua @@ -761,6 +761,24 @@ local function lspc_handle_rename_method_response(win, result) vis_apply_workspaceEdit(win, win.file, result) end +local function lspc_handle_initialize_response(ls, result) + ls.initialized = true + ls.capabilities = result.capabilities + + local params = {} + setmetatable(params, {__jsontype = 'object'}) + ls_send_notification(ls, 'initialized', params) + + -- According to nvim-lspconfig sendig the lsp server settings shortly after + -- initialization is a undocumented convention. + -- See https://github.com/neovim/nvim-lspconfig/blob/ed88435764d8b00442e66d39ec3d9c360e560783/CONTRIBUTING.md + if ls.settings then + ls_send_notification(ls, 'workspace/didChangeConfiguration', { + settings = ls.settings, + }) + end +end + -- method response dispatcher local function ls_handle_method_response(ls, method_response, req) local win = req.win @@ -788,12 +806,7 @@ local function ls_handle_method_response(ls, method_response, req) lspc_handle_goto_method_response(req, result) elseif method == 'initialize' then - ls.initialized = true - ls.capabilities = result.capabilities - local params = {} - setmetatable(params, {__jsontype = 'object'}) - - ls_send_notification(ls, 'initialized', params) + lspc_handle_initialize_response(ls, result) elseif method == 'textDocument/completion' then lspc_handle_completion_method_response(win, result, req.ctx) @@ -825,14 +838,33 @@ local function ls_handle_method_response(ls, method_response, req) ls.inflight[method_response.id] = nil end +local function lspc_handle_workspace_configuration_call(ls, params, response) + local results = {} + for _, item in ipairs(params.items) do + local t = ls.settings + for k in item.section:gmatch('[^.]+') do + if not t then + break + end + t = t[k] + end + table.insert(results, t or json.null) + end + response.result = results +end + local function ls_handle_method_call(ls, method_call) local method = method_call.method - lspc.log('Unknown method call ' .. method) local response = {id = method_call.id} - response['error'] = { - code = jsonrpc.error_codes.MethodNotFound, - message = method .. ' not implemented', - } + if method == 'workspace/configuration' then + lspc_handle_workspace_configuration_call(ls, method_call.params, response) + else + lspc.log('Unknown method call ' .. method) + response['error'] = { + code = jsonrpc.error_codes.MethodNotFound, + message = method .. ' not implemented', + } + end ls_rpc(ls, response) end @@ -1027,6 +1059,7 @@ local function ls_start_server(syntax) local ls = { name = ls_conf.name, + settings = ls_conf.settings, initialized = false, id = 0, inflight = {}, @@ -1070,6 +1103,10 @@ local function ls_start_server(syntax) capabilities = lspc.client_capabilites, } + if ls_conf.init_options then + params.initializationOptions = ls_conf.init_options + end + ls_call_method(ls, 'initialize', params) return ls diff --git a/lspc.lua b/lspc.lua index 7894507..c8e00a1 100644 --- a/lspc.lua +++ b/lspc.lua @@ -41,7 +41,10 @@ local goto_methods_capabilities = { } local client_capabilites = { - workspace = {configuration = false}, + workspace = { + configuration = true, + didChangeConfiguration = {dynamicRegistration = false}, + }, textDocument = { synchronization = {dynamicRegistration = false, didSave = true}, -- ask the server to send us only plaintext completionItems diff --git a/supported-servers.lua b/supported-servers.lua index edce982..c19ad42 100644 --- a/supported-servers.lua +++ b/supported-servers.lua @@ -20,7 +20,13 @@ return { python = {name = 'python-lsp-server', cmd = 'pylsp'}, -- lua (lua-language-server) language server configuration -- https://github.com/sumneko/lua-language-server - lua = {name = 'lua-language-server', cmd = 'lua-language-server'}, + lua = { + name = 'lua-language-server', + cmd = 'lua-language-server', + settings = { + Lua = {diagnostics = {globals = {'vis'}}, telemetry = {enable = false}}, + }, + }, -- typescript (typescript-language-server) language server configuration -- https://github.com/typescript-language-server/typescript-language-server javascript = typescript, -- cgit v1.2.3