我在Vim中开发了一个很好的可自定义状态行,但是我在显示我正在其中工作的当前模式时遇到了麻烦。
我使用字典来指示通过状态行的左侧和右侧显示哪些选项:
" Display Options {{{
let s:statusline_options = {
\ 'active': {
\ 'left': [ 'readonly', 'mode' , 'git' ],
\ 'right': [ 'time', 'project' ],
\ },
\ 'components': {
\ 'readonly': 'Statusline_readonly()',
\ 'mode': 'Statusline_mode()',
\ 'git': 'Statusline_git()',
\ 'time': "strftime(%a\ %d\ %b\ %H:%M)",
\ 'project': 'Statusline_project()'
\ },
\ 'seperators': {
\ 'readonly': ' %s',
\ 'mode': '%s >',
\ 'git': ' %s',
\ 'time': ' < ',
\ 'project': '[%s] '
\ },
\ 'components_to_color': {
\ 'mode': 1,
\ 'project': 1
\ },
\ 'theme_colors': {
\ 'default': [ '#abb2bf', '#61afef', '#98c379' ],
\ 'onedark': [ '#abb2bf', '#61afef', '#98c379' ],
\ 'materialbox': [ '#1d272b', '#fb8c00', '#43a047']
\ },
\ 'mode_map': {
\ 'n': 'NORMAL', 'i': 'INSERT', 'R': 'REPLACE', 'v': 'VISUAL', 'V': 'VISUAL', "\<C-v>": 'V-BLOCK',
\ 'c': 'COMMAND', 's': 'SELECT', 'S': 'S-LINE', "\<C-s>": 'S-BLOCK', 't': 'TERMINAL'
\ },
\ }
" }}}
然后我通过引入颜色组和分隔符并调用用户在上面的字典中指定为组件的函数来拼凑状态行的创建:
" Statusline Functions {{{
function! StatuslineComponents(side) abort
let output = ''
" Fetch the components in the statusline dictionary
for v in Fetch('active', a:side)
let method = split(Fetch('components', v), '(')
let component_color = ''
" Check if the item should be specifically coloured
if len(method) > 1 && method[1] != ')'
let output .= StatuslineColor(v) . StatuslineFormat(v, method[0], method[1])
else
let output .= StatuslineColor(v) . StatuslineFormat(v, method[0])
endif
endfor
return output
endfunction
function! StatuslineColor(component)
for v in keys(s:statusline_options.components_to_color)
if v == a:component
return '%#Component_' . v . '#'
endif
endfor
return '%#ComponentDefault#'
endfunction
function! StatuslineFormat(...)
let output = ''
let seperator = Fetch('seperators', a:1)
if a:0 > 2
for param in split(a:3, ',')
let value = call(a:2, [ param ], {})
endfor
else
let value = call(a:2, [], {})
endif
" Remove any last )'s from the value
let value = substitute(value, ')\+$', '', '')
if seperator =~ '%s'
let output = printf(seperator, value)
else
let output = value . seperator
endif
return output
endfunction
function! ChangeStatuslineColor() abort
let s:mode_colors = []
try
for mode_color in Fetch('theme_colors', g:colors_name)
let s:mode_colors += [mode_color]
endfor
catch
try
for mode_color in Fetch('theme_colors', 'default')
let s:mode_colors += [mode_color]
endfor
catch
let s:mode_colors = ['#e06c75'] + ['#e06c75'] + ['#e06c75']
endtry
endtry
if (mode() ==# 'i')
exec printf('hi ComponentDefault guifg=%s', s:mode_colors[1])
elseif (mode() =~# '\v(v|V)')
exec printf('hi ComponentDefault guifg=%s', s:mode_colors[2])
else
exec printf('hi ComponentDefault guifg=%s', s:mode_colors[0])
endif
for component in keys(s:statusline_options.components_to_color)
if (mode() ==# 'i')
exec printf('hi Component_%s guifg=%s', component, s:mode_colors[1])
elseif (mode() =~# '\v(v|V)')
exec printf('hi Component_%s guifg=%s', component, s:mode_colors[2])
else
exec printf('hi Component_%s guifg=%s', component, s:mode_colors[0])
endif
endfor
return ''
endfunction
" Fetch a value from the Statusline options
function! Fetch(key, value) abort
return get(s:statusline_options[a:key], a:value, '')
endfunction
" }}}
我要包含在状态行中的自定义函数以及关键的Statusline_mode()函数如下所示:
" Component Functions {{{
" Show the mode in the statuslin
function! Statusline_mode() abort
return get(s:statusline_options.mode_map, mode(), '')
endfunction
function! Statusline_project() abort
return GetCurrentSite(g:code_dir)
endfunction
function! Statusline_git() abort
let git = fugitive#head()
if git != ''
return ' '.fugitive#head()
else
return ''
endif
endfunction
function! Statusline_readonly() abort
if &readonly || !&modifiable
return ' '
else
return ''
endif
endfunction
" }}}
然后通过以下命令组设置状态行:
" Set The Statusline {{{
augroup statusline
hi StatusLine guibg=NONE gui=NONE
autocmd!
autocmd WinEnter,BufWinEnter,FileType,ColorScheme,SessionLoadPost
\ * let &l:statusline = StatuslineComponents('left') . '%=' . StatuslineComponents('right') |
\ call ChangeStatuslineColor()
augroup end
" }}}
我尝试过使用InsertEnter
命令组,但这似乎没有办法。
答案 0 :(得分:1)
让我们从更简约的案例开始:
set _variable_=_value_
在定义let &l:statusline = mode() . ' ' . StatuslineComponents('left') . '%=' . StatuslineComponents('right')
时,会调用mode()
一次; 它永远不会更新,并且该值会停留在'statusline'
。
要解决此问题,每当状态行更新时,您需要使用n
项来评估表达式:
%{...}
现在回到你的完整案例。这是同样的问题:
let &l:statusline = '%{mode()} ' . StatuslineComponents('left') . '%=' . StatuslineComponents('right')
虽然您更新某些autocmd WinEnter,BufWinEnter,FileType,ColorScheme,SessionLoadPost
\ * let &l:statusline = StatuslineComponents('left') . '%=' . StatuslineComponents('right') |
\ call ChangeStatuslineColor()
事件,但在模式更改时不会触发任何事件,因此您始终可以看到相同的模式。无论如何,正如:autocmd
告诉您的那样,只能从提到的状态线表达式中获取正确的值。
作为解决此问题的第一步,我会完全删除:help mode()
并将所有内容放在状态行表达式中:
:autocmd
如果遇到性能问题,我会将慢速元素(但不是let &statusline = "%{StatuslineComponents('left')}%=%{StatuslineComponents('right')}"
调用!)提取到窗口局部变量中,从状态行表达式引用它们,并使用mode()
更新它们
:autocmd