]> git.madduck.net Git - etc/vim.git/blob - autoload/ale/list.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 / list.vim
1 " Author: Bjorn Neergaard <bjorn@neersighted.com>, modified by Yann fery <yann@fery.me>
2 " Description: Manages the loclist and quickfix lists
3
4 " This flag dictates if ale open the configured loclist
5 let g:ale_open_list = get(g:, 'ale_open_list', v:false)
6 " This flag dictates if ale keeps open loclist even if there is no error in loclist
7 let g:ale_keep_list_window_open = get(g:, 'ale_keep_list_window_open', 0)
8 " This flag dictates that quickfix windows should be opened vertically
9 let g:ale_list_vertical = get(g:, 'ale_list_vertical', v:false)
10 " The window size to set for the quickfix and loclist windows
11 let g:ale_list_window_size = get(g:, 'ale_list_window_size', 10)
12 " A string format for the loclist messages.
13 let g:ale_loclist_msg_format = get(g:, 'ale_loclist_msg_format',
14 \   get(g:, 'ale_echo_msg_format', '%code: %%s')
15 \)
16
17 if !exists('s:timer_args')
18     let s:timer_args = {}
19 endif
20
21 " Return 1 if there is a buffer with buftype == 'quickfix' in buffer list
22 function! ale#list#IsQuickfixOpen() abort
23     let l:res = getqflist({ 'winid' : winnr() })
24
25     if has_key(l:res, 'winid') && l:res.winid > 0
26         return 1
27     endif
28
29     let l:res = getloclist(0, { 'winid' : winnr() })
30
31     if has_key(l:res, 'winid') && l:res.winid > 0
32         return 1
33     endif
34
35     return 0
36 endfunction
37
38 " Check if we should open the list, based on the save event being fired, and
39 " that setting being on, or that the error count is at least as high as the
40 " setting when set to an integer value.
41 function! s:ShouldOpen(buffer, loclist_len) abort
42     let l:val = ale#Var(a:buffer, 'open_list')
43     let l:saved = getbufvar(a:buffer, 'ale_save_event_fired', 0)
44
45     return l:val > 0 ? a:loclist_len >= l:val : l:val is# 'on_save' && l:saved
46 endfunction
47
48 " Check if we should close the list, based on the save event being fired, and
49 " that setting being on, or the setting just being set to an integer value.
50 function! s:ShouldClose(buffer) abort
51     let l:val = ale#Var(a:buffer, 'open_list')
52     let l:saved = getbufvar(a:buffer, 'ale_save_event_fired', 0)
53
54     return !((l:val >= 1) || (l:val is# 'on_save' && l:saved))
55 endfunction
56
57 function! s:Deduplicate(list) abort
58     let l:list = a:list
59
60     call sort(l:list, function('ale#util#LocItemCompareWithText'))
61     call uniq(l:list, function('ale#util#LocItemCompareWithText'))
62
63     return l:list
64 endfunction
65
66 function! ale#list#GetCombinedList() abort
67     let l:list = []
68
69     for l:info in values(g:ale_buffer_info)
70         call extend(l:list, l:info.loclist)
71     endfor
72
73     return s:Deduplicate(l:list)
74 endfunction
75
76 function! s:FixList(buffer, list) abort
77     let l:format = ale#Var(a:buffer, 'loclist_msg_format')
78     let l:new_list = []
79
80     for l:item in a:list
81         let l:fixed_item = copy(l:item)
82
83         let l:fixed_item.text = ale#GetLocItemMessage(l:item, l:format)
84
85         if l:item.bufnr == -1
86             " If the buffer number is invalid, remove it.
87             call remove(l:fixed_item, 'bufnr')
88         endif
89
90         call add(l:new_list, l:fixed_item)
91     endfor
92
93     return l:new_list
94 endfunction
95
96 function! s:WinFindBuf(buffer) abort
97     return exists('*win_findbuf') ? win_findbuf(str2nr(a:buffer)) : [0]
98 endfunction
99
100 function! s:SetListsImpl(timer_id, buffer, loclist) abort
101     let l:title = expand('#' . a:buffer . ':p')
102
103     if g:ale_set_quickfix
104         let l:quickfix_list = ale#list#GetCombinedList()
105
106         if has('nvim')
107             call setqflist(s:FixList(a:buffer, l:quickfix_list), ' ', l:title)
108         else
109             call setqflist(s:FixList(a:buffer, l:quickfix_list))
110             call setqflist([], 'r', {'title': l:title})
111         endif
112     elseif g:ale_set_loclist
113         " If windows support is off, win_findbuf() may not exist.
114         " We'll set result in the current window, which might not be correct,
115         " but it's better than nothing.
116         let l:ids = s:WinFindBuf(a:buffer)
117
118         let l:loclist = s:Deduplicate(a:loclist)
119
120         for l:id in l:ids
121             if has('nvim')
122                 call setloclist(l:id, s:FixList(a:buffer, l:loclist), ' ', l:title)
123             else
124                 call setloclist(l:id, s:FixList(a:buffer, l:loclist))
125                 call setloclist(l:id, [], 'r', {'title': l:title})
126             endif
127         endfor
128     endif
129
130     " Save the current view before opening/closing any window
131     call setbufvar(a:buffer, 'ale_winview', winsaveview())
132
133     " Open a window to show the problems if we need to.
134     "
135     " ShouldOpen() checks if the current buffer has enough problems to be
136     " opened.
137     if s:ShouldOpen(a:buffer, len(a:loclist))
138         let l:winnr = winnr()
139         let l:mode = mode()
140
141         " open windows vertically instead of default horizontally
142         let l:open_type = ''
143
144         if ale#Var(a:buffer, 'list_vertical') == 1
145             let l:open_type = 'vert rightbelow '
146         endif
147
148         if g:ale_set_quickfix
149             if !ale#list#IsQuickfixOpen()
150                 silent! execute l:open_type . 'copen ' . str2nr(ale#Var(a:buffer, 'list_window_size'))
151             endif
152         elseif g:ale_set_loclist
153             silent! execute l:open_type . 'lopen ' . str2nr(ale#Var(a:buffer, 'list_window_size'))
154         endif
155
156         " If focus changed, restore it (jump to the last window).
157         if l:winnr isnot# winnr()
158             wincmd p
159         endif
160
161         " Return to original mode when applicable
162         if mode() != l:mode
163             if l:mode is? 'v' || l:mode is# "\<c-v>"
164                 " Reset our last visual selection
165                 normal! gv
166             elseif l:mode is? 's' || l:mode is# "\<c-s>"
167                 " Reset our last character selection
168                 normal! "\<c-g>"
169             endif
170         endif
171
172         call s:RestoreViewIfNeeded(a:buffer)
173     endif
174
175     " If ALE isn't currently checking for more problems, close the window if
176     " needed now. This check happens inside of this timer function, so
177     " the window can be closed reliably.
178     if !ale#engine#IsCheckingBuffer(a:buffer)
179         call s:CloseWindowIfNeeded(a:buffer)
180     endif
181 endfunction
182
183 " Try to restore the window view after closing any of the lists to avoid making
184 " the it moving around, especially useful when on insert mode
185 function! s:RestoreViewIfNeeded(buffer) abort
186     let l:saved_view = getbufvar(a:buffer, 'ale_winview', {})
187
188     " Saved view is empty, can't do anything
189     if empty(l:saved_view)
190         return
191     endif
192
193     " Check whether the cursor has moved since linting was actually requested. If
194     " the user has indeed moved lines, do nothing
195     let l:current_view = winsaveview()
196
197     if l:current_view['lnum'] != l:saved_view['lnum']
198         return
199     endif
200
201     " Anchor view by topline if the list is set to open horizontally
202     if ale#Var(a:buffer, 'list_vertical') == 0
203         call winrestview({'topline': l:saved_view['topline']})
204     endif
205 endfunction
206
207 function! ale#list#SetLists(buffer, loclist) abort
208     if get(g:, 'ale_set_lists_synchronously') == 1
209     \|| getbufvar(a:buffer, 'ale_save_event_fired', 0)
210         " Update lists immediately if running a test synchronously, or if the
211         " buffer was saved.
212         "
213         " The lists need to be updated immediately when saving a buffer so
214         " that we can reliably close window automatically, if so configured.
215         call s:SetListsImpl(-1, a:buffer, a:loclist)
216     else
217         call ale#util#StartPartialTimer(
218         \   0,
219         \   function('s:SetListsImpl'),
220         \   [a:buffer, a:loclist],
221         \)
222     endif
223 endfunction
224
225 function! ale#list#ForcePopulateErrorList(populate_quickfix) abort
226     let l:quickfix_bak = g:ale_set_quickfix
227     let g:ale_set_quickfix = a:populate_quickfix
228     let l:loclist_bak = g:ale_set_loclist
229     let g:ale_set_loclist = !a:populate_quickfix
230     let l:open_list_bak = g:ale_open_list
231     let g:ale_open_list = 1
232
233     let l:buffer = bufnr('')
234     let l:loclist = get(g:ale_buffer_info, l:buffer, {'loclist': []}).loclist
235     call s:SetListsImpl(-1, l:buffer, l:loclist)
236
237     let g:ale_open_list = l:open_list_bak
238     let g:ale_set_loclist = l:loclist_bak
239     let g:ale_set_quickfix = l:quickfix_bak
240 endfunction
241
242 function! s:CloseWindowIfNeeded(buffer) abort
243     if ale#Var(a:buffer, 'keep_list_window_open') || s:ShouldClose(a:buffer)
244         return
245     endif
246
247     let l:did_close_any_list = 0
248
249     try
250         " Only close windows if the quickfix list or loclist is completely empty,
251         " including errors set through other means.
252         if g:ale_set_quickfix
253             if empty(getqflist())
254                 cclose
255                 let l:did_close_any_list = 1
256             endif
257         else
258             let l:win_ids = s:WinFindBuf(a:buffer)
259
260             for l:win_id in l:win_ids
261                 if g:ale_set_loclist && empty(getloclist(l:win_id))
262                     lclose
263                     let l:did_close_any_list = 1
264                 endif
265             endfor
266         endif
267     " Ignore 'Cannot close last window' errors.
268     catch /E444/
269     endtry
270
271     if l:did_close_any_list
272         call s:RestoreViewIfNeeded(a:buffer)
273     endif
274 endfunction