diagnostics: support sending ALE output to Neovim's diagnostics API (#4345)

Support replacing ALE's display of problems with sending problems to the Neovim diagnostics API.

:help g:ale_use_neovim_diagnostics_api

Co-authored-by: David Balatero <dbalatero@users.noreply.github.com>
Co-authored-by: Georgi Angelchev <angelchev@live.co.uk>
Co-authored-by: w0rp <devw0rp@gmail.com>
This commit is contained in:
Ben Boeckel
2023-01-29 17:25:09 +00:00
committed by GitHub
parent 0af4899605
commit 116d713f63
9 changed files with 137 additions and 12 deletions

View File

@@ -55,6 +55,7 @@ let s:global_variable_list = [
\ 'ale_sign_highlight_linenrs', \ 'ale_sign_highlight_linenrs',
\ 'ale_statusline_format', \ 'ale_statusline_format',
\ 'ale_type_map', \ 'ale_type_map',
\ 'ale_use_neovim_diagnostics_api',
\ 'ale_use_global_executables', \ 'ale_use_global_executables',
\ 'ale_virtualtext_cursor', \ 'ale_virtualtext_cursor',
\ 'ale_warn_about_trailing_blank_lines', \ 'ale_warn_about_trailing_blank_lines',

View File

@@ -184,9 +184,13 @@ endfunction
function! ale#engine#SetResults(buffer, loclist) abort function! ale#engine#SetResults(buffer, loclist) abort
let l:linting_is_done = !ale#engine#IsCheckingBuffer(a:buffer) let l:linting_is_done = !ale#engine#IsCheckingBuffer(a:buffer)
if g:ale_use_neovim_diagnostics_api
call ale#engine#SendResultsToNeovimDiagnostics(a:buffer, a:loclist)
endif
" Set signs first. This could potentially fix some line numbers. " Set signs first. This could potentially fix some line numbers.
" The List could be sorted again here by SetSigns. " The List could be sorted again here by SetSigns.
if g:ale_set_signs if !g:ale_use_neovim_diagnostics_api && g:ale_set_signs
call ale#sign#SetSigns(a:buffer, a:loclist) call ale#sign#SetSigns(a:buffer, a:loclist)
endif endif
@@ -199,11 +203,12 @@ function! ale#engine#SetResults(buffer, loclist) abort
call ale#statusline#Update(a:buffer, a:loclist) call ale#statusline#Update(a:buffer, a:loclist)
endif endif
if g:ale_set_highlights if !g:ale_use_neovim_diagnostics_api && g:ale_set_highlights
call ale#highlight#SetHighlights(a:buffer, a:loclist) call ale#highlight#SetHighlights(a:buffer, a:loclist)
endif endif
if g:ale_virtualtext_cursor is# 'all' || g:ale_virtualtext_cursor == 2 if !g:ale_use_neovim_diagnostics_api
\&& (g:ale_virtualtext_cursor is# 'all' || g:ale_virtualtext_cursor == 2)
call ale#virtualtext#SetTexts(a:buffer, a:loclist) call ale#virtualtext#SetTexts(a:buffer, a:loclist)
endif endif
@@ -214,7 +219,8 @@ function! ale#engine#SetResults(buffer, loclist) abort
call ale#cursor#EchoCursorWarning() call ale#cursor#EchoCursorWarning()
endif endif
if g:ale_virtualtext_cursor is# 'current' || g:ale_virtualtext_cursor == 1 if !g:ale_use_neovim_diagnostics_api
\&& (g:ale_virtualtext_cursor is# 'current' || g:ale_virtualtext_cursor == 1)
" Try and show the warning now. " Try and show the warning now.
" This will only do something meaningful if we're in normal mode. " This will only do something meaningful if we're in normal mode.
call ale#virtualtext#ShowCursorWarning() call ale#virtualtext#ShowCursorWarning()
@@ -238,6 +244,19 @@ function! ale#engine#SetResults(buffer, loclist) abort
endif endif
endfunction endfunction
function! ale#engine#SendResultsToNeovimDiagnostics(buffer, loclist) abort
if !has('nvim-0.6')
" We will warn the user on startup as well if they try to set
" g:ale_use_neovim_diagnostics_api outside of a Neovim context.
return
endif
" 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')
call l:SendDiagnostics(a:buffer, a:loclist)
endfunction
function! s:RemapItemTypes(type_map, loclist) abort function! s:RemapItemTypes(type_map, loclist) abort
for l:item in a:loclist for l:item in a:loclist
let l:key = l:item.type let l:key = l:item.type

View File

@@ -187,10 +187,8 @@ function! ale#virtualtext#ShowCursorWarning(...) abort
let l:buffer = bufnr('') let l:buffer = bufnr('')
if mode(1) isnot# 'n' if mode(1) isnot# 'n'
return \|| g:ale_use_neovim_diagnostics_api
endif \|| ale#ShouldDoNothing(l:buffer)
if ale#ShouldDoNothing(l:buffer)
return return
endif endif
@@ -210,12 +208,13 @@ function! ale#virtualtext#ShowCursorWarningWithDelay() abort
return return
endif endif
call s:StopCursorTimer()
if mode(1) isnot# 'n' if mode(1) isnot# 'n'
\|| g:ale_use_neovim_diagnostics_api
return return
endif endif
call s:StopCursorTimer()
let l:pos = getpos('.')[0:2] let l:pos = getpos('.')[0:2]
" Check the current buffer, line, and column number against the last " Check the current buffer, line, and column number against the last

View File

@@ -121,6 +121,7 @@ circumstances.
ALE will report problems with your code in the following ways, listed with ALE will report problems with your code in the following ways, listed with
their relevant options. their relevant options.
* Via the Neovim diagnostics API (Off by default) - |g:ale_use_neovim_diagnostics_api|
* By updating loclist. (On by default) - |g:ale_set_loclist| * By updating loclist. (On by default) - |g:ale_set_loclist|
* By updating quickfix. (Off by default) - |g:ale_set_quickfix| * By updating quickfix. (Off by default) - |g:ale_set_quickfix|
* By setting error highlights. - |g:ale_set_highlights| * By setting error highlights. - |g:ale_set_highlights|
@@ -2288,6 +2289,26 @@ g:ale_use_global_executables *g:ale_use_global_executables*
options. options.
g:ale_use_neovim_diagnostics_api *g:ale_use_neovim_diagnostics_api*
Type: |Number|
Default: `0`
If enabled, this option will disable ALE's standard UI, and instead send
all linter output to Neovim's diagnostics API. This allows you to collect
errors from nvim-lsp, ALE, and anything else that uses diagnostics all in
one place. The following options are ignored when using the diagnostics API:
- |g:ale_set_highlights|
- |g:ale_set_signs|
- |g:ale_virtualtext_cursor|
To enable this option, set the value to `1`.
This option requires Neovim 0.6+, as that version introduces the diagnostics
API.
g:ale_virtualtext_cursor *g:ale_virtualtext_cursor* g:ale_virtualtext_cursor *g:ale_virtualtext_cursor*
Type: |Number| Type: |Number|

49
lua/diagnostics.lua Normal file
View File

@@ -0,0 +1,49 @@
local module = {}
local ale_type_to_diagnostic_severity = {
E = vim.diagnostic.severity.ERROR,
W = vim.diagnostic.severity.WARN,
I = vim.diagnostic.severity.INFO
}
module.sendAleResultsToDiagnostics = function(buffer, loclist)
local diagnostics = {}
-- Convert all the ALE loclist items to the shape that Neovim's diagnostic
-- API is expecting.
for _, location in ipairs(loclist) do
table.insert(
diagnostics,
-- All line numbers from ALE are 1-indexed, but all line numbers
-- in the diagnostics API are 0-indexed, so we have to subtract 1
-- to make this work.
{
lnum = location.lnum - 1,
-- Ending line number, or if we don't have one, just make it the same
-- as the starting line number
end_lnum = (location.end_lnum or location.lnum) - 1,
-- Which column does the error start on?
col = math.max((location.col or 1) - 1, 0),
-- end_col does *not* appear to need 1 subtracted, so we don't.
end_col = location.end_col,
-- Which severity: error, warning, or info?
severity = ale_type_to_diagnostic_severity[location.type] or "E",
-- The error message
message = location.text,
-- e.g. "rubocop"
source = location.linter_name,
}
)
end
local virtualtext_enabled_set = {['all'] = true, ['2'] = true, ['current'] = true, ['1'] = true}
vim.diagnostic.set(
vim.api.nvim_create_namespace('ale'),
buffer,
diagnostics,
{ virtual_text = virtualtext_enabled_set[vim.g.ale_virtualtext_cursor] ~= nil}
)
end
return module

View File

@@ -187,6 +187,15 @@ let g:ale_deno_executable = get(g:, 'ale_deno_executable', 'deno')
" If 1, enable a popup menu for commands. " If 1, enable a popup menu for commands.
let g:ale_popup_menu_enabled = get(g:, 'ale_popup_menu_enabled', has('gui_running')) let g:ale_popup_menu_enabled = get(g:, 'ale_popup_menu_enabled', has('gui_running'))
" If 1, disables ALE's built in error display. Instead, all errors are piped
" to the diagnostics API.
let g:ale_use_neovim_diagnostics_api = get(g:, 'ale_use_neovim_diagnostics_api', 0)
if g:ale_use_neovim_diagnostics_api && !has('nvim-0.6')
" no-custom-checks
echoerr('Setting g:ale_use_neovim_diagnostics_api to 1 requires Neovim 0.6+.')
endif
if g:ale_set_balloons is 1 || g:ale_set_balloons is# 'hover' if g:ale_set_balloons is 1 || g:ale_set_balloons is# 'hover'
call ale#balloon#Enable() call ale#balloon#Enable()
endif endif

View File

@@ -11,6 +11,7 @@ Before:
Save g:ale_set_quickfix Save g:ale_set_quickfix
Save g:ale_set_signs Save g:ale_set_signs
Save g:ale_command_wrapper Save g:ale_command_wrapper
Save g:ale_use_neovim_diagnostics_api
let g:ale_command_wrapper = '' let g:ale_command_wrapper = ''
let g:ale_buffer_info = {} let g:ale_buffer_info = {}
@@ -22,6 +23,7 @@ Before:
let g:ale_set_loclist = 0 let g:ale_set_loclist = 0
let g:ale_set_highlights = 0 let g:ale_set_highlights = 0
let g:ale_echo_cursor = 0 let g:ale_echo_cursor = 0
let g:ale_use_neovim_diagnostics_api = 0
call ale#sign#Clear() call ale#sign#Clear()
@@ -74,3 +76,10 @@ Execute(The signs should be updated after linting is done):
call ale#test#FlushJobs() call ale#test#FlushJobs()
AssertEqual [['1', 'ALEWarningSign'], ['2', 'ALEErrorSign']], CollectSigns() AssertEqual [['1', 'ALEWarningSign'], ['2', 'ALEErrorSign']], CollectSigns()
Execute(Signs should not be set when diagnostics API integration is enabled):
let g:ale_use_neovim_diagnostics_api = 1
ALELint
call ale#test#FlushJobs()
AssertEqual [], CollectSigns()

View File

@@ -52,6 +52,7 @@ Before:
Save g:ale_sign_highlight_linenrs Save g:ale_sign_highlight_linenrs
Save g:ale_statusline_format Save g:ale_statusline_format
Save g:ale_type_map Save g:ale_type_map
Save g:ale_use_neovim_diagnostics_api
Save g:ale_use_global_executables Save g:ale_use_global_executables
Save g:ale_virtualtext_cursor Save g:ale_virtualtext_cursor
Save g:ale_warn_about_trailing_blank_lines Save g:ale_warn_about_trailing_blank_lines
@@ -110,6 +111,7 @@ Before:
let g:ale_sign_highlight_linenrs = 0 let g:ale_sign_highlight_linenrs = 0
let g:ale_statusline_format = ['%d error(s)', '%d warning(s)', 'OK'] let g:ale_statusline_format = ['%d error(s)', '%d warning(s)', 'OK']
let g:ale_type_map = {} let g:ale_type_map = {}
let g:ale_use_neovim_diagnostics_api = 0
let g:ale_use_global_executables = v:null let g:ale_use_global_executables = v:null
let g:ale_virtualtext_cursor = 'disabled' let g:ale_virtualtext_cursor = 'disabled'
let g:ale_warn_about_trailing_blank_lines = 1 let g:ale_warn_about_trailing_blank_lines = 1
@@ -189,6 +191,7 @@ Before:
\ 'let g:ale_sign_highlight_linenrs = 0', \ 'let g:ale_sign_highlight_linenrs = 0',
\ 'let g:ale_statusline_format = [''%d error(s)'', ''%d warning(s)'', ''OK'']', \ 'let g:ale_statusline_format = [''%d error(s)'', ''%d warning(s)'', ''OK'']',
\ 'let g:ale_type_map = {}', \ 'let g:ale_type_map = {}',
\ 'let g:ale_use_neovim_diagnostics_api = 0',
\ 'let g:ale_use_global_executables = v:null', \ 'let g:ale_use_global_executables = v:null',
\ 'let g:ale_virtualtext_cursor = ''disabled''', \ 'let g:ale_virtualtext_cursor = ''disabled''',
\ 'let g:ale_warn_about_trailing_blank_lines = 1', \ 'let g:ale_warn_about_trailing_blank_lines = 1',

View File

@@ -4,6 +4,7 @@ Before:
Save g:ale_virtualtext_delay Save g:ale_virtualtext_delay
Save g:ale_virtualtext_prefix Save g:ale_virtualtext_prefix
Save b:ale_virtualtext_prefix Save b:ale_virtualtext_prefix
Save g:ale_use_neovim_diagnostics_api
call ale#virtualtext#ResetDataForTests() call ale#virtualtext#ResetDataForTests()
@@ -36,6 +37,7 @@ Before:
\ ], \ ],
\ }, \ },
\} \}
let g:ale_use_neovim_diagnostics_api = 0
After: After:
Restore Restore
@@ -177,3 +179,16 @@ Execute(We should set errors across all lines):
\ map(prop_list(2), {_, v -> v.type}) \ map(prop_list(2), {_, v -> v.type})
endif endif
endif endif
Execute(We should not set cursor messages when Neovim diagnostics are enabled):
let g:ale_use_neovim_diagnostics_api = 1
if has('patch-9.0.0297') || has('nvim-0.8.0')
let g:ale_virtualtext_cursor = 'current'
call cursor(1, 1)
call ale#virtualtext#ShowCursorWarningWithDelay()
" Tick the timer.
sleep 1ms
AssertEqual '', ale#virtualtext#GetLastMessageForTests()
endif