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 <plug> bindings with flags
* Update documentation about ALENext and ALEPrevious
* Use ale#args#Parse in JumpWrap
This commit is contained in:
Théo Cavignac 2019-02-10 12:11:29 +01:00 committed by w0rp
parent a24f0b4d5f
commit d072d2654c
4 changed files with 163 additions and 44 deletions

View file

@ -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': []})

View file

@ -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 |<Plug>| mappings are defined for the commands: >
<Plug>(ale_previous) - ALEPrevious
<Plug>(ale_previous_wrap) - ALEPreviousWrap
<Plug>(ale_previous_error) - ALEPrevious -error
<Plug>(ale_previous_wrap_error) - ALEPrevious -wrap -error
<Plug>(ale_previous_warning) - ALEPrevious -warning
<Plug>(ale_previous_wrap_warning) - ALEPrevious -wrap -warning
<Plug>(ale_next) - ALENext
<Plug>(ale_next_wrap) - ALENextWrap
<Plug>(ale_next_error) - ALENext -error
<Plug>(ale_next_wrap_error) - ALENext -wrap -error
<Plug>(ale_next_warning) - ALENext -warning
<Plug>(ale_next_wrap_warning) - ALENext -wrap -warning
<Plug>(ale_first) - ALEFirst
<Plug>(ale_last) - ALELast
<

View file

@ -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', <q-args>)
command! -bar -nargs=* ALENext
\ :call ale#loclist_jumping#WrapJump('after', <q-args>)
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)
" <Plug> mappings for commands
nnoremap <silent> <Plug>(ale_previous) :ALEPrevious<Return>
nnoremap <silent> <Plug>(ale_previous_wrap) :ALEPreviousWrap<Return>
nnoremap <silent> <Plug>(ale_previous_error) :ALEPrevious -error<Return>
nnoremap <silent> <Plug>(ale_previous_wrap_error) :ALEPrevious -wrap -error<Return>
nnoremap <silent> <Plug>(ale_previous_warning) :ALEPrevious -warning<Return>
nnoremap <silent> <Plug>(ale_previous_wrap_warning) :ALEPrevious -wrap -warning<Return>
nnoremap <silent> <Plug>(ale_next) :ALENext<Return>
nnoremap <silent> <Plug>(ale_next_wrap) :ALENextWrap<Return>
nnoremap <silent> <Plug>(ale_next_error) :ALENext -error<Return>
nnoremap <silent> <Plug>(ale_next_wrap_error) :ALENext -wrap -error<Return>
nnoremap <silent> <Plug>(ale_next_warning) :ALENext -warning<Return>
nnoremap <silent> <Plug>(ale_next_wrap_warning) :ALENext -wrap -warning<Return>
nnoremap <silent> <Plug>(ale_first) :ALEFirst<Return>
nnoremap <silent> <Plug>(ale_last) :ALELast<Return>
nnoremap <silent> <Plug>(ale_toggle) :ALEToggle<Return>

View file

@ -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])