1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
|
" vim: ts=4 sw=4 et
scriptencoding utf-8
let s:is_enabled = 0
let s:match_base_priority = 10
" args: a:1: force enabling? (used in tests and for VimEnter callback)
function! neomake#quickfix#enable(...) abort
if has('vim_starting') && !(a:0 && a:1)
" Delay enabling for our FileType autocommand to happen as late as
" possible, since placing signs triggers a redraw, and together with
" vim-qf_resize this causes flicker.
" https://github.com/vim/vim/issues/2763
augroup neomake_qf
autocmd!
autocmd VimEnter * call neomake#quickfix#enable(1)
augroup END
return
endif
call neomake#log#debug('enabling custom quickfix list handling.')
let s:is_enabled = 1
augroup neomake_qf
autocmd!
autocmd FileType qf call neomake#quickfix#FormatQuickfix()
augroup END
if &filetype ==# 'qf'
call neomake#quickfix#FormatQuickfix()
endif
endfunction
function! neomake#quickfix#disable() abort
call neomake#log#debug('disabling custom quickfix list handling.')
let s:is_enabled = 0
if &filetype ==# 'qf'
call neomake#quickfix#FormatQuickfix()
endif
if exists('#neomake_qf')
autocmd! neomake_qf
augroup! neomake_qf
endif
endfunction
function! neomake#quickfix#is_enabled() abort
return s:is_enabled
endfunction
function! s:cursor_moved() abort
if b:neomake_start_col
if col('.') <= b:neomake_start_col + 1
call cursor(line('.'), b:neomake_start_col + 2)
endif
if exists('w:_neomake_cursor_match_id')
silent! call matchdelete(w:_neomake_cursor_match_id)
endif
if exists('*matchaddpos')
let w:_neomake_cursor_match_id = matchaddpos('neomakeCursorListNr',
\ [[line('.'), (b:neomake_start_col - b:neomake_number_len) + 2, b:neomake_number_len]],
\ s:match_base_priority+3)
else
let w:_neomake_cursor_match_id = matchadd('neomakeCursorListNr',
\ '\%' . line('.') . 'c'
\. '\%' . ((b:neomake_start_col - b:neomake_number_len) + 2) . 'c'
\. '.\{' . b:neomake_number_len . '}',
\ s:match_base_priority+3)
endif
endif
endfunction
function! s:set_qf_lines(lines) abort
let ul = &l:undolevels
setlocal modifiable nonumber undolevels=-1
call setline(1, a:lines)
let &l:undolevels = ul
setlocal nomodifiable nomodified
endfunction
function! s:clean_qf_annotations() abort
call neomake#log#debug('cleaning qf annotations.', {'bufnr': bufnr('%')})
if exists('b:_neomake_qf_orig_lines')
call s:set_qf_lines(b:_neomake_qf_orig_lines)
unlet b:_neomake_qf_orig_lines
endif
unlet b:neomake_qf
augroup neomake_qf
autocmd! * <buffer>
augroup END
call s:clean_matches()
endfunction
function! s:clean_matches() abort
if exists('w:_neomake_maker_match_id')
silent! call matchdelete(w:_neomake_maker_match_id)
endif
if exists('w:_neomake_gutter_match_id')
silent! call matchdelete(w:_neomake_gutter_match_id)
endif
if exists('w:_neomake_bufname_match_id')
silent! call matchdelete(w:_neomake_bufname_match_id)
endif
if exists('w:_neomake_cursor_match_id')
silent! call matchdelete(w:_neomake_cursor_match_id)
endif
call neomake#signs#ResetFile(bufnr('%'))
endfunction
function! neomake#quickfix#FormatQuickfix() abort
let buf = bufnr('%')
if !s:is_enabled || &filetype !=# 'qf'
if exists('b:neomake_qf')
call s:clean_qf_annotations()
endif
return
endif
let src_buf = 0
if has('patch-7.4.2215')
let is_loclist = getwininfo(win_getid())[0].loclist
if is_loclist
let qflist = getloclist(0)
else
let qflist = getqflist()
endif
else
let is_loclist = 1
let qflist = getloclist(0)
if empty(qflist)
let is_loclist = 0
let qflist = getqflist()
endif
endif
if empty(qflist) || qflist[0].text !~# ' nmcfg:{.\{-}}$'
if exists('b:neomake_qf')
call neomake#log#debug('Resetting custom qf for non-Neomake change.')
call s:clean_qf_annotations()
endif
return
endif
if is_loclist
let b:neomake_qf = 'file'
let src_buf = qflist[0].bufnr
else
let b:neomake_qf = 'project'
endif
let lines = []
let signs = []
let i = 0
let lnum_width = 0
let col_width = 0
let maker_width = 0
let nmcfg = {}
let makers = {}
for item in qflist
" Look for marker at end of entry.
if item.text[-1:] ==# '}'
let idx = strridx(item.text, ' nmcfg:{')
if idx != -1
let config = item.text[idx+7:]
try
let nmcfg = eval(config)
if !has_key(makers, nmcfg.name)
let makers[nmcfg.name] = 0
endif
let item.text = idx == 0 ? '' : item.text[:(idx-1)]
catch
call neomake#log#exception(printf(
\ 'Error when evaluating nmcfg (%s): %s.',
\ config, v:exception))
endtry
endif
endif
" Count entries.
if !empty(nmcfg)
let makers[nmcfg.name] += 1
endif
let item.maker_name = get(nmcfg, 'short', '????')
let maker_width = max([len(item.maker_name), maker_width])
if item.lnum
let lnum_width = max([len(item.lnum), lnum_width])
let col_width = max([len(item.col), col_width])
endif
let i += 1
endfor
let syntax = keys(makers)
if src_buf
for ft in split(neomake#compat#getbufvar(src_buf, '&filetype', ''), '\.')
if !empty(ft) && index(syntax, ft) == -1
call add(syntax, ft)
endif
endfor
endif
if get(b:, '_neomake_cur_syntax', []) != syntax
runtime! syntax/neomake/qf.vim
for name in syntax
execute 'runtime! syntax/neomake/'.name.'.vim '
\ . 'syntax/neomake/'.name.'/*.vim'
endfor
let b:_neomake_cur_syntax = syntax
endif
if maker_width + lnum_width + col_width > 0
let b:neomake_start_col = maker_width + lnum_width + col_width + 2
let b:neomake_number_len = lnum_width + col_width + 2
let blank_col = repeat(' ', lnum_width + col_width + 1)
else
let b:neomake_start_col = 0
let b:neomake_number_len = 0
let blank_col = ''
endif
" Count number of different buffers and cache their names.
let buffers = neomake#compat#uniq(sort(
\ filter(map(copy(qflist), 'v:val.bufnr'), 'v:val != 0')))
let buffer_names = {}
if len(buffers) > 1
for b in buffers
let bufname = bufname(b)
if empty(bufname)
let bufname = 'buf:'.b
else
let bufname = fnamemodify(bufname, ':t')
if len(bufname) > 15
let bufname = bufname[0:13].'…'
endif
endif
let buffer_names[b] = bufname
endfor
endif
let i = 1
let last_bufnr = -1
for item in qflist
if item.lnum
call add(signs, {'lnum': i, 'bufnr': buf, 'type': item.type})
endif
let i += 1
let text = item.text
if item.bufnr != 0 && !empty(buffer_names)
if last_bufnr != item.bufnr
let text = printf('[%s] %s', buffer_names[item.bufnr], text)
let last_bufnr = item.bufnr
endif
endif
if !item.lnum
call add(lines, printf('%*s %s %s',
\ maker_width, item.maker_name,
\ blank_col, text))
else
call add(lines, printf('%*s %*s:%*s %s',
\ maker_width, item.maker_name,
\ lnum_width, item.lnum,
\ col_width, item.col ? item.col : '-',
\ text))
endif
endfor
if !exists('b:_neomake_qf_orig_lines')
let b:_neomake_qf_orig_lines = getbufline('%', 1, '$')
endif
call s:set_qf_lines(lines)
if exists('+breakindent')
" Keeps the text aligned with the fake gutter.
setlocal breakindent linebreak
let &breakindentopt = 'shift:'.(b:neomake_start_col + 1)
endif
call neomake#signs#Reset(buf, 'file')
call neomake#signs#PlaceSigns(buf, signs, 'file')
call s:add_window_matches(maker_width)
augroup neomake_qf
autocmd! * <buffer>
autocmd CursorMoved <buffer> call s:cursor_moved()
" Annotate in new window, e.g. with "tab sp".
" It keeps the syntax there, so should also have the rest.
" This happens on Neovim already through CursorMoved being invoked
" always then.
if exists('##WinNew')
exe 'autocmd WinNew <buffer> call s:add_window_matches('.maker_width.')'
endif
" Clear matches when opening another buffer in the same window, with
" the original window/buffer still being visible (e.g. in another
" tab).
autocmd BufLeave <buffer> call s:on_bufleave()
augroup END
" Set title.
" Fallback without patch-7.4.2200, fix for without 8.0.1831.
if !has('patch-7.4.2200') || !exists('w:quickfix_title') || w:quickfix_title[0] ==# ':'
let maker_info = []
for [maker, c] in items(makers)
call add(maker_info, maker.'('.c.')')
endfor
let maker_info_str = join(maker_info, ', ')
if is_loclist
let prefix = 'file'
else
let prefix = 'project'
endif
let w:quickfix_title = neomake#list#get_title(prefix, src_buf, maker_info_str)
endif
endfunction
function! s:on_bufleave() abort
let s:left_winnr = winnr()
augroup neomake_qf
autocmd BufEnter * call s:on_bufenter_after_bufleave()
augroup END
endfunction
function! s:on_bufenter_after_bufleave() abort
if winnr() == s:left_winnr
call s:clean_matches()
endif
unlet s:left_winnr
augroup neomake_qf
autocmd! BufEnter
augroup END
endfunction
function! s:add_window_matches(maker_width) abort
if !b:neomake_start_col
return
endif
if exists('w:_neomake_maker_match_id')
silent! call matchdelete(w:_neomake_maker_match_id)
endif
let w:_neomake_maker_match_id = matchadd('neomakeMakerName',
\ '.*\%<'.(a:maker_width + 1).'c',
\ s:match_base_priority+1)
if exists('w:_neomake_gutter_match_id')
silent! call matchdelete(w:_neomake_gutter_match_id)
endif
let w:_neomake_gutter_match_id = matchadd('neomakeListNr',
\ '\%>'.(a:maker_width).'c'
\ .'.*\%<'.(b:neomake_start_col + 2).'c',
\ s:match_base_priority+2)
if exists('w:_neomake_bufname_match_id')
silent! call matchdelete(w:_neomake_bufname_match_id)
endif
let w:_neomake_bufname_match_id = matchadd('neomakeBufferName',
\ '.*\%<'.(a:maker_width + 1).'c',
\ s:match_base_priority+3)
endfunction
|