From d072d2654c68d1c0bf4a1cb8c15c31e989652669 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Cavignac?= Date: Sun, 10 Feb 2019 12:11:29 +0100 Subject: [PATCH] Supporting filtered jump (#2279) * Support filtered jump based on loclist item type (E or W for now) * Use flags to customize the behavior of ALENext and ALEPrevious * Update bindings with flags * Update documentation about ALENext and ALEPrevious * Use ale#args#Parse in JumpWrap --- autoload/ale/loclist_jumping.vim | 94 ++++++++++++++++++++++++++++---- doc/ale.txt | 22 ++++++++ plugin/ale.vim | 15 ++++- test/test_loclist_jumping.vader | 76 ++++++++++++++++---------- 4 files changed, 163 insertions(+), 44 deletions(-) diff --git a/autoload/ale/loclist_jumping.vim b/autoload/ale/loclist_jumping.vim index fd5ff922..c56f1a7a 100644 --- a/autoload/ale/loclist_jumping.vim +++ b/autoload/ale/loclist_jumping.vim @@ -9,7 +9,7 @@ " If there are no items or we have hit the end with wrapping off, an empty " List will be returned, otherwise a pair of [line_number, column_number] will " be returned. -function! ale#loclist_jumping#FindNearest(direction, wrap) abort +function! ale#loclist_jumping#FindNearest(direction, wrap, ...) abort let l:buffer = bufnr('') let l:pos = getcurpos() let l:info = get(g:ale_buffer_info, bufnr('%'), {'loclist': []}) @@ -17,6 +17,18 @@ function! ale#loclist_jumping#FindNearest(direction, wrap) abort let l:loclist = filter(copy(l:info.loclist), 'v:val.bufnr == l:buffer') let l:search_item = {'bufnr': l:buffer, 'lnum': l:pos[1], 'col': l:pos[2]} + if a:0 > 0 + let l:filter = a:1 + else + let l:filter = 'any' + endif + + if a:0 > 1 + let l:subtype_filter = a:2 + else + let l:subtype_filter = 'any' + endif + " When searching backwards, so we can find the next smallest match. if a:direction is# 'before' call reverse(l:loclist) @@ -41,29 +53,57 @@ function! ale#loclist_jumping#FindNearest(direction, wrap) abort \ l:search_item \) - if a:direction is# 'before' && l:cmp_value < 0 - return [l:item.lnum, l:item.col] - endif + if (l:filter is# 'any' || l:filter is# l:item.type) && + \ (l:subtype_filter is# 'any' || + \ l:subtype_filter is# get(l:item, 'sub_type', '')) - if a:direction is# 'after' && l:cmp_value > 0 - return [l:item.lnum, l:item.col] + if a:direction is# 'before' && l:cmp_value < 0 + return [l:item.lnum, l:item.col] + endif + + if a:direction is# 'after' && l:cmp_value > 0 + return [l:item.lnum, l:item.col] + endif endif endfor " If we found nothing, and the wrap option is set to 1, then we should " wrap around the list of warnings/errors - if a:wrap && !empty(l:loclist) - let l:item = l:loclist[0] - - return [l:item.lnum, l:item.col] + if a:wrap + for l:item in l:loclist + if (l:filter is# 'any' || l:filter is# l:item.type) && + \ (l:subtype_filter is# 'any' || + \ l:subtype_filter is# get(l:item, 'sub_type', '')) + return [l:item.lnum, l:item.col] + endif + endfor endif return [] endfunction " As before, find the nearest match, but position the cursor at it. -function! ale#loclist_jumping#Jump(direction, wrap) abort - let l:nearest = ale#loclist_jumping#FindNearest(a:direction, a:wrap) +function! ale#loclist_jumping#Jump(direction, ...) abort + if a:0 > 0 + let l:wrap = a:1 + else + let l:wrap = 0 + endif + + if a:0 > 1 + let l:filter = a:2 + else + let l:filter = 'any' + endif + + if a:0 > 2 + let l:subtype_filter = a:3 + else + let l:subtype_filter = 'any' + endif + + let l:nearest = ale#loclist_jumping#FindNearest(a:direction, + \ l:wrap, l:filter, l:subtype_filter) if !empty(l:nearest) normal! m` @@ -71,6 +111,36 @@ function! ale#loclist_jumping#Jump(direction, wrap) abort endif endfunction +function! ale#loclist_jumping#WrapJump(direction, sargs) abort + let [l:args, l:rest] = ale#args#Parse(['error', 'warning', 'info', 'wrap', + \ 'style', 'nostyle'], a:sargs) + + let l:wrap = 0 + let l:type_filter = 'any' + let l:subtype_filter = 'any' + + if get(l:args, 'wrap', 'nil') is# '' + let l:wrap = 1 + endif + + if get(l:args, 'error', 'nil') is# '' + let l:type_filter = 'E' + elseif get(l:args, 'warning', 'nil') is# '' + let l:type_filter = 'W' + elseif get(l:args, 'info', 'nil') is# '' + let l:type_filter = 'I' + endif + + if get(l:args, 'nostyle', 'nil') is# '' + let l:subtype_filter = 'style' + elseif get(l:args, 'style', 'nil') is# '' + let l:subtype_filter = '' + endif + + call ale#loclist_jumping#Jump(a:direction, l:wrap, l:type_filter, + \ l:subtype_filter) +endfunction + function! ale#loclist_jumping#JumpToIndex(index) abort let l:buffer = bufnr('') let l:info = get(g:ale_buffer_info, l:buffer, {'loclist': []}) diff --git a/doc/ale.txt b/doc/ale.txt index f36013ce..40d0eba9 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -2467,14 +2467,36 @@ ALELast *ALELast* `ALEPreviousWrap` and `ALENextWrap` will wrap around the file to find the last or first warning or error in the file, respectively. + `ALEPrevious` and `ALENext` take optional flags arguments to custom their + behaviour : + `-wrap` enable wrapping around the file + `-error`, `-warning` and `-info` enable jumping to errors, warnings or infos + respectively, ignoring anything else. They are mutually exclusive and if + several are provided the priority is the following: error > warning > info. + `-style` and `-nostyle` allow you to jump respectively to style error or + warning and to not style error or warning. They also are mutually + exclusive and nostyle has priority over style. + + Flags can be combined to create create custom jumping. Thus you can use + ":ALENext -wrap -error -nosyle" to jump to the next error which is not a + style error while going back to the begining of the file if needed. + `ALEFirst` goes to the first error or warning in the buffer, while `ALELast` goes to the last one. The following || mappings are defined for the commands: > (ale_previous) - ALEPrevious (ale_previous_wrap) - ALEPreviousWrap + (ale_previous_error) - ALEPrevious -error + (ale_previous_wrap_error) - ALEPrevious -wrap -error + (ale_previous_warning) - ALEPrevious -warning + (ale_previous_wrap_warning) - ALEPrevious -wrap -warning (ale_next) - ALENext (ale_next_wrap) - ALENextWrap + (ale_next_error) - ALENext -error + (ale_next_wrap_error) - ALENext -wrap -error + (ale_next_warning) - ALENext -warning + (ale_next_wrap_warning) - ALENext -wrap -warning (ale_first) - ALEFirst (ale_last) - ALELast < diff --git a/plugin/ale.vim b/plugin/ale.vim index b2e6bf8c..1df473c5 100644 --- a/plugin/ale.vim +++ b/plugin/ale.vim @@ -151,9 +151,12 @@ if g:ale_completion_enabled endif " Define commands for moving through warnings and errors. -command! -bar ALEPrevious :call ale#loclist_jumping#Jump('before', 0) +command! -bar -nargs=* ALEPrevious +\ :call ale#loclist_jumping#WrapJump('before', ) +command! -bar -nargs=* ALENext +\ :call ale#loclist_jumping#WrapJump('after', ) + command! -bar ALEPreviousWrap :call ale#loclist_jumping#Jump('before', 1) -command! -bar ALENext :call ale#loclist_jumping#Jump('after', 0) command! -bar ALENextWrap :call ale#loclist_jumping#Jump('after', 1) command! -bar ALEFirst :call ale#loclist_jumping#JumpToIndex(0) command! -bar ALELast :call ale#loclist_jumping#JumpToIndex(-1) @@ -218,8 +221,16 @@ command! -bar ALEComplete :call ale#completion#AlwaysGetCompletions(0) " mappings for commands nnoremap (ale_previous) :ALEPrevious nnoremap (ale_previous_wrap) :ALEPreviousWrap +nnoremap (ale_previous_error) :ALEPrevious -error +nnoremap (ale_previous_wrap_error) :ALEPrevious -wrap -error +nnoremap (ale_previous_warning) :ALEPrevious -warning +nnoremap (ale_previous_wrap_warning) :ALEPrevious -wrap -warning nnoremap (ale_next) :ALENext nnoremap (ale_next_wrap) :ALENextWrap +nnoremap (ale_next_error) :ALENext -error +nnoremap (ale_next_wrap_error) :ALENext -wrap -error +nnoremap (ale_next_warning) :ALENext -warning +nnoremap (ale_next_wrap_warning) :ALENext -wrap -warning nnoremap (ale_first) :ALEFirst nnoremap (ale_last) :ALELast nnoremap (ale_toggle) :ALEToggle diff --git a/test/test_loclist_jumping.vader b/test/test_loclist_jumping.vader index da9a1f57..3b6f0688 100644 --- a/test/test_loclist_jumping.vader +++ b/test/test_loclist_jumping.vader @@ -5,23 +5,24 @@ Before: \ {'type': 'E', 'bufnr': bufnr('') - 1, 'lnum': 3, 'col': 2}, \ {'type': 'E', 'bufnr': bufnr(''), 'lnum': 1, 'col': 2}, \ {'type': 'E', 'bufnr': bufnr(''), 'lnum': 1, 'col': 3}, - \ {'type': 'E', 'bufnr': bufnr(''), 'lnum': 2, 'col': 1}, + \ {'type': 'W', 'sub_type': 'style', 'bufnr': bufnr(''), 'lnum': 2, 'col': 1}, \ {'type': 'E', 'bufnr': bufnr(''), 'lnum': 2, 'col': 2}, - \ {'type': 'E', 'bufnr': bufnr(''), 'lnum': 2, 'col': 3}, - \ {'type': 'E', 'bufnr': bufnr(''), 'lnum': 2, 'col': 6}, + \ {'type': 'W', 'sub_type': 'style', 'bufnr': bufnr(''), 'lnum': 2, 'col': 3}, + \ {'type': 'W', 'bufnr': bufnr(''), 'lnum': 2, 'col': 6}, \ {'type': 'E', 'bufnr': bufnr(''), 'lnum': 2, 'col': 700}, \ {'type': 'E', 'bufnr': bufnr('') + 1, 'lnum': 3, 'col': 2}, \ ], \ }, \} - function! TestJump(position, wrap, pos) + function! TestJump(position, wrap, filter, subtype_filter, pos) call cursor(a:pos) if type(a:position) == type(0) call ale#loclist_jumping#JumpToIndex(a:position) else - call ale#loclist_jumping#Jump(a:position, a:wrap) + call ale#loclist_jumping#Jump(a:position, a:wrap, a:filter, + \ a:subtype_filter) endif return getcurpos()[1:2] @@ -36,46 +37,61 @@ Given foobar (Some imaginary filetype): 12345678 Execute(loclist jumping should jump correctly when not wrapping): - AssertEqual [2, 1], TestJump('before', 0, [2, 2]) - AssertEqual [1, 3], TestJump('before', 0, [2, 1]) - AssertEqual [2, 3], TestJump('after', 0, [2, 2]) - AssertEqual [2, 1], TestJump('after', 0, [1, 3]) - AssertEqual [2, 6], TestJump('after', 0, [2, 4]) - AssertEqual [2, 8], TestJump('after', 0, [2, 6]) + AssertEqual [2, 1], TestJump('before', 0, 'any', 'any', [2, 2]) + AssertEqual [1, 3], TestJump('before', 0, 'any', 'any', [2, 1]) + AssertEqual [2, 3], TestJump('after', 0, 'any', 'any', [2, 2]) + AssertEqual [2, 1], TestJump('after', 0, 'any', 'any', [1, 3]) + AssertEqual [2, 6], TestJump('after', 0, 'any', 'any', [2, 4]) + AssertEqual [2, 8], TestJump('after', 0, 'any', 'any', [2, 6]) Execute(loclist jumping should jump correctly when wrapping): - AssertEqual [2, 1], TestJump('before', 1, [2, 2]) - AssertEqual [1, 3], TestJump('before', 1, [2, 1]) - AssertEqual [2, 3], TestJump('after', 1, [2, 2]) - AssertEqual [2, 1], TestJump('after', 1, [1, 3]) - AssertEqual [2, 6], TestJump('after', 1, [2, 4]) + AssertEqual [2, 1], TestJump('before', 1, 'any', 'any', [2, 2]) + AssertEqual [1, 3], TestJump('before', 1, 'any', 'any', [2, 1]) + AssertEqual [2, 3], TestJump('after', 1, 'any', 'any', [2, 2]) + AssertEqual [2, 1], TestJump('after', 1, 'any', 'any', [1, 3]) + AssertEqual [2, 6], TestJump('after', 1, 'any', 'any', [2, 4]) - AssertEqual [1, 2], TestJump('after', 1, [2, 8]) - AssertEqual [2, 8], TestJump('before', 1, [1, 2]) + AssertEqual [1, 2], TestJump('after', 1, 'any', 'any', [2, 8]) + AssertEqual [2, 8], TestJump('before', 1, 'any', 'any', [1, 2]) + +Execute(loclist jumping should jump correctly with warning filters): + AssertEqual [2, 1], TestJump('after', 0, 'W', 'any', [1, 2]) + AssertEqual [2, 6], TestJump('after', 0, 'W', 'any', [2, 3]) + AssertEqual [2, 1], TestJump('after', 1, 'W', 'any', [2, 6]) + +Execute(loclist jumping should jump correctly with error filters): + AssertEqual [1, 2], TestJump('after', 1, 'E', 'any', [2, 700]) + AssertEqual [2, 2], TestJump('before', 0, 'E', 'any', [2, 700]) + AssertEqual [2, 2], TestJump('after', 1, 'E', 'any', [1, 3]) + +Execute(loclist jumping should jump correctly with sub type filters): + AssertEqual [2, 3], TestJump('after', 0, 'any', 'style', [2, 1]) + AssertEqual [2, 2], TestJump('after', 0, 'any', '', [1, 3]) + AssertEqual [2, 1], TestJump('after', 1, 'any', 'style', [2, 6]) Execute(loclist jumping not jump when the loclist is empty): let g:ale_buffer_info[bufnr('%')].loclist = [] - AssertEqual [1, 6], TestJump('before', 0, [1, 6]) - AssertEqual [1, 6], TestJump('before', 1, [1, 6]) - AssertEqual [1, 6], TestJump('after', 0, [1, 6]) - AssertEqual [1, 6], TestJump('after', 1, [1, 6]) + AssertEqual [1, 6], TestJump('before', 0, 'any', 'any', [1, 6]) + AssertEqual [1, 6], TestJump('before', 1, 'any', 'any', [1, 6]) + AssertEqual [1, 6], TestJump('after', 0, 'any', 'any', [1, 6]) + AssertEqual [1, 6], TestJump('after', 1, 'any', 'any', [1, 6]) Execute(We should be able to jump to the last item): - AssertEqual [2, 8], TestJump(-1, 0, [1, 6]) + AssertEqual [2, 8], TestJump(-1, 0, 'any', 'any', [1, 6]) Execute(We shouldn't move when jumping to the last item where there are none): let g:ale_buffer_info[bufnr('%')].loclist = [] - AssertEqual [1, 6], TestJump(-1, 0, [1, 6]) + AssertEqual [1, 6], TestJump(-1, 0, 'any', 'any', [1, 6]) Execute(We should be able to jump to the first item): - AssertEqual [1, 2], TestJump(0, 0, [1, 6]) + AssertEqual [1, 2], TestJump(0, 0, 'any', 'any', [1, 6]) Execute(We shouldn't move when jumping to the first item where there are none): let g:ale_buffer_info[bufnr('%')].loclist = [] - AssertEqual [1, 6], TestJump(0, 0, [1, 6]) + AssertEqual [1, 6], TestJump(0, 0, 'any', 'any', [1, 6]) Execute(We should be able to jump when the error line is blank): " Add a blank line at the end. @@ -84,7 +100,7 @@ Execute(We should be able to jump when the error line is blank): call add(g:ale_buffer_info[bufnr('%')].loclist, {'type': 'E', 'bufnr': bufnr(''), 'lnum': 3, 'col': 1}) AssertEqual 0, len(getline(3)) - AssertEqual [2, 8], TestJump('before', 0, [3, 1]) - AssertEqual [2, 8], TestJump('before', 1, [3, 1]) - AssertEqual [3, 1], TestJump('after', 0, [3, 1]) - AssertEqual [1, 2], TestJump('after', 1, [3, 1]) + AssertEqual [2, 8], TestJump('before', 0, 'any', 'any', [3, 1]) + AssertEqual [2, 8], TestJump('before', 1, 'any', 'any', [3, 1]) + AssertEqual [3, 1], TestJump('after', 0, 'any', 'any', [3, 1]) + AssertEqual [1, 2], TestJump('after', 1, 'any', 'any', [3, 1])