VIM 命令或表达式到 select 函数中的文本

VIM command or expression to select text from a function

在我的 .vimrc 文件中,我想配置一个极简函数来执行智能 selection。 例如尝试selectinner(),如果selection为空则尝试inner[],如果仍然为空则尝试inner{}等。 但我一开始就卡住了:从函数调用/执行命令/表达式到 select 文本。

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

仅供参考:我实际上使用 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>

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

vi(这样的命令是普通模式命令。要从 Vim 脚本函数调用它们,您需要 :normal! 命令。 :execute 用于 Ex 命令,以插入变量值(这在许多其他语言中称为 eval()),或使用特殊的 :help key-notation(我们稍后会用到)。

为了测试select离子是否被制成,Vim方便地有两个特殊标记('<'>)指定select的边界=40=]离子。不幸的是,它们仅在离开视觉模式后设置(通过对其进行操作,或通过 <Esc>)。在插件中,最好使用 <C-\><C-n> 键而不是 <Esc>;它也会 return 进入正常模式,但如果我们已经处于正常模式则不会发出哔哔声。我为此使用了一个单独的 :normal! 命令(使用 :execute 以使用特殊的键符号)以确保它也将在前一个命令序列中止时执行,因为没有这样的 selection 可以制作。

综合来看,这是您尝试的更正版本:

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

为了重新select,你可以在之后使用gv,例如通过这个映射:

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