WezTerm Lua 指南:pane:send_paste编程式注入粘贴文本与换行规范化机制
【免费下载链接】weztermA GPU-accelerated cross-platform terminal emulator and multiplexer written by @wez and implemented in Rust项目地址: https://gitcode.com/GitHub_Trending/we/wezterm
导读
pane:send_paste(text)是 WezTerm 提供的 Lua API,用于将任意文本以"模拟剪贴板粘贴"的方式直接注入到指定 pane 的输入流中,整个过程完全不经过系统剪贴板。它在快速选择(QuickSelect)、复制覆盖层、CLI 脚本自动化等场景中发挥着关键作用:你可以在不污染用户剪贴板的前提下,向终端批量送入命令与文本。阅读本文后,你将掌握send_paste的完整语义、它与send_text的本质区别、bracketed paste(括号粘贴)模式下的特殊行为,以及canonicalize_pasted_newlines换行规范化配置的每个取值与默认值演进,并能在自己的 Lua 配置中正确组合使用它们。
方法签名与基本语义
pane:send_paste(text)text:string类型,即需要发送到 pane 输入流的文本内容;- 返回值:无(失败时抛出 Lua 错误)。
该方法自版本20220624-141144-bd1b7c5d起引入。其核心语义是:像从剪贴板粘贴一样向 pane 发送text,但剪贴板并不参与其中。也就是说,它只做"输入注入",不会读写系统剪贴板,也不会改变粘贴后剪贴板中的内容。
send_paste与send_text的区别
同一个 pane 对象上还提供了pane:send_text方法,两者最直观的区别在于:
| 维度 | pane:send_paste | pane:send_text |
|---|---|---|
| 语义 | 模拟"从剪贴板粘贴" | 模拟"键盘逐键输入" |
| 换行处理 | 按canonicalize_pasted_newlines设置规范化 | 原样写入 |
| bracketed paste | 若终端开启则自动包裹\x1b[200~ ... \x1b[201~ | 不受影响 |
| 典型用途 | 注入大段文本、命令块 | 逐条发送按键级输入 |
从源码看,两者的底层实现路径也不同:send_text在 Lua 绑定中直接调用 pane 的writer()写入原始字节,而send_paste会走完整的粘贴语义管线(见下文"源码实现"一节)。
paste别名:向后兼容
在新版本中,pane:paste(text)被定义为pane:send_paste(text)的别名,用于兼容早期版本(该别名自20221119-145034-49b9839f起生效,而pane:paste本身自20201031-154415-9614e117就已存在)。
两个名称在行为上完全等价,从 Lua 绑定源码可以看到两者共享同一实现:
// lua-api-crates/mux/src/pane.rs methods.add_method("send_paste", |_, this, text: String| { let mux = get_mux()?; let pane = this.resolve(&mux)?; pane.send_paste(&text) .map_err(|e| mlua::Error::external(format!("{:#}", e)))?; Ok(()) }); // An alias of send-paste for backwards compatibility with prior releases methods.add_method("paste", |_, this, text: String| { ... pane.send_paste(&text) ... });因此在新代码中推荐统一使用send_paste,同时不必担心旧脚本中paste失效的问题。
bracketed paste 模式下的特殊行为
send_paste的一个重要特性是它会自动感知目标终端(或前台应用)是否处于 bracketed paste(括号粘贴)模式:
- 如果终端开启了 bracketed paste 模式:文本会被包裹在
ESC[200~ ... ESC[201~(即\x1b[200~ ... \x1b[201~)控制序列之间发送,并且不会对换行做任何改写(等效于canonicalize_pasted_newlines取"None"); - 如果终端未开启 bracketed paste 模式:文本按
canonicalize_pasted_newlines设置进行换行规范化后原样发送。
这一机制的意义在于:像 vim、shell 等启用了 bracketed paste 的程序,收到包裹序列后会把内容当作"一次粘贴"整体处理,从而避免多行文本被逐行执行、行内自动缩进等误操作;同时 bracketed paste 模式下不做换行改写,可以保证内容原样送达。
安全处理:De-fang 去毒化
从源码实现看,WezTerm 还会对粘贴内容做"去毒化"(de-fang)处理,移除文本中可能内嵌的ESC[200~与ESC[201~序列,防止粘贴内容中的恶意控制序列伪造粘贴边界(term/src/terminalstate/mod.rs):
let de_fanged = canon.replace("\x1b[200~", "").replace("\x1b[201~", "");这保证了即使被粘贴的文本本身包含括号粘贴控制序列,也不会干扰终端对粘贴边界的判断。
canonicalize_pasted_newlines:粘贴换行规范化配置
send_paste在不处于 bracketed paste 模式时,会依据配置项canonicalize_pasted_newlines(自20211204-082213-a66c61ee9起引入)来决定换行的改写方式。该配置接受以下取值:
| 值 | 含义 | 引入版本 | | -- | ---- | -------- | |true| 等同于"CarriageReturnAndLineFeed"|20211204-082213-a66c61ee9| |false| 等同于"None"|20211204-082213-a66c61ee9| |"None"| 文本原样通过,不做任何改写 |20220319-142410-0fcdea07| |"LineFeed"| 任意风格的换行统一改写为 LF(\n) |20220319-142410-0fcdea07| |"CarriageReturn"| 任意风格的换行统一改写为 CR(\r) |20220319-142410-0fcdea07| |"CarriageReturnAndLineFeed"| 任意风格的换行统一改写为 CRLF(\r\n) |20220319-142410-0fcdea07|
注意:字符串形式的值自
20220319-142410-0fcdea07起才被接受;在此之前的版本中,true的行为与新版的"CarriageReturnAndLineFeed"一致。
默认值的平台差异与演进
该配置的默认值在不同版本、不同平台上并不相同:
| 版本 | 平台 | 默认值 |
|---|---|---|
20211204-082213-a66c61ee9 | Windows | "CarriageReturnAndLineFeed" |
20211204-082213-a66c61ee9 | 非 Windows | "None" |
20220319-142410-0fcdea07 | 非 Windows | "CarriageReturn" |
之所以 Windows 与非 Windows 平台默认值不同,文档中给出了清晰的背景:在 Windows 上情况比较棘手——向 Windows 控制台程序粘贴时,如果没有 CRLF 则完全没有换行;但在 WSL 中粘贴带 CRLF 的内容又会出现多余空行。
在实际使用中,默认设置带来的体验是:unix shell 与 vim 收到的是 unix 风格换行(这正是绝大多数用户期望的粘贴体验),而 cmd.exe 会收到 CRLF。
源码实现纵深:从 Lua 到终端状态的调用链
为了理解send_paste的完整工作流程,可以从 Lua 绑定出发追踪其调用链:
- Lua 绑定层:lua-api-crates/mux/src/pane.rs 中注册
send_paste与paste方法,解析text参数后解析 pane 实例并调用其send_paste; - pane 抽象层:mux/src/pane.rs 中
Ptytrait 声明了fn send_paste(&self, text: &str) -> anyhow::Result<()>,任何 pane 实现(本地 pane、远端 client pane、termwiz termtab 等)都必须实现该方法; - 本地 pane 实现层:mux/src/localpane.rs 中,本地 pane 会将调用转发给内部
Terminal对象的send_paste(同时记录输入活动以供闲置检测等使用); - 终端状态层:term/src/terminalstate/mod.rs 是最终核心实现,完整执行"判断 bracketed paste → 决定换行规范化策略 → 去毒化 → 包裹/原样写入"的整个流程。
关键实现片段(term/src/terminalstate/mod.rs):
pub fn send_paste(&mut self, text: &str) -> Result<(), Error> { let mut buf = String::new(); if self.bracketed_paste { buf.push_str("\x1b[200~"); } let canon = if self.bracketed_paste { NewlineCanon::None } else { self.config.canonicalize_pasted_newlines() }; let canon = canon.canonicalize(text); let de_fanged = canon.replace("\x1b[200~", "").replace("\x1b[201~", ""); buf.push_str(&de_fanged); if self.bracketed_paste { buf.push_str("\x1b[201~"); } self.writer.write_all(buf.as_bytes())?; self.writer.flush()?; Ok(()) }而bracketed_paste标志本身由终端对应用发送的 DECSET 2004 / DECRST 2004(对应模式 2004,即 bracketed paste mode)序列的响应来维护(term/src/terminalstate/mod.rs),并可通过bracketed_paste_enabled()查询当前状态(term/src/terminalstate/mod.rs)。
调用链中的其他使用方
send_paste并不是仅供 Lua 使用的孤岛,它在 WezTerm 内部多个功能中被复用:
- QuickSelect 快速选择(wezterm-gui/src/overlay/quickselect.rs):当用户确认快速选择的结果后,将其作为粘贴内容送入 pane;
- 复制覆盖层(wezterm-gui/src/overlay/copy.rs):复制选择模式确认后把文本注入;
- CLI 命令(wezterm/src/cli/send_text.rs):
wezterm cli send-text --pane-id ...等命令在实现时也会经由 pane 的写入通道; - mux 服务器会话(wezterm-mux-server-impl/src/sessionhandler.rs):远端客户端通过多路复用协议发来的粘贴操作在此分发。
这说明send_paste是整个 WezTerm "文本注入"能力的统一入口,理解它能帮助你触类旁通地理解上述所有功能。
实战示例
例 1:向指定 pane 注入命令
在 Lua 配置(或动态执行的脚本)中,先定位 pane 再发送:
local wezterm = require 'wezterm' wezterm.on('user-var-changed', function(window, pane, name, value) -- 当某个用户变量被设置时,向 pane 注入一段文本 if name == 'inject' then pane:send_paste(value) end end)例 2:快速选择结果后自动粘贴
配合 QuickSelect 的quick_select_patterns与复制操作,可以将选中的内容作为粘贴注入:
local act = wezterm.action return { keys = { -- 使用快速选择模式,确认后将结果以粘贴形式发送 { key = 'q', mods = 'CTRL|SHIFT', action = act.QuickSelectArgs { patterns = { '[0-9a-f]{40}' }, -- 例如匹配 40 位十六进制 SHA action = wezterm.action_callback(function(window, pane) local text = window:get_selection_text_for_clipboard() pane:send_paste(text) end), }}, }, }例 3:定制换行规范化行为
若你的工作流需要让粘贴内容在 unix 环境下也保留 CRLF(例如准备喂给 Windows 风格的工具),可以在wezterm.lua中显式配置:
return { canonicalize_pasted_newlines = 'CarriageReturnAndLineFeed', -- 其他取值:'None' | 'LineFeed' | 'CarriageReturn' | true | false }例 4:利用 bracketed paste 保持原样
当应用(如 vim、某些 TUI 程序)已启用 bracketed paste 时,send_paste会自动包裹并跳过换行改写,因此你无需做任何额外配置即可获得"原样粘贴"的效果——这也是在脚本中注入多行内容时最推荐的依赖行为。
注意事项与最佳实践
- 不要在不需要时覆盖剪贴板:
send_paste的一大优势是不触碰系统剪贴板,适合在自动化脚本中批量注入文本而不会干扰用户后续的复制粘贴操作; - 换行行为取决于目标程序:同一次
send_paste,在开启 bracketed paste 的程序中与未开启的程序中,换行处理可能不同(前者原样、后者按canonicalize_pasted_newlines规范化)。调试时请先确认前台程序是否开启 DECSET 2004; - 版本兼容:字符串形式的
canonicalize_pasted_newlines取值需要20220319-142410-0fcdea07及更新版本;send_paste方法本身需要20220624-141144-bd1b7c5d及更新版本,旧版本请改用pane:paste; - 安全语义:WezTerm 会对粘贴内容做 bracketed paste 序列的去毒化处理(见上文 de-fang 逻辑),因此在
send_paste层面无需自行转义ESC[200~/ESC[201~。
延伸阅读
canonicalize_pasted_newlines配置详解pane:paste别名说明pane:send_text方法- 配置文件的组织与加载方式:配置文件指南
- 相关源码:Lua 绑定、pane 抽象、本地 pane 实现、终端状态核心实现
【免费下载链接】weztermA GPU-accelerated cross-platform terminal emulator and multiplexer written by @wez and implemented in Rust项目地址: https://gitcode.com/GitHub_Trending/we/wezterm
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考