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:
w0rp
2025-03-23 14:37:04 +00:00
parent 26ffb9dfa3
commit f90e72ae1f
9 changed files with 248 additions and 27 deletions

View File

@@ -47,6 +47,7 @@ function! ale#lsp#Register(executable_or_address, project, language, init_option
\ 'definition': 0, \ 'definition': 0,
\ 'typeDefinition': 0, \ 'typeDefinition': 0,
\ 'implementation': 0, \ 'implementation': 0,
\ 'pull_model': 0,
\ 'symbol_search': 0, \ 'symbol_search': 0,
\ 'code_actions': 0, \ 'code_actions': 0,
\ 'did_save': 0, \ 'did_save': 0,
@@ -276,6 +277,13 @@ function! ale#lsp#UpdateCapabilities(conn_id, capabilities) abort
let l:conn.capabilities.implementation = 1 let l:conn.capabilities.implementation = 1
endif 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 if get(a:capabilities, 'workspaceSymbolProvider') is v:true
let l:conn.capabilities.symbol_search = 1 let l:conn.capabilities.symbol_search = 1
endif endif
@@ -486,6 +494,10 @@ function! s:SendInitMessage(conn) abort
\ 'dynamicRegistration': v:false, \ 'dynamicRegistration': v:false,
\ 'linkSupport': v:false, \ 'linkSupport': v:false,
\ }, \ },
\ 'diagnostic': {
\ 'dynamicRegistration': v:true,
\ 'relatedDocumentSupport': v:true,
\ },
\ 'publishDiagnostics': { \ 'publishDiagnostics': {
\ 'relatedInformation': v:true, \ 'relatedInformation': v:true,
\ }, \ },

View File

@@ -200,6 +200,14 @@ function! ale#lsp#message#CodeAction(buffer, line, column, end_line, end_column,
\}] \}]
endfunction 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 function! ale#lsp#message#ExecuteCommand(command, arguments) abort
return [0, 'workspace/executeCommand', { return [0, 'workspace/executeCommand', {
\ 'command': a:command, \ 'command': a:command,

View File

@@ -21,11 +21,11 @@ let s:SEVERITY_WARNING = 2
let s:SEVERITY_INFORMATION = 3 let s:SEVERITY_INFORMATION = 3
let s:SEVERITY_HINT = 4 let s:SEVERITY_HINT = 4
" Parse the message for textDocument/publishDiagnostics " Convert Diagnostic[] data from a language server to an ALE loclist.
function! ale#lsp#response#ReadDiagnostics(response) abort function! ale#lsp#response#ReadDiagnostics(diagnostics) abort
let l:loclist = [] 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:severity = get(l:diagnostic, 'severity', 0)
let l:loclist_item = { let l:loclist_item = {
\ 'text': substitute(l:diagnostic.message, '\(\r\n\|\n\|\r\)', ' ', 'g'), \ 'text': substitute(l:diagnostic.message, '\(\r\n\|\n\|\r\)', ' ', 'g'),

View File

@@ -23,6 +23,26 @@ function! ale#lsp_linter#SetLSPLinterMap(replacement_map) abort
let s:lsp_linter_map = a:replacement_map let s:lsp_linter_map = a:replacement_map
endfunction 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. " Get all enabled LSP linters.
" This list still includes linters ignored with `ale_linters_ignore`. " This list still includes linters ignored with `ale_linters_ignore`.
" "
@@ -77,14 +97,17 @@ function! s:ShouldIgnoreDiagnostics(buffer, linter) abort
return 0 return 0
endfunction 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) let l:linter = get(s:lsp_linter_map, a:conn_id)
if empty(l:linter) if empty(l:linter)
return return
endif endif
let l:filename = ale#util#ToResource(a:response.params.uri) let l:filename = ale#util#ToResource(a:uri)
let l:escaped_name = escape( let l:escaped_name = escape(
\ fnameescape(l:filename), \ fnameescape(l:filename),
\ has('win32') ? '^' : '^,}]' \ has('win32') ? '^' : '^,}]'
@@ -100,9 +123,12 @@ function! s:HandleLSPDiagnostics(conn_id, response) abort
return return
endif endif
let l:loclist = ale#lsp#response#ReadDiagnostics(a:response) if a:diagnostics is# 'unchanged'
call ale#engine#MarkLinterInactive(l:info, l:linter)
call ale#engine#HandleLoclist(l:linter.name, l:buffer, l:loclist, 0) else
let l:loclist = ale#lsp#response#ReadDiagnostics(a:diagnostics)
call ale#engine#HandleLoclist(l:linter.name, l:buffer, l:loclist, 0)
endif
endfunction endfunction
function! s:HandleTSServerDiagnostics(response, error_type) abort 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) call s:HandleLSPErrorMessage(l:linter, a:response)
elseif l:method is# 'textDocument/publishDiagnostics' 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' elseif l:method is# 'window/showMessage'
call ale#lsp_window#HandleShowMessage( call ale#lsp_window#HandleShowMessage(
\ s:lsp_linter_map[a:conn_id].name, \ 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:save_message = ale#lsp#message#DidSave(l:buffer, l:include_text)
let l:notified = ale#lsp#Send(l:id, l:save_message) != 0 let l:notified = ale#lsp#Send(l:id, l:save_message) != 0
endif 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 endif
endfunction endfunction

View File

@@ -410,6 +410,130 @@ Execute(LSP errors should mark linters no longer active):
AssertEqual [], g:ale_buffer_info[bufnr('')].active_linter_list AssertEqual [], g:ale_buffer_info[bufnr('')].active_linter_list
Execute(LSP pull model diagnostic responses should be handled):
let b:ale_linters = ['eclipselsp']
runtime ale_linters/java/eclipselsp.vim
if has('win32')
call ale#test#SetFilename('filename,[]^$.ts')
else
call ale#test#SetFilename('filename*?,{}[]^$.java')
endif
call ale#engine#InitBufferInfo(bufnr(''))
let g:ale_buffer_info[bufnr('')].active_linter_list = ale#linter#Get('eclipselsp')
call ale#lsp_linter#SetLSPLinterMap({'1': {'name': 'eclipselsp', 'aliases': [], 'lsp': 'stdio'}})
call ale#lsp_linter#SetDiagnosticURIMap({'347': ale#util#ToURI(expand('%:p'))})
if has('win32')
AssertEqual 'filename,[]^$.ts', expand('%:p:t')
else
AssertEqual 'filename*?,{}[]^$.java', expand('%:p:t')
endif
call ale#lsp_linter#HandleLSPResponse(1, {
\ 'jsonrpc':'2.0',
\ 'id': 347,
\ 'result': {
\ 'kind': 'full',
\ 'items': [
\ {
\ 'range': {
\ 'start': {
\ 'line': 0,
\ 'character':0
\ },
\ 'end': {
\ 'line': 0,
\ 'character':0
\ }
\ },
\ 'severity': 2,
\ 'code': "",
\ 'source': 'Java',
\ 'message': 'Missing JRE 1-8'
\ }
\ ]
\ },
\})
AssertEqual
\ [
\ {
\ 'lnum': 1,
\ 'bufnr': bufnr(''),
\ 'col': 1,
\ 'pattern': '',
\ 'valid': 1,
\ 'vcol': 0,
\ 'nr': -1,
\ 'type': 'W',
\ 'text': 'Missing JRE 1-8'
\ }
\ ],
\ ale#test#GetLoclistWithoutNewerKeys()
AssertEqual [], g:ale_buffer_info[bufnr('')].active_linter_list
Execute(LSP pull model diagnostic responses that are 'unchanged' should be handled):
let b:ale_linters = ['eclipselsp']
runtime ale_linters/java/eclipselsp.vim
if has('win32')
call ale#test#SetFilename('filename,[]^$.ts')
else
call ale#test#SetFilename('filename*?,{}[]^$.java')
endif
call ale#engine#InitBufferInfo(bufnr(''))
let g:ale_buffer_info[bufnr('')].active_linter_list = ale#linter#Get('eclipselsp')
let g:ale_buffer_info[bufnr('')].loclist = [
\ {
\ 'lnum': 1,
\ 'bufnr': bufnr(''),
\ 'col': 1,
\ 'pattern': '',
\ 'valid': 1,
\ 'vcol': 0,
\ 'nr': -1,
\ 'type': 'W',
\ 'text': 'Missing JRE 1-8'
\ },
\]
call ale#lsp_linter#SetLSPLinterMap({'1': {'name': 'eclipselsp', 'aliases': [], 'lsp': 'stdio'}})
call ale#lsp_linter#SetDiagnosticURIMap({'347': ale#util#ToURI(expand('%:p'))})
if has('win32')
AssertEqual 'filename,[]^$.ts', expand('%:p:t')
else
AssertEqual 'filename*?,{}[]^$.java', expand('%:p:t')
endif
call ale#lsp_linter#HandleLSPResponse(1, {
\ 'jsonrpc':'2.0',
\ 'id': 347,
\ 'result': {
\ 'kind': 'unchanged',
\ },
\})
AssertEqual
\ [
\ {
\ 'lnum': 1,
\ 'bufnr': bufnr(''),
\ 'col': 1,
\ 'pattern': '',
\ 'valid': 1,
\ 'vcol': 0,
\ 'nr': -1,
\ 'type': 'W',
\ 'text': 'Missing JRE 1-8'
\ }
\ ],
\ g:ale_buffer_info[bufnr('')].loclist
AssertEqual [], g:ale_buffer_info[bufnr('')].active_linter_list
Execute(LSP errors should be logged in the history): Execute(LSP errors should be logged in the history):
call ale#lsp_linter#SetLSPLinterMap({'347': {'name': 'foobar', 'aliases': [], 'lsp': 'stdio'}}) call ale#lsp_linter#SetLSPLinterMap({'347': {'name': 'foobar', 'aliases': [], 'lsp': 'stdio'}})
call ale#lsp_linter#HandleLSPResponse(347, { call ale#lsp_linter#HandleLSPResponse(347, {

View File

@@ -248,6 +248,19 @@ Execute(ale#lsp#message#DidChangeConfiguration() should return correct messages)
\ ], \ ],
\ ale#lsp#message#DidChangeConfiguration(bufnr(''), g:ale_lsp_configuration) \ ale#lsp#message#DidChangeConfiguration(bufnr(''), g:ale_lsp_configuration)
Execute(ale#lsp#message#Diagnostic() should return correct messages):
AssertEqual
\ [
\ 0,
\ 'textDocument/diagnostic',
\ {
\ 'textDocument': {
\ 'uri': ale#path#ToFileURI(g:dir . '/foo/bar.ts'),
\ },
\ }
\ ],
\ ale#lsp#message#Diagnostic(bufnr(''))
Execute(ale#lsp#tsserver_message#Open() should return correct messages): Execute(ale#lsp#tsserver_message#Open() should return correct messages):
AssertEqual AssertEqual
\ [ \ [

View File

@@ -193,6 +193,10 @@ Before:
\ 'dynamicRegistration': v:false, \ 'dynamicRegistration': v:false,
\ 'linkSupport': v:false, \ 'linkSupport': v:false,
\ }, \ },
\ 'diagnostic': {
\ 'dynamicRegistration': v:true,
\ 'relatedDocumentSupport': v:true,
\ },
\ 'publishDiagnostics': { \ 'publishDiagnostics': {
\ 'relatedInformation': v:true, \ 'relatedInformation': v:true,
\ }, \ },

View File

@@ -90,6 +90,7 @@ Execute(Capabilities should be set up correctly):
\ 'includeText': 0, \ 'includeText': 0,
\ 'references': 1, \ 'references': 1,
\ 'rename': 1, \ 'rename': 1,
\ 'pull_model': 0,
\ 'symbol_search': 1, \ 'symbol_search': 1,
\ 'typeDefinition': 0, \ 'typeDefinition': 0,
\ }, \ },
@@ -122,6 +123,7 @@ Execute(Disabled capabilities should be recognised correctly):
\ 'definitionProvider': v:false, \ 'definitionProvider': v:false,
\ 'experimental': {}, \ 'experimental': {},
\ 'documentHighlightProvider': v:true, \ 'documentHighlightProvider': v:true,
\ 'diagnosticProvider': {},
\ }, \ },
\ }, \ },
\}) \})
@@ -140,6 +142,7 @@ Execute(Disabled capabilities should be recognised correctly):
\ 'includeText': 0, \ 'includeText': 0,
\ 'references': 0, \ 'references': 0,
\ 'rename': 0, \ 'rename': 0,
\ 'pull_model': 0,
\ 'symbol_search': 0, \ 'symbol_search': 0,
\ 'typeDefinition': 0, \ 'typeDefinition': 0,
\ }, \ },
@@ -182,6 +185,9 @@ Execute(Capabilities should be enabled when sent as Dictionaries):
\ 'implementationProvider': {}, \ 'implementationProvider': {},
\ 'experimental': {}, \ 'experimental': {},
\ 'documentHighlightProvider': v:true, \ 'documentHighlightProvider': v:true,
\ 'diagnosticProvider': {
\ 'interFileDependencies': v:false,
\ },
\ 'workspaceSymbolProvider': {} \ 'workspaceSymbolProvider': {}
\ }, \ },
\ }, \ },
@@ -201,6 +207,7 @@ Execute(Capabilities should be enabled when sent as Dictionaries):
\ 'includeText': 1, \ 'includeText': 1,
\ 'references': 1, \ 'references': 1,
\ 'rename': 1, \ 'rename': 1,
\ 'pull_model': 1,
\ 'symbol_search': 1, \ 'symbol_search': 1,
\ 'typeDefinition': 1, \ 'typeDefinition': 1,
\ }, \ },

View File

@@ -21,14 +21,14 @@ Execute(ale#lsp#response#ReadDiagnostics() should handle errors):
\ 'code': 'some-error', \ 'code': 'some-error',
\ } \ }
\ ], \ ],
\ ale#lsp#response#ReadDiagnostics({'params': {'uri': 'filename.ts', 'diagnostics': [ \ ale#lsp#response#ReadDiagnostics([
\ { \ {
\ 'severity': 1, \ 'severity': 1,
\ 'range': Range(2, 10, 4, 15), \ 'range': Range(2, 10, 4, 15),
\ 'code': 'some-error', \ 'code': 'some-error',
\ 'message': 'Something went wrong!', \ 'message': 'Something went wrong!',
\ }, \ },
\ ]}}) \ ])
Execute(ale#lsp#response#ReadDiagnostics() should handle warnings): Execute(ale#lsp#response#ReadDiagnostics() should handle warnings):
AssertEqual [ AssertEqual [
@@ -42,14 +42,14 @@ Execute(ale#lsp#response#ReadDiagnostics() should handle warnings):
\ 'code': 'some-warning', \ 'code': 'some-warning',
\ } \ }
\ ], \ ],
\ ale#lsp#response#ReadDiagnostics({'params': {'uri': 'filename.ts', 'diagnostics': [ \ ale#lsp#response#ReadDiagnostics([
\ { \ {
\ 'severity': 2, \ 'severity': 2,
\ 'range': Range(1, 3, 1, 3), \ 'range': Range(1, 3, 1, 3),
\ 'code': 'some-warning', \ 'code': 'some-warning',
\ 'message': 'Something went wrong!', \ 'message': 'Something went wrong!',
\ }, \ },
\ ]}}) \ ])
Execute(ale#lsp#response#ReadDiagnostics() should treat messages with missing severity as errors): Execute(ale#lsp#response#ReadDiagnostics() should treat messages with missing severity as errors):
AssertEqual [ AssertEqual [
@@ -63,13 +63,13 @@ Execute(ale#lsp#response#ReadDiagnostics() should treat messages with missing se
\ 'code': 'some-error', \ 'code': 'some-error',
\ } \ }
\ ], \ ],
\ ale#lsp#response#ReadDiagnostics({'params': {'uri': 'filename.ts', 'diagnostics': [ \ ale#lsp#response#ReadDiagnostics([
\ { \ {
\ 'range': Range(2, 10, 4, 15), \ 'range': Range(2, 10, 4, 15),
\ 'code': 'some-error', \ 'code': 'some-error',
\ 'message': 'Something went wrong!', \ 'message': 'Something went wrong!',
\ }, \ },
\ ]}}) \ ])
Execute(ale#lsp#response#ReadDiagnostics() should handle messages without codes): Execute(ale#lsp#response#ReadDiagnostics() should handle messages without codes):
AssertEqual [ AssertEqual [
@@ -82,12 +82,12 @@ Execute(ale#lsp#response#ReadDiagnostics() should handle messages without codes)
\ 'end_col': 15, \ 'end_col': 15,
\ } \ }
\ ], \ ],
\ ale#lsp#response#ReadDiagnostics({'params': {'uri': 'filename.ts', 'diagnostics': [ \ ale#lsp#response#ReadDiagnostics([
\ { \ {
\ 'range': Range(2, 10, 4, 15), \ 'range': Range(2, 10, 4, 15),
\ 'message': 'Something went wrong!', \ 'message': 'Something went wrong!',
\ }, \ },
\ ]}}) \ ])
Execute(ale#lsp#response#ReadDiagnostics() should include sources in detail): Execute(ale#lsp#response#ReadDiagnostics() should include sources in detail):
AssertEqual [ AssertEqual [
@@ -101,13 +101,13 @@ Execute(ale#lsp#response#ReadDiagnostics() should include sources in detail):
\ 'end_col': 22, \ 'end_col': 22,
\ } \ }
\ ], \ ],
\ ale#lsp#response#ReadDiagnostics({'params': {'uri': 'filename.ts', 'diagnostics': [ \ ale#lsp#response#ReadDiagnostics([
\ { \ {
\ 'range': Range(9, 14, 11, 22), \ 'range': Range(9, 14, 11, 22),
\ 'message': 'Something went wrong!', \ 'message': 'Something went wrong!',
\ 'source': 'tslint', \ 'source': 'tslint',
\ } \ }
\ ]}}) \ ])
Execute(ale#lsp#response#ReadDiagnostics() should keep detail with line breaks but replace with spaces in text): Execute(ale#lsp#response#ReadDiagnostics() should keep detail with line breaks but replace with spaces in text):
AssertEqual [ AssertEqual [
@@ -121,13 +121,13 @@ Execute(ale#lsp#response#ReadDiagnostics() should keep detail with line breaks b
\ 'end_col': 22, \ 'end_col': 22,
\ } \ }
\ ], \ ],
\ ale#lsp#response#ReadDiagnostics({'params': {'uri': 'filename.ts', 'diagnostics': [ \ ale#lsp#response#ReadDiagnostics([
\ { \ {
\ 'range': Range(9, 14, 11, 22), \ 'range': Range(9, 14, 11, 22),
\ 'message': "cannot borrow `cap` as mutable\r\nmore than once at a time\n\nmutable borrow starts here\rin previous iteration of loop", \ 'message': "cannot borrow `cap` as mutable\r\nmore than once at a time\n\nmutable borrow starts here\rin previous iteration of loop",
\ 'source': 'rustc', \ 'source': 'rustc',
\ } \ }
\ ]}}) \ ])
Execute(ale#lsp#response#ReadDiagnostics() should consider -1 to be a meaningless code): Execute(ale#lsp#response#ReadDiagnostics() should consider -1 to be a meaningless code):
AssertEqual [ AssertEqual [
@@ -140,13 +140,13 @@ Execute(ale#lsp#response#ReadDiagnostics() should consider -1 to be a meaningles
\ 'end_col': 15, \ 'end_col': 15,
\ } \ }
\ ], \ ],
\ ale#lsp#response#ReadDiagnostics({'params': {'uri': 'filename.ts', 'diagnostics': [ \ ale#lsp#response#ReadDiagnostics([
\ { \ {
\ 'range': Range(2, 10, 4, 15), \ 'range': Range(2, 10, 4, 15),
\ 'message': 'Something went wrong!', \ 'message': 'Something went wrong!',
\ 'code': -1, \ 'code': -1,
\ }, \ },
\ ]}}) \ ])
Execute(ale#lsp#response#ReadDiagnostics() should handle multiple messages): Execute(ale#lsp#response#ReadDiagnostics() should handle multiple messages):
AssertEqual [ AssertEqual [
@@ -167,7 +167,7 @@ Execute(ale#lsp#response#ReadDiagnostics() should handle multiple messages):
\ 'end_col': 4, \ 'end_col': 4,
\ }, \ },
\ ], \ ],
\ ale#lsp#response#ReadDiagnostics({'params': {'uri': 'filename.ts', 'diagnostics': [ \ ale#lsp#response#ReadDiagnostics([
\ { \ {
\ 'range': Range(0, 2, 0, 2), \ 'range': Range(0, 2, 0, 2),
\ 'message': 'Something went wrong!', \ 'message': 'Something went wrong!',
@@ -177,7 +177,7 @@ Execute(ale#lsp#response#ReadDiagnostics() should handle multiple messages):
\ 'range': Range(1, 4, 1, 4), \ 'range': Range(1, 4, 1, 4),
\ 'message': 'A warning', \ 'message': 'A warning',
\ }, \ },
\ ]}}) \ ])
Execute(ale#lsp#response#ReadDiagnostics() should use relatedInformation for detail): Execute(ale#lsp#response#ReadDiagnostics() should use relatedInformation for detail):
AssertEqual [ AssertEqual [
@@ -191,7 +191,7 @@ Execute(ale#lsp#response#ReadDiagnostics() should use relatedInformation for det
\ 'detail': "Something went wrong!\n/tmp/someotherfile.txt:43:80:\n\tmight be this" \ 'detail': "Something went wrong!\n/tmp/someotherfile.txt:43:80:\n\tmight be this"
\ } \ }
\ ], \ ],
\ ale#lsp#response#ReadDiagnostics({'params': {'uri': 'filename.ts', 'diagnostics': [ \ ale#lsp#response#ReadDiagnostics([
\ { \ {
\ 'range': Range(0, 2, 0, 2), \ 'range': Range(0, 2, 0, 2),
\ 'message': 'Something went wrong!', \ 'message': 'Something went wrong!',
@@ -206,7 +206,7 @@ Execute(ale#lsp#response#ReadDiagnostics() should use relatedInformation for det
\ } \ }
\ }] \ }]
\ } \ }
\ ]}}) \ ])
Execute(ale#lsp#response#ReadTSServerDiagnostics() should handle tsserver responses): Execute(ale#lsp#response#ReadTSServerDiagnostics() should handle tsserver responses):
AssertEqual AssertEqual