排查 kitty + tmux + nvim 中 markdown 文件被自动插入内容的问题

on 2026-04-28

现象

在 kitty → tmux → nvim 的终端栈里,每次打开 markdown 文件,文件开头都会自动插入两段内容:

  • 一段形如 tty(0.42.1) 的字符串(其中 0.42.1 是 kitty 的版本号)
  • 系统剪贴板里的当前内容

其他类型的文件(lua、sh、java 等)不受影响,只有 markdown 文件出现。


根本原因

终端查询序列泄漏

nvim 的 snacks.nvim 插件有一个图片渲染功能(image),在 markdown 文件打开时会探测当前终端是否支持 Kitty Image Protocol。探测过程会向终端发送 XTVERSION 查询

\033[>q

kitty 收到后响应:

\033P>|kitty(0.42.1)\033\\

这是一个标准的 DCS(Device Control String)序列。问题在于 tmux 对这段响应的处理:tmux 消耗了序列前缀 \033P>|ki,剩余的 tty(0.42.1)\033\\ 没有被正确路由回 nvim 的终端响应缓冲区,而是泄漏到了 stdin,被 nvim 当作键盘输入处理。

snacks.nvim 的 workaround 失效

snacks.nvim 的开发者其实已经意识到这个问题(issue #2332):在 tmux 开启 extended-keys 时,nvim 的 TermResponse autocmd 不会触发,导致终端查询响应无法被正常捕获,直接泄漏为文本。

为此 snacks.nvim 在 terminal.lua 中加了 workaround:检测到 tmux 且 extended-keyson 时,改为直接查询 tmux 获取终端名称,跳过发送 \033[>q

local ok, out = pcall(vim.fn.system, { "tmux", "show", "-g", "extended-keys" })
if ok and vim.trim(out):find(" on$") then  -- 只匹配 "on"
    ok, out = pcall(vim.fn.system, { "tmux", "display-message", "-p", "#{client_termname}" })
    ...
end

但判断条件写死了 " on$",只能匹配 extended-keys on。如果 tmux 配置的是:

set -s extended-keys always

tmux show -g extended-keys 输出的是 extended-keys always,不匹配 on$,workaround 失效,继续走发送 \033[>q 的流程,响应再次泄漏。


修复方案

核心修复:tmux extended-keys always → on

# ~/.config/tmux/tmux.conf
set -s extended-keys on   # 原来是 always

onalways 的区别:

  • on:程序主动发送 extended-keys 使能请求后,tmux 才发送扩展按键序列
  • always:不管程序有没有请求,强制发送

nvim 会主动发送使能请求,所以对 nvim 而言两者效果完全相同。改为 on 之后,snacks.nvim 的 workaround 能正确匹配,不再发送 XTVERSION 查询,问题解决。

顺手修的其他配置

tmux allow-passthrough

# 原来是 on,改为 all
set -g allow-passthrough all

on 只在 alternate screen(全屏)模式下才透传 DCS 序列,all 在所有情况下都透传,更适合 nvim 的使用场景。

tmux set-clipboard

# 原来是 on,改为 external
set -g set-clipboard external

on 让 tmux 拦截 OSC 52 剪贴板序列并自己管理,external 改为直接透传给 kitty 处理,避免 tmux 和 nvim 剪贴板操作互相干扰。

kitty clipboard_control

# ~/.config/kitty/kitty.conf
clipboard_control write-clipboard write-primary

默认配置允许终端程序通过 OSC 52 读取剪贴板。禁用读取权限后,nvim 的 OSC 52 读取请求会被拒绝,响应不会泄漏为文本。


排查过程

  1. 发现只有 markdown 文件出现问题 → 定位到 markdown 专属插件
  2. 临时禁用 snacks.nvimimage 功能后问题消失 → 确认是 snacks image 触发了终端探测
  3. 改为 backend = "kitty" 显式指定后端 → 问题仍然存在,说明探测逻辑不受 backend 影响
  4. snacks/image/terminal.lua 源码 → 发现 extended-keys always 不匹配 workaround 的 " on$" 条件
  5. 将 tmux 改为 extended-keys on → 问题彻底解决,image 功能正常工作

环境版本

组件版本
kitty0.42.1
tmux3.6a
snacks.nvimlatest (2026-04)