当Vim进入可视模式时运行命令

11

我使用一个小脚本来触发插入模式,以更改行号颜色:

function! CursorLineNrColorInsert(mode)
    " Insert mode: blue
    if a:mode == "i"
        highlight CursorLineNr ctermfg=4
        highlight CursorLineNr guifg=#268bd2

    " Replace mode: red
    elseif a:mode == "r"
        highlight CursorLineNr ctermfg=1
        highlight CursorLineNr guifg=#dc322f

    else
        highlight CursorLineNr ctermfg=0
        highlight CursorLineNr guifg=#073642

    endif
endfunction

autocmd InsertEnter * call CursorLineNrColorInsert(v:insertmode)
autocmd InsertLeave * highlight CursorLineNr ctermfg=0
autocmd InsertLeave * highlight CursorLineNr guifg=#073642

当我进入任何插入模式时,它可以很好地工作并立即更改我的行号,而在正常模式下则恢复为原始颜色。

我希望对于可视模式也能实现同样的效果:

function! CursorLineNrColorVisual(mode)
    " Visual mode: orange
    if mode()=~#"^[vV\<C-v>]"
        highlight CursorLineNr ctermfg=9
        highlight CursorLineNr guifg=#cb4b16

    else
        highlight CursorLineNr ctermfg=0
        highlight CursorLineNr guifg=#073642

    endif
endfunction

autocmd CursorMoved * call CursorLineNrColorVisual(mode())

基本上这样做是可行的,但是不能立即生效,因为该函数是在CursorMoved触发的。我该如何在激活任何可视模式时立即触发CursorLineNrColorVisual()呢?


4
没有VisualEnterVisualLeave事件。那么将vV<C-v>映射到一个包装函数,以执行颜色交替操作,如何? - romainl
4
似乎应该有VisualLeave、VisualEnter、SelectEnter和SelectLeave等命令,这将使Vim更易于编写脚本。 - trusktr
1
同意,我最初尝试直观地编写的是:àutocmd VisualEnter ...。 :) - Saucier
3个回答

11

在花费一些时间阅读:help之后,我最终得到了以下设置:

Translated text:

在花费一些时间阅读:help之后,我最终得到了以下设置:

" Colorize line numbers in insert and visual modes
" ------------------------------------------------
function! SetCursorLineNrColorInsert(mode)
    " Insert mode: blue
    if a:mode == "i"
        highlight CursorLineNr ctermfg=4 guifg=#268bd2

    " Replace mode: red
    elseif a:mode == "r"
        highlight CursorLineNr ctermfg=1 guifg=#dc322f

    endif
endfunction


function! SetCursorLineNrColorVisual()
    set updatetime=0

    " Visual mode: orange
    highlight CursorLineNr cterm=none ctermfg=9 guifg=#cb4b16
endfunction


function! ResetCursorLineNrColor()
    set updatetime=4000
    highlight CursorLineNr cterm=none ctermfg=0 guifg=#073642
endfunction


vnoremap <silent> <expr> <SID>SetCursorLineNrColorVisual SetCursorLineNrColorVisual()
nnoremap <silent> <script> v v<SID>SetCursorLineNrColorVisual
nnoremap <silent> <script> V V<SID>SetCursorLineNrColorVisual
nnoremap <silent> <script> <C-v> <C-v><SID>SetCursorLineNrColorVisual


augroup CursorLineNrColorSwap
    autocmd!
    autocmd InsertEnter * call SetCursorLineNrColorInsert(v:insertmode)
    autocmd InsertLeave * call ResetCursorLineNrColor()
    autocmd CursorHold * call ResetCursorLineNrColor()
augroup END
为了在退出可视模式后恢复行号的颜色,我必须执行以下步骤:
  1. 重新映射相关的按键绑定以调用“enter-visual-function”
  2. 进入可视模式时,该函数为“CursorHold”事件设置updatetime=0
  3. 通过autocmd CursorHold调用“leave-visual-function”
  4. 离开可视模式时,该函数为“CursorHold”事件重置updatetime=4000

作为一个额外的问题:在可视模式下,如何根据当前选择更改多行的行号颜色?目前它只会更改当前行(光标所在的行)的颜色。 - Saucier
4
试用后,我有几个小观察:1.建议在SetCursorLineNrColorVisual函数的最后一行添加return '',以防止光标在进入可视模式时跳转到当前行的开头。2. 如果按照我的建议添加了这个return ''调用,则在更新颜色之前需要按下一个键。虽然有些取巧,但你只需在每个nnoremap行的末尾添加<left><right>即可。 颜色会更新,因为它作为输入处理,而且由于它将光标向左移动再向右移动,所以你甚至不会注意到。 - PseudoPsyche
@PseudoPsyche 谢谢!我自己试图修复这个问题已经几个小时了。虽然是一个不太正规的解决方案,但确实是必要的。 - Andrew J. McGehee
@PseudoPsyche 将 return '' 改为 return 'lh' 似乎也可以解决这个问题,参见此 Github 问题评论 - Gordon Bai

5

正如romainl指出的那样,没有进入/退出可视模式的事件。 我会这样做:

function! CursorLineNrColorVisual()
    ...
    return ''   " Return nothing to make the map-expr a no-op.
endfunction
vnoremap <expr> <SID>CursorLineNrColorVisual CursorLineNrColorVisual()
nnoremap <script> v v<SID>CursorLineNrColorVisual
nnoremap <script> V V<SID>CursorLineNrColorVisual
nnoremap <script> <C-v> <C-v><SID>CursorLineNrColorVisual

另外,您可以尝试将表达式 (%{CursorLineNrColorVisual}) 放入 'statusline' 中;这将会被相当频繁地评估。


这是一种不错的方法,但是当我通过按<esc>或<C-c>从可视模式返回到正常模式时,如何将颜色恢复到其初始值? - Saucier
1
同样的方式::vnoremap <Esc> ...。但要小心,重新映射 <Esc> 可能会产生副作用(希望不会在可视模式下出现)。这仍然无法处理应用命令时离开可视选择的情况(例如 gU);为此,CursorMoved / CursorHold 将是一种备选方案。 - Ingo Karkat
正如你所说,它有一些副作用,例如对箭头键的影响。因此效果不佳。在:help中查找了几个小时后,我找到了另一种处理所有不同离开视觉模式的方法。请看我的回答。 - Saucier

3

[编辑] Promptlines插件使用此方法:

由于状态行在每次模式更改时重新绘制,因此您可以通过在状态行中添加%{AnyName(mode())}来每次更改模式时触发。然后,您可以实现一个AnyName函数来过滤当前模式。 例如:

let &stl.='%{RedrawStatuslineColors(mode())}'

function! RedrawStatuslineColors(mode)
    if a:mode == 'n'
        call NormalHighlight()
    elseif a:mode == 'i'
        call InsertHighlight()
    elseif a:mode == 'R'
        call ReplaceHighlight()
    elseif a:mode == 'v' || a:mode == 'V' || a:mode == '^V'
        call VisualHighlight()
    endif
endfunction

[编辑2] Itchyny 建议使用本主题线程中的其他方法,以避免性能问题:建议缓存模式并立即完成函数(即RedrawStatuslineColors())。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接