mirror of
https://github.com/dense-analysis/ale.git
synced 2026-01-06 11:33:30 +08:00
Close #3600 - Implement pull diagnostics in VimL
Implement pull diagnostics in the VimL implementation so ALE is able to track when servers are busy checking files. Only servers that support this feature will return diagnostics these ways.
This commit is contained in:
@@ -47,6 +47,7 @@ function! ale#lsp#Register(executable_or_address, project, language, init_option
|
||||
\ 'definition': 0,
|
||||
\ 'typeDefinition': 0,
|
||||
\ 'implementation': 0,
|
||||
\ 'pull_model': 0,
|
||||
\ 'symbol_search': 0,
|
||||
\ 'code_actions': 0,
|
||||
\ 'did_save': 0,
|
||||
@@ -276,6 +277,13 @@ function! ale#lsp#UpdateCapabilities(conn_id, capabilities) abort
|
||||
let l:conn.capabilities.implementation = 1
|
||||
endif
|
||||
|
||||
" Check if the language server supports pull model diagnostics.
|
||||
if type(get(a:capabilities, 'diagnosticProvider')) is v:t_dict
|
||||
if type(get(a:capabilities.diagnosticProvider, 'interFileDependencies')) is v:t_bool
|
||||
let l:conn.capabilities.pull_model = 1
|
||||
endif
|
||||
endif
|
||||
|
||||
if get(a:capabilities, 'workspaceSymbolProvider') is v:true
|
||||
let l:conn.capabilities.symbol_search = 1
|
||||
endif
|
||||
@@ -486,6 +494,10 @@ function! s:SendInitMessage(conn) abort
|
||||
\ 'dynamicRegistration': v:false,
|
||||
\ 'linkSupport': v:false,
|
||||
\ },
|
||||
\ 'diagnostic': {
|
||||
\ 'dynamicRegistration': v:true,
|
||||
\ 'relatedDocumentSupport': v:true,
|
||||
\ },
|
||||
\ 'publishDiagnostics': {
|
||||
\ 'relatedInformation': v:true,
|
||||
\ },
|
||||
|
||||
@@ -200,6 +200,14 @@ function! ale#lsp#message#CodeAction(buffer, line, column, end_line, end_column,
|
||||
\}]
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#message#Diagnostic(buffer) abort
|
||||
return [0, 'textDocument/diagnostic', {
|
||||
\ 'textDocument': {
|
||||
\ 'uri': ale#util#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ },
|
||||
\}]
|
||||
endfunction
|
||||
|
||||
function! ale#lsp#message#ExecuteCommand(command, arguments) abort
|
||||
return [0, 'workspace/executeCommand', {
|
||||
\ 'command': a:command,
|
||||
|
||||
@@ -21,11 +21,11 @@ let s:SEVERITY_WARNING = 2
|
||||
let s:SEVERITY_INFORMATION = 3
|
||||
let s:SEVERITY_HINT = 4
|
||||
|
||||
" Parse the message for textDocument/publishDiagnostics
|
||||
function! ale#lsp#response#ReadDiagnostics(response) abort
|
||||
" Convert Diagnostic[] data from a language server to an ALE loclist.
|
||||
function! ale#lsp#response#ReadDiagnostics(diagnostics) abort
|
||||
let l:loclist = []
|
||||
|
||||
for l:diagnostic in a:response.params.diagnostics
|
||||
for l:diagnostic in a:diagnostics
|
||||
let l:severity = get(l:diagnostic, 'severity', 0)
|
||||
let l:loclist_item = {
|
||||
\ 'text': substitute(l:diagnostic.message, '\(\r\n\|\n\|\r\)', ' ', 'g'),
|
||||
|
||||
@@ -23,6 +23,26 @@ function! ale#lsp_linter#SetLSPLinterMap(replacement_map) abort
|
||||
let s:lsp_linter_map = a:replacement_map
|
||||
endfunction
|
||||
|
||||
" A map for tracking URIs for diagnostic request IDs
|
||||
if !has_key(s:, 'diagnostic_uri_map')
|
||||
let s:diagnostic_uri_map = {}
|
||||
endif
|
||||
|
||||
" For internal use only.
|
||||
function! ale#lsp_linter#ClearDiagnosticURIMap() abort
|
||||
let s:diagnostic_uri_map = {}
|
||||
endfunction
|
||||
|
||||
" For internal use only.
|
||||
function! ale#lsp_linter#GetDiagnosticURIMap() abort
|
||||
return s:diagnostic_uri_map
|
||||
endfunction
|
||||
|
||||
" Just for tests.
|
||||
function! ale#lsp_linter#SetDiagnosticURIMap(replacement_map) abort
|
||||
let s:diagnostic_uri_map = a:replacement_map
|
||||
endfunction
|
||||
|
||||
" Get all enabled LSP linters.
|
||||
" This list still includes linters ignored with `ale_linters_ignore`.
|
||||
"
|
||||
@@ -77,14 +97,17 @@ function! s:ShouldIgnoreDiagnostics(buffer, linter) abort
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
function! s:HandleLSPDiagnostics(conn_id, response) abort
|
||||
" Handle LSP diagnostics for a given URI.
|
||||
" The special value 'unchanged' can be used for diagnostics to indicate
|
||||
" that diagnostics haven't changed since we last checked.
|
||||
function! s:HandleLSPDiagnostics(conn_id, uri, diagnostics) abort
|
||||
let l:linter = get(s:lsp_linter_map, a:conn_id)
|
||||
|
||||
if empty(l:linter)
|
||||
return
|
||||
endif
|
||||
|
||||
let l:filename = ale#util#ToResource(a:response.params.uri)
|
||||
let l:filename = ale#util#ToResource(a:uri)
|
||||
let l:escaped_name = escape(
|
||||
\ fnameescape(l:filename),
|
||||
\ has('win32') ? '^' : '^,}]'
|
||||
@@ -100,9 +123,12 @@ function! s:HandleLSPDiagnostics(conn_id, response) abort
|
||||
return
|
||||
endif
|
||||
|
||||
let l:loclist = ale#lsp#response#ReadDiagnostics(a:response)
|
||||
|
||||
call ale#engine#HandleLoclist(l:linter.name, l:buffer, l:loclist, 0)
|
||||
if a:diagnostics is# 'unchanged'
|
||||
call ale#engine#MarkLinterInactive(l:info, l:linter)
|
||||
else
|
||||
let l:loclist = ale#lsp#response#ReadDiagnostics(a:diagnostics)
|
||||
call ale#engine#HandleLoclist(l:linter.name, l:buffer, l:loclist, 0)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:HandleTSServerDiagnostics(response, error_type) abort
|
||||
@@ -204,7 +230,17 @@ function! ale#lsp_linter#HandleLSPResponse(conn_id, response) abort
|
||||
|
||||
call s:HandleLSPErrorMessage(l:linter, a:response)
|
||||
elseif l:method is# 'textDocument/publishDiagnostics'
|
||||
call s:HandleLSPDiagnostics(a:conn_id, a:response)
|
||||
let l:uri = a:response.params.uri
|
||||
let l:diagnostics = a:response.params.diagnostics
|
||||
|
||||
call s:HandleLSPDiagnostics(a:conn_id, l:uri, l:diagnostics)
|
||||
elseif has_key(s:diagnostic_uri_map, get(a:response, 'id'))
|
||||
let l:uri = remove(s:diagnostic_uri_map, a:response.id)
|
||||
let l:diagnostics = a:response.result.kind is# 'unchanged'
|
||||
\ ? 'unchanged'
|
||||
\ : a:response.result.items
|
||||
|
||||
call s:HandleLSPDiagnostics(a:conn_id, l:uri, l:diagnostics)
|
||||
elseif l:method is# 'window/showMessage'
|
||||
call ale#lsp_window#HandleShowMessage(
|
||||
\ s:lsp_linter_map[a:conn_id].name,
|
||||
@@ -530,6 +566,23 @@ function! s:CheckWithLSP(linter, details) abort
|
||||
let l:save_message = ale#lsp#message#DidSave(l:buffer, l:include_text)
|
||||
let l:notified = ale#lsp#Send(l:id, l:save_message) != 0
|
||||
endif
|
||||
|
||||
let l:diagnostic_request_id = 0
|
||||
|
||||
" If the document is updated and we can pull diagnostics, try to.
|
||||
if ale#lsp#HasCapability(l:id, 'pull_model')
|
||||
let l:diagnostic_message = ale#lsp#message#Diagnostic(l:buffer)
|
||||
|
||||
let l:diagnostic_request_id = ale#lsp#Send(l:id, l:diagnostic_message)
|
||||
endif
|
||||
|
||||
" If we are going to pull diagnostics, then mark the linter as active,
|
||||
" and remember the URI we sent the request for.
|
||||
if l:diagnostic_request_id
|
||||
call ale#engine#MarkLinterActive(l:info, a:linter)
|
||||
let s:diagnostic_uri_map[l:diagnostic_request_id] =
|
||||
\ l:diagnostic_message[2].textDocument.uri
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
||||
Reference in New Issue
Block a user