From be69af270547b0d1dd5fe947efb3f61455f709ed Mon Sep 17 00:00:00 2001 From: w0rp Date: Thu, 14 Sep 2023 00:38:12 +0100 Subject: [PATCH] #4607 No conflicts with nvim-lspconfig by default Default `g:ale_disable_lsp` to a new mode `'auto'` by default. With this setting applied, ALE will now check for the presence of nvim-lspconfig and automatically turn off particular LSP linters if already configured via nvim-lspconfig. For users that do not use `nvim-lspconfig`, everything should work as before. --- README.md | 16 +- ...anguage_server.vim => language_server.vim} | 17 +- ale_linters/dafny/dafny.vim | 2 +- ale_linters/elixir/elixir_ls.vim | 3 +- ale_linters/elm/{elm_ls.vim => ls.vim} | 9 +- ale_linters/fortran/language_server.vim | 1 + ale_linters/html/angular.vim | 2 +- ale_linters/java/javalsp.vim | 1 + ale_linters/javascript/flow_ls.vim | 3 +- ale_linters/julia/languageserver.vim | 1 + ale_linters/kotlin/languageserver.vim | 1 + ale_linters/lua/lua_language_server.vim | 2 +- ale_linters/nix/rnix_lsp.vim | 1 + ale_linters/ocaml/ols.vim | 1 + ale_linters/puppet/languageserver.vim | 1 + ale_linters/purescript/ls.vim | 1 + ale_linters/python/jedils.vim | 1 + ale_linters/r/languageserver.vim | 1 + ale_linters/reason/ls.vim | 1 + ale_linters/reason/ols.vim | 1 + ale_linters/rust/analyzer.vim | 1 + ale_linters/swift/sourcekitlsp.vim | 1 + ale_linters/terraform/terraform_ls.vim | 1 + ale_linters/vue/vls.vim | 1 + ale_linters/yaml/ls.vim | 1 + autoload/ale.vim | 22 +- autoload/ale/code_action.vim | 12 +- autoload/ale/codefix.vim | 14 +- autoload/ale/completion.vim | 10 +- autoload/ale/debugging.vim | 41 ++- autoload/ale/definition.vim | 18 +- autoload/ale/engine.vim | 2 +- autoload/ale/engine/ignore.vim | 67 +++- autoload/ale/filerename.vim | 4 +- autoload/ale/handlers/hdl_checker.vim | 4 +- autoload/ale/hover.vim | 5 +- autoload/ale/linter.vim | 10 - autoload/ale/lsp/reset.vim | 2 + autoload/ale/lsp_linter.vim | 99 +++-- autoload/ale/organize_imports.vim | 6 +- autoload/ale/references.vim | 6 +- autoload/ale/rename.vim | 14 +- autoload/ale/symbol.vim | 4 +- autoload/ale/uri/jdt.vim | 6 +- doc/ale.txt | 13 +- lspconfig.vim | 3 + lua/{ => ale}/diagnostics.lua | 0 lua/ale/util.lua | 14 + plugin/ale.vim | 4 + .../linter/test_ansible_language_server.vader | 2 +- test/linter/test_elm_ls.vader | 2 +- .../test_engine_lsp_response_handling.vader | 8 +- test/test_filetype_linter_defaults.vader | 2 +- test/test_ignoring_linters.vader | 338 ++++++++++++++---- 54 files changed, 578 insertions(+), 225 deletions(-) rename ale_linters/ansible/{ansible_language_server.vim => language_server.vim} (60%) rename ale_linters/elm/{elm_ls.vim => ls.vim} (85%) create mode 100644 lspconfig.vim rename lua/{ => ale}/diagnostics.lua (100%) create mode 100644 lua/ale/util.lua diff --git a/README.md b/README.md index a61358b4..a9d111ab 100644 --- a/README.md +++ b/README.md @@ -603,9 +603,19 @@ including the option `g:ale_lint_on_enter`, and you can run ALE manually with ALE offers an API for letting any other plugin integrate with ALE. If you are interested in writing an integration, see `:help ale-lint-other-sources`. -If you are running ALE in combination with another LSP client, you may wish -to disable ALE's LSP functionality entirely. You can add a setting to your -vimrc/init.vim to do so. +If you're running ALE in Neovim with +[nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) for configuring +particular language servers. ALE will automatically disable its LSP +functionality for any language servers configured with nvim-lspconfig by +default. The following setting is applied by default: + +```vim +let g:ale_disable_lsp = 'auto' +``` + +If you are running ALE in combination with another LSP client, you may wish to +disable ALE's LSP functionality entirely. You can change the setting to `1` to +always disable all LSP functionality. ```vim let g:ale_disable_lsp = 1 diff --git a/ale_linters/ansible/ansible_language_server.vim b/ale_linters/ansible/language_server.vim similarity index 60% rename from ale_linters/ansible/ansible_language_server.vim rename to ale_linters/ansible/language_server.vim index d1f3fb7d..0c064353 100644 --- a/ale_linters/ansible/ansible_language_server.vim +++ b/ale_linters/ansible/language_server.vim @@ -4,17 +4,17 @@ call ale#Set('ansible_language_server_executable', 'ansible-language-server') call ale#Set('ansible_language_server_config', {}) -function! ale_linters#ansible#ansible_language_server#Executable(buffer) abort +function! ale_linters#ansible#language_server#Executable(buffer) abort return ale#Var(a:buffer, 'ansible_language_server_executable') endfunction -function! ale_linters#ansible#ansible_language_server#GetCommand(buffer) abort - let l:executable = ale_linters#ansible#ansible_language_server#Executable(a:buffer) +function! ale_linters#ansible#language_server#GetCommand(buffer) abort + let l:executable = ale_linters#ansible#language_server#Executable(a:buffer) return ale#Escape(l:executable) . ' --stdio' endfunction -function! ale_linters#ansible#ansible_language_server#FindProjectRoot(buffer) abort +function! ale_linters#ansible#language_server#FindProjectRoot(buffer) abort let l:dir = fnamemodify( \ ale#path#FindNearestFile(a:buffer, 'ansible.cfg'), \ ':h' @@ -37,10 +37,11 @@ function! ale_linters#ansible#ansible_language_server#FindProjectRoot(buffer) ab endfunction call ale#linter#Define('ansible', { -\ 'name': 'ansible-language-server', +\ 'name': 'language_server', +\ 'aliases': ['ansible_language_server', 'ansible-language-server'], \ 'lsp': 'stdio', -\ 'executable': function('ale_linters#ansible#ansible_language_server#Executable'), -\ 'command': function('ale_linters#ansible#ansible_language_server#GetCommand'), -\ 'project_root': function('ale_linters#ansible#ansible_language_server#FindProjectRoot'), +\ 'executable': function('ale_linters#ansible#language_server#Executable'), +\ 'command': function('ale_linters#ansible#language_server#GetCommand'), +\ 'project_root': function('ale_linters#ansible#language_server#FindProjectRoot'), \ 'lsp_config': {b -> ale#Var(b, 'ansible_language_server_config')} \}) diff --git a/ale_linters/dafny/dafny.vim b/ale_linters/dafny/dafny.vim index 2a9f761a..8a114d22 100644 --- a/ale_linters/dafny/dafny.vim +++ b/ale_linters/dafny/dafny.vim @@ -1,4 +1,5 @@ " Author: Taylor Blau +call ale#Set('dafny_dafny_timelimit', 10) function! ale_linters#dafny#dafny#Handle(buffer, lines) abort let l:pattern = '\v(.*)\((\d+),(\d+)\): (.*): (.*)' @@ -31,7 +32,6 @@ function! ale_linters#dafny#dafny#GetCommand(buffer) abort return printf('dafny %%s /compile:0 /timeLimit:%d', ale#Var(a:buffer, 'dafny_dafny_timelimit')) endfunction -call ale#Set('dafny_dafny_timelimit', 10) call ale#linter#Define('dafny', { \ 'name': 'dafny', \ 'executable': 'dafny', diff --git a/ale_linters/elixir/elixir_ls.vim b/ale_linters/elixir/elixir_ls.vim index a7d7110c..c7dda600 100644 --- a/ale_linters/elixir/elixir_ls.vim +++ b/ale_linters/elixir/elixir_ls.vim @@ -12,7 +12,8 @@ function! ale_linters#elixir#elixir_ls#GetExecutable(buffer) abort endfunction call ale#linter#Define('elixir', { -\ 'name': 'elixir-ls', +\ 'name': 'elixir_ls', +\ 'aliases': ['elixir-ls', 'elixirls'], \ 'lsp': 'stdio', \ 'executable': function('ale_linters#elixir#elixir_ls#GetExecutable'), \ 'command': function('ale_linters#elixir#elixir_ls#GetExecutable'), diff --git a/ale_linters/elm/elm_ls.vim b/ale_linters/elm/ls.vim similarity index 85% rename from ale_linters/elm/elm_ls.vim rename to ale_linters/elm/ls.vim index a02dbf42..38a5b234 100644 --- a/ale_linters/elm/elm_ls.vim +++ b/ale_linters/elm/ls.vim @@ -10,13 +10,13 @@ call ale#Set('elm_ls_elm_format_path', '') call ale#Set('elm_ls_elm_test_path', '') call ale#Set('elm_ls_elm_analyse_trigger', 'change') -function! elm_ls#GetRootDir(buffer) abort +function! ale_linters#elm#ls#GetProjectRoot(buffer) abort let l:elm_json = ale#path#FindNearestFile(a:buffer, 'elm.json') return !empty(l:elm_json) ? fnamemodify(l:elm_json, ':p:h') : '' endfunction -function! elm_ls#GetOptions(buffer) abort +function! ale_linters#elm#ls#GetOptions(buffer) abort return { \ 'elmPath': ale#Var(a:buffer, 'elm_ls_elm_path'), \ 'elmFormatPath': ale#Var(a:buffer, 'elm_ls_elm_format_path'), @@ -26,7 +26,8 @@ function! elm_ls#GetOptions(buffer) abort endfunction call ale#linter#Define('elm', { -\ 'name': 'elm_ls', +\ 'name': 'ls', +\ 'aliases': ['elm_ls'], \ 'lsp': 'stdio', \ 'executable': {b -> ale#path#FindExecutable(b, 'elm_ls', [ \ 'node_modules/.bin/elm-language-server', @@ -34,7 +35,7 @@ call ale#linter#Define('elm', { \ 'elm-lsp' \ ])}, \ 'command': '%e --stdio', -\ 'project_root': function('elm_ls#GetRootDir'), +\ 'project_root': function('ale_linters#elm#ls#GetProjectRoot'), \ 'language': 'elm', \ 'initialization_options': function('elm_ls#GetOptions') \}) diff --git a/ale_linters/fortran/language_server.vim b/ale_linters/fortran/language_server.vim index 00aa0577..c885b699 100644 --- a/ale_linters/fortran/language_server.vim +++ b/ale_linters/fortran/language_server.vim @@ -12,6 +12,7 @@ endfunction call ale#linter#Define('fortran', { \ 'name': 'language_server', +\ 'aliases': ['fortls'], \ 'lsp': 'stdio', \ 'executable': {b -> ale#Var(b, 'fortran_language_server_executable')}, \ 'command': '%e', diff --git a/ale_linters/html/angular.vim b/ale_linters/html/angular.vim index 4f368fb4..05caf59d 100644 --- a/ale_linters/html/angular.vim +++ b/ale_linters/html/angular.vim @@ -48,7 +48,7 @@ endfunction call ale#linter#Define('html', { \ 'name': 'angular', -\ 'aliases': ['angular-language-server'], +\ 'aliases': ['angular-language-server', 'angularls'], \ 'lsp': 'stdio', \ 'executable': function('ale_linters#html#angular#GetExecutable'), \ 'command': function('ale_linters#html#angular#GetCommand'), diff --git a/ale_linters/java/javalsp.vim b/ale_linters/java/javalsp.vim index baf584c8..fa3b0e2c 100644 --- a/ale_linters/java/javalsp.vim +++ b/ale_linters/java/javalsp.vim @@ -46,6 +46,7 @@ endfunction call ale#linter#Define('java', { \ 'name': 'javalsp', +\ 'aliases': ['java_language_server'], \ 'lsp': 'stdio', \ 'executable': function('ale_linters#java#javalsp#Executable'), \ 'command': function('ale_linters#java#javalsp#Command'), diff --git a/ale_linters/javascript/flow_ls.vim b/ale_linters/javascript/flow_ls.vim index fec34011..9755ca4e 100644 --- a/ale_linters/javascript/flow_ls.vim +++ b/ale_linters/javascript/flow_ls.vim @@ -17,7 +17,8 @@ function! ale_linters#javascript#flow_ls#FindProjectRoot(buffer) abort endfunction call ale#linter#Define('javascript', { -\ 'name': 'flow-language-server', +\ 'name': 'flow_ls', +\ 'aliaes': ['flow-language-server'], \ 'lsp': 'stdio', \ 'executable': {b -> ale#path#FindExecutable(b, 'javascript_flow_ls', [ \ 'node_modules/.bin/flow', diff --git a/ale_linters/julia/languageserver.vim b/ale_linters/julia/languageserver.vim index 999ad815..fbfab517 100644 --- a/ale_linters/julia/languageserver.vim +++ b/ale_linters/julia/languageserver.vim @@ -13,6 +13,7 @@ endfunction call ale#linter#Define('julia', { \ 'name': 'languageserver', +\ 'aliases': ['julials'], \ 'lsp': 'stdio', \ 'executable': {b -> ale#Var(b, 'julia_executable')}, \ 'command': function('ale_linters#julia#languageserver#GetCommand'), diff --git a/ale_linters/kotlin/languageserver.vim b/ale_linters/kotlin/languageserver.vim index af78c0e0..18b153ae 100644 --- a/ale_linters/kotlin/languageserver.vim +++ b/ale_linters/kotlin/languageserver.vim @@ -21,6 +21,7 @@ endfunction call ale#linter#Define('kotlin', { \ 'name': 'languageserver', +\ 'aliaes': ['kotlin_language_server'], \ 'lsp': 'stdio', \ 'executable': {b -> ale#Var(b, 'kotlin_languageserver_executable')}, \ 'command': '%e', diff --git a/ale_linters/lua/lua_language_server.vim b/ale_linters/lua/lua_language_server.vim index ebddd4b5..0892ad3b 100644 --- a/ale_linters/lua/lua_language_server.vim +++ b/ale_linters/lua/lua_language_server.vim @@ -6,7 +6,7 @@ call ale#Set('lua_language_server_config', {}) call ale#linter#Define('lua', { \ 'name': 'lua_language_server', -\ 'aliases': ['lua-language-server'], +\ 'aliases': ['lua-language-server', 'lua_ls'], \ 'lsp': 'stdio', \ 'executable': {b -> ale#Var(b, 'lua_language_server_executable')}, \ 'command': '%e', diff --git a/ale_linters/nix/rnix_lsp.vim b/ale_linters/nix/rnix_lsp.vim index 949bed1c..99c0fbfa 100644 --- a/ale_linters/nix/rnix_lsp.vim +++ b/ale_linters/nix/rnix_lsp.vim @@ -9,6 +9,7 @@ endfunction call ale#linter#Define('nix', { \ 'name': 'rnix_lsp', +\ 'aliases': ['rnix'], \ 'lsp': 'stdio', \ 'executable': 'rnix-lsp', \ 'command': '%e', diff --git a/ale_linters/ocaml/ols.vim b/ale_linters/ocaml/ols.vim index ec71bdb4..b26c7826 100644 --- a/ale_linters/ocaml/ols.vim +++ b/ale_linters/ocaml/ols.vim @@ -6,6 +6,7 @@ call ale#Set('ocaml_ols_use_global', get(g:, 'ale_use_global_executables', 0)) call ale#linter#Define('ocaml', { \ 'name': 'ols', +\ 'aliases': ['ocaml-language-server'], \ 'lsp': 'stdio', \ 'executable': function('ale#handlers#ols#GetExecutable'), \ 'command': function('ale#handlers#ols#GetCommand'), diff --git a/ale_linters/puppet/languageserver.vim b/ale_linters/puppet/languageserver.vim index 2078695f..c6b12662 100644 --- a/ale_linters/puppet/languageserver.vim +++ b/ale_linters/puppet/languageserver.vim @@ -29,6 +29,7 @@ endfunction call ale#linter#Define('puppet', { \ 'name': 'languageserver', +\ 'aliases': ['puppet_languageserver'], \ 'lsp': 'stdio', \ 'executable': {b -> ale#Var(b, 'puppet_languageserver_executable')}, \ 'command': '%e --stdio', diff --git a/ale_linters/purescript/ls.vim b/ale_linters/purescript/ls.vim index a20fae47..1eaf2af7 100644 --- a/ale_linters/purescript/ls.vim +++ b/ale_linters/purescript/ls.vim @@ -41,6 +41,7 @@ endfunction call ale#linter#Define('purescript', { \ 'name': 'purescript-language-server', +\ 'aliases': ['purescriptls'], \ 'lsp': 'stdio', \ 'executable': function('ale_linters#purescript#ls#GetExecutable'), \ 'command': function('ale_linters#purescript#ls#GetCommand'), diff --git a/ale_linters/python/jedils.vim b/ale_linters/python/jedils.vim index d3e15bf2..e82abd1b 100644 --- a/ale_linters/python/jedils.vim +++ b/ale_linters/python/jedils.vim @@ -30,6 +30,7 @@ endfunction call ale#linter#Define('python', { \ 'name': 'jedils', +\ 'aliases': ['jedi_language_server'], \ 'lsp': 'stdio', \ 'executable': function('ale_linters#python#jedils#GetExecutable'), \ 'command': function('ale_linters#python#jedils#GetCommand'), diff --git a/ale_linters/r/languageserver.vim b/ale_linters/r/languageserver.vim index bab869d1..1ff23fa9 100644 --- a/ale_linters/r/languageserver.vim +++ b/ale_linters/r/languageserver.vim @@ -19,6 +19,7 @@ endfunction call ale#linter#Define('r', { \ 'name': 'languageserver', +\ 'aliases': ['r_language_server'], \ 'lsp': 'stdio', \ 'lsp_config': {b -> ale#Var(b, 'r_languageserver_config')}, \ 'executable': 'Rscript', diff --git a/ale_linters/reason/ls.vim b/ale_linters/reason/ls.vim index fb1114ae..a831b506 100644 --- a/ale_linters/reason/ls.vim +++ b/ale_linters/reason/ls.vim @@ -15,6 +15,7 @@ endfunction call ale#linter#Define('reason', { \ 'name': 'reason-language-server', +\ 'aliases': ['reason_ls'], \ 'lsp': 'stdio', \ 'executable': {buffer -> ale#Var(buffer, 'reason_ls_executable')}, \ 'command': '%e', diff --git a/ale_linters/reason/ols.vim b/ale_linters/reason/ols.vim index 9fbd9b4f..e1f408f0 100644 --- a/ale_linters/reason/ols.vim +++ b/ale_linters/reason/ols.vim @@ -6,6 +6,7 @@ call ale#Set('reason_ols_use_global', get(g:, 'ale_use_global_executables', 0)) call ale#linter#Define('reason', { \ 'name': 'ols', +\ 'aliases': ['ocaml-language-server'], \ 'lsp': 'stdio', \ 'executable': function('ale#handlers#ols#GetExecutable'), \ 'command': function('ale#handlers#ols#GetCommand'), diff --git a/ale_linters/rust/analyzer.vim b/ale_linters/rust/analyzer.vim index 3ead3871..e3141cd3 100644 --- a/ale_linters/rust/analyzer.vim +++ b/ale_linters/rust/analyzer.vim @@ -28,6 +28,7 @@ endfunction call ale#linter#Define('rust', { \ 'name': 'analyzer', +\ 'aliases': ['rust_analyzer'], \ 'lsp': 'stdio', \ 'initialization_options': {b -> ale#Var(b, 'rust_analyzer_config')}, \ 'executable': {b -> ale#Var(b, 'rust_analyzer_executable')}, diff --git a/ale_linters/swift/sourcekitlsp.vim b/ale_linters/swift/sourcekitlsp.vim index 560893bf..a403dcc1 100644 --- a/ale_linters/swift/sourcekitlsp.vim +++ b/ale_linters/swift/sourcekitlsp.vim @@ -5,6 +5,7 @@ call ale#Set('sourcekit_lsp_executable', 'sourcekit-lsp') call ale#linter#Define('swift', { \ 'name': 'sourcekitlsp', +\ 'aliases': ['sourcekit'], \ 'lsp': 'stdio', \ 'executable': {b -> ale#Var(b, 'sourcekit_lsp_executable')}, \ 'command': '%e', diff --git a/ale_linters/terraform/terraform_ls.vim b/ale_linters/terraform/terraform_ls.vim index ab35126e..7dc77941 100644 --- a/ale_linters/terraform/terraform_ls.vim +++ b/ale_linters/terraform/terraform_ls.vim @@ -30,6 +30,7 @@ endfunction call ale#linter#Define('terraform', { \ 'name': 'terraform_ls', +\ 'aliases': ['terraformls'], \ 'lsp': 'stdio', \ 'executable': {b -> ale#Var(b, 'terraform_ls_executable')}, \ 'command': function('ale_linters#terraform#terraform_ls#GetCommand'), diff --git a/ale_linters/vue/vls.vim b/ale_linters/vue/vls.vim index 4bd75286..009effd0 100644 --- a/ale_linters/vue/vls.vim +++ b/ale_linters/vue/vls.vim @@ -12,6 +12,7 @@ endfunction call ale#linter#Define('vue', { \ 'name': 'vls', +\ 'aliases': ['vuels'], \ 'lsp': 'stdio', \ 'executable': {b -> ale#path#FindExecutable(b, 'vue_vls', [ \ 'node_modules/.bin/vls', diff --git a/ale_linters/yaml/ls.vim b/ale_linters/yaml/ls.vim index 8e3f6d00..79510ffe 100644 --- a/ale_linters/yaml/ls.vim +++ b/ale_linters/yaml/ls.vim @@ -26,6 +26,7 @@ endfunction call ale#linter#Define('yaml', { \ 'name': 'yaml-language-server', +\ 'aliases': ['yamlls'], \ 'lsp': 'stdio', \ 'executable': function('ale_linters#yaml#ls#GetExecutable'), \ 'command': function('ale_linters#yaml#ls#GetCommand'), diff --git a/autoload/ale.vim b/autoload/ale.vim index 23315913..5212099e 100644 --- a/autoload/ale.vim +++ b/autoload/ale.vim @@ -7,9 +7,6 @@ let g:ale_echo_msg_error_str = get(g:, 'ale_echo_msg_error_str', 'Error') let g:ale_echo_msg_info_str = get(g:, 'ale_echo_msg_info_str', 'Info') let g:ale_echo_msg_log_str = get(g:, 'ale_echo_msg_log_str', 'Log') let g:ale_echo_msg_warning_str = get(g:, 'ale_echo_msg_warning_str', 'Warning') -" Ignoring linters, for disabling some, or ignoring LSP diagnostics. -let g:ale_linters_ignore = get(g:, 'ale_linters_ignore', {}) -let g:ale_disable_lsp = get(g:, 'ale_disable_lsp', 0) " LSP window/showMessage format let g:ale_lsp_show_message_format = get(g:, 'ale_lsp_show_message_format', '%severity%:%linter%: %s') @@ -100,7 +97,24 @@ function! s:Lint(buffer, should_lint_file, timer_id) abort " Use the filetype from the buffer let l:filetype = getbufvar(a:buffer, '&filetype') let l:linters = ale#linter#Get(l:filetype) - let l:linters = ale#linter#RemoveIgnored(a:buffer, l:filetype, l:linters) + + let l:ignore_config = ale#Var(a:buffer, 'linters_ignore') + let l:disable_lsp = ale#Var(a:buffer, 'disable_lsp') + + " Load code to ignore linters only if we need to. + if ( + \ !empty(l:ignore_config) + \ || l:disable_lsp is 1 + \ || l:disable_lsp is v:true + \ || (l:disable_lsp is# 'auto' && get(g:, 'lspconfig', 0)) + \) + let l:linters = ale#engine#ignore#Exclude( + \ l:filetype, + \ l:linters, + \ l:ignore_config, + \ l:disable_lsp, + \) + endif " Tell other sources that they can start checking the buffer now. let g:ale_want_results_buffer = a:buffer diff --git a/autoload/ale/code_action.vim b/autoload/ale/code_action.vim index db31aad5..4167e907 100644 --- a/autoload/ale/code_action.vim +++ b/autoload/ale/code_action.vim @@ -339,17 +339,7 @@ function! ale#code_action#GetCodeActions(options) abort silent! aunmenu PopUp.Refactor\.\.\. " Only display the menu items if there's an LSP server. - let l:has_lsp = 0 - - for l:linter in ale#linter#Get(&filetype) - if !empty(l:linter.lsp) - let l:has_lsp = 1 - - break - endif - endfor - - if l:has_lsp + if len(ale#lsp_linter#GetEnabled(bufnr(''))) > 0 if !empty(expand('')) silent! anoremenu PopUp.Rename :ALERename endif diff --git a/autoload/ale/codefix.vim b/autoload/ale/codefix.vim index 34ce3e15..6eaadb23 100644 --- a/autoload/ale/codefix.vim +++ b/autoload/ale/codefix.vim @@ -473,15 +473,9 @@ function! ale#codefix#Execute(range, ...) abort endif let l:MenuCallback = get(a:000, 0, v:null) - let l:lsp_linters = [] + let l:linters = ale#lsp_linter#GetEnabled(bufnr('')) - for l:linter in ale#linter#Get(&filetype) - if !empty(l:linter.lsp) - call add(l:lsp_linters, l:linter) - endif - endfor - - if empty(l:lsp_linters) + if empty(l:linters) if l:MenuCallback is v:null call s:message('No active LSPs') else @@ -491,7 +485,7 @@ function! ale#codefix#Execute(range, ...) abort return endif - for l:lsp_linter in l:lsp_linters - call s:ExecuteGetCodeFix(l:lsp_linter, a:range, l:MenuCallback) + for l:linter in l:linters + call s:ExecuteGetCodeFix(l:linter, a:range, l:MenuCallback) endfor endfunction diff --git a/autoload/ale/completion.vim b/autoload/ale/completion.vim index c05ca53d..4fd02721 100644 --- a/autoload/ale/completion.vim +++ b/autoload/ale/completion.vim @@ -824,6 +824,8 @@ endfunction " the current buffer. 1 will be returned if there's a potential source of " completion data ALE can use, and 0 will be returned otherwise. function! ale#completion#CanProvideCompletions() abort + " NOTE: We can report that ALE can provide completions to Deoplete from + " here, and we might ignore linters still below. for l:linter in ale#linter#Get(&filetype) if !empty(l:linter.lsp) return 1 @@ -890,11 +892,9 @@ function! ale#completion#GetCompletions(...) abort let l:started = 0 - for l:linter in ale#linter#Get(&filetype) - if !empty(l:linter.lsp) - if ale#lsp_linter#StartLSP(l:buffer, l:linter, l:Callback) - let l:started = 1 - endif + for l:linter in ale#lsp_linter#GetEnabled(l:buffer) + if ale#lsp_linter#StartLSP(l:buffer, l:linter, l:Callback) + let l:started = 1 endif endfor diff --git a/autoload/ale/debugging.vim b/autoload/ale/debugging.vim index 1becf332..89f5258f 100644 --- a/autoload/ale/debugging.vim +++ b/autoload/ale/debugging.vim @@ -201,6 +201,35 @@ function! s:EchoLSPErrorMessages(all_linter_names) abort endfor endfunction +function! s:GetIgnoredLinters(buffer, enabled_linters) abort + let l:filetype = &filetype + let l:ignore_config = ale#Var(a:buffer, 'linters_ignore') + let l:disable_lsp = ale#Var(a:buffer, 'disable_lsp') + + if ( + \ !empty(l:ignore_config) + \ || l:disable_lsp is 1 + \ || l:disable_lsp is v:true + \ || (l:disable_lsp is# 'auto' && get(g:, 'lspconfig', 0)) + \) + let l:non_ignored = ale#engine#ignore#Exclude( + \ l:filetype, + \ a:enabled_linters, + \ l:ignore_config, + \ l:disable_lsp, + \) + else + let l:non_ignored = copy(a:enabled_linters) + endif + + call map(l:non_ignored, 'v:val.name') + + return filter( + \ copy(a:enabled_linters), + \ 'index(l:non_ignored, v:val.name) < 0' + \) +endfunction + function! ale#debugging#Info(...) abort let l:options = (a:0 > 0) ? a:1 : {} let l:show_preview_info = get(l:options, 'preview') @@ -208,7 +237,6 @@ function! ale#debugging#Info(...) abort let l:buffer = bufnr('') let l:filetype = &filetype - " We get the list of enabled linters for free by the above function. let l:enabled_linters = deepcopy(ale#linter#Get(l:filetype)) " But have to build the list of available linters ourselves. @@ -232,13 +260,10 @@ function! ale#debugging#Info(...) abort let l:fixers = uniq(sort(l:fixers[0] + l:fixers[1])) let l:fixers_string = join(map(copy(l:fixers), '"\n " . v:val'), '') - let l:non_ignored_names = map( - \ copy(ale#linter#RemoveIgnored(l:buffer, l:filetype, l:enabled_linters)), - \ 'v:val[''name'']', - \) - let l:ignored_names = filter( - \ copy(l:enabled_names), - \ 'index(l:non_ignored_names, v:val) < 0' + " Get the names of ignored linters. + let l:ignored_names = map( + \ s:GetIgnoredLinters(l:buffer, l:enabled_linters), + \ 'v:val.name' \) call s:Echo(' Current Filetype: ' . l:filetype) diff --git a/autoload/ale/definition.vim b/autoload/ale/definition.vim index fd6cd2e1..251bdcc5 100644 --- a/autoload/ale/definition.vim +++ b/autoload/ale/definition.vim @@ -168,26 +168,20 @@ function! s:GoToLSPDefinition(linter, options, capability) abort endfunction function! ale#definition#GoTo(options) abort - for l:linter in ale#linter#Get(&filetype) - if !empty(l:linter.lsp) - call s:GoToLSPDefinition(l:linter, a:options, 'definition') - endif + for l:linter in ale#lsp_linter#GetEnabled(bufnr('')) + call s:GoToLSPDefinition(l:linter, a:options, 'definition') endfor endfunction function! ale#definition#GoToType(options) abort - for l:linter in ale#linter#Get(&filetype) - if !empty(l:linter.lsp) - call s:GoToLSPDefinition(l:linter, a:options, 'typeDefinition') - endif + for l:linter in ale#lsp_linter#GetEnabled(bufnr('')) + call s:GoToLSPDefinition(l:linter, a:options, 'typeDefinition') endfor endfunction function! ale#definition#GoToImpl(options) abort - for l:linter in ale#linter#Get(&filetype) - if !empty(l:linter.lsp) - call s:GoToLSPDefinition(l:linter, a:options, 'implementation') - endif + for l:linter in ale#lsp_linter#GetEnabled(bufnr('')) + call s:GoToLSPDefinition(l:linter, a:options, 'implementation') endfor endfunction diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim index 185d54db..7e337191 100644 --- a/autoload/ale/engine.vim +++ b/autoload/ale/engine.vim @@ -253,7 +253,7 @@ function! ale#engine#SendResultsToNeovimDiagnostics(buffer, loclist) abort " Keep the Lua surface area really small in the VimL part of ALE, " and just require the diagnostics.lua module on demand. - let l:SendDiagnostics = luaeval('require("diagnostics").sendAleResultsToDiagnostics') + let l:SendDiagnostics = luaeval('require("ale.diagnostics").sendAleResultsToDiagnostics') call l:SendDiagnostics(a:buffer, a:loclist) endfunction diff --git a/autoload/ale/engine/ignore.vim b/autoload/ale/engine/ignore.vim index 80574656..8ac36eb5 100644 --- a/autoload/ale/engine/ignore.vim +++ b/autoload/ale/engine/ignore.vim @@ -1,6 +1,26 @@ " Author: w0rp " Description: Code for ignoring linters. Only loaded and if configured. +" A map for remapping lspconfig server names to linter names or aliases in +" ALE. We should change the names where they will conflict with names in ALE. +" +" Notes on names from nvim-lspconfig not included here. +" +" * 'rubocop' is run in a language server mode +" * 'eslint' is run via 'vscode-eslint-language-server' +let s:lspconfig_map = { +\ 'als': 'adals', +\ 'ansiblels': 'ansible-language-server', +\ 'bicep': 'bicep_language_server', +\ 'cmake': 'cmake_language_server', +\ 'denols': 'deno', +\ 'erlangls': 'erlang_ls', +\ 'html': 'vscodehtml', +\ 'ocamlls': 'ocaml-language-server', +\ 'ols': 'odin-lsp', +\ 'puppet': 'puppet_languageserver', +\} + " Given a filetype and a configuration for ignoring linters, return a List of " Strings for linter names to ignore. function! ale#engine#ignore#GetList(filetype, config) abort @@ -21,24 +41,51 @@ function! ale#engine#ignore#GetList(filetype, config) abort return [] endfunction +" This function can be mocked in tests. +function! ale#engine#ignore#GetLSPConfigNames() abort + return luaeval('require ''ale.util''.configured_lspconfig_servers()') +endfunction + +function! s:GetMappedLSPConfigNames() abort + " Check the lspconfig flag before calling luaeval. + if !get(g:, 'lspconfig', 0) + return [] + endif + + let l:lspconfig_servers = ale#engine#ignore#GetLSPConfigNames() + + return map( + \ !empty(l:lspconfig_servers) ? l:lspconfig_servers : [], + \ {_, val -> get(s:lspconfig_map, val, val) } + \) +endfunction + " Given a List of linter descriptions, exclude the linters to be ignored. function! ale#engine#ignore#Exclude(filetype, all_linters, config, disable_lsp) abort let l:names_to_remove = ale#engine#ignore#GetList(a:filetype, a:config) + + " If configured to automatically ignore otherwise configured LSP linter + " names, add them to the names to remove. This could ignore linters + " with matching names that are not marked as LSP linters. + if a:disable_lsp is# 'auto' + call extend(l:names_to_remove, s:GetMappedLSPConfigNames()) + endif + + let l:ignore_all_lsps = a:disable_lsp is 1 || a:disable_lsp is v:true let l:filtered_linters = [] for l:linter in a:all_linters - let l:name_list = [l:linter.name] + l:linter.aliases - let l:should_include = 1 + let l:should_include = index(l:names_to_remove, l:linter.name) == -1 + let l:i = 0 - for l:name in l:name_list - if index(l:names_to_remove, l:name) >= 0 - let l:should_include = 0 - break - endif - endfor + while l:should_include && l:i < len(l:linter.aliases) + let l:name = l:linter.aliases[l:i] + let l:should_include = index(l:names_to_remove, l:name) == -1 + let l:i += 1 + endwhile - if a:disable_lsp && has_key(l:linter, 'lsp') && l:linter.lsp isnot# '' - let l:should_include = 0 + if l:should_include && l:ignore_all_lsps + let l:should_include = empty(get(l:linter, 'lsp')) endif if l:should_include diff --git a/autoload/ale/filerename.vim b/autoload/ale/filerename.vim index ec20d279..93cf78e1 100644 --- a/autoload/ale/filerename.vim +++ b/autoload/ale/filerename.vim @@ -94,9 +94,10 @@ function! s:ExecuteFileRename(linter, options) abort endfunction function! ale#filerename#Execute() abort + let l:buffer = bufnr('') let l:lsp_linters = [] - for l:linter in ale#linter#Get(&filetype) + for l:linter in ale#lsp_linter#GetEnabled(l:buffer) if l:linter.lsp is# 'tsserver' call add(l:lsp_linters, l:linter) endif @@ -108,7 +109,6 @@ function! ale#filerename#Execute() abort return endif - let l:buffer = bufnr('') let l:old_name = expand('#' . l:buffer . ':p') let l:new_name = ale#util#Input('New file name: ', l:old_name, 'file') diff --git a/autoload/ale/handlers/hdl_checker.vim b/autoload/ale/handlers/hdl_checker.vim index e871b083..d45f86e1 100644 --- a/autoload/ale/handlers/hdl_checker.vim +++ b/autoload/ale/handlers/hdl_checker.vim @@ -61,7 +61,8 @@ endfunction " Define the hdl_checker linter for a given filetype. function! ale#handlers#hdl_checker#DefineLinter(filetype) abort call ale#linter#Define(a:filetype, { - \ 'name': 'hdl-checker', + \ 'name': 'hdl_checker', + \ 'aliases': ['hdl-checker'], \ 'lsp': 'stdio', \ 'language': a:filetype, \ 'executable': function('ale#handlers#hdl_checker#GetExecutable'), @@ -70,4 +71,3 @@ function! ale#handlers#hdl_checker#DefineLinter(filetype) abort \ 'initialization_options': function('ale#handlers#hdl_checker#GetInitOptions'), \ }) endfunction - diff --git a/autoload/ale/hover.vim b/autoload/ale/hover.vim index 6ad43316..7070b86d 100644 --- a/autoload/ale/hover.vim +++ b/autoload/ale/hover.vim @@ -329,10 +329,9 @@ function! ale#hover#Show(buffer, line, col, opt) abort let l:show_documentation = get(a:opt, 'show_documentation', 0) let l:Callback = function('s:OnReady', [a:line, a:col, a:opt]) - for l:linter in ale#linter#Get(getbufvar(a:buffer, '&filetype')) + for l:linter in ale#lsp_linter#GetEnabled(a:buffer) " Only tsserver supports documentation requests at the moment. - if !empty(l:linter.lsp) - \&& (!l:show_documentation || l:linter.lsp is# 'tsserver') + if !l:show_documentation || l:linter.lsp is# 'tsserver' call ale#lsp_linter#StartLSP(a:buffer, l:linter, l:Callback) endif endfor diff --git a/autoload/ale/linter.vim b/autoload/ale/linter.vim index 73c47109..20104fbc 100644 --- a/autoload/ale/linter.vim +++ b/autoload/ale/linter.vim @@ -417,16 +417,6 @@ function! ale#linter#Get(original_filetypes) abort return reverse(l:combined_linters) endfunction -function! ale#linter#RemoveIgnored(buffer, filetype, linters) abort - " Apply ignore lists for linters only if needed. - let l:ignore_config = ale#Var(a:buffer, 'linters_ignore') - let l:disable_lsp = ale#Var(a:buffer, 'disable_lsp') - - return !empty(l:ignore_config) || l:disable_lsp - \ ? ale#engine#ignore#Exclude(a:filetype, a:linters, l:ignore_config, l:disable_lsp) - \ : a:linters -endfunction - " Given a buffer and linter, get the executable String for the linter. function! ale#linter#GetExecutable(buffer, linter) abort let l:Executable = a:linter.executable diff --git a/autoload/ale/lsp/reset.vim b/autoload/ale/lsp/reset.vim index 2fc7f0a2..85188b5a 100644 --- a/autoload/ale/lsp/reset.vim +++ b/autoload/ale/lsp/reset.vim @@ -15,6 +15,8 @@ function! ale#lsp#reset#StopAllLSPs() abort for l:buffer_string in keys(g:ale_buffer_info) let l:buffer = str2nr(l:buffer_string) + " Non-ignored and disabled linters are included here so we can + " clear results for them after we ignore or disable them. for l:linter in ale#linter#Get(getbufvar(l:buffer, '&filetype')) if !empty(l:linter.lsp) call ale#engine#HandleLoclist(l:linter.name, l:buffer, [], 0) diff --git a/autoload/ale/lsp_linter.vim b/autoload/ale/lsp_linter.vim index 1c98d628..05a0294c 100644 --- a/autoload/ale/lsp_linter.vim +++ b/autoload/ale/lsp_linter.vim @@ -27,28 +27,62 @@ function! ale#lsp_linter#SetLSPLinterMap(replacement_map) abort let s:lsp_linter_map = a:replacement_map endfunction -" Check if diagnostics for a particular linter should be ignored. -function! s:ShouldIgnore(buffer, linter_name) abort - " Ignore all diagnostics if LSP integration is disabled. - if ale#Var(a:buffer, 'disable_lsp') - return 1 - endif - - let l:config = ale#Var(a:buffer, 'linters_ignore') - - " Don't load code for ignoring diagnostics if there's nothing to ignore. - if empty(l:config) - return 0 - endif - +" Get all enabled LSP linters. +" This list still includes linters ignored with `ale_linters_ignore`. +" +" `ale_linters_ignore` is designed to allow language servers to be used for +" their functionality while ignoring the diagnostics they return. +function! ale#lsp_linter#GetEnabled(buffer) abort let l:filetype = getbufvar(a:buffer, '&filetype') - let l:ignore_list = ale#engine#ignore#GetList(l:filetype, l:config) + " Only LSP linters are included here. + let l:linters = filter(ale#linter#Get(l:filetype), '!empty(v:val.lsp)') + let l:disable_lsp = ale#Var(a:buffer, 'disable_lsp') - return index(l:ignore_list, a:linter_name) >= 0 + " Only load code for ignoring linters if we need it. + if ( + \ l:disable_lsp is 1 + \ || l:disable_lsp is v:true + \ || (l:disable_lsp is# 'auto' && get(g:, 'lspconfig', 0)) + \) + let l:linters = ale#engine#ignore#Exclude( + \ l:filetype, + \ l:linters, + \ [], + \ l:disable_lsp, + \) + endif + + return l:linters +endfunction + +" Check if diagnostics for a particular linter should be ignored. +function! s:ShouldIgnoreDiagnostics(buffer, linter) abort + let l:config = ale#Var(a:buffer, 'linters_ignore') + let l:disable_lsp = ale#Var(a:buffer, 'disable_lsp') + + " Only load code for ignoring linters if we need it. + if ( + \ !empty(l:config) + \ || l:disable_lsp is 1 + \ || l:disable_lsp is v:true + \ || (l:disable_lsp is# 'auto' && get(g:, 'lspconfig', 0)) + \) + " Re-use the ignore implementation just for this linter. + return empty( + \ ale#engine#ignore#Exclude( + \ getbufvar(a:buffer, '&filetype'), + \ [a:linter], + \ l:config, + \ l:disable_lsp, + \ ) + \) + endif + + return 0 endfunction function! s:HandleLSPDiagnostics(conn_id, response) abort - let l:linter_name = s:lsp_linter_map[a:conn_id] + let l:linter = s:lsp_linter_map[a:conn_id] let l:filename = ale#util#ToResource(a:response.params.uri) let l:escaped_name = escape( \ fnameescape(l:filename), @@ -61,17 +95,22 @@ function! s:HandleLSPDiagnostics(conn_id, response) abort return endif - if s:ShouldIgnore(l:buffer, l:linter_name) + if s:ShouldIgnoreDiagnostics(l:buffer, l:linter) return endif let l:loclist = ale#lsp#response#ReadDiagnostics(a:response) - call ale#engine#HandleLoclist(l:linter_name, l:buffer, l:loclist, 0) + call ale#engine#HandleLoclist(l:linter.name, l:buffer, l:loclist, 0) endfunction function! s:HandleTSServerDiagnostics(response, error_type) abort - let l:linter_name = 'tsserver' + " Re-create a fake linter object for tsserver. + let l:linter = { + \ 'name': 'tsserver', + \ 'aliases': [], + \ 'lsp': 'tsserver', + \} let l:escaped_name = escape( \ fnameescape(a:response.body.file), \ has('win32') ? '^' : '^,}]' @@ -83,9 +122,9 @@ function! s:HandleTSServerDiagnostics(response, error_type) abort return endif - call ale#engine#MarkLinterInactive(l:info, l:linter_name) + call ale#engine#MarkLinterInactive(l:info, l:linter.name) - if s:ShouldIgnore(l:buffer, l:linter_name) + if s:ShouldIgnoreDiagnostics(l:buffer, l:linter) return endif @@ -123,15 +162,15 @@ function! s:HandleTSServerDiagnostics(response, error_type) abort \ + get(l:info, 'suggestion_loclist', []) \ + get(l:info, 'syntax_loclist', []) - call ale#engine#HandleLoclist(l:linter_name, l:buffer, l:loclist, 0) + call ale#engine#HandleLoclist(l:linter.name, l:buffer, l:loclist, 0) endfunction -function! s:HandleLSPErrorMessage(linter_name, response) abort +function! s:HandleLSPErrorMessage(linter, response) abort if !g:ale_history_enabled || !g:ale_history_log_output return endif - if empty(a:linter_name) + if empty(a:linter) return endif @@ -141,7 +180,7 @@ function! s:HandleLSPErrorMessage(linter_name, response) abort return endif - call ale#lsp_linter#AddErrorMessage(a:linter_name, l:message) + call ale#lsp_linter#AddErrorMessage(a:linter.name, l:message) endfunction function! ale#lsp_linter#AddErrorMessage(linter_name, message) abort @@ -160,14 +199,14 @@ function! ale#lsp_linter#HandleLSPResponse(conn_id, response) abort let l:method = get(a:response, 'method', '') if get(a:response, 'jsonrpc', '') is# '2.0' && has_key(a:response, 'error') - let l:linter_name = get(s:lsp_linter_map, a:conn_id, '') + let l:linter = get(s:lsp_linter_map, a:conn_id, {}) - call s:HandleLSPErrorMessage(l:linter_name, a:response) + call s:HandleLSPErrorMessage(l:linter, a:response) elseif l:method is# 'textDocument/publishDiagnostics' call s:HandleLSPDiagnostics(a:conn_id, a:response) elseif l:method is# 'window/showMessage' call ale#lsp_window#HandleShowMessage( - \ s:lsp_linter_map[a:conn_id], + \ s:lsp_linter_map[a:conn_id].name, \ g:ale_lsp_show_message_format, \ a:response.params \) @@ -472,7 +511,7 @@ function! s:CheckWithLSP(linter, details) abort call ale#lsp#RegisterCallback(l:id, l:Callback) " Remember the linter this connection is for. - let s:lsp_linter_map[l:id] = a:linter.name + let s:lsp_linter_map[l:id] = a:linter if a:linter.lsp is# 'tsserver' let l:message = ale#lsp#tsserver_message#Geterr(l:buffer) diff --git a/autoload/ale/organize_imports.vim b/autoload/ale/organize_imports.vim index 395e6fbd..fb00bc21 100644 --- a/autoload/ale/organize_imports.vim +++ b/autoload/ale/organize_imports.vim @@ -57,9 +57,7 @@ function! s:OrganizeImports(linter) abort endfunction function! ale#organize_imports#Execute() abort - for l:linter in ale#linter#Get(&filetype) - if !empty(l:linter.lsp) - call s:OrganizeImports(l:linter) - endif + for l:linter in ale#lsp_linter#GetEnabled(bufnr('')) + call s:OrganizeImports(l:linter) endfor endfunction diff --git a/autoload/ale/references.vim b/autoload/ale/references.vim index c32663fe..df253c9c 100644 --- a/autoload/ale/references.vim +++ b/autoload/ale/references.vim @@ -179,9 +179,7 @@ function! ale#references#Find(...) abort let l:column = min([l:column, len(getline(l:line))]) let l:Callback = function('s:OnReady', [l:line, l:column, l:options]) - for l:linter in ale#linter#Get(&filetype) - if !empty(l:linter.lsp) - call ale#lsp_linter#StartLSP(l:buffer, l:linter, l:Callback) - endif + for l:linter in ale#lsp_linter#GetEnabled(l:buffer) + call ale#lsp_linter#StartLSP(l:buffer, l:linter, l:Callback) endfor endfunction diff --git a/autoload/ale/rename.vim b/autoload/ale/rename.vim index 53c9fb5c..d7bc8699 100644 --- a/autoload/ale/rename.vim +++ b/autoload/ale/rename.vim @@ -178,15 +178,9 @@ function! s:ExecuteRename(linter, options) abort endfunction function! ale#rename#Execute() abort - let l:lsp_linters = [] + let l:linters = ale#lsp_linter#GetEnabled(bufnr('')) - for l:linter in ale#linter#Get(&filetype) - if !empty(l:linter.lsp) - call add(l:lsp_linters, l:linter) - endif - endfor - - if empty(l:lsp_linters) + if empty(l:linters) call s:message('No active LSPs') return @@ -201,8 +195,8 @@ function! ale#rename#Execute() abort return endif - for l:lsp_linter in l:lsp_linters - call s:ExecuteRename(l:lsp_linter, { + for l:linter in l:linters + call s:ExecuteRename(l:linter, { \ 'old_name': l:old_name, \ 'new_name': l:new_name, \}) diff --git a/autoload/ale/symbol.vim b/autoload/ale/symbol.vim index 6c65f1b2..ba971e74 100644 --- a/autoload/ale/symbol.vim +++ b/autoload/ale/symbol.vim @@ -102,8 +102,8 @@ function! ale#symbol#Search(args) abort call setbufvar(l:buffer, 'ale_symbol_request_made', 0) let l:Callback = function('s:OnReady', [l:query, l:options]) - for l:linter in ale#linter#Get(getbufvar(l:buffer, '&filetype')) - if !empty(l:linter.lsp) && l:linter.lsp isnot# 'tsserver' + for l:linter in ale#lsp_linter#GetEnabled(l:buffer) + if l:linter.lsp isnot# 'tsserver' call ale#lsp_linter#StartLSP(l:buffer, l:linter, l:Callback) endif endfor diff --git a/autoload/ale/uri/jdt.vim b/autoload/ale/uri/jdt.vim index 46cea268..11a22d34 100644 --- a/autoload/ale/uri/jdt.vim +++ b/autoload/ale/uri/jdt.vim @@ -39,6 +39,8 @@ endfunction function! ale#uri#jdt#OpenJDTLink(encoded_uri, line, column, options, conn_id) abort let l:found_eclipselsp = v:false + " We should only arrive here from a 'go to definition' request, so we'll + " assume the eclipselsp linter is enabled. for l:linter in ale#linter#Get('java') if l:linter.name is# 'eclipselsp' let l:found_eclipselsp = v:true @@ -87,8 +89,8 @@ function! ale#uri#jdt#ReadJDTLink(encoded_uri) abort let l:linter_map = ale#lsp_linter#GetLSPLinterMap() - for l:conn_id in keys(l:linter_map) - if l:linter_map[l:conn_id] is# 'eclipselsp' + for [l:conn_id, l:linter] in items(l:linter_map) + if l:linter.name is# 'eclipselsp' let l:root = l:conn_id[stridx(l:conn_id, ':')+1:] endif endfor diff --git a/doc/ale.txt b/doc/ale.txt index 423204a3..211ac1c3 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -1002,11 +1002,16 @@ g:ale_detail_to_floating_preview *g:ale_detail_to_floating_preview* g:ale_disable_lsp *g:ale_disable_lsp* *b:ale_disable_lsp* - Type: |Number| - Default: `0` + Type: |Number| OR |String| + Default: `'auto'` - When this option is set to `1`, ALE ignores all linters powered by LSP, - and also `tsserver`. + When this option is set to `'auto'`, ALE will automatically disable linters + that it detects as having already been configured with the nvim-lspconfig + plugin. When this option is set to `1`, ALE ignores all linters powered by + LSP, and also `tsserver`. + + Any linters that are disabled will also not be usable for LSP functionality + other than just linting. Please see also |ale-lsp|. diff --git a/lspconfig.vim b/lspconfig.vim new file mode 100644 index 00000000..0e25fdc2 --- /dev/null +++ b/lspconfig.vim @@ -0,0 +1,3 @@ +if get(g:, 'lspconfig', 0) + " lspconfig is installed. +endif diff --git a/lua/diagnostics.lua b/lua/ale/diagnostics.lua similarity index 100% rename from lua/diagnostics.lua rename to lua/ale/diagnostics.lua diff --git a/lua/ale/util.lua b/lua/ale/util.lua new file mode 100644 index 00000000..98b3bbd9 --- /dev/null +++ b/lua/ale/util.lua @@ -0,0 +1,14 @@ +local M = {} + +function M.configured_lspconfig_servers() + local configs = require 'lspconfig.configs' + local keys = {} + + for key, _ in pairs(configs) do + table.insert(keys, key) + end + + return keys +end + +return M diff --git a/plugin/ale.vim b/plugin/ale.vim index 2a58707d..a721bd89 100644 --- a/plugin/ale.vim +++ b/plugin/ale.vim @@ -60,6 +60,10 @@ let g:ale_filetype_blacklist = [ let g:ale_linters = get(g:, 'ale_linters', {}) " This option can be changed to only enable explicitly selected linters. let g:ale_linters_explicit = get(g:, 'ale_linters_explicit', 0) +" Ignoring linters, for disabling some, or ignoring LSP diagnostics. +let g:ale_linters_ignore = get(g:, 'ale_linters_ignore', {}) +" Disabling all language server functionality. +let g:ale_disable_lsp = get(g:, 'ale_disable_lsp', 'auto') " This Dictionary configures which functions will be used for fixing problems. let g:ale_fixers = get(g:, 'ale_fixers', {}) diff --git a/test/linter/test_ansible_language_server.vader b/test/linter/test_ansible_language_server.vader index 3766972e..33634274 100644 --- a/test/linter/test_ansible_language_server.vader +++ b/test/linter/test_ansible_language_server.vader @@ -1,5 +1,5 @@ Before: - call ale#assert#SetUpLinterTest('ansible', 'ansible_language_server') + call ale#assert#SetUpLinterTest('ansible', 'language_server') After: call ale#assert#TearDownLinterTest() diff --git a/test/linter/test_elm_ls.vader b/test/linter/test_elm_ls.vader index 98b01c96..69ae2170 100644 --- a/test/linter/test_elm_ls.vader +++ b/test/linter/test_elm_ls.vader @@ -1,5 +1,5 @@ Before: - call ale#assert#SetUpLinterTest('elm', 'elm_ls') + call ale#assert#SetUpLinterTest('elm', 'ls') After: call ale#assert#TearDownLinterTest() diff --git a/test/lsp/test_engine_lsp_response_handling.vader b/test/lsp/test_engine_lsp_response_handling.vader index 50ceef4d..b00a6942 100644 --- a/test/lsp/test_engine_lsp_response_handling.vader +++ b/test/lsp/test_engine_lsp_response_handling.vader @@ -302,7 +302,7 @@ Execute(LSP diagnostics responses should be handled correctly): endif call ale#engine#InitBufferInfo(bufnr('')) - call ale#lsp_linter#SetLSPLinterMap({'1': 'eclipselsp'}) + call ale#lsp_linter#SetLSPLinterMap({'1': {'name': 'eclipselsp', 'aliases': [], 'lsp': 'stdio'}}) if has('win32') AssertEqual 'filename,[]^$.ts', expand('%:p:t') @@ -357,7 +357,7 @@ Execute(LSP diagnostics responses on project root should not populate loclist): runtime ale_linters/java/eclipselsp.vim call ale#test#SetFilename('filename.java') call ale#engine#InitBufferInfo(bufnr('')) - call ale#lsp_linter#SetLSPLinterMap({'1': 'eclipselsp'}) + call ale#lsp_linter#SetLSPLinterMap({'1': {'name': 'eclipselsp', 'aliases': [], 'lsp': 'stdio'}}) call ale#lsp_linter#HandleLSPResponse(1, { \ 'jsonrpc':'2.0', @@ -395,7 +395,7 @@ Execute(LSP errors should mark linters no longer active): runtime ale_linters/python/pylsp.vim call ale#test#SetFilename('filename.py') call ale#engine#InitBufferInfo(bufnr('')) - call ale#lsp_linter#SetLSPLinterMap({1: 'pylsp'}) + call ale#lsp_linter#SetLSPLinterMap({'1': {'name': 'pylsp', 'aliases': [], 'lsp': 'stdio'}}) let g:ale_buffer_info[bufnr('')].active_linter_list = ale#linter#Get('python') Assert !empty(g:ale_buffer_info[bufnr('')].active_linter_list) @@ -411,7 +411,7 @@ Execute(LSP errors should mark linters no longer active): AssertEqual [], g:ale_buffer_info[bufnr('')].active_linter_list Execute(LSP errors should be logged in the history): - call ale#lsp_linter#SetLSPLinterMap({'347': 'foobar'}) + call ale#lsp_linter#SetLSPLinterMap({'347': {'name': 'foobar', 'aliases': [], 'lsp': 'stdio'}}) call ale#lsp_linter#HandleLSPResponse(347, { \ 'jsonrpc': '2.0', \ 'error': { diff --git a/test/test_filetype_linter_defaults.vader b/test/test_filetype_linter_defaults.vader index 4de77bad..3f183f86 100644 --- a/test/test_filetype_linter_defaults.vader +++ b/test/test_filetype_linter_defaults.vader @@ -119,7 +119,7 @@ Execute(The defaults for the zsh filetype should be correct): Execute(The defaults for the verilog filetype should be correct): " This filetype isn't configured with default, so we can test loading all " available linters with this. - AssertEqual ['hdl-checker', 'iverilog', 'verilator', 'vlog', 'xvlog', 'yosys'], GetLinterNames('verilog') + AssertEqual ['hdl_checker', 'iverilog', 'verilator', 'vlog', 'xvlog', 'yosys'], GetLinterNames('verilog') let g:ale_linters_explicit = 1 diff --git a/test/test_ignoring_linters.vader b/test/test_ignoring_linters.vader index f97a0cf6..c84a8943 100644 --- a/test/test_ignoring_linters.vader +++ b/test/test_ignoring_linters.vader @@ -1,10 +1,60 @@ Before: + Save g:lspconfig + Save g:ale_linters_ignore + Save g:ale_buffer_info Save g:ale_disable_lsp + let g:lspconfig = 0 + let g:lspconfig_names = {} + let g:ale_disable_lsp = 0 + let g:linters = [] + let g:loclist = [] + let g:run_linters_called = 0 + + runtime autoload/ale/engine.vim + runtime autoload/ale/engine/ignore.vim + + " Mock the engine function so we can set it up. + function! ale#engine#RunLinters(buffer, linters, should_lint_file) abort + let g:linters = a:linters + let g:run_linters_called = 1 + endfunction + + function! ale#engine#HandleLoclist(linter_name, buffer, loclist, from_other_source) abort + let g:loclist = a:loclist + endfunction + + function! ale#engine#ignore#GetLSPConfigNames() abort + return g:lspconfig_names + endfunction + + call ale#linter#Define('foobar', { + \ 'name': 'testlinter', + \ 'callback': 'TestCallback', + \ 'executable': has('win32') ? 'cmd' : 'true', + \ 'command': has('win32') ? 'echo' : 'true', + \}) + call ale#test#SetDirectory('/testplugin/test') + After: + unlet! b:ale_linted + unlet! b:ale_linters_ignore + unlet! b:ale_quitting + unlet! b:ale_save_event_fired + unlet! b:ale_disable_lsp + unlet! g:linters + unlet! g:loclist + unlet! g:lsp_message + unlet! g:lspconfig_names + Restore - unlet! b:ale_disable_lsp + call ale#test#RestoreDirectory() + call ale#linter#Reset() + call ale#lsp_linter#ClearLSPData() + + runtime autoload/ale/engine.vim + runtime autoload/ale/engine/ignore.vim Execute(GetList should ignore some invalid values): AssertEqual [], ale#engine#ignore#GetList('', 'foo') @@ -105,7 +155,7 @@ Execute(Exclude should handle Dictionaries): \ 0, \ ) -Execute(Exclude should filter LSP linters when g:ale_disable_lsp is set to 1): +Execute(Exclude should filter LSP linters when ale_disable_lsp is set to 1): AssertEqual \ [ \ {'name': 'linter1', 'aliases': [], 'lsp': ''}, @@ -122,70 +172,81 @@ Execute(Exclude should filter LSP linters when g:ale_disable_lsp is set to 1): \ 1, \ ) -Execute(Exclude should filter LSP linters when b:ale_disable_lsp is set to 1): - AssertEqual - \ [ - \ {'name': 'linter1', 'aliases': [], 'lsp': ''}, - \ {'name': 'linter2', 'aliases': []}, - \ ], - \ ale#engine#ignore#Exclude( - \ 'foo', - \ [ - \ {'name': 'linter1', 'aliases': [], 'lsp': ''}, - \ {'name': 'linter2', 'aliases': []}, - \ {'name': 'linter3', 'aliases': [], 'lsp': 'stdio'}, - \ ], - \ [], - \ 1, - \ ) - -Before: - Save g:ale_linters_ignore - Save g:ale_buffer_info - Save g:ale_disable_lsp - - let g:ale_disable_lsp = 0 - - let g:linters = [] - let g:loclist = [] - let g:run_linters_called = 0 - - runtime autoload/ale/engine.vim - - " Mock the engine function so we can set it up. - function! ale#engine#RunLinters(buffer, linters, should_lint_file) abort - let g:linters = a:linters - let g:run_linters_called = 1 - endfunction - - function! ale#engine#HandleLoclist(linter_name, buffer, loclist, from_other_source) abort - let g:loclist = a:loclist - endfunction - - call ale#linter#Define('foobar', { - \ 'name': 'testlinter', - \ 'callback': 'TestCallback', - \ 'executable': has('win32') ? 'cmd' : 'true', - \ 'command': has('win32') ? 'echo' : 'true', +Execute(Exclude should remove lspconfig linters with ale_disable_lsp = 'auto'): + let g:lspconfig = 1 + " A map is used here so you can easily see what the ignore mapping should be + " remapping the nvim-lspconfig names to. + let g:lspconfig_names = keys({ + \ 'als': 'adals', + \ 'ansiblels': 'ansible-language-server', + \ 'bicep': 'bicep_language_server', + \ 'cmake': 'cmake_language_server', + \ 'denols': 'deno', + \ 'erlangls': 'erlang_ls', + \ 'html': 'vscodehtml', + \ 'ocamlls': 'ocaml-language-server', + \ 'puppet': 'puppet_languageserver', + \ 'pyright': 'pyright', \}) - call ale#test#SetDirectory('/testplugin/test') -After: - Restore + " We should keep bicep, as it's different tool. + AssertEqual + \ [ + \ {'name': 'bicep', 'aliases': []}, + \ ], + \ ale#engine#ignore#Exclude( + \ 'foo', + \ [ + \ {'name': 'adals', 'aliases': []}, + \ {'name': 'language_server', 'aliases': ['ansible-language-server']}, + \ {'name': 'cmake_language_server', 'aliases': []}, + \ {'name': 'deno', 'aliases': []}, + \ {'name': 'erlang_ls', 'aliases': []}, + \ {'name': 'vscodehtml', 'aliases': []}, + \ {'name': 'bicep', 'aliases': []}, + \ {'name': 'ols', 'aliases': ['ocaml-language-server']}, + \ {'name': 'languageserver', 'aliases': ['puppet_languageserver']}, + \ {'name': 'pyright', 'aliases': []}, + \ ], + \ [], + \ 'auto', + \ ) - unlet! b:ale_linted - unlet! b:ale_linters_ignore - unlet! b:ale_quitting - unlet! b:ale_save_event_fired - unlet! b:ale_disable_lsp - unlet! g:linters - unlet! g:loclist - unlet! g:lsp_message +Execute(Exclude should check that the nvim-lspconfig plugin is installed with ale_disable_lsp = 'auto'): + let g:lspconfig = 0 + let g:lspconfig_names = ['pyright'] - call ale#test#RestoreDirectory() - call ale#linter#Reset() - call ale#lsp_linter#ClearLSPData() - runtime autoload/ale/engine.vim + " We should keep pyright here, because g:lspconfig is 0. + AssertEqual + \ [ + \ {'name': 'pyright', 'aliases': []}, + \ ], + \ ale#engine#ignore#Exclude( + \ 'foo', + \ [ + \ {'name': 'pyright', 'aliases': []}, + \ ], + \ [], + \ 'auto', + \ ) + +Execute(Exclude should handle the lspconfig result being a Dictionary): + let g:lspconfig = 1 + let g:lspconfig_names = {} + + " We should keep pyright here, because the configuration is empty. + AssertEqual + \ [ + \ {'name': 'pyright', 'aliases': []}, + \ ], + \ ale#engine#ignore#Exclude( + \ 'foo', + \ [ + \ {'name': 'pyright', 'aliases': []}, + \ ], + \ [], + \ 'auto', + \ ) Given foobar(An empty file): Execute(Global ignore lists should be applied for linters): @@ -265,7 +326,7 @@ Execute(Buffer ignore lists should be applied for tsserver): Execute(Buffer ignore lists should be applied for LSP linters): call ale#test#SetFilename('filename.py') call ale#engine#InitBufferInfo(bufnr('')) - call ale#lsp_linter#SetLSPLinterMap({'347': 'lsplinter'}) + call ale#lsp_linter#SetLSPLinterMap({'347': {'name': 'lsplinter', 'aliases': [], 'lsp': 'stdio'}}) let g:lsp_message = { \ 'jsonrpc': '2.0', @@ -357,10 +418,63 @@ Execute(ale_disable_lsp should be applied for tsserver): AssertEqual [], g:loclist +Execute(ale_disable_lsp = 'auto' should be applied for tsserver): + let g:lspconfig = 1 + let g:lspconfig_names = ['tsserver'] + + call ale#test#SetFilename('filename.ts') + call ale#engine#InitBufferInfo(bufnr('')) + + let g:lsp_message = { + \ 'seq': 0, + \ 'type': 'event', + \ 'event': 'syntaxDiag', + \ 'body': { + \ 'file': g:dir . '/filename.ts', + \ 'diagnostics':[ + \ { + \ 'start': { + \ 'line':2, + \ 'offset':14, + \ }, + \ 'end': { + \ 'line':2, + \ 'offset':15, + \ }, + \ 'text': ''','' expected.', + \ "code":1005 + \ }, + \ ], + \ }, + \} + + call ale#lsp_linter#HandleLSPResponse(347, g:lsp_message) + + AssertEqual + \ [ + \ { + \ 'lnum': 2, + \ 'col': 14, + \ 'nr': 1005, + \ 'code': '1005', + \ 'type': 'E', + \ 'end_col': 14, + \ 'end_lnum': 2, + \ 'text': ''','' expected.', + \ }, + \ ], + \ g:loclist + + let g:loclist = [] + let g:ale_disable_lsp = 'auto' + call ale#lsp_linter#HandleLSPResponse(347, g:lsp_message) + + AssertEqual [], g:loclist + Execute(ale_disable_lsp should be applied for LSP linters): call ale#test#SetFilename('filename.py') call ale#engine#InitBufferInfo(bufnr('')) - call ale#lsp_linter#SetLSPLinterMap({'347': 'lsplinter'}) + call ale#lsp_linter#SetLSPLinterMap({'347': {'name': 'lsplinter', 'aliases': [], 'lsp': 'stdio'}}) let g:lsp_message = { \ 'jsonrpc': '2.0', @@ -401,3 +515,99 @@ Execute(ale_disable_lsp should be applied for LSP linters): call ale#lsp_linter#HandleLSPResponse(347, g:lsp_message) AssertEqual [], g:loclist + +Execute(ale_disable_lsp = 'auto' should be applied for LSP linters): + let g:lspconfig = 1 + let g:lspconfig_names = ['lsplinter'] + + call ale#test#SetFilename('filename.py') + call ale#engine#InitBufferInfo(bufnr('')) + call ale#lsp_linter#SetLSPLinterMap({'347': {'name': 'lsplinter', 'aliases': [], 'lsp': 'stdio'}}) + + let g:lsp_message = { + \ 'jsonrpc': '2.0', + \ 'method': 'textDocument/publishDiagnostics', + \ 'params': { + \ 'uri': ale#path#ToFileURI(expand('%:p')), + \ 'diagnostics': [ + \ { + \ 'severity': 1, + \ 'message': 'x', + \ 'range': { + \ 'start': {'line': 0, 'character': 9}, + \ 'end': {'line': 0, 'character': 9}, + \ }, + \ } + \ ], + \ }, + \} + + call ale#lsp_linter#HandleLSPResponse(347, g:lsp_message) + + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'col': 10, + \ 'type': 'E', + \ 'end_col': 9, + \ 'end_lnum': 1, + \ 'text': 'x', + \ } + \ ], + \ g:loclist + + let g:ale_disable_lsp = 'auto' + let g:loclist = [] + + call ale#lsp_linter#HandleLSPResponse(347, g:lsp_message) + + AssertEqual [], g:loclist + +Execute(ale_disable_lsp = 'auto' should ignore LSP linters by alias too): + let g:lspconfig = 1 + let g:lspconfig_names = ['lsplinter'] + + call ale#test#SetFilename('filename.py') + call ale#engine#InitBufferInfo(bufnr('')) + call ale#lsp_linter#SetLSPLinterMap({'347': {'name': 'notthis', 'aliases': ['lsplinter'], 'lsp': 'stdio'}}) + + let g:lsp_message = { + \ 'jsonrpc': '2.0', + \ 'method': 'textDocument/publishDiagnostics', + \ 'params': { + \ 'uri': ale#path#ToFileURI(expand('%:p')), + \ 'diagnostics': [ + \ { + \ 'severity': 1, + \ 'message': 'x', + \ 'range': { + \ 'start': {'line': 0, 'character': 9}, + \ 'end': {'line': 0, 'character': 9}, + \ }, + \ } + \ ], + \ }, + \} + + call ale#lsp_linter#HandleLSPResponse(347, g:lsp_message) + + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'col': 10, + \ 'type': 'E', + \ 'end_col': 9, + \ 'end_lnum': 1, + \ 'text': 'x', + \ } + \ ], + \ g:loclist + + let g:ale_disable_lsp = 'auto' + let g:loclist = [] + + call ale#lsp_linter#HandleLSPResponse(347, g:lsp_message) + + AssertEqual [], g:loclist