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.
1 " Author: Bjorn Neergaard <bjorn@neersighted.com>, modified by Yann fery <yann@fery.me>
2 " Description: Manages the loclist and quickfix lists
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')
17 if !exists('s:timer_args')
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() })
25 if has_key(l:res, 'winid') && l:res.winid > 0
29 let l:res = getloclist(0, { 'winid' : winnr() })
31 if has_key(l:res, 'winid') && l:res.winid > 0
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)
45 return l:val > 0 ? a:loclist_len >= l:val : l:val is# 'on_save' && l:saved
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)
54 return !((l:val >= 1) || (l:val is# 'on_save' && l:saved))
57 function! s:Deduplicate(list) abort
60 call sort(l:list, function('ale#util#LocItemCompareWithText'))
61 call uniq(l:list, function('ale#util#LocItemCompareWithText'))
66 function! ale#list#GetCombinedList() abort
69 for l:info in values(g:ale_buffer_info)
70 call extend(l:list, l:info.loclist)
73 return s:Deduplicate(l:list)
76 function! s:FixList(buffer, list) abort
77 let l:format = ale#Var(a:buffer, 'loclist_msg_format')
81 let l:fixed_item = copy(l:item)
83 let l:fixed_item.text = ale#GetLocItemMessage(l:item, l:format)
86 " If the buffer number is invalid, remove it.
87 call remove(l:fixed_item, 'bufnr')
90 call add(l:new_list, l:fixed_item)
96 function! s:WinFindBuf(buffer) abort
97 return exists('*win_findbuf') ? win_findbuf(str2nr(a:buffer)) : [0]
100 function! s:SetListsImpl(timer_id, buffer, loclist) abort
101 let l:title = expand('#' . a:buffer . ':p')
103 if g:ale_set_quickfix
104 let l:quickfix_list = ale#list#GetCombinedList()
107 call setqflist(s:FixList(a:buffer, l:quickfix_list), ' ', l:title)
109 call setqflist(s:FixList(a:buffer, l:quickfix_list))
110 call setqflist([], 'r', {'title': l:title})
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)
118 let l:loclist = s:Deduplicate(a:loclist)
122 call setloclist(l:id, s:FixList(a:buffer, l:loclist), ' ', l:title)
124 call setloclist(l:id, s:FixList(a:buffer, l:loclist))
125 call setloclist(l:id, [], 'r', {'title': l:title})
130 " Save the current view before opening/closing any window
131 call setbufvar(a:buffer, 'ale_winview', winsaveview())
133 " Open a window to show the problems if we need to.
135 " ShouldOpen() checks if the current buffer has enough problems to be
137 if s:ShouldOpen(a:buffer, len(a:loclist))
138 let l:winnr = winnr()
141 " open windows vertically instead of default horizontally
144 if ale#Var(a:buffer, 'list_vertical') == 1
145 let l:open_type = 'vert rightbelow '
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'))
152 elseif g:ale_set_loclist
153 silent! execute l:open_type . 'lopen ' . str2nr(ale#Var(a:buffer, 'list_window_size'))
156 " If focus changed, restore it (jump to the last window).
157 if l:winnr isnot# winnr()
161 " Return to original mode when applicable
163 if l:mode is? 'v' || l:mode is# "\<c-v>"
164 " Reset our last visual selection
166 elseif l:mode is? 's' || l:mode is# "\<c-s>"
167 " Reset our last character selection
172 call s:RestoreViewIfNeeded(a:buffer)
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)
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', {})
188 " Saved view is empty, can't do anything
189 if empty(l:saved_view)
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()
197 if l:current_view['lnum'] != l:saved_view['lnum']
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']})
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
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)
217 call ale#util#StartPartialTimer(
219 \ function('s:SetListsImpl'),
220 \ [a:buffer, a:loclist],
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
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)
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
242 function! s:CloseWindowIfNeeded(buffer) abort
243 if ale#Var(a:buffer, 'keep_list_window_open') || s:ShouldClose(a:buffer)
247 let l:did_close_any_list = 0
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())
255 let l:did_close_any_list = 1
258 let l:win_ids = s:WinFindBuf(a:buffer)
260 for l:win_id in l:win_ids
261 if g:ale_set_loclist && empty(getloclist(l:win_id))
263 let l:did_close_any_list = 1
267 " Ignore 'Cannot close last window' errors.
271 if l:did_close_any_list
272 call s:RestoreViewIfNeeded(a:buffer)