diff --git a/autoload/ale.vim b/autoload/ale.vim index dddb41db..f6c23d72 100644 --- a/autoload/ale.vim +++ b/autoload/ale.vim @@ -94,6 +94,11 @@ function! s:Lint(buffer, should_lint_file, timer_id) abort \ ? ale#engine#ignore#Exclude(l:filetype, l:linters, l:ignore_config) \ : l:linters + " Tell other sources that they can start checking the buffer now. + let g:ale_want_results_buffer = a:buffer + silent doautocmd User ALEWantResults + unlet! g:ale_want_results_buffer + " Don't set up buffer data and so on if there are no linters to run. if !has_key(g:ale_buffer_info, a:buffer) && empty(l:linters) return diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim index 05db0e49..e85e4d66 100644 --- a/autoload/ale/engine.vim +++ b/autoload/ale/engine.vim @@ -79,6 +79,7 @@ function! ale#engine#InitBufferInfo(buffer) abort let g:ale_buffer_info[a:buffer] = { \ 'job_list': [], \ 'active_linter_list': [], + \ 'active_other_sources_list': [], \ 'loclist': [], \ 'temporary_file_list': [], \ 'temporary_directory_list': [], @@ -97,6 +98,7 @@ function! ale#engine#IsCheckingBuffer(buffer) abort let l:info = get(g:ale_buffer_info, a:buffer, {}) return !empty(get(l:info, 'active_linter_list', [])) + \ || !empty(get(l:info, 'active_other_sources_list', [])) endfunction " Register a temporary file to be managed with the ALE engine for @@ -177,20 +179,27 @@ function! s:GatherOutput(job_id, line) abort endif endfunction -function! ale#engine#HandleLoclist(linter_name, buffer, loclist) abort +function! ale#engine#HandleLoclist(linter_name, buffer, loclist, from_other_source) abort let l:info = get(g:ale_buffer_info, a:buffer, {}) if empty(l:info) return endif - " Remove this linter from the list of active linters. - " This may have already been done when the job exits. - call filter(l:info.active_linter_list, 'v:val isnot# a:linter_name') + if !a:from_other_source + " Remove this linter from the list of active linters. + " This may have already been done when the job exits. + call filter(l:info.active_linter_list, 'v:val isnot# a:linter_name') + endif " Make some adjustments to the loclists to fix common problems, and also " to set default values for loclist items. - let l:linter_loclist = ale#engine#FixLocList(a:buffer, a:linter_name, a:loclist) + let l:linter_loclist = ale#engine#FixLocList( + \ a:buffer, + \ a:linter_name, + \ a:from_other_source, + \ a:loclist, + \) " Remove previous items for this linter. call filter(l:info.loclist, 'v:val.linter_name isnot# a:linter_name') @@ -263,7 +272,7 @@ function! s:HandleExit(job_id, exit_code) abort let l:loclist = [] endtry - call ale#engine#HandleLoclist(l:linter.name, l:buffer, l:loclist) + call ale#engine#HandleLoclist(l:linter.name, l:buffer, l:loclist, 0) endfunction function! ale#engine#SetResults(buffer, loclist) abort @@ -335,7 +344,7 @@ function! s:RemapItemTypes(type_map, loclist) abort endfor endfunction -function! ale#engine#FixLocList(buffer, linter_name, loclist) abort +function! ale#engine#FixLocList(buffer, linter_name, from_other_source, loclist) abort let l:bufnr_map = {} let l:new_loclist = [] @@ -368,6 +377,10 @@ function! ale#engine#FixLocList(buffer, linter_name, loclist) abort \ 'linter_name': a:linter_name, \} + if a:from_other_source + let l:item.from_other_source = 1 + endif + if has_key(l:old_item, 'code') let l:item.code = l:old_item.code endif @@ -691,6 +704,7 @@ endfunction function! s:RemoveProblemsForDisabledLinters(buffer, linters) abort " Figure out which linters are still enabled, and remove " problems for linters which are no longer enabled. + " Problems from other sources will be kept. let l:name_map = {} for l:linter in a:linters @@ -699,7 +713,7 @@ function! s:RemoveProblemsForDisabledLinters(buffer, linters) abort call filter( \ get(g:ale_buffer_info[a:buffer], 'loclist', []), - \ 'get(l:name_map, get(v:val, ''linter_name''))', + \ 'get(v:val, ''from_other_source'') || get(l:name_map, get(v:val, ''linter_name''))', \) endfunction diff --git a/autoload/ale/lsp/reset.vim b/autoload/ale/lsp/reset.vim index c7c97a47..2fc7f0a2 100644 --- a/autoload/ale/lsp/reset.vim +++ b/autoload/ale/lsp/reset.vim @@ -17,7 +17,7 @@ function! ale#lsp#reset#StopAllLSPs() abort 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, []) + call ale#engine#HandleLoclist(l:linter.name, l:buffer, [], 0) endif endfor endfor diff --git a/autoload/ale/lsp_linter.vim b/autoload/ale/lsp_linter.vim index 55190483..fa4d2f86 100644 --- a/autoload/ale/lsp_linter.vim +++ b/autoload/ale/lsp_linter.vim @@ -38,7 +38,7 @@ function! s:HandleLSPDiagnostics(conn_id, response) abort let l:loclist = ale#lsp#response#ReadDiagnostics(a:response) - call ale#engine#HandleLoclist(l:linter_name, l:buffer, l:loclist) + call ale#engine#HandleLoclist(l:linter_name, l:buffer, l:loclist, 0) endfunction function! s:HandleTSServerDiagnostics(response, error_type) abort @@ -81,7 +81,7 @@ function! s:HandleTSServerDiagnostics(response, error_type) abort let l:loclist = get(l:info, 'semantic_loclist', []) \ + get(l:info, 'syntax_loclist', []) - call ale#engine#HandleLoclist(l:linter_name, l:buffer, l:loclist) + call ale#engine#HandleLoclist(l:linter_name, l:buffer, l:loclist, 0) endfunction function! s:HandleLSPErrorMessage(linter_name, response) abort diff --git a/autoload/ale/other_source.vim b/autoload/ale/other_source.vim new file mode 100644 index 00000000..1a092034 --- /dev/null +++ b/autoload/ale/other_source.vim @@ -0,0 +1,21 @@ +" Tell ALE that another source has started checking a buffer. +function! ale#other_source#StartChecking(buffer, linter_name) abort + call ale#engine#InitBufferInfo(a:buffer) + let l:list = g:ale_buffer_info[a:buffer].active_other_sources_list + + call add(l:list, a:linter_name) + call uniq(sort(l:list)) +endfunction + +" Show some results, and stop checking a buffer. +" To clear results or cancel checking a buffer, an empty List can be given. +function! ale#other_source#ShowResults(buffer, linter_name, loclist) abort + call ale#engine#InitBufferInfo(a:buffer) + let l:info = g:ale_buffer_info[a:buffer] + + " Remove this linter name from the active list. + let l:list = l:info.active_other_sources_list + call filter(l:list, 'v:val isnot# a:linter_name') + + call ale#engine#HandleLoclist(a:linter_name, a:buffer, a:loclist, 1) +endfunction diff --git a/doc/ale.txt b/doc/ale.txt index e190cf2a..2a8f17d4 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -9,6 +9,7 @@ CONTENTS *ale-contents* 1. Introduction.........................|ale-introduction| 2. Supported Languages & Tools..........|ale-support| 3. Linting..............................|ale-lint| + 3.1 Other Sources.....................|ale-lint-other-sources| 4. Fixing Problems......................|ale-fix| 5. Language Server Protocol Support.....|ale-lsp| 5.1 Completion........................|ale-completion| @@ -574,6 +575,68 @@ ALE offers several options for controlling which linters are run. * Only running linters you asked for. - |g:ale_linters_explicit| +------------------------------------------------------------------------------- +3.1 Other Sources *ale-lint-other-sources* + +Problems for a buffer can be taken from other sources and rendered by ALE. +This allows ALE to be used in combination with other plugins which also want +to display any problems they might find with a buffer. ALE's API includes the +following components for making this possible. + +* |ale#other_source#StartChecking()| - Tell ALE that a buffer is being checked. +* |ale#other_source#ShowResults()| - Show results from another source. +* |ALEWantResults| - A signal for when ALE wants results. + +Other resources can provide results for ALE to display at any time, following +ALE's loclist format. (See |ale-loclist-format|) For example: > + + " Tell ALE to show some results. + " This function can be called at any time. + call ale#other_source#ShowResults(bufnr(''), 'some-linter-name', [ + \ {'text': 'Something went wrong', lnum: 13}, + \]) +< + +Other sources should use a unique name for identifying themselves. A single +linter name can be used for all problems from another source, or a series of +unique linter names can be used. Results can be cleared for that source by +providing an empty List. + +|ale#other_source#StartChecking()| should be called whenever another source +starts checking a buffer, so other tools can know that a buffer is being +checked by some plugin. The |ALEWantResults| autocmd event can be used to +start checking a buffer for problems every time that ALE does. When +|ALEWantResults| is signaled, |g:ale_want_results_buffer| will be set to the +number of the buffer that ALE wants to check. +|ale#other_source#StartChecking()| should be called synchronously, and other +sources should perform their checks on a buffer in the background +asynchronously, so they don't interrupt editing. + +A plugin might integrate its own checks with ALE like so: > + + augroup SomeGroupName + autocmd! + autocmd User ALEWantResults call Hook(g:ale_want_results_buffer) + augroup END + + function! DoBackgroundWork(buffer) abort + " Start some work in the background here. + " ... + " Then call WorkDone(a:buffer, results) + endfunction + + function! Hook(buffer) abort + " Tell ALE we're going to check this buffer. + call ale#other_source#StartChecking(a:buffer, 'some-name') + call DoBackgroundWork(a:buffer) + endfunction + + function! WorkDone(buffer, results) abort + " Send results to ALE after they have been collected. + call ale#other_source#ShowResults(buffer, 'some-name', a:results) + endfunction +< + =============================================================================== 4. Fixing Problems *ale-fix* @@ -2763,6 +2826,25 @@ ale#linter#PreventLoading(filetype) *ale#linter#PreventLoading()* |runtimepath| for that filetype. This function can be called from vimrc or similar to prevent ALE from loading linters. +ale#other_source#ShowResults(buffer, linter_name, loclist) + *ale#other_source#ShowResults()* + + Show results from another source of information. + + `buffer` must be a valid buffer number, and `linter_name` must be a unique + name for identifying another source of information. The `loclist` given + where the problems in a buffer are, and should be provided in the format ALE + uses for regular linter results. See |ale-loclist-format|. + + +ale#other_source#StartChecking(buffer, linter_name) + *ale#other_source#StartChecking()* + + Tell ALE that another source of information has started checking a buffer. + + `buffer` must be a valid buffer number, and `linter_name` must be a unique + name for identifying another source of information. + ale#statusline#Count(buffer) *ale#statusline#Count()* @@ -2791,10 +2873,21 @@ b:ale_linted *b:ale_linted* echo getbufvar(bufnr(''), 'ale_linted', 0) > 0 ? 'checked' : 'not checked' < +g:ale_want_results_buffer *g:ale_want_results_buffer* + + `g:ale_want_results_buffer` is set to the number of the buffer being checked + when the |ALEWantResults| event is signaled. This variable should be read to + figure out which buffer other sources should lint. + + ALELintPre *ALELintPre-autocmd* + *ALELintPre* ALELintPost *ALELintPost-autocmd* + *ALELintPost* ALEFixPre *ALEFixPre-autocmd* + *ALEFixPre* ALEFixPost *ALEFixPost-autocmd* + *ALEFixPost* These |User| autocommands are triggered before and after every lint or fix cycle. They can be used to update statuslines, send notifications, etc. @@ -2808,7 +2901,7 @@ ALEFixPost *ALEFixPost-autocmd* autocmd! autocmd User ALELintPre hi Statusline ctermfg=darkgrey autocmd User ALELintPost hi Statusline ctermfg=NONE - augroup end + augroup END < Or to display the progress in the statusline: > @@ -2818,10 +2911,11 @@ ALEFixPost *ALEFixPost-autocmd* autocmd! autocmd User ALELintPre let s:ale_running = 1 | redrawstatus autocmd User ALELintPost let s:ale_running = 0 | redrawstatus - augroup end + augroup END < ALEJobStarted *ALEJobStarted-autocmd* + *ALEJobStarted* This |User| autocommand is triggered immediately after a job is successfully run. This provides better accuracy for checking linter status with @@ -2829,6 +2923,22 @@ ALEJobStarted *ALEJobStarted-autocmd* triggered before any linters are executed. +ALEWantResults *ALEWantResults-autocmd* + *ALEWantResults* + + This |User| autocommand is triggered before ALE begins a lint cycle. Another + source can respond by calling |ale#other_source#StartChecking()|, and + |ALELintPre| will be signaled thereafter, to allow other plugins to know + that another source is checking the buffer. + + |g:ale_want_results_buffer| will be set to the number for a buffer being + checked when the event is signaled, and deleted after the event is done. + This variable should be read to know which buffer to check. + + Other plugins can use this event to start checking buffers when ALE events + for checking buffers are triggered. + + =============================================================================== 10. Special Thanks *ale-special-thanks* diff --git a/test/fix/test_ale_fix.vader b/test/fix/test_ale_fix.vader index 67b8b212..539972a4 100644 --- a/test/fix/test_ale_fix.vader +++ b/test/fix/test_ale_fix.vader @@ -23,7 +23,7 @@ Before: autocmd! autocmd User ALEFixPre let g:pre_success = 1 autocmd User ALEFixPost let g:post_success = 1 - augroup end + augroup END if !has('win32') let &shell = '/bin/bash' @@ -180,7 +180,6 @@ After: unlet! g:ale_emulate_job_failure unlet! b:ale_fixers unlet! b:ale_fix_on_save - augroup! VaderTest delfunction AddCarets delfunction AddDollars delfunction DoNothing @@ -204,6 +203,12 @@ After: delfunction FixWithJSONPostProcessing delfunction JSONPostProcessor + augroup VaderTest + autocmd! + augroup END + + augroup! VaderTest + call ale#test#RestoreDirectory() call ale#fix#registry#ResetToDefaults() diff --git a/test/test_alejobstarted_autocmd.vader b/test/test_alejobstarted_autocmd.vader index 388e5439..6fa1ff8e 100644 --- a/test/test_alejobstarted_autocmd.vader +++ b/test/test_alejobstarted_autocmd.vader @@ -23,10 +23,11 @@ After: let g:ale_run_synchronously = 0 - try - augroup! VaderTest - catch - endtry + augroup VaderTest + autocmd! + augroup END + + augroup! VaderTest unlet! g:job_started_success @@ -38,7 +39,7 @@ Execute(Run a lint cycle with an actual job to check for ALEJobStarted): augroup VaderTest autocmd! autocmd User ALEJobStarted let g:job_started_success = 1 - augroup end + augroup END ALELint diff --git a/test/test_alelint_autocmd.vader b/test/test_alelint_autocmd.vader index 9a7a6a43..332cb36f 100644 --- a/test/test_alelint_autocmd.vader +++ b/test/test_alelint_autocmd.vader @@ -9,10 +9,11 @@ After: let g:ale_run_synchronously = 0 let g:ale_buffer_info = {} - try - augroup! VaderTest - catch - endtry + augroup VaderTest + autocmd! + augroup END + + augroup! VaderTest Given testft(An empty file): Execute(Run a lint cycle, and check that a variable is set in the autocmd): @@ -20,7 +21,7 @@ Execute(Run a lint cycle, and check that a variable is set in the autocmd): autocmd! autocmd User ALELintPre let g:pre_success = 1 autocmd User ALELintPost let g:post_success = 1 - augroup end + augroup END call ale#Queue(0) diff --git a/test/test_checkingbuffer_autocmd.vader b/test/test_checkingbuffer_autocmd.vader index 9e3a8188..9e642b15 100644 --- a/test/test_checkingbuffer_autocmd.vader +++ b/test/test_checkingbuffer_autocmd.vader @@ -31,7 +31,7 @@ After: augroup VaderTest autocmd! - augroup end + augroup END augroup! VaderTest @@ -40,7 +40,7 @@ Execute(ALELintPre should not return success on ale#engine#IsCheckingBuffer): augroup VaderTest autocmd! autocmd User ALELintPre let g:checking_buffer = ale#engine#IsCheckingBuffer(bufnr('')) ? 1 : 0 - augroup end + augroup END ALELint @@ -50,7 +50,7 @@ Execute(ALEJobStarted should return success on ale#engine#IsCheckingBuffer): augroup VaderTest autocmd! autocmd User ALEJobStarted let g:checking_buffer = ale#engine#IsCheckingBuffer(bufnr('')) ? 1 : 0 - augroup end + augroup END ALELint diff --git a/test/test_ignoring_linters.vader b/test/test_ignoring_linters.vader index f47c5e81..866f9e0d 100644 --- a/test/test_ignoring_linters.vader +++ b/test/test_ignoring_linters.vader @@ -108,7 +108,7 @@ Before: let g:run_linters_called = 1 endfunction - function! ale#engine#HandleLoclist(linter_name, buffer, loclist) abort + function! ale#engine#HandleLoclist(linter_name, buffer, loclist, from_other_source) abort let g:loclist = a:loclist endfunction diff --git a/test/test_linter_type_mapping.vader b/test/test_linter_type_mapping.vader index 0131b5f0..0ec22a56 100644 --- a/test/test_linter_type_mapping.vader +++ b/test/test_linter_type_mapping.vader @@ -16,7 +16,7 @@ Execute(It should be possible to remap errors to style errors): \ {'type': 'W', 'sub_type': 'style', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1, 'linter_name': 'foo'}, \ {'type': 'I', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1, 'linter_name': 'foo'}, \ ], - \ ale#engine#FixLocList(bufnr(''), 'foo', [ + \ ale#engine#FixLocList(bufnr(''), 'foo', 0, [ \ {'type': 'E', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1}, \ {'type': 'E', 'sub_type': 'style', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1}, \ {'type': 'W', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1}, @@ -35,7 +35,7 @@ Execute(It should be possible to remap errors to style errors with buffer-local \ {'type': 'W', 'sub_type': 'style', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1, 'linter_name': 'foo'}, \ {'type': 'I', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1, 'linter_name': 'foo'}, \ ], - \ ale#engine#FixLocList(bufnr(''), 'foo', [ + \ ale#engine#FixLocList(bufnr(''), 'foo', 0, [ \ {'type': 'E', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1}, \ {'type': 'E', 'sub_type': 'style', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1}, \ {'type': 'W', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1}, @@ -54,7 +54,7 @@ Execute(It should be possible to remap warnings to style warnings): \ {'type': 'W', 'sub_type': 'style', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1, 'linter_name': 'foo'}, \ {'type': 'I', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1, 'linter_name': 'foo'}, \ ], - \ ale#engine#FixLocList(bufnr(''), 'foo', [ + \ ale#engine#FixLocList(bufnr(''), 'foo', 0, [ \ {'type': 'E', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1}, \ {'type': 'E', 'sub_type': 'style', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1}, \ {'type': 'W', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1}, @@ -73,7 +73,7 @@ Execute(It should be possible to remap style errors to errors): \ {'type': 'W', 'sub_type': 'style', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1, 'linter_name': 'foo'}, \ {'type': 'I', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1, 'linter_name': 'foo'}, \ ], - \ ale#engine#FixLocList(bufnr(''), 'foo', [ + \ ale#engine#FixLocList(bufnr(''), 'foo', 0, [ \ {'type': 'E', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1}, \ {'type': 'E', 'sub_type': 'style', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1}, \ {'type': 'W', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1}, @@ -92,7 +92,7 @@ Execute(It should be possible to remap style warnings to warnings): \ {'type': 'W', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1, 'linter_name': 'foo'}, \ {'type': 'I', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1, 'linter_name': 'foo'}, \ ], - \ ale#engine#FixLocList(bufnr(''), 'foo', [ + \ ale#engine#FixLocList(bufnr(''), 'foo', 0, [ \ {'type': 'E', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1}, \ {'type': 'E', 'sub_type': 'style', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1}, \ {'type': 'W', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1}, @@ -111,7 +111,7 @@ Execute(It should be possible to info problems to warnings): \ {'type': 'W', 'sub_type': 'style', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1, 'linter_name': 'foo'}, \ {'type': 'W', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1, 'linter_name': 'foo'}, \ ], - \ ale#engine#FixLocList(bufnr(''), 'foo', [ + \ ale#engine#FixLocList(bufnr(''), 'foo', 0, [ \ {'type': 'E', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1}, \ {'type': 'E', 'sub_type': 'style', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1}, \ {'type': 'W', 'lnum': 1, 'text': 'x', 'bufnr': bufnr(''), 'col': 0, 'vcol': 0, 'nr': -1}, diff --git a/test/test_loclist_corrections.vader b/test/test_loclist_corrections.vader index 48aa1f78..343620a5 100644 --- a/test/test_loclist_corrections.vader +++ b/test/test_loclist_corrections.vader @@ -41,6 +41,7 @@ Execute(FixLocList should set all the default values correctly): \ ale#engine#FixLocList( \ bufnr('%'), \ 'foobar', + \ 0, \ [{'text': 'a', 'lnum': 2}, {'text': 'b', 'lnum': 2}], \ ) @@ -61,6 +62,7 @@ Execute(FixLocList should use the values we supply): \ ale#engine#FixLocList( \ bufnr('%'), \ 'foobar', + \ 0, \ [{ \ 'text': 'a', \ 'lnum': 3, @@ -89,6 +91,7 @@ Execute(FixLocList should set items with lines beyond the end to the last line): \ ale#engine#FixLocList( \ bufnr('%'), \ 'foobar', + \ 0, \ [{'text': 'a', 'lnum': 11}], \ ) @@ -109,6 +112,7 @@ Execute(FixLocList should move line 0 to line 1): \ ale#engine#FixLocList( \ bufnr('%'), \ 'foobar', + \ 0, \ [{'text': 'a', 'lnum': 0}], \ ) @@ -130,6 +134,7 @@ Execute(FixLocList should convert line and column numbers correctly): \ ale#engine#FixLocList( \ bufnr('%'), \ 'foobar', + \ 0, \ [{'text': 'a', 'lnum': '010', 'col': '010'}], \ ) @@ -163,6 +168,7 @@ Execute(FixLocList should pass on end_col values): \ ale#engine#FixLocList( \ bufnr('%'), \ 'foobar', + \ 0, \ [ \ {'text': 'a', 'lnum': '010', 'col': '010', 'end_col': '012'}, \ {'text': 'a', 'lnum': '010', 'col': '011', 'end_col': 12}, @@ -200,6 +206,7 @@ Execute(FixLocList should pass on end_lnum values): \ ale#engine#FixLocList( \ bufnr('%'), \ 'foobar', + \ 0, \ [ \ {'text': 'a', 'lnum': '010', 'col': '010', 'end_col': '012', 'end_lnum': '013'}, \ {'text': 'a', 'lnum': '010', 'col': '011', 'end_col': 12, 'end_lnum': 13}, @@ -224,6 +231,7 @@ Execute(FixLocList should allow subtypes to be set): \ ale#engine#FixLocList( \ bufnr('%'), \ 'foobar', + \ 0, \ [{'text': 'a', 'lnum': 11, 'sub_type': 'style'}], \ ) @@ -285,6 +293,7 @@ Execute(FixLocList should accept filenames): \ ale#engine#FixLocList( \ bufnr('%'), \ 'foobar', + \ 0, \ [ \ {'text': 'a', 'lnum': 2, 'filename': expand('%:p')}, \ {'text': 'a', 'lnum': 3, 'filename': expand('%:p')}, @@ -322,6 +331,7 @@ Execute(FixLocList should interpret temporary filenames as being the current buf \ ale#engine#FixLocList( \ bufnr(''), \ 'foobar', + \ 0, \ [ \ {'text': 'a', 'lnum': 2, 'filename': b:temp_name}, \ {'text': 'a', 'lnum': 3, 'filename': substitute(b:temp_name, '\\', '/', 'g')}, @@ -346,9 +356,43 @@ Execute(The error code should be passed on): \ ale#engine#FixLocList( \ bufnr('%'), \ 'foobar', + \ 0, \ [{'text': 'a', 'lnum': 11, 'code': 'some-code'}], \ ) +Execute(FixLocList should mark problems as coming from other sources if requested): + AssertEqual + \ [ + \ { + \ 'text': 'a', + \ 'lnum': 2, + \ 'col': 0, + \ 'bufnr': bufnr('%'), + \ 'vcol': 0, + \ 'type': 'E', + \ 'nr': -1, + \ 'linter_name': 'foobar', + \ 'from_other_source': 1, + \ }, + \ { + \ 'text': 'b', + \ 'lnum': 2, + \ 'col': 0, + \ 'bufnr': bufnr('%'), + \ 'vcol': 0, + \ 'type': 'E', + \ 'nr': -1, + \ 'linter_name': 'foobar', + \ 'from_other_source': 1, + \ }, + \], + \ ale#engine#FixLocList( + \ bufnr('%'), + \ 'foobar', + \ 1, + \ [{'text': 'a', 'lnum': 2}, {'text': 'b', 'lnum': 2}], + \ ) + Given(A file with Japanese multi-byte text): はじめまして! -私はワープです。 @@ -367,6 +411,7 @@ Execute(character positions should be converted to byte positions): \ ale#engine#FixLocList( \ bufnr('%'), \ 'foobar', + \ 0, \ [ \ {'text': 'a', 'lnum': 1, 'col': 0, 'vcol': 1}, \ {'text': 'a', 'lnum': 1, 'col': 1, 'vcol': 1}, diff --git a/test/test_other_sources.vader b/test/test_other_sources.vader new file mode 100644 index 00000000..e6f9911c --- /dev/null +++ b/test/test_other_sources.vader @@ -0,0 +1,153 @@ +Before: + Save g:ale_buffer_info + Save g:ale_set_signs + Save g:ale_set_quickfix + Save g:ale_set_loclist + Save g:ale_set_highlights + Save g:ale_echo_cursor + + let g:ale_buffer_info = {} + let g:ale_run_synchronously = 1 + let g:ale_set_lists_synchronously = 1 + let g:ale_set_signs = 0 + let g:ale_set_quickfix = 0 + let g:ale_set_loclist = 1 + let g:ale_set_highlights = 0 + let g:ale_echo_cursor = 0 + + function! TestCallback(buffer, output) + return [] + endfunction + + call ale#linter#Define('foobar', { + \ 'name': 'testlinter', + \ 'callback': 'TestCallback', + \ 'executable': has('win32') ? 'cmd' : 'echo', + \ 'command': has('win32') ? 'echo foo bar' : '/bin/sh -c ''echo foo bar''', + \}) + +After: + Restore + + unlet! b:ale_linters + unlet! g:want_results_signaled + unlet! g:want_results_buffer_value + unlet! g:lint_pre_signaled + unlet! g:ale_run_synchronously + unlet! g:ale_set_lists_synchronously + + delfunction TestCallback + + augroup VaderTest + autocmd! + augroup END + + augroup! VaderTest + + call ale#linter#Reset() + call setloclist(0, []) + +Given foobar (Some imaginary filetype): +Execute(StartChecking should mark a buffer as being actively checked): + Assert !ale#engine#IsCheckingBuffer(bufnr('')) + + call ale#other_source#StartChecking(bufnr(''), 'other-source-linter') + + Assert ale#engine#IsCheckingBuffer(bufnr('')) + +Execute(ShowResults sould make a buffer inactive): + call ale#other_source#StartChecking(bufnr(''), 'other-source-linter') + call ale#other_source#StartChecking(bufnr(''), 'second-other-source-linter') + + call ale#other_source#ShowResults(bufnr(''), 'other-source-linter', []) + + Assert ale#engine#IsCheckingBuffer(bufnr('')) + + call ale#other_source#ShowResults(bufnr(''), 'second-other-source-linter', []) + + Assert !ale#engine#IsCheckingBuffer(bufnr('')) + +Execute(ShowResults should show results at any time): + call ale#other_source#ShowResults(bufnr(''), 'other-source-linter', [ + \ {'text': 'xyz', 'lnum': 1}, + \]) + + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'bufnr': bufnr(''), + \ 'col': 0, + \ 'valid': 1, + \ 'vcol': 0, + \ 'nr': -1, + \ 'type': 'E', + \ 'pattern': '', + \ 'text': 'xyz', + \ }, + \ ], + \ ale#test#GetLoclistWithoutModule() + + call ale#other_source#ShowResults(bufnr(''), 'other-source-linter', []) + + AssertEqual [], ale#test#GetLoclistWithoutModule() + +Execute(A regular lint cycle shouldn't clear results from other sources): + call ale#other_source#ShowResults(bufnr(''), 'other-source-linter', [ + \ {'text': 'xyz', 'lnum': 1}, + \]) + ALELint + + AssertEqual + \ [ + \ { + \ 'lnum': 1, + \ 'bufnr': bufnr(''), + \ 'col': 0, + \ 'valid': 1, + \ 'vcol': 0, + \ 'nr': -1, + \ 'type': 'E', + \ 'pattern': '', + \ 'text': 'xyz', + \ }, + \ ], + \ ale#test#GetLoclistWithoutModule() + +Execute(ALEWantResults should be signaled when a buffer is checked): + augroup VaderTest + autocmd! + autocmd User ALEWantResults let g:want_results_signaled = 1 + autocmd User ALELintPre let g:lint_pre_signaled = 1 + augroup END + + " Even when all linters are disabled, we should send the signal. + let b:ale_linters = [] + ALELint + + Assert get(g:, 'want_results_signaled') + Assert !get(g:, 'lint_pre_signaled') + +Execute(ALEWantResults should set a variable indicating which buffer is being checked): + augroup VaderTest + autocmd! + autocmd User ALEWantResults let g:want_results_buffer_value = g:ale_want_results_buffer + augroup END + + let b:ale_linters = [] + ALELint + + AssertEqual bufnr(''), g:want_results_buffer_value + +Execute(ALEWantResults should lead to an ALELintPre signal if another source responds): + augroup VaderTest + autocmd! + autocmd User ALEWantResults call ale#other_source#StartChecking(bufnr(''), 'other-source-linter') + autocmd User ALELintPre let g:lint_pre_signaled = 1 + augroup END + + " Even when all linters are disabled, we should send the signal. + let b:ale_linters = [] + ALELint + + Assert get(g:, 'lint_pre_signaled') diff --git a/test/test_redundant_tsserver_rendering_avoided.vader b/test/test_redundant_tsserver_rendering_avoided.vader index 41cbe5e0..a292b014 100644 --- a/test/test_redundant_tsserver_rendering_avoided.vader +++ b/test/test_redundant_tsserver_rendering_avoided.vader @@ -45,7 +45,7 @@ Before: let g:ale_buffer_info = {bufnr(''): {'loclist': []}} let g:ale_handle_loclist_called = 0 - function! ale#engine#HandleLoclist(linter_name, buffer, loclist) abort + function! ale#engine#HandleLoclist(linter_name, buffer, loclist, from_other_source) abort let g:ale_handle_loclist_called = 1 endfunction diff --git a/test/test_setting_problems_found_in_previous_buffers.vader b/test/test_setting_problems_found_in_previous_buffers.vader index 4604005a..36eeb4ca 100644 --- a/test/test_setting_problems_found_in_previous_buffers.vader +++ b/test/test_setting_problems_found_in_previous_buffers.vader @@ -9,14 +9,14 @@ Before: let g:ale_buffer_info = {} call ale#engine#InitBufferInfo(bufnr('') + 1) let g:ale_buffer_info[bufnr('') + 1].loclist = - \ ale#engine#FixLocList(bufnr('') + 1, 'linter_one', [ + \ ale#engine#FixLocList(bufnr('') + 1, 'linter_one', 0, [ \ {'lnum': 1, 'filename': expand('%:p'), 'text': 'foo'}, \ {'lnum': 2, 'filename': expand('%:p'), 'text': 'bar'}, \ {'lnum': 2, 'text': 'ignore this one'}, \ ]) call ale#engine#InitBufferInfo(bufnr('') + 2) let g:ale_buffer_info[bufnr('') + 2].loclist = - \ ale#engine#FixLocList(bufnr('') + 2, 'linter_one', [ + \ ale#engine#FixLocList(bufnr('') + 2, 'linter_one', 0, [ \ {'lnum': 1, 'filename': expand('%:p'), 'text': 'foo'}, \ {'lnum': 3, 'filename': expand('%:p'), 'text': 'baz'}, \ {'lnum': 5, 'text': 'ignore this one'},