From 7481facd7373dda4667c401695e6d8000ef52362 Mon Sep 17 00:00:00 2001 From: w0rp Date: Sun, 23 Oct 2016 22:41:00 +0100 Subject: [PATCH 1/4] #107 Stop jobs when buffers close --- autoload/ale.vim | 5 ++- autoload/ale/cleanup.vim | 15 +++----- autoload/ale/engine.vim | 60 +++++++++++++++++++++--------- autoload/ale/sign.vim | 8 ++-- autoload/ale/statusline.vim | 40 +++++++++++++------- plugin/ale.vim | 4 +- test/test_cleanup.vader | 23 +++--------- test/test_linting_sets_signs.vader | 40 ++++++++++++++++++++ test/test_statusline.vader | 52 +++++++++++++++----------- 9 files changed, 161 insertions(+), 86 deletions(-) create mode 100644 test/test_linting_sets_signs.vader diff --git a/autoload/ale.vim b/autoload/ale.vim index 44cd5825..3e86c8f9 100644 --- a/autoload/ale.vim +++ b/autoload/ale.vim @@ -27,8 +27,11 @@ function! ale#Lint(...) abort let l:buffer = bufnr('%') let l:linters = ale#linter#Get(&filetype) + " Initialise the buffer information if needed. + call ale#engine#InitBufferInfo(l:buffer) + " Set a variable telling us to clear the loclist later. - let g:ale_buffer_should_reset_map[l:buffer] = 1 + let g:ale_buffer_info[l:buffer].should_reset = 1 for l:linter in l:linters " Check if a given linter has a program which can be executed. diff --git a/autoload/ale/cleanup.vim b/autoload/ale/cleanup.vim index 45eccb98..40a8a359 100644 --- a/autoload/ale/cleanup.vim +++ b/autoload/ale/cleanup.vim @@ -2,19 +2,16 @@ " Description: Utility functions related to cleaning state. function! ale#cleanup#Buffer(buffer) abort - if has_key(g:ale_buffer_count_map, a:buffer) - call remove(g:ale_buffer_count_map, a:buffer) - endif - if has_key(g:ale_buffer_loclist_map, a:buffer) call remove(g:ale_buffer_loclist_map, a:buffer) endif - if has_key(g:ale_buffer_should_reset_map, a:buffer) - call remove(g:ale_buffer_should_reset_map, a:buffer) - endif + if has_key(g:ale_buffer_info, a:buffer) + " When buffers are removed, clear all of the jobs. + for l:job in get(g:ale_buffer_info[a:buffer], 'job_list', []) + call ale#engine#ClearJob(l:job) + endfor - if has_key(g:ale_buffer_sign_dummy_map, a:buffer) - call remove(g:ale_buffer_sign_dummy_map, a:buffer) + call remove(g:ale_buffer_info, a:buffer) endif endfunction diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim index 5cdcd7ce..3af60ab6 100644 --- a/autoload/ale/engine.vim +++ b/autoload/ale/engine.vim @@ -20,7 +20,17 @@ function! s:GetJobID(job) abort return ch_info(job_getchannel(a:job)).id endfunction -function! s:ClearJob(job) abort +function! ale#engine#InitBufferInfo(buffer) abort + if !has_key(g:ale_buffer_info, a:buffer) + let g:ale_buffer_info[a:buffer] = { + \ 'job_list': [], + \ 'should_reset': 1, + \ 'dummy_sign_set': 0, + \} + endif +endfunction + +function! ale#engine#ClearJob(job) abort let l:job_id = s:GetJobID(a:job) let l:linter = s:job_info_map[l:job_id].linter @@ -37,7 +47,26 @@ function! s:ClearJob(job) abort endif call remove(s:job_info_map, l:job_id) - call remove(l:linter, 'job') +endfunction + +function! s:StopPreviousJobs(buffer, linter) abort + let l:new_job_list = [] + + for l:job in g:ale_buffer_info[a:buffer].job_list + let l:job_id = s:GetJobID(l:job) + + if has_key(s:job_info_map, l:job_id) + \&& s:job_info_map[l:job_id].linter.name ==# a:linter.name + " Stop jobs which match the buffer and linter. + call ale#engine#ClearJob(l:job) + else + " Keep other jobs in the list. + call add(l:new_job_list, l:job) + endif + endfor + + " Update the list, removing the previously run job. + let g:ale_buffer_info[a:buffer].job_list = l:new_job_list endfunction function! s:GatherOutput(job, data) abort @@ -71,17 +100,11 @@ function! s:HandleExit(job) abort endif let l:job_info = s:job_info_map[l:job_id] - - call s:ClearJob(a:job) - let l:linter = l:job_info.linter let l:output = l:job_info.output let l:buffer = l:job_info.buffer - if !has_key(g:ale_buffer_should_reset_map, l:buffer) - " A job ended for a buffer which has been closed, so stop here. - return - endif + call s:StopPreviousJobs(l:buffer, l:linter) let l:linter_loclist = ale#util#GetFunction(l:linter.callback)(l:buffer, l:output) @@ -92,8 +115,10 @@ function! s:HandleExit(job) abort let l:item.linter_name = l:linter.name endfor - if g:ale_buffer_should_reset_map[l:buffer] - let g:ale_buffer_should_reset_map[l:buffer] = 0 + if g:ale_buffer_info[l:buffer].should_reset + " Set the flag for resetting the loclist to 0 again, so we won't + " empty the list later. + let g:ale_buffer_info[l:buffer].should_reset = 0 let g:ale_buffer_loclist_map[l:buffer] = [] endif @@ -150,10 +175,8 @@ function! s:FixLocList(buffer, loclist) abort endfunction function! ale#engine#Invoke(buffer, linter) abort - if has_key(a:linter, 'job') - " Stop previous jobs for the same linter. - call s:ClearJob(a:linter.job) - endif + " Stop previous jobs for the same linter. + call s:StopPreviousJobs(a:buffer, a:linter) if has_key(a:linter, 'command_callback') " If there is a callback for generating a command, call that instead. @@ -227,7 +250,8 @@ function! ale#engine#Invoke(buffer, linter) abort " Only proceed if the job is being run. if has('nvim') || (l:job !=# 'no process' && job_status(l:job) ==# 'run') - let a:linter.job = l:job + " Add the job to the list of jobs, so we can track them. + call add(g:ale_buffer_info[a:buffer].job_list, l:job) " Store the ID for the job in the map to read back again. let s:job_info_map[s:GetJobID(l:job)] = { @@ -270,8 +294,8 @@ function! ale#engine#WaitForJobs(deadline) abort let l:job_list = [] - for l:job_id in keys(s:job_info_map) - call add(l:job_list, s:job_info_map[l:job_id].linter.job) + for l:info in values(g:ale_buffer_info) + call extend(l:job_list, l:info.job_list) endfor let l:should_wait_more = 1 diff --git a/autoload/ale/sign.vim b/autoload/ale/sign.vim index 23666287..deec47f2 100644 --- a/autoload/ale/sign.vim +++ b/autoload/ale/sign.vim @@ -89,13 +89,13 @@ function! ale#sign#SetSigns(buffer, loclist) abort let l:signlist = ale#sign#CombineSigns(a:loclist) if len(l:signlist) > 0 || g:ale_sign_column_always - if !get(g:ale_buffer_sign_dummy_map, a:buffer, 0) + if !g:ale_buffer_info[a:buffer].dummy_sign_set " Insert a dummy sign if one is missing. execute 'sign place ' . g:ale_sign_offset \ . ' line=1 name=ALEDummySign buffer=' \ . a:buffer - let g:ale_buffer_sign_dummy_map[a:buffer] = 1 + let g:ale_buffer_info[a:buffer].dummy_sign_set = 1 endif endif @@ -121,10 +121,10 @@ function! ale#sign#SetSigns(buffer, loclist) abort endfor if !g:ale_sign_column_always && len(l:signlist) > 0 - if get(g:ale_buffer_sign_dummy_map, a:buffer, 0) + if g:ale_buffer_info[a:buffer].dummy_sign_set execute 'sign unplace ' . g:ale_sign_offset . ' buffer=' . a:buffer - let g:ale_buffer_sign_dummy_map[a:buffer] = 0 + let g:ale_buffer_info[a:buffer].dummy_sign_set = 0 endif endif endfunction diff --git a/autoload/ale/statusline.vim b/autoload/ale/statusline.vim index 8a31bc39..48b4d687 100644 --- a/autoload/ale/statusline.vim +++ b/autoload/ale/statusline.vim @@ -14,34 +14,48 @@ function! ale#statusline#Update(buffer, loclist) abort endif endfor - let g:ale_buffer_count_map[a:buffer] = [l:errors, l:warnings] + let g:ale_buffer_info[a:buffer].count = [l:errors, l:warnings] +endfunction + +" Set the error and warning counts, calling for an update only if needed. +" If counts cannot be set, return 0. +function! s:SetupCount(buffer) abort + if !has_key(g:ale_buffer_info, a:buffer) + " Linters have not been run for the buffer yet, so stop here. + return 0 + endif + + " Cache is cold, so manually ask for an update. + if !has_key(g:ale_buffer_info[a:buffer], 'count') + call ale#statusline#Update(a:buffer, get(g:ale_buffer_loclist_map, a:buffer, [])) + endif + + return 1 endfunction " Returns a tuple of errors and warnings for use in third-party integrations. function! ale#statusline#Count(buffer) abort - " Cache is cold, so manually ask for an update. - if !has_key(g:ale_buffer_count_map, a:buffer) - call ale#statusline#Update(a:buffer, get(g:ale_buffer_loclist_map, a:buffer, [])) + if !s:SetupCount(a:buffer) + return [0, 0] endif - return g:ale_buffer_count_map[a:buffer] + return g:ale_buffer_info[a:buffer].count endfunction " Returns a formatted string that can be integrated in the statusline. function! ale#statusline#Status() abort + let [l:error_format, l:warning_format, l:no_errors] = g:ale_statusline_format let l:buffer = bufnr('%') - " Cache is cold, so manually ask for an update. - if !has_key(g:ale_buffer_count_map, l:buffer) - call ale#statusline#Update(l:buffer, get(g:ale_buffer_loclist_map, l:buffer, [])) + if !s:SetupCount(l:buffer) + return l:no_errors endif + let [l:error_count, l:warning_count] = g:ale_buffer_info[l:buffer].count + " Build strings based on user formatting preferences. - let l:errors = g:ale_buffer_count_map[l:buffer][0] ? - \ printf(g:ale_statusline_format[0], g:ale_buffer_count_map[l:buffer][0]) : '' - let l:warnings = g:ale_buffer_count_map[l:buffer][1] ? - \ printf(g:ale_statusline_format[1], g:ale_buffer_count_map[l:buffer][1]) : '' - let l:no_errors = g:ale_statusline_format[2] + let l:errors = l:error_count ? printf(l:error_format, l:error_count) : '' + let l:warnings = l:warning_count ? printf(l:warning_format, l:warning_count) : '' " Different formats based on the combination of errors and warnings. if empty(l:errors) && empty(l:warnings) diff --git a/plugin/ale.vim b/plugin/ale.vim index d1c29902..7e88f688 100644 --- a/plugin/ale.vim +++ b/plugin/ale.vim @@ -24,10 +24,8 @@ endif " Globals -let g:ale_buffer_count_map = {} let g:ale_buffer_loclist_map = {} -let g:ale_buffer_should_reset_map = {} -let g:ale_buffer_sign_dummy_map = {} +let g:ale_buffer_info = {} " User Configuration diff --git a/test/test_cleanup.vader b/test/test_cleanup.vader index 4f6827f4..a700d6b5 100644 --- a/test/test_cleanup.vader +++ b/test/test_cleanup.vader @@ -1,32 +1,21 @@ Before: let g:buffer = bufnr('%') - let g:ale_buffer_count_map = { - \ g:buffer: [1, 1], - \ 10347: [1, 1], - \} let g:ale_buffer_loclist_map = { \ g:buffer : [], \ 10347: [], \} - let g:ale_buffer_should_reset_map = { - \ g:buffer : 1, - \ 10347: 1, - \} - let g:ale_buffer_sign_dummy_map = { - \ g:buffer : 1, - \ 10347: 1, + + let g:ale_buffer_info = { + \ g:buffer : {}, + \ 10347: {}, \} After: unlet! g:buffer - let g:ale_buffer_count_map = {} let g:ale_buffer_loclist_map = {} - let g:ale_buffer_should_reset_map = {} - let g:ale_buffer_sign_dummy_map = {} + let g:ale_buffer_info = {} Execute('ALE globals should be cleared when the buffer is closed.'): :q! - AssertEqual {10347: [1, 1]}, g:ale_buffer_count_map AssertEqual {10347: []}, g:ale_buffer_loclist_map - AssertEqual {10347: 1}, g:ale_buffer_should_reset_map - AssertEqual {10347: 1}, g:ale_buffer_sign_dummy_map + AssertEqual {10347: {}}, g:ale_buffer_info diff --git a/test/test_linting_sets_signs.vader b/test/test_linting_sets_signs.vader new file mode 100644 index 00000000..1f323fe8 --- /dev/null +++ b/test/test_linting_sets_signs.vader @@ -0,0 +1,40 @@ +Given javascript (Some JavaScript with problems): + var y = 3+3; + var y = 3 + +Before: + sign unplace * + let g:actual_sign_list = [] + let g:expected_sign_list = [ + \ ['1', 'ALEWarningSign'], + \ ['2', 'ALEErrorSign'], + \] + + function! g:CollectSigns() + redir => l:output + silent exec 'sign place' + redir END + + for l:line in split(l:output, "\n") + let l:match = matchlist(l:line, 'line=\(\d\+\).*name=\(ALE[a-zA-Z]\+\)') + + if len(l:match) > 0 + call add(g:actual_sign_list, [l:match[1], l:match[2]]) + endif + endfor + endfunction + +After: + sign unplace * + let g:ale_buffer_loclist_map = {} + let g:ale_buffer_info = {} + delfunction g:CollectSigns + unlet g:actual_sign_list + unlet g:expected_sign_list + +Execute(The signs should be updated after linting is done): + call ale#Lint() + call ale#engine#WaitForJobs(2000) + call g:CollectSigns() + + AssertEqual g:expected_sign_list, g:actual_sign_list diff --git a/test/test_statusline.vader b/test/test_statusline.vader index da4c693f..43245e97 100644 --- a/test/test_statusline.vader +++ b/test/test_statusline.vader @@ -1,49 +1,59 @@ Before: let g:ale_buffer_loclist_map = {} + let g:ale_statusline_format = ['%sE', '%sW', 'OKIE'] + +After: + let g:ale_buffer_loclist_map = {} + let g:ale_buffer_info = {} Execute (Count should be 0 when data is empty): + let g:ale_buffer_info = {} AssertEqual [0, 0], ale#statusline#Count(bufnr('%')) -Before: - let g:ale_buffer_count_map = {'44': [1, 2]} - Execute (Count should read data from the cache): + let g:ale_buffer_info = {'44': {'count': [1, 2]}} AssertEqual [1, 2], ale#statusline#Count(44) -Execute (Update the cache with new data): +Execute (The count should be correct after an update): + let g:ale_buffer_info = {'44': {}} call ale#statusline#Update(44, []) - -Then (The cache should reflect the new data): AssertEqual [0, 0], ale#statusline#Count(44) -Before: - let g:ale_buffer_loclist_map = {'1': [{'lnum': 1, 'bufnr': 1, 'vcol': 0, 'linter_name': 'testlinter', 'nr': -1, 'type': 'E', 'col': 1, 'text': 'Test Error'}]} - Execute (Count should be match the loclist): - AssertEqual [1, 0], ale#statusline#Count(1) + let g:ale_buffer_info = {bufnr('%'): {}} + let g:ale_buffer_loclist_map = {bufnr('%'): [ + \ { + \ 'lnum': 1, + \ 'bufnr': 1, + \ 'vcol': 0, + \ 'linter_name': 'testlinter', + \ 'nr': -1, + \ 'type': 'E', + \ 'col': 1, + \ 'text': 'Test Error', + \ }, + \]} + AssertEqual [1, 0], ale#statusline#Count(bufnr('%')) Execute (Output should be empty for non-existant buffer): AssertEqual [0, 0], ale#statusline#Count(9001) -Before: - let g:ale_statusline_format = ['%sE', '%sW', 'OKIE'] - -Execute (Given some errors): +Execute (Statusline is formatted to the users preference for just errors): + let g:ale_buffer_info = {bufnr('%'): {}} call ale#statusline#Update(bufnr('%'), [{'type': 'E'}, {'type': 'E'}]) -Then (Statusline is formatted to the users preference): AssertEqual '2E', ale#statusline#Status() -Execute (Given some warnings): +Execute (Statusline is formatted to the users preference for just warnings): + let g:ale_buffer_info = {bufnr('%'): {}} call ale#statusline#Update(bufnr('%'), [{'type': 'W'}, {'type': 'W'}, {'type': 'W'}]) -Then (Statusline is formatted to the users preference): AssertEqual '3W', ale#statusline#Status() -Execute (Given some warnings, and errors): +Execute (Statusline is formatted to the users preference for errors and warnings): + let g:ale_buffer_info = {bufnr('%'): {}} call ale#statusline#Update(bufnr('%'), [{'type': 'E'}, {'type': 'W'}, {'type': 'W'}]) -Then (Statusline is formatted to the users preference): AssertEqual '1E 2W', ale#statusline#Status() -Execute (Given a lack of data): +Execute (Statusline is formatted to the users preference for no errors or warnings): + let g:ale_buffer_info = {bufnr('%'): {}} call ale#statusline#Update(bufnr('%'), []) -Then (Statusline is formatted to the users preference): AssertEqual 'OKIE', ale#statusline#Status() From 0dbf08f6d5466262e66a6000d83be7270a9cef53 Mon Sep 17 00:00:00 2001 From: w0rp Date: Mon, 24 Oct 2016 09:58:45 +0100 Subject: [PATCH 2/4] Add some comments to some lines to make things clearer. --- autoload/ale/engine.vim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim index 3af60ab6..5a257505 100644 --- a/autoload/ale/engine.vim +++ b/autoload/ale/engine.vim @@ -104,6 +104,8 @@ function! s:HandleExit(job) abort let l:output = l:job_info.output let l:buffer = l:job_info.buffer + " Call the same function for stopping jobs again to clean up the job + " which just closed. call s:StopPreviousJobs(l:buffer, l:linter) let l:linter_loclist = ale#util#GetFunction(l:linter.callback)(l:buffer, l:output) @@ -294,6 +296,7 @@ function! ale#engine#WaitForJobs(deadline) abort let l:job_list = [] + " Gather all of the jobs from every buffer. for l:info in values(g:ale_buffer_info) call extend(l:job_list, l:info.job_list) endfor From c546f47cc0c003053fb157b0ca2cced46b5429bc Mon Sep 17 00:00:00 2001 From: w0rp Date: Mon, 24 Oct 2016 20:21:32 +0100 Subject: [PATCH 3/4] Merge everything into the one global map. --- autoload/ale/cleanup.vim | 4 --- autoload/ale/cursor.vim | 4 +-- autoload/ale/engine.vim | 22 ++++++++++---- autoload/ale/statusline.vim | 2 +- doc/ale.txt | 24 +++++----------- plugin/ale.vim | 5 ++-- test/test_alelint_autocmd.vader | 3 ++ test/test_cleanup.vader | 6 ---- test/test_cursor_warnings.vader | 38 +++++++++++++------------ test/test_get_loclist.vader | 31 ++++++++++++++++++++ test/test_linting_sets_signs.vader | 1 - test/test_linting_updates_loclist.vader | 28 +++++++++++++++--- test/test_statusline.vader | 31 ++++++++++---------- 13 files changed, 122 insertions(+), 77 deletions(-) create mode 100644 test/test_get_loclist.vader diff --git a/autoload/ale/cleanup.vim b/autoload/ale/cleanup.vim index 40a8a359..0a1cd391 100644 --- a/autoload/ale/cleanup.vim +++ b/autoload/ale/cleanup.vim @@ -2,10 +2,6 @@ " Description: Utility functions related to cleaning state. function! ale#cleanup#Buffer(buffer) abort - if has_key(g:ale_buffer_loclist_map, a:buffer) - call remove(g:ale_buffer_loclist_map, a:buffer) - endif - if has_key(g:ale_buffer_info, a:buffer) " When buffers are removed, clear all of the jobs. for l:job in get(g:ale_buffer_info[a:buffer], 'job_list', []) diff --git a/autoload/ale/cursor.vim b/autoload/ale/cursor.vim index a20212c1..7f490339 100644 --- a/autoload/ale/cursor.vim +++ b/autoload/ale/cursor.vim @@ -46,12 +46,12 @@ function! ale#cursor#EchoCursorWarning(...) abort let l:buffer = bufnr('%') - if !has_key(g:ale_buffer_loclist_map, l:buffer) + if !has_key(g:ale_buffer_info, l:buffer) return endif let l:pos = getcurpos() - let l:loclist = g:ale_buffer_loclist_map[l:buffer] + let l:loclist = g:ale_buffer_info[l:buffer].loclist let l:index = ale#util#BinarySearch(l:loclist, l:pos[1], l:pos[2]) if l:index >= 0 diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim index 5a257505..9af1f536 100644 --- a/autoload/ale/engine.vim +++ b/autoload/ale/engine.vim @@ -26,6 +26,7 @@ function! ale#engine#InitBufferInfo(buffer) abort \ 'job_list': [], \ 'should_reset': 1, \ 'dummy_sign_set': 0, + \ 'loclist': [], \} endif endfunction @@ -121,28 +122,28 @@ function! s:HandleExit(job) abort " Set the flag for resetting the loclist to 0 again, so we won't " empty the list later. let g:ale_buffer_info[l:buffer].should_reset = 0 - let g:ale_buffer_loclist_map[l:buffer] = [] + let g:ale_buffer_info[l:buffer].loclist = [] endif " Add the loclist items from the linter. - call extend(g:ale_buffer_loclist_map[l:buffer], l:linter_loclist) + call extend(g:ale_buffer_info[l:buffer].loclist, l:linter_loclist) " Sort the loclist again. " We need a sorted list so we can run a binary search against it " for efficient lookup of the messages in the cursor handler. - call sort(g:ale_buffer_loclist_map[l:buffer], 'ale#util#LocItemCompare') + call sort(g:ale_buffer_info[l:buffer].loclist, 'ale#util#LocItemCompare') if g:ale_set_loclist - call setloclist(0, g:ale_buffer_loclist_map[l:buffer]) + call setloclist(0, g:ale_buffer_info[l:buffer].loclist) endif if g:ale_set_signs - call ale#sign#SetSigns(l:buffer, g:ale_buffer_loclist_map[l:buffer]) + call ale#sign#SetSigns(l:buffer, g:ale_buffer_info[l:buffer].loclist) endif if exists('*ale#statusline#Update') " Don't load/run if not already loaded. - call ale#statusline#Update(l:buffer, g:ale_buffer_loclist_map[l:buffer]) + call ale#statusline#Update(l:buffer, g:ale_buffer_info[l:buffer].loclist) endif " Call user autocommands. This allows users to hook into ALE's lint cycle. @@ -281,6 +282,15 @@ function! ale#engine#Invoke(buffer, linter) abort endif endfunction +" Given a buffer number, return the warnings and errors for a given buffer. +function! ale#engine#GetLoclist(buffer) abort + if !has_key(g:ale_buffer_info, a:buffer) + return [] + endif + + return g:ale_buffer_info[a:buffer].loclist +endfunction + " This function can be called with a timeout to wait for all jobs to finish. " If the jobs to not finish in the given number of milliseconds, " an exception will be thrown. diff --git a/autoload/ale/statusline.vim b/autoload/ale/statusline.vim index 48b4d687..c01dd34d 100644 --- a/autoload/ale/statusline.vim +++ b/autoload/ale/statusline.vim @@ -27,7 +27,7 @@ function! s:SetupCount(buffer) abort " Cache is cold, so manually ask for an update. if !has_key(g:ale_buffer_info[a:buffer], 'count') - call ale#statusline#Update(a:buffer, get(g:ale_buffer_loclist_map, a:buffer, [])) + call ale#statusline#Update(a:buffer, g:ale_buffer_info[a:buffer].loclist) endif return 1 diff --git a/doc/ale.txt b/doc/ale.txt index c879a98b..ca558f26 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -145,23 +145,6 @@ g:ale_linter_aliases *g:ale_linter_aliases* different set of linters from the type it is being mapped to. -g:ale_buffer_loclist_map *g:ale_buffer_loclist_map* - - Type: |Dictionary| - Default: `{}` - - This variable is used internally by ALE for tracking the warnings and - errors for a particular buffer. The dictionary maps a buffer number to - a |List| of |Dictionary| items in the format accepted by |setqflist()|, - with a minor addition of a `linter_name` for each object which describes - the linter which reported the warnings and errors. (A buffer may run - multiple linters in combination on the same filetype.) - - NOTE: This variable should not be modified outside of the plugin itself, - but can be read in other plugins whenever information about the current - errors and warnings ALE is reporting is needed. - - g:ale_lint_on_text_changed *g:ale_lint_on_text_changed* Type: |Number| @@ -556,6 +539,13 @@ ale#Queue(delay) *ale#Queue()* again from the same buffer +ale#engine#GetLoclist(buffer) *ale#engine#GetLoclist()* + + Given a buffer number, this function will rerurn the list of warnings and + errors reported by ALE for a given buffer in the format accepted by + |setqflist()|. + + ale#linter#Define(filetype, linter) *ale#linter#Define()* Given a |String| for a filetype and a |Dictionary| Describing a linter configuration, add a linter for the given filetype. The dictionaries each diff --git a/plugin/ale.vim b/plugin/ale.vim index 7e88f688..289a9d94 100644 --- a/plugin/ale.vim +++ b/plugin/ale.vim @@ -22,9 +22,8 @@ if !s:ale_has_required_features finish endif -" Globals - -let g:ale_buffer_loclist_map = {} +" This global variable is used internally by ALE for tracking information for +" each buffer which linters are being run against. let g:ale_buffer_info = {} " User Configuration diff --git a/test/test_alelint_autocmd.vader b/test/test_alelint_autocmd.vader index 86772b61..22fe1935 100644 --- a/test/test_alelint_autocmd.vader +++ b/test/test_alelint_autocmd.vader @@ -7,11 +7,14 @@ Before: After: augroup! VaderTest + let g:ale_buffer_info = {} Given vim (Some vimscript): set nocompatible + Execute (Lint it): call ale#Lint() call ale#engine#WaitForJobs(2000) + Then (Autocommands should have run): AssertEqual g:success, 1 diff --git a/test/test_cleanup.vader b/test/test_cleanup.vader index a700d6b5..ec2b38aa 100644 --- a/test/test_cleanup.vader +++ b/test/test_cleanup.vader @@ -1,9 +1,5 @@ Before: let g:buffer = bufnr('%') - let g:ale_buffer_loclist_map = { - \ g:buffer : [], - \ 10347: [], - \} let g:ale_buffer_info = { \ g:buffer : {}, @@ -12,10 +8,8 @@ Before: After: unlet! g:buffer - let g:ale_buffer_loclist_map = {} let g:ale_buffer_info = {} Execute('ALE globals should be cleared when the buffer is closed.'): :q! - AssertEqual {10347: []}, g:ale_buffer_loclist_map AssertEqual {10347: {}}, g:ale_buffer_info diff --git a/test/test_cursor_warnings.vader b/test/test_cursor_warnings.vader index 364db4bc..b12f2451 100644 --- a/test/test_cursor_warnings.vader +++ b/test/test_cursor_warnings.vader @@ -1,17 +1,18 @@ Before: - let g:ale_buffer_loclist_map = { - \ bufnr('%'): [ - \ { - \ 'lnum': 1, - \ 'bufnr': bufnr('%'), - \ 'vcol': 0, - \ 'linter_name': 'eslint', - \ 'nr': -1, - \ 'type': 'E', - \ 'col': 10, - \ 'text': 'Missing semicolon. (semi)' - \ }, - \ { + let g:ale_buffer_info = { + \ bufnr('%'): { + \ 'loclist': [ + \ { + \ 'lnum': 1, + \ 'bufnr': bufnr('%'), + \ 'vcol': 0, + \ 'linter_name': 'eslint', + \ 'nr': -1, + \ 'type': 'E', + \ 'col': 10, + \ 'text': 'Missing semicolon. (semi)' + \ }, + \ { \ 'lnum': 2, \ 'bufnr': bufnr('%'), \ 'vcol': 0, @@ -20,8 +21,8 @@ Before: \ 'type': 'W', \ 'col': 10, \ 'text': 'Infix operators must be spaced. (space-infix-ops)' - \ }, - \ { + \ }, + \ { \ 'lnum': 2, \ 'bufnr': bufnr('%'), \ 'vcol': 0, @@ -30,14 +31,15 @@ Before: \ 'type': 'E', \ 'col': 15, \ 'text': 'Missing radix parameter (radix)' - \ } - \ ], + \ } + \ ], + \ }, \} After: unlet! g:output unlet! g:lines - let g:ale_buffer_loclist_map = {} + let g:ale_buffer_info = {} Given javascript(A Javscript file with warnings/errors): var x = 3 diff --git a/test/test_get_loclist.vader b/test/test_get_loclist.vader new file mode 100644 index 00000000..14696998 --- /dev/null +++ b/test/test_get_loclist.vader @@ -0,0 +1,31 @@ +Before: + let g:loclist = [ + \ { + \ 'lnum': 1, + \ 'bufnr': bufnr('%'), + \ 'vcol': 0, + \ 'linter_name': 'eslint', + \ 'nr': -1, + \ 'type': 'E', + \ 'col': 10, + \ 'text': 'Missing semicolon. (semi)' + \ }, + \ { + \ 'lnum': 2, + \ 'bufnr': bufnr('%'), + \ 'vcol': 0, + \ 'linter_name': 'eslint', + \ 'nr': -1, + \ 'type': 'W', + \ 'col': 10, + \ 'text': 'Infix operators must be spaced. (space-infix-ops)' + \ }, + \] + let g:ale_buffer_info = {'1': {'loclist': g:loclist}} + +After: + unlet g:loclist + let g:ale_buffer_info = {} + +Execute(GetLoclist should return the loclist): + AssertEqual g:loclist, ale#engine#GetLoclist(1) diff --git a/test/test_linting_sets_signs.vader b/test/test_linting_sets_signs.vader index 1f323fe8..e96e490b 100644 --- a/test/test_linting_sets_signs.vader +++ b/test/test_linting_sets_signs.vader @@ -26,7 +26,6 @@ Before: After: sign unplace * - let g:ale_buffer_loclist_map = {} let g:ale_buffer_info = {} delfunction g:CollectSigns unlet g:actual_sign_list diff --git a/test/test_linting_updates_loclist.vader b/test/test_linting_updates_loclist.vader index 920a4e46..06f11d8b 100644 --- a/test/test_linting_updates_loclist.vader +++ b/test/test_linting_updates_loclist.vader @@ -3,15 +3,35 @@ Given javascript (Some JavaScript with problems): var y = 3 Before: - let g:ale_buffer_loclist_map = {} - let g:expected_data = {bufnr('%'): [{'lnum': 1, 'bufnr': bufnr('%'), 'vcol': 0, 'linter_name': 'eslint', 'nr': -1, 'type': 'W', 'col': 10, 'text': 'Infix operators must be spaced. [Warning/space-infix-ops]'}, {'lnum': 2, 'bufnr': bufnr('%'), 'vcol': 0, 'linter_name': 'eslint', 'nr': -1, 'type': 'E', 'col': 10, 'text': 'Missing semicolon. [Error/semi]'}]} + let g:expected_data = [ + \ { + \ 'lnum': 1, + \ 'bufnr': bufnr('%'), + \ 'vcol': 0, + \ 'linter_name': 'eslint', + \ 'nr': -1, + \ 'type': 'W', + \ 'col': 10, + \ 'text': 'Infix operators must be spaced. [Warning/space-infix-ops]', + \ }, + \ { + \ 'lnum': 2, + \ 'bufnr': bufnr('%'), + \ 'vcol': 0, + \ 'linter_name': 'eslint', + \ 'nr': -1, + \ 'type': 'E', + \ 'col': 10, + \ 'text': 'Missing semicolon. [Error/semi]', + \ } + \] After: - let g:ale_buffer_loclist_map = {} unlet g:expected_data Execute(The loclist should be updated after linting is done): call ale#Lint() call ale#engine#WaitForJobs(2000) - AssertEqual g:expected_data, g:ale_buffer_loclist_map + AssertEqual ['' . bufnr('%')], keys(g:ale_buffer_info) + AssertEqual g:expected_data, g:ale_buffer_info[bufnr('%')].loclist diff --git a/test/test_statusline.vader b/test/test_statusline.vader index 43245e97..05e60479 100644 --- a/test/test_statusline.vader +++ b/test/test_statusline.vader @@ -1,9 +1,7 @@ Before: - let g:ale_buffer_loclist_map = {} let g:ale_statusline_format = ['%sE', '%sW', 'OKIE'] After: - let g:ale_buffer_loclist_map = {} let g:ale_buffer_info = {} Execute (Count should be 0 when data is empty): @@ -20,19 +18,22 @@ Execute (The count should be correct after an update): AssertEqual [0, 0], ale#statusline#Count(44) Execute (Count should be match the loclist): - let g:ale_buffer_info = {bufnr('%'): {}} - let g:ale_buffer_loclist_map = {bufnr('%'): [ - \ { - \ 'lnum': 1, - \ 'bufnr': 1, - \ 'vcol': 0, - \ 'linter_name': 'testlinter', - \ 'nr': -1, - \ 'type': 'E', - \ 'col': 1, - \ 'text': 'Test Error', - \ }, - \]} + let g:ale_buffer_info = { + \ bufnr('%'): { + \ 'loclist': [ + \ { + \ 'lnum': 1, + \ 'bufnr': 1, + \ 'vcol': 0, + \ 'linter_name': 'testlinter', + \ 'nr': -1, + \ 'type': 'E', + \ 'col': 1, + \ 'text': 'Test Error', + \ }, + \ ], + \ }, + \} AssertEqual [1, 0], ale#statusline#Count(bufnr('%')) Execute (Output should be empty for non-existant buffer): From 73c9a1f96539481536de56fa2bd4c005c1af15ed Mon Sep 17 00:00:00 2001 From: w0rp Date: Tue, 25 Oct 2016 14:09:58 +0100 Subject: [PATCH 4/4] #148 Do not run ALE for NERDTree or Unite.vim buffers. --- plugin/ale.vim | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/plugin/ale.vim b/plugin/ale.vim index 289a9d94..64c20c66 100644 --- a/plugin/ale.vim +++ b/plugin/ale.vim @@ -7,6 +7,7 @@ if exists('g:loaded_ale') finish endif + let g:loaded_ale = 1 " A flag for detecting if the required features are set. @@ -28,6 +29,19 @@ let g:ale_buffer_info = {} " User Configuration +" This option prevents ALE autocmd commands from being run for particular +" filetypes which can cause issues. +let g:ale_filetype_blacklist = ['nerdtree', 'unite'] + +" This function lets you define autocmd commands which blacklist particular +" filetypes. +function! ALEAutoCMD(events, function_call) + execute 'autocmd ' + \ . a:events + \ ' * if index(g:ale_filetype_blacklist, &filetype) < 0 | call ' + \ . a:function_call +endfunction + " This Dictionary configures which linters are enabled for which languages. let g:ale_linters = get(g:, 'ale_linters', {}) @@ -45,7 +59,7 @@ let g:ale_lint_on_text_changed = get(g:, 'ale_lint_on_text_changed', 1) if g:ale_lint_on_text_changed augroup ALERunOnTextChangedGroup autocmd! - autocmd TextChanged,TextChangedI * call ale#Queue(g:ale_lint_delay) + call ALEAutoCMD('TextChanged,TextChangedI', 'ale#Queue(g:ale_lint_delay)') augroup END endif @@ -54,7 +68,7 @@ let g:ale_lint_on_enter = get(g:, 'ale_lint_on_enter', 1) if g:ale_lint_on_enter augroup ALERunOnEnterGroup autocmd! - autocmd BufEnter,BufRead * call ale#Queue(100) + call ALEAutoCMD('BufEnter,BufRead', 'ale#Queue(100)') augroup END endif @@ -63,7 +77,7 @@ let g:ale_lint_on_save = get(g:, 'ale_lint_on_save', 0) if g:ale_lint_on_save augroup ALERunOnSaveGroup autocmd! - autocmd BufWrite * call ale#Queue(0) + call ALEAutoCMD('BufWrite', 'ale#Queue(0)') augroup END endif @@ -100,7 +114,7 @@ let g:ale_echo_cursor = get(g:, 'ale_echo_cursor', 1) if g:ale_echo_cursor augroup ALECursorGroup autocmd! - autocmd CursorMoved,CursorHold * call ale#cursor#EchoCursorWarningWithDelay() + call ALEAutoCMD('CursorMoved,CursorHold', 'ale#cursor#EchoCursorWarningWithDelay()') augroup END endif @@ -122,7 +136,7 @@ let g:ale_warn_about_trailing_whitespace = augroup ALECleanupGroup autocmd! " Clean up buffers automatically when they are unloaded. - autocmd BufUnload * call ale#cleanup#Buffer(expand('')) + call ALEAutoCMD('BufUnload', "ale#cleanup#Buffer(expand(''))") augroup END " Backwards Compatibility