diff options
-rw-r--r-- | autoload/ale/assert.vim | 13 | ||||
-rw-r--r-- | autoload/ale/linter.vim | 12 | ||||
-rw-r--r-- | autoload/ale/lsp.vim | 21 | ||||
-rw-r--r-- | autoload/ale/lsp_linter.vim | 21 | ||||
-rw-r--r-- | doc/ale-development.txt | 1 | ||||
-rw-r--r-- | doc/ale.txt | 13 | ||||
-rw-r--r-- | test/command_callback/test_elixir_ls_command_callbacks.vader | 1 | ||||
-rw-r--r-- | test/command_callback/test_golangserver_command_callback.vader | 3 | ||||
-rw-r--r-- | test/command_callback/test_scala_sbtserver.vader | 2 | ||||
-rw-r--r-- | test/lsp/test_other_initialize_message_handling.vader | 1 | ||||
-rw-r--r-- | test/lsp/test_update_config.vader | 17 | ||||
-rw-r--r-- | test/test_linter_defintion_processing.vader | 27 |
12 files changed, 122 insertions, 10 deletions
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(<args>) command! -nargs=0 AssertLinterNotExecuted :call ale#assert#LinterNotExecuted() command! -nargs=+ AssertLSPOptions :call ale#assert#LSPOptions(<args>) + command! -nargs=+ AssertLSPConfig :call ale#assert#LSPConfig(<args>) command! -nargs=+ AssertLSPLanguage :call ale#assert#LSPLanguage(<args>) command! -nargs=+ AssertLSPProject :call ale#assert#LSPProject(<args>) command! -nargs=+ AssertLSPAddress :call ale#assert#LSPAddress(<args>) @@ -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.vim b/autoload/ale/lsp.vim index b7908e74..f55096c2 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': [], @@ -210,6 +212,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 fa4d2f86..42d67398 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,9 @@ function! ale#lsp_linter#StartLSP(buffer, linter) abort call ale#lsp#MarkConnectionAsTsserver(l:conn_id) endif + let l:config = ale#lsp_linter#GetConfig(a:buffer, a:linter) let l:language_id = ale#util#GetFunction(a:linter.language_callback)(a:buffer) - if !empty(get(a:linter, 'lsp_config')) - " set LSP configuration options (workspace/didChangeConfiguration) - let l:config_message = ale#lsp#message#DidChangeConfiguration(a:buffer, a:linter.lsp_config) - call ale#lsp#Send(l:conn_id, l:config_message) - endif - let l:details = { \ 'buffer': a:buffer, \ 'connection_id': l:conn_id, @@ -204,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) diff --git a/doc/ale-development.txt b/doc/ale-development.txt index ac72d615..1e168130 100644 --- a/doc/ale-development.txt +++ b/doc/ale-development.txt @@ -306,6 +306,7 @@ given the above setup are as follows. `AssertLinterNotExecuted` - Check that linters will not be executed. `AssertLSPLanguage language` - Check the language given to an LSP server. `AssertLSPOptions options_dict` - Check the options given to an LSP server. +`AssertLSPConfig config_dict` - Check the config given to an LSP server. `AssertLSPProject project_root` - Check the root given to an LSP server. `AssertLSPAddress address` - Check the address to an LSP server. diff --git a/doc/ale.txt b/doc/ale.txt index 6340091c..59b9f352 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -2685,6 +2685,9 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()* `initialization_options_callback` may be defined to pass initialization options to the LSP. + An optional `lsp_config` or `lsp_config_callback` may + be defined to pass configuration settings to the LSP. + `address_callback` A |String| or |Funcref| for a callback function accepting a buffer number. A |String| should be returned with an address to connect to. @@ -2745,6 +2748,16 @@ ale#linter#Define(filetype, linter) *ale#linter#Define()* This can be used in place of `initialization_options` when more complicated processing is needed. + `lsp_config` A |Dictionary| of configuration settings for LSPs. + This will be fed (as JSON) to the LSP in the + workspace/didChangeConfiguration command. + + `lsp_config_callback` A |String| or |Funcref| for a callback function + accepting a buffer number. A |Dictionary| should be + returned for configuration settings to pass the LSP. + This can be used in place of `lsp_config` when more + complicated processing is needed. + Only one of `command`, `command_callback`, or `command_chain` should be specified. `command_callback` is generally recommended when a command string needs to be generated dynamically, or any global options are used. diff --git a/test/command_callback/test_elixir_ls_command_callbacks.vader b/test/command_callback/test_elixir_ls_command_callbacks.vader index 0d00354b..f79be9b4 100644 --- a/test/command_callback/test_elixir_ls_command_callbacks.vader +++ b/test/command_callback/test_elixir_ls_command_callbacks.vader @@ -26,4 +26,5 @@ Execute(should set correct LSP values): AssertLSPLanguage 'elixir' AssertLSPOptions {} + AssertLSPConfig {} AssertLSPProject ale#path#Simplify(g:dir . '/mix_paths/wrapped_project') diff --git a/test/command_callback/test_golangserver_command_callback.vader b/test/command_callback/test_golangserver_command_callback.vader index ee88e1a4..90fdc26f 100644 --- a/test/command_callback/test_golangserver_command_callback.vader +++ b/test/command_callback/test_golangserver_command_callback.vader @@ -56,7 +56,7 @@ Execute(should set go-langserver for go app1): call ale#test#SetFilename('go_paths/go1/prj1/file.go') AssertLSPLanguage 'go' - AssertLSPOptions {} + AssertLSPConfig {} AssertLSPProject ale#path#Simplify(g:dir . '/go_paths/go1') Execute(should set go-langserver for go app2): @@ -64,4 +64,5 @@ Execute(should set go-langserver for go app2): AssertLSPLanguage 'go' AssertLSPOptions {} + AssertLSPConfig {} AssertLSPProject ale#path#Simplify(g:dir . '/go_paths/go2') diff --git a/test/command_callback/test_scala_sbtserver.vader b/test/command_callback/test_scala_sbtserver.vader index 1b708bd9..1c7d8472 100644 --- a/test/command_callback/test_scala_sbtserver.vader +++ b/test/command_callback/test_scala_sbtserver.vader @@ -9,11 +9,13 @@ Execute(should set sbtserver for sbt project with build.sbt): call ale#test#SetFilename('../scala_fixtures/valid_sbt_project/Main.scala') AssertLSPLanguage 'scala' AssertLSPOptions {} + AssertLSPConfig {} AssertLSPProject ale#path#Simplify(g:dir . 'command_callback/../scala_fixtures/valid_sbt_project') AssertLSPAddress '127.0.0.1:4273' Execute(should not set sbtserver for sbt project without build.sbt): call ale#test#SetFilename('../scala_fixtures/invalid_sbt_project/Main.scala') AssertLSPLanguage 'scala' AssertLSPOptions {} + AssertLSPConfig {} AssertLSPProject '' AssertLSPAddress '127.0.0.1:4273' diff --git a/test/lsp/test_other_initialize_message_handling.vader b/test/lsp/test_other_initialize_message_handling.vader index 072f8c4b..2f59535d 100644 --- a/test/lsp/test_other_initialize_message_handling.vader +++ b/test/lsp/test_other_initialize_message_handling.vader @@ -7,6 +7,7 @@ Before: \ 'initialized': 0, \ 'init_request_id': 0, \ 'init_options': {}, + \ 'config': {}, \ 'callback_list': [], \ 'message_queue': [], \ 'capabilities_queue': [], diff --git a/test/lsp/test_update_config.vader b/test/lsp/test_update_config.vader new file mode 100644 index 00000000..07068bc8 --- /dev/null +++ b/test/lsp/test_update_config.vader @@ -0,0 +1,17 @@ +Before: + runtime autoload/ale/lsp.vim + + let g:conn_id = ale#lsp#Register('executable', '/foo/bar', {}) + +After: + Restore + + unlet! g:conn_id + + runtime autoload/ale/lsp.vim + +Execute(Only send updates when the configuration dictionary changes): + AssertEqual 0, ale#lsp#UpdateConfig(g:conn_id, bufnr(''), {}) + AssertEqual 1, ale#lsp#UpdateConfig(g:conn_id, bufnr(''), {'a': 1}) + AssertEqual 0, ale#lsp#UpdateConfig(g:conn_id, bufnr(''), {'a': 1}) + AssertEqual 1, ale#lsp#UpdateConfig(g:conn_id, bufnr(''), {}) diff --git a/test/test_linter_defintion_processing.vader b/test/test_linter_defintion_processing.vader index a28edf9e..d967761d 100644 --- a/test/test_linter_defintion_processing.vader +++ b/test/test_linter_defintion_processing.vader @@ -501,6 +501,31 @@ Execute(PreProcess should throw when initialization_options_callback is not a ca \}) AssertEqual '`initialization_options_callback` must be a callback if defined', g:vader_exception +Execute(PreProcess should complain about using lsp_config and lsp_config_callback together): + let g:linter = { + \ 'name': 'x', + \ 'lsp': 'socket', + \ 'address_callback': 'X', + \ 'language': 'x', + \ 'project_root_callback': 'x', + \ 'lsp_config': 'x', + \ 'lsp_config_callback': 'x', + \} + + AssertThrows call ale#linter#PreProcess('testft', g:linter) + AssertEqual 'Only one of `lsp_config` or `lsp_config_callback` should be set', g:vader_exception + +Execute(PreProcess should throw when lsp_config_callback is not a callback): + AssertThrows call ale#linter#PreProcess('testft', { + \ 'name': 'foo', + \ 'lsp': 'socket', + \ 'address_callback': 'X', + \ 'language': 'x', + \ 'project_root_callback': 'x', + \ 'lsp_config_callback': {}, + \}) + AssertEqual '`lsp_config_callback` must be a callback if defined', g:vader_exception + Execute(PreProcess should accept LSP configuration options via lsp_config): let g:ale_lsp_configuration = { \ 'foo': 'bar' @@ -517,7 +542,6 @@ Execute(PreProcess should accept LSP configuration options via lsp_config): AssertEqual {'foo': 'bar'}, ale#linter#PreProcess('testft', g:linter).lsp_config - Execute(PreProcess should throw when lsp_config is not a Dictionary): AssertThrows call ale#linter#PreProcess('testft', { \ 'name': 'foo', @@ -528,4 +552,3 @@ Execute(PreProcess should throw when lsp_config is not a Dictionary): \ 'lsp_config': 'x', \}) AssertEqual '`lsp_config` must be a Dictionary', g:vader_exception - |