如何防止光标在离开 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。真的:当反复使用 aEsc 时,光标不会“滑开”。 然而,恕我直言,这个假设不是很正确,至少从看起来直观的 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,插入模式位置将介于 cD 之间。普通的 ESC 会将光标放在c 上; &lt;C-O&gt;:stopinsert&lt;CR&gt;(或反引号方法)会将光标放在D

如果按 a,插入模式位置将介于 De 之间。普通的 ESC 会将光标放在D 上; &lt;C-O&gt;:stopinsert&lt;CR&gt; 会将光标放在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

但是:请记住,这只会处理 ia 作为进入方法:如果您使用可视块模式、IA 或其他方式,您需要想出要匹配的新命令(并且有很多)。因此,我强烈建议您不要这样做。

就个人而言,我建议习惯默认行为。您可以轻松地将 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 来设置一个标志,然后重新映射插入模式&lt;Esc&gt; 来检查那个标志,如果设置了,使用&lt;C-O&gt;:stopinsert&lt;CR&gt;,然后清除标志或者方式。 我不相信这对于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] &gt; 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] &gt; 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 的插入模式时向后移动一个字符?的主要内容,如果未能解决你的问题,请参考以下文章

Linux vim常用选项

vim的使用

VIM

vim工具的编辑模式及命令模式

vim常用命令(iOS)

vim