mirror of
https://github.com/SpaceVim/SpaceVim.git
synced 2025-03-19 06:05:42 +08:00
433 lines
16 KiB
VimL
433 lines
16 KiB
VimL
scriptencoding utf-8
|
|
|
|
unlockvar s:unknown_counts
|
|
let s:unknown_counts = {}
|
|
lockvar s:unknown_counts
|
|
|
|
let s:counts = {}
|
|
|
|
" Key: bufnr, Value: dict with cache keys.
|
|
let s:cache = {}
|
|
|
|
" For debugging.
|
|
let g:neomake#statusline#_s = s:
|
|
|
|
function! s:clear_cache(bufnr) abort
|
|
if has_key(s:cache, a:bufnr)
|
|
unlet s:cache[a:bufnr]
|
|
endif
|
|
endfunction
|
|
|
|
function! neomake#statusline#clear_cache() abort
|
|
let s:cache = {}
|
|
endfunction
|
|
|
|
function! s:incCount(counts, item, buf) abort
|
|
if !empty(a:item.type) && (!a:buf || a:item.bufnr ==# a:buf)
|
|
let type = toupper(a:item.type)
|
|
let a:counts[type] = get(a:counts, type, 0) + 1
|
|
if a:buf
|
|
call s:clear_cache(a:buf)
|
|
else
|
|
let s:cache = {}
|
|
endif
|
|
return 1
|
|
endif
|
|
return 0
|
|
endfunction
|
|
|
|
" Refresh statusline when make run finished.
|
|
function! neomake#statusline#make_finished(make_info) abort
|
|
if a:make_info.options.file_mode
|
|
let bufnr = a:make_info.options.bufnr
|
|
if !empty(a:make_info.finished_jobs) && !has_key(s:counts, bufnr)
|
|
let s:counts[bufnr] = {}
|
|
endif
|
|
call s:clear_cache(bufnr)
|
|
else
|
|
let s:cache = {}
|
|
if !empty(a:make_info.finished_jobs) && !has_key(s:counts, 'project')
|
|
let s:counts['project'] = {}
|
|
endif
|
|
endif
|
|
|
|
" Trigger refreshing of statuslines for all windows.
|
|
" Using ":redrawstatus" is problematic (https://github.com/vim/vim/issues/4850).
|
|
" Could do it only for affected windows (via bufnr of finished_jobs, but
|
|
" it might still affect other windows).
|
|
" This cannot be tested using Vader, but was done manually, using a maker
|
|
" that changes entries for a buffer in another window.
|
|
" This could also be done when starting, to (better) handle non-current
|
|
" windows.
|
|
for w in range(1, winnr('$'))
|
|
call setwinvar(w, '&stl', getwinvar(w, '&stl'))
|
|
endfor
|
|
endfunction
|
|
|
|
function! neomake#statusline#ResetCountsForBuf(...) abort
|
|
let bufnr = a:0 ? +a:1 : bufnr('%')
|
|
call s:clear_cache(bufnr)
|
|
if has_key(s:counts, bufnr)
|
|
let r = s:counts[bufnr] != {}
|
|
unlet s:counts[bufnr]
|
|
if r
|
|
call neomake#utils#hook('NeomakeCountsChanged', {
|
|
\ 'reset': 1, 'file_mode': 1, 'bufnr': bufnr})
|
|
endif
|
|
return r
|
|
endif
|
|
return 0
|
|
endfunction
|
|
|
|
function! neomake#statusline#ResetCountsForProject(...) abort
|
|
let s:cache = {}
|
|
if !has_key(s:counts, 'project')
|
|
return 0
|
|
endif
|
|
let r = s:counts['project'] != {}
|
|
let bufnr = bufnr('%')
|
|
unlet s:counts['project']
|
|
if r
|
|
call neomake#utils#hook('NeomakeCountsChanged', {
|
|
\ 'reset': 1, 'file_mode': 0, 'bufnr': bufnr})
|
|
endif
|
|
return r
|
|
endfunction
|
|
|
|
function! neomake#statusline#ResetCounts() abort
|
|
let r = neomake#statusline#ResetCountsForProject()
|
|
for bufnr in keys(s:counts)
|
|
let r = neomake#statusline#ResetCountsForBuf(bufnr) || r
|
|
endfor
|
|
let s:counts = {}
|
|
return r
|
|
endfunction
|
|
|
|
function! neomake#statusline#AddLoclistCount(buf, item) abort
|
|
let s:counts[a:buf] = get(s:counts, a:buf, {})
|
|
return s:incCount(s:counts[a:buf], a:item, a:buf)
|
|
endfunction
|
|
|
|
function! neomake#statusline#AddQflistCount(item) abort
|
|
let s:counts['project'] = get(s:counts, 'project', {})
|
|
return s:incCount(s:counts['project'], a:item, 0)
|
|
endfunction
|
|
|
|
function! neomake#statusline#LoclistCounts(...) abort
|
|
let buf = a:0 ? a:1 : bufnr('%')
|
|
if buf is# 'all'
|
|
return s:counts
|
|
endif
|
|
return get(s:counts, buf, {})
|
|
endfunction
|
|
|
|
function! neomake#statusline#QflistCounts() abort
|
|
return get(s:counts, 'project', s:unknown_counts)
|
|
endfunction
|
|
|
|
function! s:showErrWarning(counts, prefix) abort
|
|
let w = get(a:counts, 'W', 0)
|
|
let e = get(a:counts, 'E', 0)
|
|
if w || e
|
|
let result = a:prefix
|
|
if e
|
|
let result .= 'E:'.e
|
|
endif
|
|
if w
|
|
if e
|
|
let result .= ','
|
|
endif
|
|
let result .= 'W:'.w
|
|
endif
|
|
return result
|
|
else
|
|
return ''
|
|
endif
|
|
endfunction
|
|
|
|
function! neomake#statusline#LoclistStatus(...) abort
|
|
return s:showErrWarning(neomake#statusline#LoclistCounts(), a:0 ? a:1 : '')
|
|
endfunction
|
|
|
|
function! neomake#statusline#QflistStatus(...) abort
|
|
return s:showErrWarning(neomake#statusline#QflistCounts(), a:0 ? a:1 : '')
|
|
endfunction
|
|
|
|
" Get counts for a bufnr or 'project'.
|
|
" Returns all counts when used without arguments.
|
|
function! neomake#statusline#get_counts(...) abort
|
|
if a:0
|
|
return get(s:counts, a:1, s:unknown_counts)
|
|
endif
|
|
return s:counts
|
|
endfunction
|
|
|
|
|
|
let s:formatter = {
|
|
\ 'args': {},
|
|
\ }
|
|
function! s:formatter.running_job_names() abort
|
|
let jobs = get(self.args, 'running_jobs', s:running_jobs(self.args.bufnr))
|
|
let sep = get(self.args, 'running_jobs_separator', ', ')
|
|
let format_running_job_file = get(self.args, 'format_running_job_file', '%s')
|
|
let format_running_job_project = get(self.args, 'format_running_job_project', '%s!')
|
|
let formatted = []
|
|
for job in jobs
|
|
if job.file_mode
|
|
call add(formatted, printf(format_running_job_file, job.name))
|
|
else
|
|
call add(formatted, printf(format_running_job_project, job.name))
|
|
endif
|
|
endfor
|
|
return join(formatted, sep)
|
|
endfunction
|
|
|
|
function! s:formatter._substitute(m) abort
|
|
if has_key(self.args, a:m)
|
|
return self.args[a:m]
|
|
endif
|
|
if !has_key(self, a:m)
|
|
let self.errors += [printf('Unknown statusline format: {{%s}}.', a:m)]
|
|
return '{{'.a:m.'}}'
|
|
endif
|
|
try
|
|
return call(self[a:m], [], self)
|
|
catch
|
|
call neomake#log#error(printf(
|
|
\ 'Error while formatting statusline: %s.', v:exception))
|
|
endtry
|
|
endfunction
|
|
|
|
function! s:formatter.format(f, args) abort
|
|
if empty(a:f)
|
|
return a:f
|
|
endif
|
|
let self.args = a:args
|
|
let self.errors = []
|
|
let r = substitute(a:f, '{{\(.\{-}\)}}', '\=self._substitute(submatch(1))', 'g')
|
|
if !empty(self.errors)
|
|
call neomake#log#error(printf(
|
|
\ 'Error%s when formatting %s: %s',
|
|
\ len(self.errors) > 1 ? 's' : '',
|
|
\ string(a:f), join(self.errors, ', ')))
|
|
endif
|
|
return r
|
|
endfunction
|
|
|
|
|
|
function! s:running_jobs(bufnr) abort
|
|
return filter(copy(neomake#GetJobs()),
|
|
\ "v:val.bufnr == a:bufnr && !get(v:val, 'canceled', 0)")
|
|
endfunction
|
|
|
|
function! s:format_running(format_running, options, bufnr, running_jobs) abort
|
|
let args = {'bufnr': a:bufnr, 'running_jobs': a:running_jobs}
|
|
for opt in ['running_jobs_separator', 'format_running_job_project', 'format_running_job_file']
|
|
if has_key(a:options, opt)
|
|
let args[opt] = a:options[opt]
|
|
endif
|
|
endfor
|
|
return s:formatter.format(a:format_running, args)
|
|
endfunction
|
|
|
|
function! neomake#statusline#get_status(bufnr, options) abort
|
|
let filemode_jobs = []
|
|
let project_jobs = []
|
|
let format_running = get(a:options, 'format_running', '… ({{running_job_names}})')
|
|
if format_running isnot 0
|
|
let running_jobs = s:running_jobs(a:bufnr)
|
|
if !empty(running_jobs)
|
|
for j in running_jobs
|
|
if j.file_mode
|
|
let filemode_jobs += [j]
|
|
else
|
|
let project_jobs += [j]
|
|
endif
|
|
endfor
|
|
endif
|
|
endif
|
|
|
|
let r_loclist = ''
|
|
let r_quickfix = ''
|
|
|
|
let use_highlights_with_defaults = get(a:options, 'use_highlights_with_defaults', 1)
|
|
|
|
" Location list counts.
|
|
let loclist_counts = get(s:counts, a:bufnr, s:unknown_counts)
|
|
if !empty(filemode_jobs)
|
|
let r_loclist = s:format_running(format_running, a:options, a:bufnr, filemode_jobs)
|
|
elseif empty(loclist_counts)
|
|
if loclist_counts is s:unknown_counts
|
|
let format_unknown = get(a:options, 'format_loclist_unknown', '?')
|
|
let r_loclist = s:formatter.format(format_unknown, {'bufnr': a:bufnr})
|
|
else
|
|
let format_ok = get(a:options, 'format_loclist_ok', use_highlights_with_defaults ? '%#NeomakeStatusGood#✓%#NeomakeStatReset#' : '✓')
|
|
let r_loclist = s:formatter.format(format_ok, {'bufnr': a:bufnr})
|
|
endif
|
|
else
|
|
let format_loclist = get(a:options, 'format_loclist_issues',
|
|
\ use_highlights_with_defaults ? '%s%%#NeomakeStatReset#' : '%s')
|
|
if !empty(format_loclist)
|
|
let loclist = ''
|
|
for [type, c] in items(loclist_counts)
|
|
if has_key(a:options, 'format_loclist_type_'.type)
|
|
let format = a:options['format_loclist_type_'.type]
|
|
elseif has_key(a:options, 'format_loclist_type_default')
|
|
let format = a:options['format_loclist_type_default']
|
|
else
|
|
let hl = ''
|
|
if use_highlights_with_defaults
|
|
if hlexists('NeomakeStatColorType'.type)
|
|
let hl = '%#NeomakeStatColorType{{type}}#'
|
|
elseif hlexists('NeomakeStatColorDefault')
|
|
let hl = '%#NeomakeStatColorDefault#'
|
|
endif
|
|
endif
|
|
let format = hl.' {{type}}:{{count}} '
|
|
endif
|
|
let loclist .= s:formatter.format(format, {
|
|
\ 'bufnr': a:bufnr,
|
|
\ 'count': c,
|
|
\ 'type': type})
|
|
endfor
|
|
let r_loclist = printf(format_loclist, loclist)
|
|
endif
|
|
endif
|
|
|
|
" Quickfix counts.
|
|
let qflist_counts = get(s:counts, 'project', s:unknown_counts)
|
|
if !empty(project_jobs)
|
|
let r_quickfix = s:format_running(format_running, a:options, a:bufnr, project_jobs)
|
|
elseif empty(qflist_counts)
|
|
let format_ok = get(a:options, 'format_quickfix_ok', '')
|
|
if !empty(format_ok)
|
|
let r_quickfix = s:formatter.format(format_ok, {'bufnr': a:bufnr})
|
|
endif
|
|
else
|
|
let format_quickfix = get(a:options, 'format_quickfix_issues',
|
|
\ use_highlights_with_defaults ? '%s%%#NeomakeStatReset#' : '%s')
|
|
if !empty(format_quickfix)
|
|
let quickfix = ''
|
|
for [type, c] in items(qflist_counts)
|
|
if has_key(a:options, 'format_quickfix_type_'.type)
|
|
let format = a:options['format_quickfix_type_'.type]
|
|
elseif has_key(a:options, 'format_quickfix_type_default')
|
|
let format = a:options['format_quickfix_type_default']
|
|
else
|
|
let hl = ''
|
|
if use_highlights_with_defaults
|
|
if hlexists('NeomakeStatColorQuickfixType'.type)
|
|
let hl = '%#NeomakeStatColorQuickfixType{{type}}#'
|
|
elseif hlexists('NeomakeStatColorQuickfixDefault')
|
|
let hl = '%#NeomakeStatColorQuickfixDefault#'
|
|
endif
|
|
endif
|
|
let format = hl.' Q{{type}}:{{count}} '
|
|
endif
|
|
if !empty(format)
|
|
let quickfix .= s:formatter.format(format, {
|
|
\ 'bufnr': a:bufnr,
|
|
\ 'count': c,
|
|
\ 'type': type})
|
|
endif
|
|
endfor
|
|
let r_quickfix = printf(format_quickfix, quickfix)
|
|
endif
|
|
endif
|
|
|
|
let format_lists = get(a:options, 'format_lists', '{{loclist}}{{lists_sep}}{{quickfix}}')
|
|
if empty(r_loclist) || empty(r_quickfix)
|
|
let lists_sep = ''
|
|
else
|
|
let lists_sep = get(a:options, 'lists_sep', ' ')
|
|
endif
|
|
return s:formatter.format(format_lists, {'loclist': r_loclist, 'quickfix': r_quickfix, 'lists_sep': lists_sep})
|
|
endfunction
|
|
|
|
function! neomake#statusline#get(bufnr, ...) abort
|
|
let options = a:0 ? a:1 : {}
|
|
let cache_key = string(options)
|
|
if !exists('s:cache[a:bufnr][cache_key]')
|
|
if !has_key(s:cache, a:bufnr)
|
|
let s:cache[a:bufnr] = {}
|
|
endif
|
|
let bufnr = +a:bufnr
|
|
|
|
" TODO: needs to go into cache key then!
|
|
if getbufvar(bufnr, '&filetype') ==# 'qf'
|
|
let s:cache[bufnr][cache_key] = ''
|
|
else
|
|
let [disabled, src] = neomake#config#get_with_source('disabled', -1, {'bufnr': bufnr, 'log_source': 'statusline#get'})
|
|
if src ==# 'default'
|
|
let disabled_scope = ''
|
|
else
|
|
let disabled_scope = src[0]
|
|
endif
|
|
if disabled != -1 && disabled
|
|
" Automake Disabled
|
|
let format_disabled_info = get(options, 'format_disabled_info', '{{disabled_scope}}-')
|
|
let disabled_info = s:formatter.format(format_disabled_info,
|
|
\ {'disabled_scope': disabled_scope})
|
|
" Defaults to showing the disabled information (i.e. scope)
|
|
let format_disabled = get(options, 'format_status_disabled', '{{disabled_info}} %s')
|
|
let outer_format = s:formatter.format(format_disabled, {'disabled_info': disabled_info})
|
|
else
|
|
" Automake Enabled
|
|
" Defaults to showing only the status
|
|
let format_enabled = get(options, 'format_status_enabled', '%s')
|
|
let outer_format = s:formatter.format(format_enabled, {})
|
|
endif
|
|
let format_status = get(options, 'format_status', '%s')
|
|
let status = neomake#statusline#get_status(bufnr, options)
|
|
|
|
let r = printf(outer_format, printf(format_status, status))
|
|
|
|
let s:cache[bufnr][cache_key] = r
|
|
endif
|
|
endif
|
|
return s:cache[a:bufnr][cache_key]
|
|
endfunction
|
|
|
|
" XXX: TODO: cleanup/doc?!
|
|
function! neomake#statusline#DefineHighlights() abort
|
|
for suffix in ['', 'NC']
|
|
let hl = 'StatusLine'.suffix
|
|
|
|
" Highlight used for resetting color (used after counts).
|
|
exe 'hi default link NeomakeStatReset'.suffix.' StatusLine'.suffix
|
|
|
|
" Uses "green" for NeomakeStatusGood, but the default with
|
|
" NeomakeStatusGoodNC (since it might be underlined there, and should
|
|
" not stand out in general there).
|
|
exe 'hi default NeomakeStatusGood'.suffix
|
|
\ . ' ctermfg=' . (suffix ? neomake#utils#GetHighlight(hl, 'fg') : 'green')
|
|
\ . ' guifg=' . (suffix ? neomake#utils#GetHighlight(hl, 'fg#') : 'green')
|
|
\ . ' ctermbg='.neomake#utils#GetHighlight(hl, 'bg')
|
|
\ . ' guifg='.neomake#utils#GetHighlight(hl, 'bg#')
|
|
\ . (neomake#utils#GetHighlight(hl, 'underline') ? ' cterm=underline' : '')
|
|
\ . (neomake#utils#GetHighlight(hl, 'underline#') ? ' gui=underline' : '')
|
|
\ . (neomake#utils#GetHighlight(hl, 'reverse') ? ' cterm=reverse' : '')
|
|
\ . (neomake#utils#GetHighlight(hl, 'reverse#') ? ' gui=reverse' : '')
|
|
endfor
|
|
|
|
" Default highlight for type counts.
|
|
exe 'hi default NeomakeStatColorDefault cterm=NONE ctermfg=white ctermbg=blue'
|
|
hi link NeomakeStatColorQuickfixDefault NeomakeStatColorDefault
|
|
|
|
" Specific highlights for types. Only used if defined.
|
|
exe 'hi default NeomakeStatColorTypeE cterm=NONE ctermfg=white ctermbg=red'
|
|
hi link NeomakeStatColorQuickfixTypeE NeomakeStatColorTypeE
|
|
|
|
exe 'hi default NeomakeStatColorTypeW cterm=NONE ctermfg=white ctermbg=yellow'
|
|
hi link NeomakeStatColorQuickfixTypeW NeomakeStatColorTypeW
|
|
endfunction
|
|
|
|
" Global augroup, gets configured always currently when autoloaded.
|
|
augroup neomake_statusline
|
|
autocmd!
|
|
autocmd BufWipeout * call s:clear_cache(expand('<abuf>'))
|
|
autocmd ColorScheme * call neomake#statusline#DefineHighlights()
|
|
augroup END
|
|
call neomake#statusline#DefineHighlights()
|
|
" vim: ts=4 sw=4 et
|