我正在使用 vim 并配置了:terminal
bash 和 vi 模式。为此,我配置了.inputrc
以下配置:https://vim.fandom.com/wiki/Use_vi_shortcuts_in_terminal。我注意到我的键绑定与终端中的绑定不配合。例如。我正在用 切换缓冲区<TAB>
。该键绑定无法区分 bash 是:terminal
处于插入模式还是正常模式,因此我需要输入<C-w>w
才能继续。当我在我的中使用以下映射时.vimrc
:
tmap <TAB> <C-w>w
:terminal
那么当我的 bash处于插入模式时,我不能再使用 tab 了。 Vimtmap
对 vi 模式insert
和normal
模式都使用映射。
是否可以仅在 bash in处于正常模式<TAB>
时切换缓冲区并在 bash in处于插入模式时执行自动完成?:terminal
<TAB>
:terminal
答案1
Vim 无法真正判断其终端内的 bash shell 是在 vi-insert 模式下运行还是在 vi-normal 模式下运行。事实上,它甚至无法判断它是使用 vi 还是 emacs 模式。或者在某些时候,当您在 bash 中运行命令时,讨论 bash 是否处于插入模式或正常模式甚至没有意义,因为从技术上讲,它都不处于任何一种模式......
所以为了实现这种集成,你需要让 bash 告诉 Vim 它处于哪种模式,或者类似地,让 bash 解释键绑定并当触发特殊键时从 bash 调用 Vim 命令。
就我个人而言,我认为后者是最简单的,在 bash 中进行键绑定,因为上次我发现并没有真正可靠的方法来连接 bash 的模式更改,甚至无法可靠地检测何时将要发出新命令被执行。 (从这个意义上说,zsh 似乎比 bash 好得多,它有更多的钩子,而且通常更容易使用。)
这里缺少的另一部分是从 Vim 终端内运行的 shell 调用 Vim 命令。您可以使用终端 API,它允许您使用 bash 中的转义序列来调用 Vim 中的函数。
对于 bash 绑定,请参阅bind
命令,我相信您可以传递它-m
来为特定模式(例如 vi-normal)创建绑定,并且您可以-x
在特定组合键被按下时用来执行某些代码(或函数)进入。
例如,这将在模式(与 readline 中的“正常”vi 模式匹配)下按下时<Tab>
(与 Ctrl+I 相同)调用函数:wincmd_next()
vi
bind -m vi -x '"\C-i": wincmd_next'
对于Vim终端API,您可以导出Tapi_wincmd
可以从shell调用的函数。然后从 bash 你应该能够使用:
echo -ne '\e]51;["call","Tapi_wincmd",["w"]]\a'
所以只需将其转换为wincmd_next
函数即可:
wincmd_next () {
echo -ne '\e]51;["call","Tapi_wincmd",["w"]]\a'
}
然后就可以编写相应的Vim函数了:
function! Tapi_wincmd(bufnum, arglist)
execute 'wincmd' a:arglist[0]
endfunction
这样,上面的 bash 命令将导致 Vim 循环到下一个窗口。
终端 API 采用 JSON 编码参数。默认情况下,仅Tapi_
允许从终端 API 调用使用前缀命名的函数。因此,您可以决定要从 Vim 向终端应用程序公开多少以及哪些特定功能和参数。
我相信这些指针应该足以让您将设置您所描述的特定场景所需的所有内容放在一起,其中涉及按下 Tab 时切换 Vim 窗口,但是仅有的当 shell 处于 vi-正常模式时。
此方法的另一种方法是,:tnoremap
每当将击键发送到在终端上运行的应用程序时,无论是 vi-正常模式下的 bash、vi-insert 模式下的 bash 还是其他应用程序,都可以挂钩击键。
正如您在问题中提到的,您可以使用以下方式进行配置:
tnoremap <Tab> <C-w>w
但这并没有让你有办法使用实际的终端中的 Tab,这在 vi-insert(或 emacs)模式下完成时可能很有用。
您可以通过映射不同的按键将实际的 Tab 发送到终端应用程序来解决此问题,例如,以下之一:
tnoremap <S-Tab> <Tab> " Shift-Tab
tnoremap <C-v><Tab> <Tab> " Ctrl-V, Tab
tnoremap <C-P> <Tab> " Ctrl-P