From b5a7593577e1ada3f81fdaa68862ad4e93dcb5a5 Mon Sep 17 00:00:00 2001 From: Jon Parise Date: Tue, 30 Oct 2018 08:54:40 -0700 Subject: Add a `lsp_config_callback` linter option This is the callback-based variant of the existing `lsp_config` linter option. It serves the same purpose but can be used when more complicated processing is needed. `lsp_config` and `lsp_config_callback` are mutually exclusive options; if both an given, a linter preprocessing error will be raised. The runtime logic has been wrapped in `ale#lsp_linter#GetConfig` for convenience, similar to `ale#lsp_linter#GetOptions`. This also adds documentation and an `AssertLSPConfig` test function for completeness. --- autoload/ale/assert.vim | 13 +++++++++++++ autoload/ale/linter.vim | 12 +++++++++++- autoload/ale/lsp_linter.vim | 20 +++++++++++++++++--- 3 files changed, 41 insertions(+), 4 deletions(-) (limited to 'autoload') diff --git a/autoload/ale/assert.vim b/autoload/ale/assert.vim index a1bfd0b7..ed08ed09 100644 --- a/autoload/ale/assert.vim +++ b/autoload/ale/assert.vim @@ -85,6 +85,14 @@ function! ale#assert#LSPOptions(expected_options) abort AssertEqual a:expected_options, l:initialization_options endfunction +function! ale#assert#LSPConfig(expected_config) abort + let l:buffer = bufnr('') + let l:linter = s:GetLinter() + let l:config = ale#lsp_linter#GetConfig(l:buffer, l:linter) + + AssertEqual a:expected_config, l:config +endfunction + function! ale#assert#LSPLanguage(expected_language) abort let l:buffer = bufnr('') let l:linter = s:GetLinter() @@ -147,6 +155,7 @@ function! ale#assert#SetUpLinterTest(filetype, name) abort command! -nargs=+ AssertLinter :call ale#assert#Linter() command! -nargs=0 AssertLinterNotExecuted :call ale#assert#LinterNotExecuted() command! -nargs=+ AssertLSPOptions :call ale#assert#LSPOptions() + command! -nargs=+ AssertLSPConfig :call ale#assert#LSPConfig() command! -nargs=+ AssertLSPLanguage :call ale#assert#LSPLanguage() command! -nargs=+ AssertLSPProject :call ale#assert#LSPProject() command! -nargs=+ AssertLSPAddress :call ale#assert#LSPAddress() @@ -172,6 +181,10 @@ function! ale#assert#TearDownLinterTest() abort delcommand AssertLSPOptions endif + if exists(':AssertLSPConfig') + delcommand AssertLSPConfig + endif + if exists(':AssertLSPLanguage') delcommand AssertLSPLanguage endif diff --git a/autoload/ale/linter.vim b/autoload/ale/linter.vim index dbf9f221..114765e6 100644 --- a/autoload/ale/linter.vim +++ b/autoload/ale/linter.vim @@ -257,7 +257,17 @@ function! ale#linter#PreProcess(filetype, linter) abort let l:obj.initialization_options = a:linter.initialization_options endif - if has_key(a:linter, 'lsp_config') + if has_key(a:linter, 'lsp_config_callback') + if has_key(a:linter, 'lsp_config') + throw 'Only one of `lsp_config` or `lsp_config_callback` should be set' + endif + + let l:obj.lsp_config_callback = a:linter.lsp_config_callback + + if !s:IsCallback(l:obj.lsp_config_callback) + throw '`lsp_config_callback` must be a callback if defined' + endif + elseif has_key(a:linter, 'lsp_config') if type(a:linter.lsp_config) isnot v:t_dict throw '`lsp_config` must be a Dictionary' endif diff --git a/autoload/ale/lsp_linter.vim b/autoload/ale/lsp_linter.vim index fa4d2f86..2ffa6522 100644 --- a/autoload/ale/lsp_linter.vim +++ b/autoload/ale/lsp_linter.vim @@ -140,6 +140,18 @@ function! ale#lsp_linter#GetOptions(buffer, linter) abort return l:initialization_options endfunction +function! ale#lsp_linter#GetConfig(buffer, linter) abort + let l:config = {} + + if has_key(a:linter, 'lsp_config_callback') + let l:config = ale#util#GetFunction(a:linter.lsp_config_callback)(a:buffer) + elseif has_key(a:linter, 'lsp_config') + let l:config = a:linter.lsp_config + endif + + return l:config +endfunction + " Given a buffer, an LSP linter, start up an LSP linter and get ready to " receive messages for the document. function! ale#lsp_linter#StartLSP(buffer, linter) abort @@ -188,14 +200,16 @@ function! ale#lsp_linter#StartLSP(buffer, linter) abort call ale#lsp#MarkConnectionAsTsserver(l:conn_id) endif - let l:language_id = ale#util#GetFunction(a:linter.language_callback)(a:buffer) + let l:config = ale#lsp_linter#GetConfig(a:buffer, a:linter) - if !empty(get(a:linter, 'lsp_config')) + if !empty(l:config) " set LSP configuration options (workspace/didChangeConfiguration) - let l:config_message = ale#lsp#message#DidChangeConfiguration(a:buffer, a:linter.lsp_config) + let l:config_message = ale#lsp#message#DidChangeConfiguration(a:buffer, l:config) call ale#lsp#Send(l:conn_id, l:config_message) endif + let l:language_id = ale#util#GetFunction(a:linter.language_callback)(a:buffer) + let l:details = { \ 'buffer': a:buffer, \ 'connection_id': l:conn_id, -- cgit v1.2.3 From 2ac9e2a29e3c570e8aac5d8f5404921e8d816006 Mon Sep 17 00:00:00 2001 From: Jon Parise Date: Tue, 30 Oct 2018 15:47:19 -0700 Subject: Only send LSP config updates when the dict changes Each LSP connection now stores its configuration dictionary. It is initially empty (`{}`) and is updated each time the LSP connection is started. When a change is detected, the workspace/didChangeConfiguration message is sent to the LSP servers with the updated configuration. --- autoload/ale/lsp.vim | 21 +++++++++++++++++++++ autoload/ale/lsp_linter.vim | 9 ++------- 2 files changed, 23 insertions(+), 7 deletions(-) (limited to 'autoload') diff --git a/autoload/ale/lsp.vim b/autoload/ale/lsp.vim index 196cbe80..74712de1 100644 --- a/autoload/ale/lsp.vim +++ b/autoload/ale/lsp.vim @@ -19,6 +19,7 @@ function! ale#lsp#Register(executable_or_address, project, init_options) abort " initialized: 0 if the connection is ready, 1 otherwise. " init_request_id: The ID for the init request. " init_options: Options to send to the server. + " config: Configuration settings to send to the server. " callback_list: A list of callbacks for handling LSP responses. " message_queue: Messages queued for sending to callbacks. " capabilities_queue: The list of callbacks to call with capabilities. @@ -32,6 +33,7 @@ function! ale#lsp#Register(executable_or_address, project, init_options) abort \ 'initialized': 0, \ 'init_request_id': 0, \ 'init_options': a:init_options, + \ 'config': {}, \ 'callback_list': [], \ 'message_queue': [], \ 'capabilities_queue': [], @@ -205,6 +207,25 @@ function! s:UpdateCapabilities(conn, capabilities) abort endif endfunction +" Update a connection's configuration dictionary and notify LSP servers +" of any changes since the last update. Returns 1 if a configuration +" update was sent; otherwise 0 will be returned. +function! ale#lsp#UpdateConfig(conn_id, buffer, config) abort + let l:conn = get(s:connections, a:conn_id, {}) + + if empty(l:conn) || a:config ==# l:conn.config " no-custom-checks + return 0 + endif + + let l:conn.config = a:config + let l:message = ale#lsp#message#DidChangeConfiguration(a:buffer, a:config) + + call ale#lsp#Send(a:conn_id, l:message) + + return 1 +endfunction + + function! ale#lsp#HandleInitResponse(conn, response) abort if get(a:response, 'method', '') is# 'initialize' let a:conn.initialized = 1 diff --git a/autoload/ale/lsp_linter.vim b/autoload/ale/lsp_linter.vim index 2ffa6522..42d67398 100644 --- a/autoload/ale/lsp_linter.vim +++ b/autoload/ale/lsp_linter.vim @@ -201,13 +201,6 @@ function! ale#lsp_linter#StartLSP(buffer, linter) abort endif let l:config = ale#lsp_linter#GetConfig(a:buffer, a:linter) - - if !empty(l:config) - " set LSP configuration options (workspace/didChangeConfiguration) - let l:config_message = ale#lsp#message#DidChangeConfiguration(a:buffer, l:config) - call ale#lsp#Send(l:conn_id, l:config_message) - endif - let l:language_id = ale#util#GetFunction(a:linter.language_callback)(a:buffer) let l:details = { @@ -218,6 +211,8 @@ function! ale#lsp_linter#StartLSP(buffer, linter) abort \ 'language_id': l:language_id, \} + call ale#lsp#UpdateConfig(l:conn_id, a:buffer, l:config) + if ale#lsp#OpenDocument(l:conn_id, a:buffer, l:language_id) if g:ale_history_enabled && !empty(l:command) call ale#history#Add(a:buffer, 'started', l:conn_id, l:command) -- cgit v1.2.3