mirror of
https://github.com/dense-analysis/ale.git
synced 2025-12-25 05:21:08 +08:00
1. Add ale.setup and ale.setup.buffer for pure Lua configuration. 2. Update many global settings to use Booleans instead of numbers to make types easiert to work with in Lua. 3. Radically reformat documentation and fix errors to make documentation more usable for Neovim users.
275 lines
8.9 KiB
VimL
275 lines
8.9 KiB
VimL
" Author: Bjorn Neergaard <bjorn@neersighted.com>, modified by Yann fery <yann@fery.me>
|
|
" Description: Manages the loclist and quickfix lists
|
|
|
|
" This flag dictates if ale open the configured loclist
|
|
let g:ale_open_list = get(g:, 'ale_open_list', v:false)
|
|
" This flag dictates if ale keeps open loclist even if there is no error in loclist
|
|
let g:ale_keep_list_window_open = get(g:, 'ale_keep_list_window_open', 0)
|
|
" This flag dictates that quickfix windows should be opened vertically
|
|
let g:ale_list_vertical = get(g:, 'ale_list_vertical', v:false)
|
|
" The window size to set for the quickfix and loclist windows
|
|
let g:ale_list_window_size = get(g:, 'ale_list_window_size', 10)
|
|
" A string format for the loclist messages.
|
|
let g:ale_loclist_msg_format = get(g:, 'ale_loclist_msg_format',
|
|
\ get(g:, 'ale_echo_msg_format', '%code: %%s')
|
|
\)
|
|
|
|
if !exists('s:timer_args')
|
|
let s:timer_args = {}
|
|
endif
|
|
|
|
" Return 1 if there is a buffer with buftype == 'quickfix' in buffer list
|
|
function! ale#list#IsQuickfixOpen() abort
|
|
let l:res = getqflist({ 'winid' : winnr() })
|
|
|
|
if has_key(l:res, 'winid') && l:res.winid > 0
|
|
return 1
|
|
endif
|
|
|
|
let l:res = getloclist(0, { 'winid' : winnr() })
|
|
|
|
if has_key(l:res, 'winid') && l:res.winid > 0
|
|
return 1
|
|
endif
|
|
|
|
return 0
|
|
endfunction
|
|
|
|
" Check if we should open the list, based on the save event being fired, and
|
|
" that setting being on, or that the error count is at least as high as the
|
|
" setting when set to an integer value.
|
|
function! s:ShouldOpen(buffer, loclist_len) abort
|
|
let l:val = ale#Var(a:buffer, 'open_list')
|
|
let l:saved = getbufvar(a:buffer, 'ale_save_event_fired', 0)
|
|
|
|
return l:val > 0 ? a:loclist_len >= l:val : l:val is# 'on_save' && l:saved
|
|
endfunction
|
|
|
|
" Check if we should close the list, based on the save event being fired, and
|
|
" that setting being on, or the setting just being set to an integer value.
|
|
function! s:ShouldClose(buffer) abort
|
|
let l:val = ale#Var(a:buffer, 'open_list')
|
|
let l:saved = getbufvar(a:buffer, 'ale_save_event_fired', 0)
|
|
|
|
return !((l:val >= 1) || (l:val is# 'on_save' && l:saved))
|
|
endfunction
|
|
|
|
function! s:Deduplicate(list) abort
|
|
let l:list = a:list
|
|
|
|
call sort(l:list, function('ale#util#LocItemCompareWithText'))
|
|
call uniq(l:list, function('ale#util#LocItemCompareWithText'))
|
|
|
|
return l:list
|
|
endfunction
|
|
|
|
function! ale#list#GetCombinedList() abort
|
|
let l:list = []
|
|
|
|
for l:info in values(g:ale_buffer_info)
|
|
call extend(l:list, l:info.loclist)
|
|
endfor
|
|
|
|
return s:Deduplicate(l:list)
|
|
endfunction
|
|
|
|
function! s:FixList(buffer, list) abort
|
|
let l:format = ale#Var(a:buffer, 'loclist_msg_format')
|
|
let l:new_list = []
|
|
|
|
for l:item in a:list
|
|
let l:fixed_item = copy(l:item)
|
|
|
|
let l:fixed_item.text = ale#GetLocItemMessage(l:item, l:format)
|
|
|
|
if l:item.bufnr == -1
|
|
" If the buffer number is invalid, remove it.
|
|
call remove(l:fixed_item, 'bufnr')
|
|
endif
|
|
|
|
call add(l:new_list, l:fixed_item)
|
|
endfor
|
|
|
|
return l:new_list
|
|
endfunction
|
|
|
|
function! s:WinFindBuf(buffer) abort
|
|
return exists('*win_findbuf') ? win_findbuf(str2nr(a:buffer)) : [0]
|
|
endfunction
|
|
|
|
function! s:SetListsImpl(timer_id, buffer, loclist) abort
|
|
let l:title = expand('#' . a:buffer . ':p')
|
|
|
|
if g:ale_set_quickfix
|
|
let l:quickfix_list = ale#list#GetCombinedList()
|
|
|
|
if has('nvim')
|
|
call setqflist(s:FixList(a:buffer, l:quickfix_list), ' ', l:title)
|
|
else
|
|
call setqflist(s:FixList(a:buffer, l:quickfix_list))
|
|
call setqflist([], 'r', {'title': l:title})
|
|
endif
|
|
elseif g:ale_set_loclist
|
|
" If windows support is off, win_findbuf() may not exist.
|
|
" We'll set result in the current window, which might not be correct,
|
|
" but it's better than nothing.
|
|
let l:ids = s:WinFindBuf(a:buffer)
|
|
|
|
let l:loclist = s:Deduplicate(a:loclist)
|
|
|
|
for l:id in l:ids
|
|
if has('nvim')
|
|
call setloclist(l:id, s:FixList(a:buffer, l:loclist), ' ', l:title)
|
|
else
|
|
call setloclist(l:id, s:FixList(a:buffer, l:loclist))
|
|
call setloclist(l:id, [], 'r', {'title': l:title})
|
|
endif
|
|
endfor
|
|
endif
|
|
|
|
" Save the current view before opening/closing any window
|
|
call setbufvar(a:buffer, 'ale_winview', winsaveview())
|
|
|
|
" Open a window to show the problems if we need to.
|
|
"
|
|
" ShouldOpen() checks if the current buffer has enough problems to be
|
|
" opened.
|
|
if s:ShouldOpen(a:buffer, len(a:loclist))
|
|
let l:winnr = winnr()
|
|
let l:mode = mode()
|
|
|
|
" open windows vertically instead of default horizontally
|
|
let l:open_type = ''
|
|
|
|
if ale#Var(a:buffer, 'list_vertical') == 1
|
|
let l:open_type = 'vert rightbelow '
|
|
endif
|
|
|
|
if g:ale_set_quickfix
|
|
if !ale#list#IsQuickfixOpen()
|
|
silent! execute l:open_type . 'copen ' . str2nr(ale#Var(a:buffer, 'list_window_size'))
|
|
endif
|
|
elseif g:ale_set_loclist
|
|
silent! execute l:open_type . 'lopen ' . str2nr(ale#Var(a:buffer, 'list_window_size'))
|
|
endif
|
|
|
|
" If focus changed, restore it (jump to the last window).
|
|
if l:winnr isnot# winnr()
|
|
wincmd p
|
|
endif
|
|
|
|
" Return to original mode when applicable
|
|
if mode() != l:mode
|
|
if l:mode is? 'v' || l:mode is# "\<c-v>"
|
|
" Reset our last visual selection
|
|
normal! gv
|
|
elseif l:mode is? 's' || l:mode is# "\<c-s>"
|
|
" Reset our last character selection
|
|
normal! "\<c-g>"
|
|
endif
|
|
endif
|
|
|
|
call s:RestoreViewIfNeeded(a:buffer)
|
|
endif
|
|
|
|
" If ALE isn't currently checking for more problems, close the window if
|
|
" needed now. This check happens inside of this timer function, so
|
|
" the window can be closed reliably.
|
|
if !ale#engine#IsCheckingBuffer(a:buffer)
|
|
call s:CloseWindowIfNeeded(a:buffer)
|
|
endif
|
|
endfunction
|
|
|
|
" Try to restore the window view after closing any of the lists to avoid making
|
|
" the it moving around, especially useful when on insert mode
|
|
function! s:RestoreViewIfNeeded(buffer) abort
|
|
let l:saved_view = getbufvar(a:buffer, 'ale_winview', {})
|
|
|
|
" Saved view is empty, can't do anything
|
|
if empty(l:saved_view)
|
|
return
|
|
endif
|
|
|
|
" Check whether the cursor has moved since linting was actually requested. If
|
|
" the user has indeed moved lines, do nothing
|
|
let l:current_view = winsaveview()
|
|
|
|
if l:current_view['lnum'] != l:saved_view['lnum']
|
|
return
|
|
endif
|
|
|
|
" Anchor view by topline if the list is set to open horizontally
|
|
if ale#Var(a:buffer, 'list_vertical') == 0
|
|
call winrestview({'topline': l:saved_view['topline']})
|
|
endif
|
|
endfunction
|
|
|
|
function! ale#list#SetLists(buffer, loclist) abort
|
|
if get(g:, 'ale_set_lists_synchronously') == 1
|
|
\|| getbufvar(a:buffer, 'ale_save_event_fired', 0)
|
|
" Update lists immediately if running a test synchronously, or if the
|
|
" buffer was saved.
|
|
"
|
|
" The lists need to be updated immediately when saving a buffer so
|
|
" that we can reliably close window automatically, if so configured.
|
|
call s:SetListsImpl(-1, a:buffer, a:loclist)
|
|
else
|
|
call ale#util#StartPartialTimer(
|
|
\ 0,
|
|
\ function('s:SetListsImpl'),
|
|
\ [a:buffer, a:loclist],
|
|
\)
|
|
endif
|
|
endfunction
|
|
|
|
function! ale#list#ForcePopulateErrorList(populate_quickfix) abort
|
|
let l:quickfix_bak = g:ale_set_quickfix
|
|
let g:ale_set_quickfix = a:populate_quickfix
|
|
let l:loclist_bak = g:ale_set_loclist
|
|
let g:ale_set_loclist = !a:populate_quickfix
|
|
let l:open_list_bak = g:ale_open_list
|
|
let g:ale_open_list = 1
|
|
|
|
let l:buffer = bufnr('')
|
|
let l:loclist = get(g:ale_buffer_info, l:buffer, {'loclist': []}).loclist
|
|
call s:SetListsImpl(-1, l:buffer, l:loclist)
|
|
|
|
let g:ale_open_list = l:open_list_bak
|
|
let g:ale_set_loclist = l:loclist_bak
|
|
let g:ale_set_quickfix = l:quickfix_bak
|
|
endfunction
|
|
|
|
function! s:CloseWindowIfNeeded(buffer) abort
|
|
if ale#Var(a:buffer, 'keep_list_window_open') || s:ShouldClose(a:buffer)
|
|
return
|
|
endif
|
|
|
|
let l:did_close_any_list = 0
|
|
|
|
try
|
|
" Only close windows if the quickfix list or loclist is completely empty,
|
|
" including errors set through other means.
|
|
if g:ale_set_quickfix
|
|
if empty(getqflist())
|
|
cclose
|
|
let l:did_close_any_list = 1
|
|
endif
|
|
else
|
|
let l:win_ids = s:WinFindBuf(a:buffer)
|
|
|
|
for l:win_id in l:win_ids
|
|
if g:ale_set_loclist && empty(getloclist(l:win_id))
|
|
lclose
|
|
let l:did_close_any_list = 1
|
|
endif
|
|
endfor
|
|
endif
|
|
" Ignore 'Cannot close last window' errors.
|
|
catch /E444/
|
|
endtry
|
|
|
|
if l:did_close_any_list
|
|
call s:RestoreViewIfNeeded(a:buffer)
|
|
endif
|
|
endfunction
|