用于从函数中选择文本的VIM命令或表达式

时间:2018-02-07 11:11:25

标签: vim neovim

在我的.vimrc文件中,我想配置一个简约功能来执行智能选择。 例如,尝试选择inner(),如果选择为空则尝试inner [],如果仍为空则尝试内部{}等。 但是我一开始就陷入困境:调用/执行命令/表达式来从函数中选择文本。

function! SelectInnerBrackets():
    " failed attempts
    call visualmode()
    execute "vi("
    execute "visual! i("
endfunction

Fyi:我实际上使用neovim,但它可能对这个问题没有影响。

编辑:基于@Ingo Karkat提出的解决方案,我分享了我的最后一段代码。请注意,它不能与交叉嵌套分隔符完美配合。

function! SelectInner(delimiter)
    " We have to switch to normal mode to compare positions
    execute "normal! vi".a:delimiter."\<C-\>\<C-n>"
    return getpos("'<") != getpos("'>")
endfunction

function! TrySelectInner(delimiters)
    for delimiter in a:delimiters
        if SelectInner(delimiter)
            normal! gv
            break
        endif
    endfor
endfunction

" quickly select a word, expression or brackets content
nnoremap W viw
nnoremap E :call TrySelectInner(["'", '"'])<CR>
nnoremap R :call TrySelectInner(['(', '[', '{'])<CR>

1 个答案:

答案 0 :(得分:2)

如果您阅读:help visualmode(),您会注意到(非参数版本的)函数是一个没有副作用的查询。只是:call没有任何好处,并且您不需要当前/以前的可视模式,因为您自己构建选择。

vi(之类的命令是普通模式命令。要从Vimscript函数调用它们,您需要:normal!命令。 :execute用于Ex命令,以便插入变量值(在许多其他语言中称为eval()),或使用特殊:help key-notation(稍后我们将使用它)。< / p>

为了测试是否进行了选择,Vim方便地有两个特殊标记('<'>)来指定选择的边界。不幸的是,它们仅在视觉模式被保留后(通过操作或通过<Esc>)设置。在插件中,最好使用<C-\><C-n>键而不是<Esc>;它也将返回正常模式,但如果我们已经处于正常模式,则不会发出蜂鸣声。我使用单独的:normal!命令(:execute使用特殊键符号)以确保它在前一个命令序列中止时也会执行,因为不能进行这样的选择。

总之,这是您尝试的更正版本:

function! SelectInnerBrackets()
    echomsg "trying (...)"
    normal! vi(
    execute "normal! \<C-\>\<C-n>"
    if getpos("'<") != getpos("'>") | return 1 | endif

    echomsg "trying [...]"
    normal! vi[
    execute "normal! \<C-\>\<C-n>"
    if getpos("'<") != getpos("'>") | return 1 | endif

    echomsg "trying {...}"
    normal! vi{
    execute "normal! \<C-\>\<C-n>"
    if getpos("'<") != getpos("'>") | return 1 | endif

    echomsg "nothing found"
    return 0
endfunction

要重新选择,您可以在之后使用gv,例如通过这种映射:

nnoremap <Leader>V :if SelectInnerBrackets() <Bar> execute "normal! gv" <Bar> endif<CR>