272 lines
8.5 KiB
VimL
272 lines
8.5 KiB
VimL
scriptencoding utf8
|
|
" Author: w0rp <devw0rp@gmail.com>
|
|
" Description: This module implements error/warning highlighting.
|
|
|
|
if !hlexists('ALEError')
|
|
highlight link ALEError SpellBad
|
|
endif
|
|
|
|
if !hlexists('ALEStyleError')
|
|
highlight link ALEStyleError ALEError
|
|
endif
|
|
|
|
if !hlexists('ALEWarning')
|
|
highlight link ALEWarning SpellCap
|
|
endif
|
|
|
|
if !hlexists('ALEStyleWarning')
|
|
highlight link ALEStyleWarning ALEWarning
|
|
endif
|
|
|
|
if !hlexists('ALEInfo')
|
|
highlight link ALEInfo ALEWarning
|
|
endif
|
|
|
|
" The maximum number of items for the second argument of matchaddpos()
|
|
let s:MAX_POS_VALUES = 8
|
|
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
|
|
if a:line >= a:end_line
|
|
" For single lines, just return the one position.
|
|
return [[[a:line, a:col, a:end_col - a:col + 1]]]
|
|
endif
|
|
|
|
" Get positions from the first line at the first column, up to a large
|
|
" integer for highlighting up to the end of the line, followed by
|
|
" the lines in-between, for highlighting entire lines, and
|
|
" a highlight for the last line, up to the end column.
|
|
let l:all_positions =
|
|
\ [[a:line, a:col, s:MAX_COL_SIZE]]
|
|
\ + range(a:line + 1, a:end_line - 1)
|
|
\ + [[a:end_line, 1, a:end_col]]
|
|
|
|
return map(
|
|
\ range(0, len(l:all_positions) - 1, s:MAX_POS_VALUES),
|
|
\ 'l:all_positions[v:val : v:val + s:MAX_POS_VALUES - 1]',
|
|
\)
|
|
endfunction
|
|
|
|
" Given a loclist for current items to highlight, remove all highlights
|
|
" except these which have matching loclist item entries.
|
|
|
|
function! ale#highlight#RemoveHighlights() abort
|
|
if ale#highlight#HasNeovimApi()
|
|
if get(b:, 'ale_nvim_highlight_id', 0)
|
|
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
|
|
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 - 1, 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
|
|
|
|
function! s:highlight_line(bufnr, lnum, group) abort
|
|
call matchaddpos(a:group, [a:lnum])
|
|
endfunction
|
|
|
|
function! s:highlight_range(bufnr, range, group) abort
|
|
" 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)'
|
|
\)
|
|
endfunction
|
|
|
|
function! ale#highlight#UpdateHighlights() abort
|
|
let l:item_list = get(b:, 'ale_enabled', 1) && g:ale_enabled
|
|
\ ? get(b:, 'ale_highlight_items', [])
|
|
\ : []
|
|
|
|
call ale#highlight#RemoveHighlights()
|
|
|
|
for l:item in l:item_list
|
|
if l:item.type is# 'W'
|
|
if get(l:item, 'sub_type', '') is# 'style'
|
|
let l:group = 'ALEStyleWarning'
|
|
else
|
|
let l:group = 'ALEWarning'
|
|
endif
|
|
elseif l:item.type is# 'I'
|
|
let l:group = 'ALEInfo'
|
|
elseif get(l:item, 'sub_type', '') is# 'style'
|
|
let l:group = 'ALEStyleError'
|
|
else
|
|
let l:group = 'ALEError'
|
|
endif
|
|
|
|
let l:range = {
|
|
\ 'lnum': l:item.lnum,
|
|
\ 'col': l:item.col,
|
|
\ 'end_lnum': get(l:item, 'end_lnum', l:item.lnum),
|
|
\ 'end_col': get(l:item, 'end_col', l:item.col)
|
|
\}
|
|
|
|
call s:highlight_range(l:item.bufnr, l:range, l:group)
|
|
endfor
|
|
|
|
" If highlights are enabled and signs are not enabled, we should still
|
|
" offer line highlights by adding a separate set of highlights.
|
|
if !g:ale_set_signs
|
|
let l:available_groups = {
|
|
\ 'ALEWarningLine': hlexists('ALEWarningLine'),
|
|
\ 'ALEInfoLine': hlexists('ALEInfoLine'),
|
|
\ 'ALEErrorLine': hlexists('ALEErrorLine'),
|
|
\}
|
|
|
|
for l:item in l:item_list
|
|
if l:item.type is# 'W'
|
|
let l:group = 'ALEWarningLine'
|
|
elseif l:item.type is# 'I'
|
|
let l:group = 'ALEInfoLine'
|
|
else
|
|
let l:group = 'ALEErrorLine'
|
|
endif
|
|
|
|
if l:available_groups[l:group]
|
|
call s:highlight_line(l:item.bufnr, l:item.lnum, l:group)
|
|
endif
|
|
endfor
|
|
endif
|
|
endfunction
|
|
|
|
function! ale#highlight#BufferHidden(buffer) abort
|
|
" Remove highlights right away when buffers are hidden.
|
|
" They will be restored later when buffers are entered.
|
|
call ale#highlight#RemoveHighlights()
|
|
endfunction
|
|
|
|
augroup ALEHighlightBufferGroup
|
|
autocmd!
|
|
autocmd BufEnter * call ale#highlight#UpdateHighlights()
|
|
autocmd BufHidden * call ale#highlight#BufferHidden(expand('<abuf>'))
|
|
augroup END
|
|
|
|
function! ale#highlight#SetHighlights(buffer, loclist) abort
|
|
let l:new_list = getbufvar(a:buffer, 'ale_enabled', 1) && g:ale_enabled
|
|
\ ? filter(copy(a:loclist), 'v:val.bufnr == a:buffer && v:val.col > 0')
|
|
\ : []
|
|
|
|
" Set the list in the buffer variable.
|
|
call setbufvar(str2nr(a:buffer), 'ale_highlight_items', l:new_list)
|
|
|
|
" Update highlights for the current buffer, which may or may not
|
|
" be the buffer we just set highlights for.
|
|
call ale#highlight#UpdateHighlights()
|
|
endfunction
|