Vim 粘贴后缩进错乱:paste 模式、括号粘贴与 Neovim 排查

2026 年维护说明:前半部分是面向当前 Vim 与 Neovim 的排查指南。末尾的存档部分完整保留 2019 年源导出,包括原有措辞、Markdown、命令和当年的 Stack Overflow 链接。旧的 :set paste 流程今天仍可作为 Vim 的手动备用方案;当前 Vim 已能识别终端的 bracketed paste,而 Neovim 会自动处理粘贴并且已经移除 pastetoggle。这三条路径不能再混为一谈。

最短答案

如果从终端粘贴代码后,每一行都比上一行更靠右,先确认实际运行的是哪一个编辑器:

:version
:echo has('nvim')

然后选择对应路径:

环境应怎样处理
Vim,偶尔一次粘贴错乱运行 :set paste,在 Insert 模式粘贴,完成后立刻运行 :set nopaste
Vim,反复出现错乱优先修复 bracketed paste;确实无法修复时,才把 set pastetoggle=<F3> 作为手动备用方案
Neovim不要添加 pastetoggle;它已被移除。正常的终端或 GUI 粘贴应自动处理
Vim 或 Neovim 能使用系统剪贴板寄存器退出 Insert 模式后用 "+p,直接绕开终端把粘贴内容模拟成按键的路径

不要在 Vim 配置中永久写入 set paste。paste 模式会有意关闭或改变映射、缩写、缩进、制表、换行和格式化行为。一直开着它,虽然解决了一次粘贴,却会悄悄破坏正常编辑。

为什么粘贴后会形成“缩进楼梯”

终端可能把粘贴内容作为一连串普通按键交给编辑器。如果编辑器无法区分“粘贴”和“逐字输入”,每个换行都会触发 autoindentsmartindentcindentindentexpr、注释续行或其他格式规则。剪贴板中原有的缩进又叠加上编辑器新算出的缩进,于是变成:

if condition:
    first_call()
        second_call()
            third_call()

Bracketed paste 解决的是输入分类问题。支持它的终端会在粘贴内容前后加开始和结束标记,Vim 或 Neovim 因而能把整块内容当作粘贴,而不是普通输入。前提是终端、SSH、tmux 等复用器和编辑器组成的完整链路都能保留并理解该协议。

Vim:手动 paste 流程

对于 Vim,2019 年的做法今天仍可作为备用方案:

:set paste

进入 Insert 模式并粘贴。完成后返回 Normal 模式,恢复正常行为:

:set nopaste

如果确实经常需要手动切换,当前 Vim 帮助仍然记录了 pastetoggle

set pastetoggle=<F3>

粘贴前后各按一次 <F3>pastetoggle 与普通 Insert 模式映射不同:Vim 在 paste 开启时会忽略 Insert 模式映射,而这个专用选项仍能把 paste 模式关掉。

随时可以检查当前状态:

:set paste?

如果粘贴结束后 Vim 仍显示 paste,运行 :set nopaste。Vim FAQ 也明确警告,不要让该选项永久开启。

当前 Vim:优先使用 bracketed paste

当前 Vim 在 :help xterm-bracketed-paste 中记录了括号粘贴。终端协议可用时,Vim 会收到粘贴边界标记,把内容按字面插入,而不会将它解释为命令。这条路径正常时,不需要手动切换。

手动开关在终端链路过旧或配置异常时仍然有用,但它应是备用方案,不应成为第一项永久配置。Vim 文档还提醒,bracketed paste 不是安全保证:不同终端的实现可能不同,把不可信文本粘进编辑器或命令行前仍应先检查内容。

Neovim:删除旧的切换设置

Neovim 当前的 provider 文档说明,TUI 粘贴依赖终端的 bracketed-paste 能力,终端支持时会自动工作;GUI 则调用 Neovim 的 paste API。从 Neovim 0.9 起,pastetoggle 已被移除,paste 选项也已标为 obsolete。

因此,下面这条旧 Vim 设置不应出现在当前 Neovim 配置里:

set pastetoggle=<F3>

如果它在 init.vim 中引发 unknown option 错误,直接删除。若 Neovim 粘贴仍会重新缩进或插入原始转义标记,应排查终端、复用器、GUI 或配置链路,而不是重新实现已经移除的开关。

可重复的排查顺序

1. 确认编辑器和版本

使用 :version,不要只凭 shell 命令名或界面外观判断。Vim 与 Neovim 共用配置时,很容易把已废弃的选项也一起带过去。

2. 隔离用户配置

让同一个编辑器在不加载用户配置的状态下启动:

vim --clean
nvim --clean

粘贴一小段无害文本。clean 会话正常时,检查日常配置、filetype 插件、缩进插件和粘贴映射;clean 会话也失败时,终端、SSH 或复用器链路更值得怀疑。这个对比只是证据,不是最终证明;修改终端设置前,应分别在 tmux 或远程会话内外重复测试。

3. 检查 Vim 选项状态和来源

在 Vim 中运行:

:verbose set paste?
:verbose set autoindent? smartindent? cindent? indentexpr?

verbose 有时会显示最后修改选项的脚本。遗留的 paste 可以解释为什么粘贴后映射或缩进失效;意外的 indentexpr 或 filetype 插件,则可能解释为什么只有某一种文件会被改写。

4. 用寄存器绕开终端粘贴

如果编辑器能访问系统剪贴板,在 Normal 模式从 + 寄存器粘贴:

"+p

在 Insert 模式中,依次按 Ctrl-RCtrl-O+。文档中的 CTRL-R CTRL-O {register} 会按字面插入寄存器内容并避免自动缩进。这样可以把“编辑器缩进问题”和“终端粘贴协议问题”分开。

如果 + 寄存器不可用,那是剪贴板功能或 provider 问题,并不能证明 paste 模式坏了。Neovim 中运行 :checkhealth;内置健康检查包含剪贴板支持。Vim 中查看 :version 的 clipboard 功能,再使用该构建和操作系统真正支持的剪贴板方法。

5. 比较传输链路

如果缓冲区里出现 ^[[200~ 之类的文字,说明 bracketed-paste 标记到达了缓冲区,却没有被识别。如果 tmux 外、SSH 外或另一个终端中的行为不同,应先比较这些层,再修改 Vim 缩进选项。不要随意把网上的终端转义码复制进配置:当前 Vim 可以利用 terminfo,而与终端不匹配的手工值会制造第二个问题。

症状—原因对照表

症状优先检查的边界第一步
粘贴的每一行逐渐右移粘贴被当作普通输入解释先试 clean 会话,再试 Vim 临时 :set paste
粘贴后映射、缩写或正常缩进失效Vim 仍停留在 paste 模式:set paste?,然后 :set nopaste
Neovim 报告 pastetoggle unknownNeovim 配置中混入了 Vim 专用旧设置删除它,测试自动粘贴与 nvim --clean
出现字面量 ^[[200~^[[201~终端链路没有处理括号粘贴标记对比 tmux/SSH 外和另一个终端
"+p 失败或 + 寄存器为空缺少剪贴板功能或 providerNeovim 用 :checkhealth;Vim 看 :version
仅某一种 filetype 出问题filetype 或缩进脚本参与了处理:verbose set indentexpr? cindent? autoindent?

最小判断规则

  1. 当前 Vim 和 Neovim 优先使用自动 bracketed paste。
  2. 系统剪贴板寄存器可用时,优先使用 "+p
  3. :set paste 后接 :set nopaste 只作为 Vim 备用方案。
  4. pastetoggle 只用于 Vim,并且只在手动备用方案确实反复需要时使用。
  5. clean 会话仍然失败时,先查终端传输,再改缩进插件。

2019 年源存档(原文完整保留)

https://stackoverflow.com/questions/2514445/turning-off-auto-indent-when-pasting-text-into-vim

To turn off autoindent when you paste code, there’s a special “paste” mode.

Type

:set paste

Then paste your code. Note that the text in the tooltip now says -- INSERT (paste) --.

After you pasted your code, turn off the paste-mode, so that auto-indenting when you type works correctly again.

:set nopaste

However, I always found that cumbersome. That’s why I map <F3> such that it can switch between paste and nopaste modes while editing the text! I add this to .vimrc

set pastetoggle=<F3>

一手文档

Leave a Reply