#653 - Always set loclist or quickfix in a timer callback, which prevents errors E924, E925, and E926
This commit is contained in:
parent
47a8ebc8b9
commit
1a524ca63e
@ -708,7 +708,7 @@ function! s:RemoveProblemsForDisabledLinters(buffer, linters) abort
|
|||||||
|
|
||||||
call filter(
|
call filter(
|
||||||
\ get(g:ale_buffer_info[a:buffer], 'loclist', []),
|
\ get(g:ale_buffer_info[a:buffer], 'loclist', []),
|
||||||
\ 'get(l:name_map, v:val.linter_name)',
|
\ 'get(l:name_map, get(v:val, ''linter_name''))',
|
||||||
\)
|
\)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
@ -45,55 +45,3 @@ function! ale#events#FileChangedEvent(buffer) abort
|
|||||||
call s:LintOnEnter(a:buffer)
|
call s:LintOnEnter(a:buffer)
|
||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" When changing quickfix or a loclist window while the window is open
|
|
||||||
" from autocmd events and while navigating from one buffer to another, Vim
|
|
||||||
" will complain saying that the window has closed or that the lists have
|
|
||||||
" changed.
|
|
||||||
"
|
|
||||||
" This timer callback just accepts those errors when they appear.
|
|
||||||
function! s:HitReturn(...) abort
|
|
||||||
if ale#util#Mode() is# 'r'
|
|
||||||
redir => l:output
|
|
||||||
silent mess
|
|
||||||
redir end
|
|
||||||
|
|
||||||
if get(split(l:output, "\n"), -1, '') =~# '^E92[456]'
|
|
||||||
call ale#util#FeedKeys("\<CR>", 'n')
|
|
||||||
|
|
||||||
" If we hit one of the errors and cleared it, then Vim didn't
|
|
||||||
" move to the position we wanted. Change the position to the one
|
|
||||||
" the user selected.
|
|
||||||
if exists('g:ale_list_window_selection')
|
|
||||||
let l:pos = getcurpos()
|
|
||||||
let [l:pos[1], l:pos[2]] = g:ale_list_window_selection
|
|
||||||
call setpos('.', l:pos)
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
" Always clear the last selection when trying to cancel the errors above
|
|
||||||
" here. This prevents us from remembering invalid positions.
|
|
||||||
unlet! g:ale_list_window_selection
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! ale#events#BufWinLeave() abort
|
|
||||||
call timer_start(0, function('s:HitReturn'))
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" Grab the position for a problem from the loclist or quickfix windows
|
|
||||||
" when moving through selections. This selection will be remembered and
|
|
||||||
" set as the position when jumping to an error in another file.
|
|
||||||
function! ale#events#ParseLoclistWindowItemPosition() abort
|
|
||||||
" Parses lines like
|
|
||||||
" test.txt|72 col 5 error| ...
|
|
||||||
" test.txt|72| ...
|
|
||||||
let l:match = matchlist(getline('.'), '\v^[^|]+\|(\d+)( [^ ]+ )?(\d*)')
|
|
||||||
|
|
||||||
if !empty(l:match)
|
|
||||||
let g:ale_list_window_selection = [
|
|
||||||
\ l:match[1] + 0,
|
|
||||||
\ max([l:match[3] + 0, 1]),
|
|
||||||
\]
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
" Author: Bjorn Neergaard <bjorn@neersighted.com>, modified by Yann fery <yann@fery.me>
|
" Author: Bjorn Neergaard <bjorn@neersighted.com>, modified by Yann fery <yann@fery.me>
|
||||||
" Description: Manages the loclist and quickfix lists
|
" Description: Manages the loclist and quickfix lists
|
||||||
|
|
||||||
|
if !exists('s:timer_args')
|
||||||
|
let s:timer_args = {}
|
||||||
|
endif
|
||||||
|
|
||||||
" Return 1 if there is a buffer with buftype == 'quickfix' in bufffer list
|
" Return 1 if there is a buffer with buftype == 'quickfix' in bufffer list
|
||||||
function! ale#list#IsQuickfixOpen() abort
|
function! ale#list#IsQuickfixOpen() abort
|
||||||
for l:buf in range(1, bufnr('$'))
|
for l:buf in range(1, bufnr('$'))
|
||||||
@ -52,7 +56,7 @@ function! s:FixList(list) abort
|
|||||||
return l:new_list
|
return l:new_list
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! ale#list#SetLists(buffer, loclist) abort
|
function! s:SetListsImpl(timer_id, buffer, loclist) abort
|
||||||
let l:title = expand('#' . a:buffer . ':p')
|
let l:title = expand('#' . a:buffer . ':p')
|
||||||
|
|
||||||
if g:ale_set_quickfix
|
if g:ale_set_quickfix
|
||||||
@ -115,7 +119,19 @@ function! ale#list#SetLists(buffer, loclist) abort
|
|||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! ale#list#CloseWindowIfNeeded(buffer) abort
|
function! ale#list#SetLists(buffer, loclist) abort
|
||||||
|
if get(g:, 'ale_set_lists_synchronously') == 1
|
||||||
|
call s:SetListsImpl(-1, a:buffer, a:loclist)
|
||||||
|
else
|
||||||
|
call ale#util#StartPartialTimer(
|
||||||
|
\ 0,
|
||||||
|
\ function('s:SetListsImpl'),
|
||||||
|
\ [a:buffer, a:loclist],
|
||||||
|
\)
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:CloseWindowIfNeededImpl(timer_id, buffer) abort
|
||||||
if ale#Var(a:buffer, 'keep_list_window_open') || !s:ShouldOpen(a:buffer)
|
if ale#Var(a:buffer, 'keep_list_window_open') || !s:ShouldOpen(a:buffer)
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
@ -134,3 +150,15 @@ function! ale#list#CloseWindowIfNeeded(buffer) abort
|
|||||||
catch /E444/
|
catch /E444/
|
||||||
endtry
|
endtry
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
function! ale#list#CloseWindowIfNeeded(buffer) abort
|
||||||
|
if get(g:, 'ale_set_lists_synchronously') == 1
|
||||||
|
call s:CloseWindowIfNeededImpl(-1, a:buffer)
|
||||||
|
else
|
||||||
|
call ale#util#StartPartialTimer(
|
||||||
|
\ 0,
|
||||||
|
\ function('s:CloseWindowIfNeededImpl'),
|
||||||
|
\ [a:buffer],
|
||||||
|
\)
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
@ -267,3 +267,33 @@ function! ale#util#Writefile(buffer, lines, filename) abort
|
|||||||
|
|
||||||
call writefile(l:corrected_lines, a:filename) " no-custom-checks
|
call writefile(l:corrected_lines, a:filename) " no-custom-checks
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
if !exists('s:patial_timers')
|
||||||
|
let s:partial_timers = {}
|
||||||
|
endif
|
||||||
|
|
||||||
|
function! s:ApplyPartialTimer(timer_id) abort
|
||||||
|
let [l:Callback, l:args] = remove(s:partial_timers, a:timer_id)
|
||||||
|
call call(l:Callback, [a:timer_id] + l:args)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" Given a delay, a callback, a List of arguments, start a timer with
|
||||||
|
" timer_start() and call the callback provided with [timer_id] + args.
|
||||||
|
"
|
||||||
|
" The timer must not be stopped with timer_stop().
|
||||||
|
" Use ale#util#StopPartialTimer() instead, which can stop any timer, and will
|
||||||
|
" clear any arguments saved for executing callbacks later.
|
||||||
|
function! ale#util#StartPartialTimer(delay, callback, args) abort
|
||||||
|
let l:timer_id = timer_start(a:delay, function('s:ApplyPartialTimer'))
|
||||||
|
let s:partial_timers[l:timer_id] = [a:callback, a:args]
|
||||||
|
|
||||||
|
return l:timer_id
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! ale#util#StopPartialTimer(timer_id) abort
|
||||||
|
call timer_stop(a:timer_id)
|
||||||
|
|
||||||
|
if has_key(s:partial_timers, a:timer_id)
|
||||||
|
call remove(s:partial_timers, a:timer_id)
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
augroup ALEQuickfixCursorMovedEvent
|
|
||||||
autocmd! * <buffer>
|
|
||||||
autocmd CursorMoved <buffer> call ale#events#ParseLoclistWindowItemPosition()
|
|
||||||
augroup END
|
|
@ -226,7 +226,6 @@ function! ALEInitAuGroups() abort
|
|||||||
autocmd BufWinEnter,BufRead * call ale#Queue(0, 'lint_file', str2nr(expand('<abuf>')))
|
autocmd BufWinEnter,BufRead * call ale#Queue(0, 'lint_file', str2nr(expand('<abuf>')))
|
||||||
" Track when the file is changed outside of Vim.
|
" Track when the file is changed outside of Vim.
|
||||||
autocmd FileChangedShellPost * call ale#events#FileChangedEvent(str2nr(expand('<abuf>')))
|
autocmd FileChangedShellPost * call ale#events#FileChangedEvent(str2nr(expand('<abuf>')))
|
||||||
autocmd BufWinLeave * call ale#events#BufWinLeave()
|
|
||||||
endif
|
endif
|
||||||
augroup END
|
augroup END
|
||||||
|
|
||||||
|
@ -60,11 +60,6 @@ After:
|
|||||||
|
|
||||||
call ALEInitAuGroups()
|
call ALEInitAuGroups()
|
||||||
|
|
||||||
" Clean up the quickfix group.
|
|
||||||
augroup ALEQuickfixCursorMovedEvent
|
|
||||||
autocmd! * <buffer>
|
|
||||||
augroup END
|
|
||||||
|
|
||||||
Execute (g:ale_lint_on_text_changed = 0 should bind no events):
|
Execute (g:ale_lint_on_text_changed = 0 should bind no events):
|
||||||
let g:ale_lint_on_text_changed = 0
|
let g:ale_lint_on_text_changed = 0
|
||||||
|
|
||||||
@ -139,7 +134,6 @@ Execute (g:ale_lint_on_enter = 1 should bind the required events):
|
|||||||
\ 'BufEnter * call ale#events#EnterEvent(str2nr(expand(''<abuf>'')))',
|
\ 'BufEnter * call ale#events#EnterEvent(str2nr(expand(''<abuf>'')))',
|
||||||
\ 'BufReadPost * call ale#Queue(0, ''lint_file'', str2nr(expand(''<abuf>'')))',
|
\ 'BufReadPost * call ale#Queue(0, ''lint_file'', str2nr(expand(''<abuf>'')))',
|
||||||
\ 'BufWinEnter * call ale#Queue(0, ''lint_file'', str2nr(expand(''<abuf>'')))',
|
\ 'BufWinEnter * call ale#Queue(0, ''lint_file'', str2nr(expand(''<abuf>'')))',
|
||||||
\ 'BufWinLeave * call ale#events#BufWinLeave()',
|
|
||||||
\ 'FileChangedShellPost * call ale#events#FileChangedEvent(str2nr(expand(''<abuf>'')))',
|
\ 'FileChangedShellPost * call ale#events#FileChangedEvent(str2nr(expand(''<abuf>'')))',
|
||||||
\], CheckAutocmd('ALERunOnEnterGroup')
|
\], CheckAutocmd('ALERunOnEnterGroup')
|
||||||
|
|
||||||
@ -222,12 +216,3 @@ Execute(Disabling completion should remove autocmd events correctly):
|
|||||||
|
|
||||||
AssertEqual [], CheckAutocmd('ALECompletionGroup')
|
AssertEqual [], CheckAutocmd('ALECompletionGroup')
|
||||||
AssertEqual 0, g:ale_completion_enabled
|
AssertEqual 0, g:ale_completion_enabled
|
||||||
|
|
||||||
Execute(The cursor events should be set up for the quickfix list):
|
|
||||||
runtime! ftplugin/qf.vim
|
|
||||||
|
|
||||||
AssertEqual
|
|
||||||
\ [
|
|
||||||
\ 'CursorMoved <buffer> call ale#events#ParseLoclistWindowItemPosition()',
|
|
||||||
\ ],
|
|
||||||
\ CheckAutocmd('ALEQuickfixCursorMovedEvent')
|
|
||||||
|
@ -1,96 +0,0 @@
|
|||||||
Before:
|
|
||||||
let b:fake_mode = 'r'
|
|
||||||
let b:feedkeys_calls = []
|
|
||||||
|
|
||||||
" Mock mode() and feedkeys() for the check
|
|
||||||
function! ale#util#Mode(...) abort
|
|
||||||
return b:fake_mode
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! ale#util#FeedKeys(...) abort
|
|
||||||
call add(b:feedkeys_calls, a:000)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! CheckError(mode, message, expected_list) abort
|
|
||||||
let b:fake_mode = a:mode
|
|
||||||
|
|
||||||
echom a:message
|
|
||||||
|
|
||||||
call ale#events#BufWinLeave()
|
|
||||||
AssertEqual [], b:feedkeys_calls
|
|
||||||
|
|
||||||
sleep 1ms
|
|
||||||
AssertEqual a:expected_list, b:feedkeys_calls
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
After:
|
|
||||||
unlet! b:fake_mode
|
|
||||||
unlet! b:feedkeys_calls
|
|
||||||
unlet! g:ale_list_window_selection
|
|
||||||
|
|
||||||
delfunction CheckError
|
|
||||||
|
|
||||||
runtime autoload/ale/util.vim
|
|
||||||
|
|
||||||
Execute(The BufWinLeave event function should hide E924 errors):
|
|
||||||
" For some reason, this test fails the first time when running in NeoVim
|
|
||||||
" in Docker, so just execute this twice.
|
|
||||||
echom 'E924'
|
|
||||||
call ale#events#BufWinLeave()
|
|
||||||
sleep 1ms
|
|
||||||
let b:feedkeys_calls = []
|
|
||||||
|
|
||||||
call CheckError('r', 'E924', [["\<CR>", 'n']])
|
|
||||||
|
|
||||||
Execute(The BufWinLeave event function should hide E925 errors):
|
|
||||||
call CheckError('r', 'E925', [["\<CR>", 'n']])
|
|
||||||
|
|
||||||
Execute(The BufWinLeave event function should hide E926 errors):
|
|
||||||
call CheckError('r', 'E926', [["\<CR>", 'n']])
|
|
||||||
|
|
||||||
Execute(The BufWinLeave event function should ignore other errors):
|
|
||||||
call CheckError('r', 'E999', [])
|
|
||||||
|
|
||||||
Execute(The BufWinLeave event function not send keys for other modes):
|
|
||||||
call CheckError('n', 'E924', [])
|
|
||||||
|
|
||||||
Execute(The last window selection should always be cleared by the timer):
|
|
||||||
let g:ale_list_window_selection = [347, 2]
|
|
||||||
|
|
||||||
" The message and mode shouldn't matter, we should still clear the variable.
|
|
||||||
echom 'xyz'
|
|
||||||
let b:fake_mode = 'n'
|
|
||||||
call ale#events#BufWinLeave()
|
|
||||||
sleep 1ms
|
|
||||||
|
|
||||||
Assert !has_key(g:, 'ale_list_window_selection')
|
|
||||||
|
|
||||||
Given qf(A quickfix list with some errors):
|
|
||||||
test.txt|23 col 9 warning| Some warning
|
|
||||||
test.txt|72 col 25 error| Some column error
|
|
||||||
test.txt|93 error| Some line error
|
|
||||||
|
|
||||||
Execute(Line and column numbers should be parsed by the quickfix event function):
|
|
||||||
call setpos('.', [bufnr(''), 2, 1, 0])
|
|
||||||
call ale#events#ParseLoclistWindowItemPosition()
|
|
||||||
AssertEqual [72, 25], g:ale_list_window_selection
|
|
||||||
|
|
||||||
Execute(Just Line numbers should be parsed by the quickfix event function):
|
|
||||||
call setpos('.', [bufnr(''), 3, 1, 0])
|
|
||||||
call ale#events#ParseLoclistWindowItemPosition()
|
|
||||||
AssertEqual [93, 1], g:ale_list_window_selection
|
|
||||||
|
|
||||||
Given python(Some example Python file):
|
|
||||||
class FooBar:
|
|
||||||
def whatever(self):
|
|
||||||
self.do_something()
|
|
||||||
|
|
||||||
Execute(We should jump to the window selection after cancelling the errors):
|
|
||||||
call setpos('.', [bufnr(''), 1, 1, 0])
|
|
||||||
let g:ale_list_window_selection = [3, 9]
|
|
||||||
|
|
||||||
echom 'E925'
|
|
||||||
call ale#events#BufWinLeave()
|
|
||||||
sleep 1ms
|
|
||||||
|
|
||||||
AssertEqual [3, 9], getcurpos()[1:2]
|
|
38
test/test_set_list_timers.vader
Normal file
38
test/test_set_list_timers.vader
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
Before:
|
||||||
|
Save g:ale_set_lists_synchronously
|
||||||
|
Save g:ale_open_list
|
||||||
|
|
||||||
|
let g:ale_set_lists_synchronously = 0
|
||||||
|
|
||||||
|
After:
|
||||||
|
Restore
|
||||||
|
|
||||||
|
sleep 1ms
|
||||||
|
call setloclist(0, [])
|
||||||
|
lclose
|
||||||
|
|
||||||
|
Execute(The SetLists function should work when run in a timer):
|
||||||
|
call ale#list#SetLists(bufnr(''), [
|
||||||
|
\ {'bufnr': bufnr(''), 'lnum': 5, 'col': 5, 'text': 'x', 'type': 'E'},
|
||||||
|
\])
|
||||||
|
sleep 1ms
|
||||||
|
AssertEqual [{
|
||||||
|
\ 'lnum': 5,
|
||||||
|
\ 'bufnr': bufnr(''),
|
||||||
|
\ 'col': 5,
|
||||||
|
\ 'text': 'x',
|
||||||
|
\ 'valid': 1,
|
||||||
|
\ 'vcol': 0,
|
||||||
|
\ 'nr': 0,
|
||||||
|
\ 'type': 'E',
|
||||||
|
\ 'pattern': '',
|
||||||
|
\}], getloclist(0)
|
||||||
|
|
||||||
|
Execute(The CloseWindowIfNeeded function should work when run in a timer):
|
||||||
|
let g:ale_open_list = 1
|
||||||
|
lopen
|
||||||
|
|
||||||
|
call ale#list#CloseWindowIfNeeded(bufnr(''))
|
||||||
|
sleep 1ms
|
||||||
|
|
||||||
|
Assert !ale#list#IsQuickfixOpen(), 'The window was not closed!'
|
@ -1,9 +1,11 @@
|
|||||||
Before:
|
Before:
|
||||||
Save g:ale_statusline_format, g:ale_buffer_info
|
Save g:ale_statusline_format
|
||||||
|
Save g:ale_buffer_info
|
||||||
|
|
||||||
let g:ale_buffer_info = {}
|
let g:ale_buffer_info = {}
|
||||||
|
|
||||||
" A function for conveniently creating expected count objects.
|
" A function for conveniently creating expected count objects.
|
||||||
function Counts(data) abort
|
function! Counts(data) abort
|
||||||
let l:res = {
|
let l:res = {
|
||||||
\ '0': 0,
|
\ '0': 0,
|
||||||
\ '1': 0,
|
\ '1': 0,
|
||||||
@ -28,6 +30,7 @@ Before:
|
|||||||
|
|
||||||
After:
|
After:
|
||||||
Restore
|
Restore
|
||||||
|
|
||||||
delfunction Counts
|
delfunction Counts
|
||||||
|
|
||||||
Execute (Count should be 0 when data is empty):
|
Execute (Count should be 0 when data is empty):
|
||||||
@ -126,16 +129,13 @@ Execute (Status() should return the custom 'OK' string with the old format):
|
|||||||
AssertEqual 'OKIE', ale#statusline#Status()
|
AssertEqual 'OKIE', ale#statusline#Status()
|
||||||
|
|
||||||
Execute(ale#statusline#Update shouldn't blow up when globals are undefined):
|
Execute(ale#statusline#Update shouldn't blow up when globals are undefined):
|
||||||
unlet! g:ale_buffer_info
|
|
||||||
unlet! g:ale_statusline_format
|
unlet! g:ale_statusline_format
|
||||||
call ale#statusline#Update(1, [])
|
call ale#statusline#Update(1, [])
|
||||||
|
|
||||||
Execute(ale#statusline#Count should return 0 counts when globals are undefined):
|
Execute(ale#statusline#Count should return 0 counts when globals are undefined):
|
||||||
unlet! g:ale_buffer_info
|
|
||||||
unlet! g:ale_statusline_format
|
unlet! g:ale_statusline_format
|
||||||
AssertEqual Counts({}), ale#statusline#Count(1)
|
AssertEqual Counts({}), ale#statusline#Count(1)
|
||||||
|
|
||||||
Execute(ale#statusline#Status should return 'OK' when globals are undefined):
|
Execute(ale#statusline#Status should return 'OK' when globals are undefined):
|
||||||
unlet! g:ale_buffer_info
|
|
||||||
unlet! g:ale_statusline_format
|
unlet! g:ale_statusline_format
|
||||||
AssertEqual 'OK', ale#statusline#Status()
|
AssertEqual 'OK', ale#statusline#Status()
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
" vint: -ProhibitSetNoCompatible
|
" vint: -ProhibitSetNoCompatible
|
||||||
|
|
||||||
|
" Make most tests just set lists synchronously when run in Docker.
|
||||||
|
let g:ale_set_lists_synchronously = 1
|
||||||
|
|
||||||
" Load builtin plugins
|
" Load builtin plugins
|
||||||
" We need this because run_vim.sh sets -i NONE
|
" We need this because run_vim.sh sets -i NONE
|
||||||
set runtimepath=/home/vim,$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after,/testplugin,/vader
|
set runtimepath=/home/vim,$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after,/testplugin,/vader
|
||||||
|
Loading…
Reference in New Issue
Block a user