]> git.madduck.net Git - etc/vim.git/blob - autoload/ale/highlight.vim

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

Squashed '.vim/bundle/ale/' content from commit 22185c4c
[etc/vim.git] / autoload / ale / highlight.vim
1 scriptencoding utf8
2 " Author: w0rp <devw0rp@gmail.com>
3 " Description: This module implements error/warning highlighting.
4
5 if !hlexists('ALEError')
6     highlight link ALEError SpellBad
7 endif
8
9 if !hlexists('ALEStyleError')
10     highlight link ALEStyleError ALEError
11 endif
12
13 if !hlexists('ALEWarning')
14     highlight link ALEWarning SpellCap
15 endif
16
17 if !hlexists('ALEStyleWarning')
18     highlight link ALEStyleWarning ALEWarning
19 endif
20
21 if !hlexists('ALEInfo')
22     highlight link ALEInfo ALEWarning
23 endif
24
25 " The maximum number of items for the second argument of matchaddpos()
26 let s:MAX_POS_VALUES = 8
27 let s:MAX_COL_SIZE = 1073741824 " pow(2, 30)
28
29 let s:has_nvim_highlight = exists('*nvim_buf_add_highlight') && exists('*nvim_buf_clear_namespace')
30
31 if s:has_nvim_highlight
32     let s:ns_id = nvim_create_namespace('ale_highlight')
33 endif
34
35 " Wrappers are necessary to test this functionality by faking the calls in tests.
36 function! ale#highlight#nvim_buf_add_highlight(buffer, ns_id, hl_group, line, col_start, col_end) abort
37     " Ignore all errors for adding highlights.
38     try
39         call nvim_buf_add_highlight(a:buffer, a:ns_id, a:hl_group, a:line, a:col_start, a:col_end)
40     catch
41     endtry
42 endfunction
43
44 function! ale#highlight#nvim_buf_clear_namespace(buffer, ns_id, line_start, line_end) abort
45     call nvim_buf_clear_namespace(a:buffer, a:ns_id, a:line_start, a:line_end)
46 endfunction
47
48 function! ale#highlight#CreatePositions(line, col, end_line, end_col) abort
49     if a:line >= a:end_line
50         " For single lines, just return the one position.
51         return [[[a:line, a:col, a:end_col - a:col + 1]]]
52     endif
53
54     " Get positions from the first line at the first column, up to a large
55     " integer for highlighting up to the end of the line, followed by
56     " the lines in-between, for highlighting entire lines, and
57     " a highlight for the last line, up to the end column.
58     let l:all_positions =
59     \   [[a:line, a:col, s:MAX_COL_SIZE]]
60     \   + range(a:line + 1, a:end_line - 1)
61     \   + [[a:end_line, 1, a:end_col]]
62
63     return map(
64     \   range(0, len(l:all_positions) - 1, s:MAX_POS_VALUES),
65     \   'l:all_positions[v:val : v:val + s:MAX_POS_VALUES - 1]',
66     \)
67 endfunction
68
69 " Given a loclist for current items to highlight, remove all highlights
70 " except these which have matching loclist item entries.
71
72 function! ale#highlight#RemoveHighlights() abort
73     if s:has_nvim_highlight
74         call ale#highlight#nvim_buf_clear_namespace(bufnr(''), s:ns_id, 0, -1)
75     else
76         for l:match in getmatches()
77             if l:match.group =~? '\v^ALE(Style)?(Error|Warning|Info)(Line)?$'
78                 call matchdelete(l:match.id)
79             endif
80         endfor
81     endif
82 endfunction
83
84 " Same semantics of matchaddpos but will use nvim_buf_add_highlight if
85 " available. This involves iterating over the position list, switching from
86 " 1-based indexing to 0-based indexing, and translating the multiple ways
87 " that position can be specified for matchaddpos into line + col_start +
88 " col_end.
89 function! s:matchaddpos(group, pos_list) abort
90     if s:has_nvim_highlight
91         for l:pos in a:pos_list
92             let l:line = type(l:pos) == v:t_number
93             \   ? l:pos - 1
94             \   : l:pos[0] - 1
95
96             if type(l:pos) == v:t_number || len(l:pos) == 1
97                 let l:col_start = 0
98                 let l:col_end = s:MAX_COL_SIZE
99             else
100                 let l:col_start = l:pos[1] - 1
101                 let l:col_end = l:col_start + get(l:pos, 2, 1)
102             endif
103
104             call ale#highlight#nvim_buf_add_highlight(
105             \   bufnr(''),
106             \   s:ns_id,
107             \   a:group,
108             \   l:line,
109             \   l:col_start,
110             \   l:col_end,
111             \)
112         endfor
113     else
114         call matchaddpos(a:group, a:pos_list)
115     endif
116 endfunction
117
118 function! s:highlight_line(bufnr, lnum, group) abort
119     call s:matchaddpos(a:group, [a:lnum])
120 endfunction
121
122 function! s:highlight_range(bufnr, range, group) abort
123     " Set all of the positions, which are chunked into Lists which
124     " are as large as will be accepted by matchaddpos.
125     call map(
126     \   ale#highlight#CreatePositions(
127     \       a:range.lnum,
128     \       a:range.col,
129     \       a:range.end_lnum,
130     \       a:range.end_col
131     \   ),
132     \   's:matchaddpos(a:group, v:val)'
133     \)
134 endfunction
135
136 function! ale#highlight#UpdateHighlights() abort
137     let l:item_list = get(b:, 'ale_enabled', 1) && g:ale_enabled
138     \   ? get(b:, 'ale_highlight_items', [])
139     \   : []
140
141     call ale#highlight#RemoveHighlights()
142
143     for l:item in l:item_list
144         if l:item.type is# 'W'
145             if get(l:item, 'sub_type', '') is# 'style'
146                 let l:group = 'ALEStyleWarning'
147             else
148                 let l:group = 'ALEWarning'
149             endif
150         elseif l:item.type is# 'I'
151             let l:group = 'ALEInfo'
152         elseif get(l:item, 'sub_type', '') is# 'style'
153             let l:group = 'ALEStyleError'
154         else
155             let l:group = 'ALEError'
156         endif
157
158         let l:range = {
159         \   'lnum': l:item.lnum,
160         \   'col': l:item.col,
161         \   'end_lnum': get(l:item, 'end_lnum', l:item.lnum),
162         \   'end_col': get(l:item, 'end_col', l:item.col)
163         \}
164
165         call s:highlight_range(l:item.bufnr, l:range, l:group)
166     endfor
167
168     " If highlights are enabled and signs are not enabled, we should still
169     " offer line highlights by adding a separate set of highlights.
170     if !g:ale_set_signs
171         let l:available_groups = {
172         \   'ALEWarningLine': hlexists('ALEWarningLine'),
173         \   'ALEInfoLine': hlexists('ALEInfoLine'),
174         \   'ALEErrorLine': hlexists('ALEErrorLine'),
175         \}
176
177         for l:item in l:item_list
178             if l:item.type is# 'W'
179                 let l:group = 'ALEWarningLine'
180             elseif l:item.type is# 'I'
181                 let l:group = 'ALEInfoLine'
182             else
183                 let l:group = 'ALEErrorLine'
184             endif
185
186             if l:available_groups[l:group]
187                 call s:highlight_line(l:item.bufnr, l:item.lnum, l:group)
188             endif
189         endfor
190     endif
191 endfunction
192
193 function! ale#highlight#BufferHidden(buffer) abort
194     " Remove highlights right away when buffers are hidden.
195     " They will be restored later when buffers are entered.
196     call ale#highlight#RemoveHighlights()
197 endfunction
198
199 augroup ALEHighlightBufferGroup
200     autocmd!
201     autocmd BufEnter * call ale#highlight#UpdateHighlights()
202     autocmd BufHidden * call ale#highlight#BufferHidden(expand('<abuf>'))
203 augroup END
204
205 function! ale#highlight#SetHighlights(buffer, loclist) abort
206     let l:new_list = getbufvar(a:buffer, 'ale_enabled', 1) && g:ale_enabled
207     \   ? filter(copy(a:loclist), 'v:val.bufnr == a:buffer && v:val.col > 0')
208     \   : []
209
210     " Set the list in the buffer variable.
211     call setbufvar(str2nr(a:buffer), 'ale_highlight_items', l:new_list)
212
213     let l:exclude_list = ale#Var(a:buffer, 'exclude_highlights')
214
215     if !empty(l:exclude_list)
216         call filter(l:new_list, 'empty(ale#util#GetMatches(v:val.text, l:exclude_list))')
217     endif
218
219     " Update highlights for the current buffer, which may or may not
220     " be the buffer we just set highlights for.
221     call ale#highlight#UpdateHighlights()
222 endfunction