排查 kitty + tmux + nvim 中 markdown 文件被自动插入内容的问题
现象
在 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-keys 为 on 时,改为直接查询 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
on 和 always 的区别:
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 读取请求会被拒绝,响应不会泄漏为文本。
排查过程
- 发现只有 markdown 文件出现问题 → 定位到 markdown 专属插件
- 临时禁用
snacks.nvim的image功能后问题消失 → 确认是 snacks image 触发了终端探测 - 改为
backend = "kitty"显式指定后端 → 问题仍然存在,说明探测逻辑不受 backend 影响 - 读
snacks/image/terminal.lua源码 → 发现extended-keys always不匹配 workaround 的" on$"条件 - 将 tmux 改为
extended-keys on→ 问题彻底解决,image 功能正常工作
环境版本
| 组件 | 版本 |
|---|---|
| kitty | 0.42.1 |
| tmux | 3.6a |
| snacks.nvim | latest (2026-04) |