如何防止光标在离开 Vim 的插入模式时向后移动一个字符?
Posted
技术标签:
【中文标题】如何防止光标在离开 Vim 的插入模式时向后移动一个字符?【英文标题】:How to prevent the cursor from moving back one character on leaving Insert mode in Vim? 【发布时间】:2011-01-18 17:16:39 【问题描述】:是否可以取消上述行为?
加分任务:想办法强制 Vim 在退出插入模式后立即刷新光标位置。
【问题讨论】:
有趣的问题。我想知道这种行为背后的原因是什么。我刚刚接受了。 我试过了。有点不方便随着光标的移动,如果我离开编辑模式,我可以看到。 @StefanoBorini 在 SO 的this question here 中有部分解释。据我了解:当您退出插入模式时,vi 不知道您是使用a
还是i
进入的,因此它假定为a
。真的:当反复使用 a
和 Esc 时,光标不会“滑开”。
然而,恕我直言,这个假设不是很正确,至少从看起来直观的 POV 来看(我们称之为 insert 模式,不是吗)?
我有Powerline甚至命令行,所以两行可以让我清楚我是否刚刚退出插入模式,光标的移动完全是当我想用快速<esc>D
等删除光标后的内容时,这是不必要的,并且会让我绊倒。我认为基于:autocmd InsertLeave
的东西可以工作吗??
【参考方案1】:
虽然我不建议更改默认光标机制, 实现相关行为的一种方法是使用以下 插入模式映射。
:inoremap <silent> <Esc> <Esc>`^
这里 Esc 键在插入模式下被重载到另外
运行`^
命令将光标移动到它所在的位置
是最后一次离开插入模式。因为在这个映射中
在使用 Esc 离开插入模式后立即执行,
与它的光标相比,光标向右左移一个字符
具有默认行为的位置。
与其他一些解决方法不同,这个方法不需要 Vim
使用+ex_extra
功能编译。
【讨论】:
是的,我认为它更加一致。附加已经意味着将光标向右移动一个字符,无论您是否输入了字符。 这取决于您认为光标在哪里:如果您认为光标就在突出显示的字符之后,则a
插入当前位置,i
将光标移回并插入。无论哪种方式都是合乎逻辑的,具体取决于您认为光标所在的位置。我猜写 vi 的人认为它是在突出显示的字符之后。
不幸的是,当按下箭头键和功能键时,此解决方案会在终端中弄乱 Vim。我正在尝试此修复程序:autocmd InsertLeave * :normal `^
@Nathan:这是个好建议!但是,我建议习惯默认的 Vim 行为。它得到了回报。
@NathanNeff 这是我见过的最好的解决方案,远远超过你回复的实际答案。【参考方案2】:
虽然有一些技巧可以解决这个问题(例如前两篇文章中提到的 ESC 映射),但没有一致的方法可以做到这一点。原因是无法确定用于进入插入模式的方法。具体来说,给定字符串abcDefg
,光标在D
:
如果您按 i,插入模式位置将介于 c
和 D
之间。普通的 ESC 会将光标放在c
上; <C-O>:stopinsert<CR>
(或反引号方法)会将光标放在D
。
如果按 a,插入模式位置将介于 D
和 e
之间。普通的 ESC 会将光标放在D
上; <C-O>:stopinsert<CR>
会将光标放在e
上。
如果你真的想这样做,你可以用这样的东西来捏造它:
let insert_command = "inoremap <ESC> <C-O>:stopinsert<CR>"
let append_command = "iunmap <ESC>"
nnoremap i :exe insert_command<CR>i
nnoremap a :exe append_command<CR>a
但是:请记住,这只会处理 i
和 a
作为进入方法:如果您使用可视块模式、I
或 A
或其他方式,您需要想出要匹配的新命令(并且有很多)。因此,我强烈建议您不要这样做。
就个人而言,我建议习惯默认行为。您可以轻松地将 i
逻辑化 OR 逻辑化 a
。如果您将i
的默认值更改为逻辑a
的默认值,那么您在使用标准vi/vim 安装时会感到困惑。
【讨论】:
【参考方案3】:基于Nathan Neff'scomment,我发现的最佳方法是
autocmd InsertLeave * :normal! `^
set virtualedit=onemore
autocmd
将光标移回插入模式结束时的位置(即,与默认值相比向前移动一个)。
virtualedit
使其在行尾始终保持一致(因此它可以位于该行最后一个字符的前面)。
(编辑:normal!
以避免递归映射)
【讨论】:
【参考方案4】:inoremap <silent> <Esc> <C-O>:stopinsert<CR>
在你的.vimrc
。
【讨论】:
如果您使用i
进入插入模式,此映射将使其符合逻辑,但如果您使用a
进入插入模式,则不符合逻辑。
好吧,你可以重新映射正常模式i
来设置一个标志,然后重新映射插入模式<Esc>
来检查那个标志,如果设置了,使用<C-O>:stopinsert<CR>
,然后清除标志或者方式。
我不相信这对于a
来说是完全不合逻辑的。一般来说,在每次更改之后,当我离开该更改时,我已经完成了该更改,接下来我想做的事情可能涉及进行不同的更改。因此,对我来说,在离开插入模式时(无论我如何输入),我通常更喜欢光标位于下一个字符上,这样我就准备好进行下一次更改,而不是准备好更改我刚刚的内容插入。 (主要是从我的评论中复制的:unix.stackexchange.com/a/11403/38050)
这个对我来说适用于 VIM 7.4 和 VIM 8.2 但 7.4 并没有在退出插入模式时更新模式行。【参考方案5】:
我相信正确的做法是
au InsertLeave * call cursor([getpos('.')[1], getpos('.')[2]+1])
【讨论】:
不过,这在第一列上表现不正确。在call
之前插入if (getpos('.')[2] > 1) |
以修复它。 (此外,它应该是 augroup
的一部分,以防止在重新加载包含此自动命令的 .vimrc
时累积向前跳转。)
你能举个例子说明如何做augroup
的事情吗?谢谢
这对我来说效果最好,但我不得不使用“set timeoutlen=100”,因为 InsertLeave 事件需要一整秒才能触发
你的意思是ttimeoutlen
?我个人将ttimeoutlen
设置为10
毫秒:ttimeoutlen
是给终端仿真器客户端以发出终端转义序列的超时余地,例如右箭头键的 4 字节序列\033[[C
。插入模式直到超时之后才真正退出的原因应该很明显 - Vim 不会知道您的终端是否即将跟进转义序列,或者将 Esc 作为退出插入模式提交命令。仔细注意并意识到您的 Vim 设置已经这样做了,延迟退出插入模式。
我无法编辑此答案,但这是 Kyle Strand 在第一条评论中谈到的内容:autocmd! InsertLeave * if (getpos('.')[2] > 1) | call cursor([getpos('.')[1], getpos('.')[2]+1]) | endif
所以自动命令不会在第一列中中断。另外,请在此处查看类似的解决方案:vim.fandom.com/wiki/…【参考方案6】:
有一种方法from the Vim Tips wiki 对我很有效...我不知道多少年了:
" Leave insert mode to the *right* of the final location of the insertion
" pointer
" From http://vim.wikia.com/wiki/Prevent_escape_from_moving_the_cursor_one_character_to_the_left
let CursorColumnI = 0 "the cursor column position in INSERT
autocmd InsertEnter * let CursorColumnI = col('.')
autocmd CursorMovedI * let CursorColumnI = col('.')
autocmd InsertLeave * if col('.') != CursorColumnI | call cursor(0, col('.')+1) | endif
【讨论】:
【参考方案7】:怎么样:
:imap <Esc> <Esc><Right>
【讨论】:
在某些设置下,如果编辑发生在当前行的末尾,则会将光标移动到下一行。 即使忽略行尾的事情,如果您使用i
进入插入模式,这将变得合乎逻辑,但如果您使用a
进入插入模式,则不合逻辑。以上是关于如何防止光标在离开 Vim 的插入模式时向后移动一个字符?的主要内容,如果未能解决你的问题,请参考以下文章