diff --git a/autoload/ale.vim b/autoload/ale.vim index 55125a04..6e16a8ee 100644 --- a/autoload/ale.vim +++ b/autoload/ale.vim @@ -5,6 +5,15 @@ let s:lint_timer = -1 let s:queued_buffer_number = -1 let s:should_lint_file_for_buffer = {} +let s:queue_error_delay_ms = 1000 * 60 * 2 + +if !exists('s:dont_queue_until') + let s:dont_queue_until = -1 +endif + +if !exists('s:dont_lint_until') + let s:dont_lint_until = -1 +endif " Return 1 if a file is too large for ALE to handle. function! ale#FileTooLarge() abort @@ -31,26 +40,39 @@ function! ale#Queue(delay, ...) abort throw 'too many arguments!' endif + let l:now = ale#util#ClockMilliseconds() + + if l:now < s:dont_queue_until + return + endif + + let s:dont_queue_until = l:now + s:queue_error_delay_ms + " Default linting_flag to '' let l:linting_flag = get(a:000, 0, '') + let l:buffer = get(a:000, 1, bufnr('')) - if l:linting_flag !=# '' && l:linting_flag !=# 'lint_file' + call s:ALEQueueImpl(a:delay, l:linting_flag, l:buffer) + + let s:dont_queue_until = -1 +endfunction + +function! s:ALEQueueImpl(delay, linting_flag, buffer) abort + if a:linting_flag !=# '' && a:linting_flag !=# 'lint_file' throw "linting_flag must be either '' or 'lint_file'" endif - let l:buffer = get(a:000, 1, bufnr('')) - - if type(l:buffer) != type(0) + if type(a:buffer) != type(0) throw 'buffer_number must be a Number' endif - if ale#ShouldDoNothing(l:buffer) + if ale#ShouldDoNothing(a:buffer) return endif " Remember that we want to check files for this buffer. " We will remember this until we finally run the linters, via any event. - if l:linting_flag is# 'lint_file' + if a:linting_flag is# 'lint_file' let s:should_lint_file_for_buffer[bufnr('%')] = 1 endif @@ -59,24 +81,24 @@ function! ale#Queue(delay, ...) abort let s:lint_timer = -1 endif - let l:linters = ale#linter#Get(getbufvar(l:buffer, '&filetype')) + let l:linters = ale#linter#Get(getbufvar(a:buffer, '&filetype')) " Don't set up buffer data and so on if there are no linters to run. if empty(l:linters) " If we have some previous buffer data, then stop any jobs currently " running and clear everything. - if has_key(g:ale_buffer_info, l:buffer) - call ale#engine#RunLinters(l:buffer, [], 1) + if has_key(g:ale_buffer_info, a:buffer) + call ale#engine#RunLinters(a:buffer, [], 1) endif return endif if a:delay > 0 - let s:queued_buffer_number = l:buffer + let s:queued_buffer_number = a:buffer let s:lint_timer = timer_start(a:delay, function('ale#Lint')) else - call ale#Lint(-1, l:buffer) + call ale#Lint(-1, a:buffer) endif endfunction @@ -92,22 +114,36 @@ function! ale#Lint(...) abort let l:buffer = bufnr('') endif - if ale#ShouldDoNothing(l:buffer) + let l:now = ale#util#ClockMilliseconds() + + if l:now < s:dont_lint_until + return + endif + + let s:dont_lint_until = l:now + s:queue_error_delay_ms + + call s:ALELintImpl(l:buffer) + + let s:dont_lint_until = -1 +endfunction + +function! s:ALELintImpl(buffer) abort + if ale#ShouldDoNothing(a:buffer) return endif " Use the filetype from the buffer - let l:linters = ale#linter#Get(getbufvar(l:buffer, '&filetype')) + let l:linters = ale#linter#Get(getbufvar(a:buffer, '&filetype')) let l:should_lint_file = 0 " Check if we previously requested checking the file. - if has_key(s:should_lint_file_for_buffer, l:buffer) - unlet s:should_lint_file_for_buffer[l:buffer] + if has_key(s:should_lint_file_for_buffer, a:buffer) + unlet s:should_lint_file_for_buffer[a:buffer] " Lint files if they exist. - let l:should_lint_file = filereadable(expand('#' . l:buffer . ':p')) + let l:should_lint_file = filereadable(expand('#' . a:buffer . ':p')) endif - call ale#engine#RunLinters(l:buffer, l:linters, l:should_lint_file) + call ale#engine#RunLinters(a:buffer, l:linters, l:should_lint_file) endfunction " Reset flags indicating that files should be checked for all buffers. @@ -115,6 +151,11 @@ function! ale#ResetLintFileMarkers() abort let s:should_lint_file_for_buffer = {} endfunction +function! ale#ResetErrorDelays() abort + let s:dont_queue_until = -1 + let s:dont_lint_until = -1 +endfunction + let g:ale_has_override = get(g:, 'ale_has_override', {}) " Call has(), but check a global Dictionary so we can force flags on or off diff --git a/doc/ale.txt b/doc/ale.txt index 0550e4de..b0323f85 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -1180,6 +1180,12 @@ ale#Queue(delay, [linting_flag, buffer_number]) *ale#Queue()* An optional `buffer_number` argument can be given for specifying the buffer to check. The active buffer (`bufnr('')`) will be checked by default. + *ale-cool-down* + If an exception is thrown when queuing/running ALE linters, ALE will enter + a cool down period where it will stop checking anything for a short period + of time. This is to prevent ALE from seriously annoying users if a linter + is broken, or when developing ALE itself. + ale#engine#CreateDirectory(buffer) *ale#engine#CreateDirectory()* diff --git a/test/test_lint_error_delay.vader b/test/test_lint_error_delay.vader new file mode 100644 index 00000000..32d82715 --- /dev/null +++ b/test/test_lint_error_delay.vader @@ -0,0 +1,18 @@ +Before: + Save g:ale_filetype_blacklist + + " Delete some variable which should be defined. + unlet! g:ale_filetype_blacklist + +After: + Restore + + call ale#ResetErrorDelays() + +Execute(ALE should stop queuing for a while after exceptions are thrown): + AssertThrows call ale#Queue(100) + call ale#Queue(100) + +Execute(ALE should stop linting for a while after exceptions are thrown): + AssertThrows call ale#Lint() + call ale#Lint()