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 " 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 " List will be returned, otherwise a pair of [line_number, column_number] will
" be returned. " be returned.
function! ale#loclist_jumping#FindNearest(direction, wrap) abort function! ale#loclist_jumping#FindNearest(direction, wrap, ...) abort
let l:buffer = bufnr('') let l:buffer = bufnr('')
let l:pos = getcurpos() let l:pos = getcurpos()
let l:info = get(g:ale_buffer_info, bufnr('%'), {'loclist': []}) 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: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]} 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. " When searching backwards, so we can find the next smallest match.
if a:direction is# 'before' if a:direction is# 'before'
call reverse(l:loclist) call reverse(l:loclist)
@ -41,29 +53,57 @@ function! ale#loclist_jumping#FindNearest(direction, wrap) abort
\ l:search_item \ l:search_item
\) \)
if a:direction is# 'before' && l:cmp_value < 0 if (l:filter is# 'any' || l:filter is# l:item.type) &&
return [l:item.lnum, l:item.col] \ (l:subtype_filter is# 'any' ||
endif \ l:subtype_filter is# get(l:item, 'sub_type', ''))
if a:direction is# 'after' && l:cmp_value > 0 if a:direction is# 'before' && l:cmp_value < 0
return [l:item.lnum, l:item.col] 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 endif
endfor endfor
" If we found nothing, and the wrap option is set to 1, then we should " If we found nothing, and the wrap option is set to 1, then we should
" wrap around the list of warnings/errors " wrap around the list of warnings/errors
if a:wrap && !empty(l:loclist) if a:wrap
let l:item = l:loclist[0] for l:item in l:loclist
if (l:filter is# 'any' || l:filter is# l:item.type) &&
return [l:item.lnum, l:item.col] \ (l:subtype_filter is# 'any' ||
\ l:subtype_filter is# get(l:item, 'sub_type', ''))
return [l:item.lnum, l:item.col]
endif
endfor
endif endif
return [] return []
endfunction endfunction
" As before, find the nearest match, but position the cursor at it. " As before, find the nearest match, but position the cursor at it.
function! ale#loclist_jumping#Jump(direction, wrap) abort function! ale#loclist_jumping#Jump(direction, ...) abort
let l:nearest = ale#loclist_jumping#FindNearest(a:direction, a:wrap) 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) if !empty(l:nearest)
normal! m` normal! m`
@ -71,6 +111,36 @@ function! ale#loclist_jumping#Jump(direction, wrap) abort
endif endif
endfunction 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 function! ale#loclist_jumping#JumpToIndex(index) abort
let l:buffer = bufnr('') let l:buffer = bufnr('')
let l:info = get(g:ale_buffer_info, l:buffer, {'loclist': []}) 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 `ALEPreviousWrap` and `ALENextWrap` will wrap around the file to find
the last or first warning or error in the file, respectively. 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` `ALEFirst` goes to the first error or warning in the buffer, while `ALELast`
goes to the last one. goes to the last one.
The following |<Plug>| mappings are defined for the commands: > The following |<Plug>| mappings are defined for the commands: >
<Plug>(ale_previous) - ALEPrevious <Plug>(ale_previous) - ALEPrevious
<Plug>(ale_previous_wrap) - ALEPreviousWrap <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) - ALENext
<Plug>(ale_next_wrap) - ALENextWrap <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_first) - ALEFirst
<Plug>(ale_last) - ALELast <Plug>(ale_last) - ALELast
< <

View File

@ -151,9 +151,12 @@ if g:ale_completion_enabled
endif endif
" Define commands for moving through warnings and errors. " 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 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 ALENextWrap :call ale#loclist_jumping#Jump('after', 1)
command! -bar ALEFirst :call ale#loclist_jumping#JumpToIndex(0) command! -bar ALEFirst :call ale#loclist_jumping#JumpToIndex(0)
command! -bar ALELast :call ale#loclist_jumping#JumpToIndex(-1) 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 " <Plug> mappings for commands
nnoremap <silent> <Plug>(ale_previous) :ALEPrevious<Return> nnoremap <silent> <Plug>(ale_previous) :ALEPrevious<Return>
nnoremap <silent> <Plug>(ale_previous_wrap) :ALEPreviousWrap<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) :ALENext<Return>
nnoremap <silent> <Plug>(ale_next_wrap) :ALENextWrap<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_first) :ALEFirst<Return>
nnoremap <silent> <Plug>(ale_last) :ALELast<Return> nnoremap <silent> <Plug>(ale_last) :ALELast<Return>
nnoremap <silent> <Plug>(ale_toggle) :ALEToggle<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('') - 1, 'lnum': 3, 'col': 2},
\ {'type': 'E', 'bufnr': bufnr(''), 'lnum': 1, 'col': 2}, \ {'type': 'E', 'bufnr': bufnr(''), 'lnum': 1, 'col': 2},
\ {'type': 'E', 'bufnr': bufnr(''), 'lnum': 1, 'col': 3}, \ {'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': 2},
\ {'type': 'E', 'bufnr': bufnr(''), 'lnum': 2, 'col': 3}, \ {'type': 'W', 'sub_type': 'style', 'bufnr': bufnr(''), 'lnum': 2, 'col': 3},
\ {'type': 'E', 'bufnr': bufnr(''), 'lnum': 2, 'col': 6}, \ {'type': 'W', 'bufnr': bufnr(''), 'lnum': 2, 'col': 6},
\ {'type': 'E', 'bufnr': bufnr(''), 'lnum': 2, 'col': 700}, \ {'type': 'E', 'bufnr': bufnr(''), 'lnum': 2, 'col': 700},
\ {'type': 'E', 'bufnr': bufnr('') + 1, 'lnum': 3, 'col': 2}, \ {'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) call cursor(a:pos)
if type(a:position) == type(0) if type(a:position) == type(0)
call ale#loclist_jumping#JumpToIndex(a:position) call ale#loclist_jumping#JumpToIndex(a:position)
else 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 endif
return getcurpos()[1:2] return getcurpos()[1:2]
@ -36,46 +37,61 @@ Given foobar (Some imaginary filetype):
12345678 12345678
Execute(loclist jumping should jump correctly when not wrapping): Execute(loclist jumping should jump correctly when not wrapping):
AssertEqual [2, 1], TestJump('before', 0, [2, 2]) AssertEqual [2, 1], TestJump('before', 0, 'any', 'any', [2, 2])
AssertEqual [1, 3], TestJump('before', 0, [2, 1]) AssertEqual [1, 3], TestJump('before', 0, 'any', 'any', [2, 1])
AssertEqual [2, 3], TestJump('after', 0, [2, 2]) AssertEqual [2, 3], TestJump('after', 0, 'any', 'any', [2, 2])
AssertEqual [2, 1], TestJump('after', 0, [1, 3]) AssertEqual [2, 1], TestJump('after', 0, 'any', 'any', [1, 3])
AssertEqual [2, 6], TestJump('after', 0, [2, 4]) AssertEqual [2, 6], TestJump('after', 0, 'any', 'any', [2, 4])
AssertEqual [2, 8], TestJump('after', 0, [2, 6]) AssertEqual [2, 8], TestJump('after', 0, 'any', 'any', [2, 6])
Execute(loclist jumping should jump correctly when wrapping): Execute(loclist jumping should jump correctly when wrapping):
AssertEqual [2, 1], TestJump('before', 1, [2, 2]) AssertEqual [2, 1], TestJump('before', 1, 'any', 'any', [2, 2])
AssertEqual [1, 3], TestJump('before', 1, [2, 1]) AssertEqual [1, 3], TestJump('before', 1, 'any', 'any', [2, 1])
AssertEqual [2, 3], TestJump('after', 1, [2, 2]) AssertEqual [2, 3], TestJump('after', 1, 'any', 'any', [2, 2])
AssertEqual [2, 1], TestJump('after', 1, [1, 3]) AssertEqual [2, 1], TestJump('after', 1, 'any', 'any', [1, 3])
AssertEqual [2, 6], TestJump('after', 1, [2, 4]) AssertEqual [2, 6], TestJump('after', 1, 'any', 'any', [2, 4])
AssertEqual [1, 2], TestJump('after', 1, [2, 8]) AssertEqual [1, 2], TestJump('after', 1, 'any', 'any', [2, 8])
AssertEqual [2, 8], TestJump('before', 1, [1, 2]) 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): Execute(loclist jumping not jump when the loclist is empty):
let g:ale_buffer_info[bufnr('%')].loclist = [] let g:ale_buffer_info[bufnr('%')].loclist = []
AssertEqual [1, 6], TestJump('before', 0, [1, 6]) AssertEqual [1, 6], TestJump('before', 0, 'any', 'any', [1, 6])
AssertEqual [1, 6], TestJump('before', 1, [1, 6]) AssertEqual [1, 6], TestJump('before', 1, 'any', 'any', [1, 6])
AssertEqual [1, 6], TestJump('after', 0, [1, 6]) AssertEqual [1, 6], TestJump('after', 0, 'any', 'any', [1, 6])
AssertEqual [1, 6], TestJump('after', 1, [1, 6]) AssertEqual [1, 6], TestJump('after', 1, 'any', 'any', [1, 6])
Execute(We should be able to jump to the last item): 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): Execute(We shouldn't move when jumping to the last item where there are none):
let g:ale_buffer_info[bufnr('%')].loclist = [] 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): 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): Execute(We shouldn't move when jumping to the first item where there are none):
let g:ale_buffer_info[bufnr('%')].loclist = [] 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): Execute(We should be able to jump when the error line is blank):
" Add a blank line at the end. " 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}) call add(g:ale_buffer_info[bufnr('%')].loclist, {'type': 'E', 'bufnr': bufnr(''), 'lnum': 3, 'col': 1})
AssertEqual 0, len(getline(3)) AssertEqual 0, len(getline(3))
AssertEqual [2, 8], TestJump('before', 0, [3, 1]) AssertEqual [2, 8], TestJump('before', 0, 'any', 'any', [3, 1])
AssertEqual [2, 8], TestJump('before', 1, [3, 1]) AssertEqual [2, 8], TestJump('before', 1, 'any', 'any', [3, 1])
AssertEqual [3, 1], TestJump('after', 0, [3, 1]) AssertEqual [3, 1], TestJump('after', 0, 'any', 'any', [3, 1])
AssertEqual [1, 2], TestJump('after', 1, [3, 1]) AssertEqual [1, 2], TestJump('after', 1, 'any', 'any', [3, 1])