在Vim

时间:2017-07-23 21:27:53

标签: vim

我在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命令组,但这似乎没有办法。

1 个答案:

答案 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