Optionally use neovim's api-highlights (#2169)
This commit is contained in:
parent
2f3bce5a1d
commit
114198e082
2 changed files with 285 additions and 15 deletions
|
@ -26,6 +26,41 @@ endif
|
||||||
let s:MAX_POS_VALUES = 8
|
let s:MAX_POS_VALUES = 8
|
||||||
let s:MAX_COL_SIZE = 1073741824 " pow(2, 30)
|
let s:MAX_COL_SIZE = 1073741824 " pow(2, 30)
|
||||||
|
|
||||||
|
" Check if we have neovim's buffer highlight API
|
||||||
|
"
|
||||||
|
" Below we define some functions' implementation conditionally if this API
|
||||||
|
" exists or not.
|
||||||
|
"
|
||||||
|
" The API itself is more ergonomic and neovim performs highlights positions
|
||||||
|
" rebases during edits so we see less stalled highlights.
|
||||||
|
let s:nvim_api = exists('*nvim_buf_add_highlight') && exists('*nvim_buf_clear_namespace')
|
||||||
|
|
||||||
|
function! ale#highlight#HasNeovimApi() abort
|
||||||
|
return s:nvim_api
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! ale#highlight#nvim_buf_clear_namespace(...) abort
|
||||||
|
return call('nvim_buf_clear_namespace', a:000)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! ale#highlight#nvim_buf_add_highlight(...) abort
|
||||||
|
return call('nvim_buf_add_highlight', a:000)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:ale_nvim_highlight_id(bufnr) abort
|
||||||
|
let l:id = getbufvar(a:bufnr, 'ale_nvim_highlight_id', -1)
|
||||||
|
|
||||||
|
if l:id is -1
|
||||||
|
" NOTE: This will highlight nothing but will allocate new id
|
||||||
|
let l:id = ale#highlight#nvim_buf_add_highlight(
|
||||||
|
\ a:bufnr, 0, '', 0, 0, -1
|
||||||
|
\)
|
||||||
|
call setbufvar(a:bufnr, 'ale_nvim_highlight_id', l:id)
|
||||||
|
endif
|
||||||
|
|
||||||
|
return l:id
|
||||||
|
endfunction
|
||||||
|
|
||||||
function! ale#highlight#CreatePositions(line, col, end_line, end_col) abort
|
function! ale#highlight#CreatePositions(line, col, end_line, end_col) abort
|
||||||
if a:line >= a:end_line
|
if a:line >= a:end_line
|
||||||
" For single lines, just return the one position.
|
" For single lines, just return the one position.
|
||||||
|
@ -49,12 +84,90 @@ endfunction
|
||||||
|
|
||||||
" Given a loclist for current items to highlight, remove all highlights
|
" Given a loclist for current items to highlight, remove all highlights
|
||||||
" except these which have matching loclist item entries.
|
" except these which have matching loclist item entries.
|
||||||
|
|
||||||
function! ale#highlight#RemoveHighlights() abort
|
function! ale#highlight#RemoveHighlights() abort
|
||||||
for l:match in getmatches()
|
if ale#highlight#HasNeovimApi()
|
||||||
if l:match.group =~# '^ALE'
|
if get(b:, 'ale_nvim_highlight_id', 0)
|
||||||
call matchdelete(l:match.id)
|
let l:bufnr = bufnr('%')
|
||||||
|
" NOTE: 0, -1 means from 0 line till the end of buffer
|
||||||
|
call ale#highlight#nvim_buf_clear_namespace(
|
||||||
|
\ l:bufnr,
|
||||||
|
\ b:ale_nvim_highlight_id,
|
||||||
|
\ 0, -1
|
||||||
|
\)
|
||||||
endif
|
endif
|
||||||
endfor
|
else
|
||||||
|
for l:match in getmatches()
|
||||||
|
if l:match.group =~# '^ALE'
|
||||||
|
call matchdelete(l:match.id)
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:highlight_line(bufnr, lnum, group) abort
|
||||||
|
if ale#highlight#HasNeovimApi()
|
||||||
|
let l:highlight_id = s:ale_nvim_highlight_id(a:bufnr)
|
||||||
|
call ale#highlight#nvim_buf_add_highlight(
|
||||||
|
\ a:bufnr, l:highlight_id, a:group,
|
||||||
|
\ a:lnum, 0, -1
|
||||||
|
\)
|
||||||
|
else
|
||||||
|
call matchaddpos(a:group, [a:lnum])
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:highlight_range(bufnr, range, group) abort
|
||||||
|
if ale#highlight#HasNeovimApi()
|
||||||
|
let l:highlight_id = s:ale_nvim_highlight_id(a:bufnr)
|
||||||
|
" NOTE: lines and columns indicies are 0-based in nvim_buf_* API.
|
||||||
|
let l:lnum = a:range.lnum - 1
|
||||||
|
let l:end_lnum = a:range.end_lnum - 1
|
||||||
|
let l:col = a:range.col - 1
|
||||||
|
let l:end_col = a:range.end_col
|
||||||
|
|
||||||
|
if l:lnum >= l:end_lnum
|
||||||
|
" For single lines, just return the one position.
|
||||||
|
call ale#highlight#nvim_buf_add_highlight(
|
||||||
|
\ a:bufnr, l:highlight_id, a:group,
|
||||||
|
\ l:lnum, l:col, l:end_col
|
||||||
|
\)
|
||||||
|
else
|
||||||
|
" highlight first line from start till the line end
|
||||||
|
call ale#highlight#nvim_buf_add_highlight(
|
||||||
|
\ a:bufnr, l:highlight_id, a:group,
|
||||||
|
\ l:lnum, l:col, -1
|
||||||
|
\)
|
||||||
|
|
||||||
|
" highlight all lines between the first and last entirely
|
||||||
|
let l:cur = l:lnum + 1
|
||||||
|
|
||||||
|
while l:cur < l:end_lnum
|
||||||
|
call ale#highlight#nvim_buf_add_highlight(
|
||||||
|
\ a:bufnr, l:highlight_id, a:group,
|
||||||
|
\ l:cur, 0, -1
|
||||||
|
\ )
|
||||||
|
let l:cur += 1
|
||||||
|
endwhile
|
||||||
|
|
||||||
|
call ale#highlight#nvim_buf_add_highlight(
|
||||||
|
\ a:bufnr, l:highlight_id, a:group,
|
||||||
|
\ l:end_lnum, 0, l:end_col
|
||||||
|
\)
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
" Set all of the positions, which are chunked into Lists which
|
||||||
|
" are as large as will be accepted by matchaddpos.
|
||||||
|
call map(
|
||||||
|
\ ale#highlight#CreatePositions(
|
||||||
|
\ a:range.lnum,
|
||||||
|
\ a:range.col,
|
||||||
|
\ a:range.end_lnum,
|
||||||
|
\ a:range.end_col
|
||||||
|
\ ),
|
||||||
|
\ 'matchaddpos(a:group, v:val)'
|
||||||
|
\)
|
||||||
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! ale#highlight#UpdateHighlights() abort
|
function! ale#highlight#UpdateHighlights() abort
|
||||||
|
@ -79,17 +192,14 @@ function! ale#highlight#UpdateHighlights() abort
|
||||||
let l:group = 'ALEError'
|
let l:group = 'ALEError'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let l:line = l:item.lnum
|
let l:range = {
|
||||||
let l:col = l:item.col
|
\ 'lnum': l:item.lnum,
|
||||||
let l:end_line = get(l:item, 'end_lnum', l:line)
|
\ 'col': l:item.col,
|
||||||
let l:end_col = get(l:item, 'end_col', l:col)
|
\ 'end_lnum': get(l:item, 'end_lnum', l:item.lnum),
|
||||||
|
\ 'end_col': get(l:item, 'end_col', l:item.col)
|
||||||
|
\}
|
||||||
|
|
||||||
" Set all of the positions, which are chunked into Lists which
|
call s:highlight_range(l:item.bufnr, l:range, l:group)
|
||||||
" are as large as will be accepted by matchaddpos.
|
|
||||||
call map(
|
|
||||||
\ ale#highlight#CreatePositions(l:line, l:col, l:end_line, l:end_col),
|
|
||||||
\ 'matchaddpos(l:group, v:val)'
|
|
||||||
\)
|
|
||||||
endfor
|
endfor
|
||||||
|
|
||||||
" If highlights are enabled and signs are not enabled, we should still
|
" If highlights are enabled and signs are not enabled, we should still
|
||||||
|
@ -111,7 +221,7 @@ function! ale#highlight#UpdateHighlights() abort
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if l:available_groups[l:group]
|
if l:available_groups[l:group]
|
||||||
call matchaddpos(l:group, [l:item.lnum])
|
call s:highlight_line(l:item.bufnr, l:item.lnum, l:group)
|
||||||
endif
|
endif
|
||||||
endfor
|
endfor
|
||||||
endif
|
endif
|
||||||
|
|
160
test/test_nvim_api_highlight.vader
Normal file
160
test/test_nvim_api_highlight.vader
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
Before:
|
||||||
|
Save g:ale_enabled
|
||||||
|
Save g:ale_set_signs
|
||||||
|
|
||||||
|
let g:nvim_buf_clear_namespace_calls = []
|
||||||
|
let g:nvim_buf_add_highlight_calls = []
|
||||||
|
|
||||||
|
call ale#test#SetDirectory('/testplugin/test/highlight')
|
||||||
|
call ale#test#SetFilename('dummy.txt')
|
||||||
|
|
||||||
|
runtime autoload/ale/highlight.vim
|
||||||
|
|
||||||
|
let g:ale_set_signs = 1
|
||||||
|
let g:ale_enabled = 1
|
||||||
|
let b:ale_nvim_highlight_id = 42
|
||||||
|
let b:ale_highlight_items = []
|
||||||
|
|
||||||
|
function! ale#highlight#nvim_buf_clear_namespace(...) abort
|
||||||
|
call add(g:nvim_buf_clear_namespace_calls, a:000)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! ale#highlight#nvim_buf_add_highlight(...) abort
|
||||||
|
call add(g:nvim_buf_add_highlight_calls, a:000)
|
||||||
|
return 42 " returns namespace id
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
After:
|
||||||
|
Restore
|
||||||
|
|
||||||
|
unlet! b:ale_enabled
|
||||||
|
unlet! b:ale_nvim_highlight_id
|
||||||
|
unlet! b:ale_highlight_items
|
||||||
|
|
||||||
|
unlet! g:nvim_buf_clear_namespace_calls
|
||||||
|
unlet! g:nvim_buf_add_highlight_calls
|
||||||
|
|
||||||
|
call ale#test#RestoreDirectory()
|
||||||
|
call ale#linter#Reset()
|
||||||
|
|
||||||
|
runtime autoload/ale/highlight.vim
|
||||||
|
|
||||||
|
Given foobar (Some imaginary filetype):
|
||||||
|
<contents>
|
||||||
|
|
||||||
|
Execute(Check usage of nvim_buf_clear_namespace):
|
||||||
|
if ale#highlight#HasNeovimApi()
|
||||||
|
call ale#highlight#SetHighlights(bufnr(''), [])
|
||||||
|
|
||||||
|
AssertEqual 1, len(g:nvim_buf_clear_namespace_calls)
|
||||||
|
AssertEqual
|
||||||
|
\ [[bufnr(''), 42, 0, -1]],
|
||||||
|
\ g:nvim_buf_clear_namespace_calls
|
||||||
|
endif
|
||||||
|
|
||||||
|
Execute(Check usage of nvim_buf_add_highlight / single char):
|
||||||
|
if ale#highlight#HasNeovimApi()
|
||||||
|
call ale#highlight#SetHighlights(bufnr(''), [
|
||||||
|
\ {
|
||||||
|
\ 'bufnr': bufnr(''),
|
||||||
|
\ 'type': 'E',
|
||||||
|
\ 'lnum': 2,
|
||||||
|
\ 'col': 4,
|
||||||
|
\ }
|
||||||
|
\])
|
||||||
|
|
||||||
|
" Highlights are cleared on update
|
||||||
|
AssertEqual 1, len(g:nvim_buf_clear_namespace_calls)
|
||||||
|
AssertEqual [[bufnr(''), 42, 0, -1]], g:nvim_buf_clear_namespace_calls
|
||||||
|
|
||||||
|
" Should highlight a single char by lnum, col
|
||||||
|
AssertEqual 1, len(g:nvim_buf_add_highlight_calls)
|
||||||
|
AssertEqual
|
||||||
|
\ [[bufnr(''), 42, 'ALEError', 1, 3, 4]],
|
||||||
|
\ g:nvim_buf_add_highlight_calls
|
||||||
|
endif
|
||||||
|
|
||||||
|
Execute(Check usage of nvim_buf_add_highlight / single line span):
|
||||||
|
if ale#highlight#HasNeovimApi()
|
||||||
|
call ale#highlight#SetHighlights(bufnr(''), [
|
||||||
|
\ {
|
||||||
|
\ 'bufnr': bufnr(''),
|
||||||
|
\ 'type': 'E',
|
||||||
|
\ 'lnum': 2,
|
||||||
|
\ 'col': 4,
|
||||||
|
\ 'end_lnum': 2,
|
||||||
|
\ 'end_col': 10,
|
||||||
|
\ }
|
||||||
|
\])
|
||||||
|
|
||||||
|
" Highlights are cleared on update
|
||||||
|
AssertEqual 1, len(g:nvim_buf_clear_namespace_calls)
|
||||||
|
AssertEqual [[bufnr(''), 42, 0, -1]], g:nvim_buf_clear_namespace_calls
|
||||||
|
|
||||||
|
" Should highlight a span between col and end_col on lnum
|
||||||
|
AssertEqual 1, len(g:nvim_buf_add_highlight_calls)
|
||||||
|
AssertEqual
|
||||||
|
\ [[bufnr(''), 42, 'ALEError', 1, 3, 10]],
|
||||||
|
\ g:nvim_buf_add_highlight_calls
|
||||||
|
endif
|
||||||
|
|
||||||
|
Execute(Check usage of nvim_buf_add_highlight / multiple lines span):
|
||||||
|
if ale#highlight#HasNeovimApi()
|
||||||
|
call ale#highlight#SetHighlights(bufnr(''), [
|
||||||
|
\ {
|
||||||
|
\ 'bufnr': bufnr(''),
|
||||||
|
\ 'type': 'E',
|
||||||
|
\ 'lnum': 2,
|
||||||
|
\ 'col': 4,
|
||||||
|
\ 'end_lnum': 5,
|
||||||
|
\ 'end_col': 10,
|
||||||
|
\ }
|
||||||
|
\])
|
||||||
|
|
||||||
|
" Highlights are cleared on update
|
||||||
|
AssertEqual 1, len(g:nvim_buf_clear_namespace_calls)
|
||||||
|
AssertEqual [[bufnr(''), 42, 0, -1]], g:nvim_buf_clear_namespace_calls
|
||||||
|
|
||||||
|
" Should highlight all lines from lnum till end_lnum
|
||||||
|
AssertEqual 4, len(g:nvim_buf_add_highlight_calls)
|
||||||
|
AssertEqual
|
||||||
|
\ [
|
||||||
|
\ [bufnr(''), 42, 'ALEError', 1, 3, -1],
|
||||||
|
\ [bufnr(''), 42, 'ALEError', 2, 0, -1],
|
||||||
|
\ [bufnr(''), 42, 'ALEError', 3, 0, -1],
|
||||||
|
\ [bufnr(''), 42, 'ALEError', 4, 0, 10]
|
||||||
|
\ ],
|
||||||
|
\ g:nvim_buf_add_highlight_calls
|
||||||
|
endif
|
||||||
|
|
||||||
|
Execute(Check usage of nvim_buf_add_highlight / line highights):
|
||||||
|
let g:ale_set_signs = 0
|
||||||
|
|
||||||
|
if ale#highlight#HasNeovimApi()
|
||||||
|
call ale#highlight#SetHighlights(bufnr(''), [
|
||||||
|
\ {
|
||||||
|
\ 'bufnr': bufnr(''),
|
||||||
|
\ 'type': 'E',
|
||||||
|
\ 'lnum': 2,
|
||||||
|
\ 'col': 4,
|
||||||
|
\ 'end_lnum': 5,
|
||||||
|
\ 'end_col': 10,
|
||||||
|
\ }
|
||||||
|
\])
|
||||||
|
|
||||||
|
" Highlights are cleared on update
|
||||||
|
AssertEqual 1, len(g:nvim_buf_clear_namespace_calls)
|
||||||
|
AssertEqual [[bufnr(''), 42, 0, -1]], g:nvim_buf_clear_namespace_calls
|
||||||
|
|
||||||
|
" Now the last highlight should be put on the entire line
|
||||||
|
AssertEqual 5, len(g:nvim_buf_add_highlight_calls)
|
||||||
|
AssertEqual
|
||||||
|
\ [
|
||||||
|
\ [bufnr(''), 42, 'ALEError', 1, 3, -1],
|
||||||
|
\ [bufnr(''), 42, 'ALEError', 2, 0, -1],
|
||||||
|
\ [bufnr(''), 42, 'ALEError', 3, 0, -1],
|
||||||
|
\ [bufnr(''), 42, 'ALEError', 4, 0, 10],
|
||||||
|
\ [bufnr(''), 42, 'ALEErrorLine', 2, 0, -1]
|
||||||
|
\ ],
|
||||||
|
\ g:nvim_buf_add_highlight_calls
|
||||||
|
endif
|
Reference in a new issue