命令直通车
让短语与词库条目在选中时执行动作——打开网址、启动程序、模拟按键、切换输入法状态
命令直通车(Command Bar)把短语 / 用户词库 / 系统词库的条目从"展开成文本"升级为"执行带副作用的动作":打开 URL、启动程序、模拟按键、写入剪贴板、加词到用户库、切换输入法状态等,让输入法兼作快捷命令启动器。
如何触发
命令用一段英文字母编码触发:输入编码 → 候选区出现命令条目 → 选中即执行。
| 输入 | 选中后 |
|---|---|
cobd | 用默认浏览器打开 baidu.com |
cojk | 输出 「」,光标停在中间,可用跳出键越过 |
coac | 把剪贴板内容加入用户词库 |
coen | 切换中英文模式 |
系统短语包已内置一批以 co 开头的示例命令,可在 设置 → 词库 → 短语 中浏览、开关或仿写。
触发码只能是英文字母
数字键会被"选中第 N 候选"逻辑吃掉,中文标点在按键阶段就被转换,二者都进不了短语层。因此触发码只能用 ASCII 字母(如 cobd),既不能含数字,也不能用中文标点。想携带查询内容时,用 clip() / last() 等数据源,而不是把词跟在编码后面。
编写位置
命令短语可放在三处,都会自动解析:
| 位置 | 作用范围 | 入口 |
|---|---|---|
| 快捷短语 | 全局,跨方案有效 | 设置 → 词库 → 短语 |
| 用户词库 | 仅当前方案 | 设置 → 词库 → 方案 → 用户词库 |
| 系统词库 | 仅当前方案 | 设置 → 词库 → 方案 → 系统词库 |
推荐放快捷短语
绝大部分命令放快捷短语,拼音 / 五笔 / 混输下都可用。只有方案专属的命令才放用户词库。
四种标记
短语与词库条目共用一个 text 字段,靠内容形式区分类型。命令能力通过以 $ 开头的标记触发。
| 标记 | 含义 | 前缀输入时 | 完整编码时 |
|---|---|---|---|
$CC(显示名, 动作...) | 命令 | 显示(可关) | 显示 |
$CC1(显示名, 动作...) | 命令,顶层等同 $CC(但不能嵌进 $SS) | 显示(可关) | 显示 |
$AA(组名, 字符串) | 字符组 | 显示组名导航 | 展开为 N 个单字符 |
$SS(组名, 元素...) | 数组(字符组的一般形式) | 显示组名导航 | 展开为 N 个成员 |
cobd = $CC("打开百度", open("https://baidu.com"))
cobd = $CC1("打开百度", open("https://baidu.com"))
zzbd = $AA("标点", "、。·ˉˇ¨〃々—~‖…")
zzgo = $SS("常用站点", $CC("百度", open("https://baidu.com")), $CC("GitHub", open("https://github.com")))`$CC` 与 `$CC1` 在顶层等价
早期 $CC 只在完整编码时出现、$CC1 才参与前缀列举。现在两者都参与:判据是
「没有显式写 {prefix: false}」,而 $CC1 只是把 prefix: true 显式写了出来。
不想让某条命令出现在前缀列表里,给它加 {prefix: false}:
cobd = $CC("打开百度", open("https://baidu.com"), {prefix: false})唯一的例外在 $SS 的元素位置:那里只能嵌 $CC,写 $CC1 会让整条短语解析失败、
在候选里直接消失且没有任何提示。组前缀由 $SS 统一控制,元素不得自带 prefix。
另外前缀列举本身还受 input.phrase.min_prefix 约束(默认 2),
打得太短不会列。
一步失败就别往下跑:{on_error: "stop"} 0.121 新增
动作链默认是「前一步失败,后一步照跑」。想要「前一步成功才做下一步」,在同一个修饰符袋里写 on_error:
coim = $CC("导入词库", wind.cli("dict import D:\\words.txt"), ui.toast("导入成功"), {on_error: "stop"})不写时默认 continue,与历史行为一致——默认不能改成 stop:已有词条依赖「前一步失败后一步照跑」(比如剪贴板被占用时仍要把光标移回去)。
不写 stop 的代价是假成功:上面那条链里 wind.cli 失败了,后面那句「导入成功」照样弹。这个修饰符本就是用来防假成功的,所以值写错会直接报错,而不是落回默认——失灵时的表现恰好就是它要防的那件事。
$AA 是 $SS 的字符串简写:$AA 自动按字符拆开,$SS 显式列举每个元素,元素既可是字符串(选中上屏),也可是嵌入的 $CC(...)(选中执行动作)。输入前缀(如 zz)看到组名导航条目,选中后自动补全到完整码进入二级选择;输入完整码(如 zzbd)直接展开为成员候选。
`$SS` 的元素只能是字符串或 `$CC(...)`
元素位置上的 $CC 不得带 prefix 修饰符——组前缀由 $SS 统一控制。所以 $CC1(...)
(它等价于显式写了 prefix: true)和 $CC(..., {prefix: false}) 在这里都会让整条短语
解析失败,现象是打出编码后候选里什么都没有、也没有任何报错。要控制这一组要不要参与前缀
列举,把 {prefix: false} 写在 $SS 自己的末尾。
表达式语法
$CC / $CC1 内部是表达式,只有三种形态:
| 形态 | 例子 |
|---|---|
| 字面量 | "hello"、42、3.14 |
| 标识符 | last、now、code(等价零参调用) |
| 函数调用 | open("https://...")、clip.copy("x")、key.tap("Enter") |
- 字符串内插:字符串中用
{expr}内插子表达式,如$CC("百度搜 {clip()}", open("https://baidu.com/s?wd={url(clip())}")) $$转义:要输出字面$就写$$,否则$M之类会被当模板变量替换。例如text = "工资: $$M"显示工资: $M.仅作命名空间:.只用于函数名分隔(clip.copy、key.tap),不能取属性或索引。用last(1)而非last.1,用len(code)而非code.length- 具名参数:部分函数接受
名字=值形式的可选参数(目前是proc.run的cwd/verb/show、proc.shell的cwd,以及ime.pair的jump),如proc.run("dict.exe", cwd="D:\\Dict")。规则三条:必须写在所有位置参数之后、同一个名字不能写两次、名字必须是该函数登记过的(写错会报错,不会被当没写过)。值和普通参数一样可以内插,如cwd="{clip()}"。
`k=v` 与 `{k: v}` 不是一回事
k=v 是函数的参数,管这一次调用;$CC(..., {prefix: false}) 那种花括号是短语的修饰符,管这条候选怎么显示。两者位置和作用对象都不同,写错地方会直接报错而不是悄悄生效。
路径里的反斜杠要写两个
字符串里的 \ 是转义引导符,写 Windows 路径时必须写成 \\:
# ✅ 正确
cotmp = $CC("[打开临时目录]", open("D:\\notes\\temp"))
# ❌ 错误:`\n` 被当成换行、`\t` 被当成 Tab
cotmp = $CC("[打开临时目录]", open("D:\notes\temp"))被吃掉的是这几个:\n \t \r 分别变成换行、Tab、回车,\\ \" \' \{ \} \( \) \$ 变成对应的单个字符。其余组合(如 \我、\x)原样保留反斜杠——这正是坑人的地方:D:\我的文档 恰好能用,于是很容易以为单反斜杠没问题,直到某天路径里出现 \notes、\tools、\report,才发现路径被截断成了带换行/Tab 的怪字符串,而且看不出哪里错了。
别靠「试出来能用」判断
路径写单反斜杠时能否工作,取决于下一个字母恰好是不是转义字母。同一条短语换个目录名就可能失效,且失败时只弹一句「命令执行失败」,不会告诉你是反斜杠的问题。统一写 \\,不要逐条去试。
网络共享路径开头是四个
UNC 路径(\\服务器\共享名)本身就以两个反斜杠开头,每个都要按上面的规则写成两个,所以开头一共是四个:
| 想访问的路径 | 命令里写 |
|---|---|
\\nas\share\dict.exe | \\\\nas\\share\\dict.exe |
\\192.168.1.5\tools | \\\\192.168.1.5\\tools |
conas = $CC("[打开共享]", open("\\\\nas\\share"))规则本身没变,仍然是「一个 \ 写成 \\」——只是 UNC 的开头恰好有两个。写成两个(\\nas)解析出来是单个反斜杠开头的 \nas,那不是 UNC 路径,打不开;而这同样属于上面说的「不报错,只是打不开」。
换个地方写,还是两个
输入法内部只有一份真实文本:库里存的就是输入时用的,存储层对转义一无所知。转义只发生在文本进出系统的边界上,一共三类:
| 边界 | 方向 | 做的事 |
|---|---|---|
词库 / 短语文件(方案的 .dict.yaml、导入的 TSV/TXT、备份的 .wdict.yaml) | 读入、导出 | \\→\、\n→换行、\t→Tab,其余组合原样保留 |
| 设置 → 词库 的编辑框 | 显示、保存 | 与上一行互逆:库里的换行显示成 \n、制表显示成 \t,保存时再还原回去 |
| 候选窗 | 只显示 | 换行画成 ↵、制表画成 ⇥;上屏的仍是真换行 |
命令栏语法条目的反斜杠不参与这三层——$CC / $CC1 / $SS / $AA 以及含 {} 的模板,它们的 \ 由表达式解析独占,边界再转一次就成了双重展开。所以上面那条「写两个」在哪儿写都一样:设置页的输入框、方案的词库文件、配置分发包里内嵌的码表,写法完全相同,照搬不用换算。
而纯文本词条没有表达式那一层,边界规则直接落到它身上:
| 想得到的内容 | 词库文件 / 设置页里写 |
|---|---|
C:\Users\note | C:\\Users\\note |
| 两行文本 | 第一行\n第二行 |
| 带一个制表符 | 列一\t列二 |
0.114 – 0.117 的命令短语要写四个
- 这几个版本里,命令短语经设置页或词库文件写入时会被多转义一层:路径分隔符得写四个反斜杠(
open("D:\\\\notes"))才能真正得到一个。写进方案自己的 TOML 不过这一层,仍是两个——同一条命令在不同载体规则不同,正是该修的地方。 - 各处统一为两个 0.118 新增:命令栏语法条目在边界上原样穿过。已经按四个写好的短语不用动——库里存的本来就是两个,升级后照常工作,只是设置页里显示的数量从四个变回两个(导出的文件同理)。
内部目录变量
不想硬编码安装位置或用户名(换台机器、换个盘符就失效),可以用 ${变量名} 引用输入法自己的目录:
| 变量 | 指向 | 典型位置 |
|---|---|---|
${APP_DIR} | 程序安装目录(wind_input.exe 所在目录) | C:\Program Files\WindInput |
${USER_DATA} | 漫游用户数据目录 | %APPDATA%\WindInput |
${LOCAL_DATA} | 本机用户数据目录(不随漫游同步) | %LOCALAPPDATA%\WindInput |
# 打开安装目录
coad = $CC("[打开安装目录]", open("${APP_DIR}"))
# 上屏安装目录路径
cotd = $CC("[输出安装目录]", type("${APP_DIR}"))
# 拼接子目录 —— 反斜杠同样要写两个
colg = $CC("[打开日志]", open("${LOCAL_DATA}\\logs"))变量在解析阶段就被替换成绝对路径,可用在任何字符串位置(动作参数、显示名、拼接片段)。这三个变量与 CLI 的路径参数完全一致,写法可以互相照搬。
不认识的 ${...} 会原样保留
只有上表三个是内部目录变量。写错名字(如 ${APPDIR})不会报错,而是原样留下字面的 ${APPDIR} ——因为 ${YC} 这类短语模板变量也用同样的写法,命令栏不能把它们当错误吞掉。所以路径打不开时,先核对变量名拼写。
若你确实想输出字面的 $ 且后面紧跟 {,写 \${...}。
常用示例
# 打开网址
cobd = $CC("打开百度", open("https://baidu.com"))
cogh = $CC("打开 GitHub", open("https://github.com"))
# 搜索剪贴板 / 上次上屏内容
cobs = $CC("百度搜 · {clip()}", web.search("baidu", clip()))
cozd = $CC("汉典 · {last()}", open("https://www.zdic.net/hans/{url(last())}"))
# 配对符号(光标落两段之间,可用跳出键越过右段)
cojk = $CC("「」", ime.pair("「", "」"))
# 删除当前行
codl = $CC("[删行]", key.seq("Home", "Shift+End", "Backspace"))
# 启动程序
cono = $CC("打开记事本", proc.run("notepad.exe"))
# 加词到用户库(编码自动生成)
coac = $CC("加词 · {clip()}", dict.add(clip()))
# 计算剪贴板表达式
coca = $CC("={calc(clip())}", type(calc(clip())))
# 切换输入法状态
coen = $CC("切中英", ime.toggle("cn-en"))
cots = $CC("切主题", ime.theme_cycle())
# 字符组
zzbd = $AA("标点", "、。·ˉˇ¨〃々—~‖…")
zzsx = $AA("数学", "+-<=>±×÷∈∏∑")内置函数参考
函数只有一种返回值:字符串——数字、布尔在这里也是字符串(len("abc") 得到 3,可以直接拼进文本),动作函数返回空串。记号:s 字符串、n 整数、参数后 ? 表示可选。
每张表先给速查,表后的小节讲具体怎么写。
四条通用规则
- 索引从 1 起,按字符数。
code(2)/sub(s, 2)/split(s, ",", 2)/dict.rev(s, 2)里的2都指第 2 个字符(第 2 段);汉字、emoji 各算一个。sub与split还接受负数,-1是最后一个。 - 取不到就给空串,不报错。 索引越界(
sub("abc", 9))、历史不够长(last(9))、剪贴板为空,结果都是空串。这是刻意的:$SS里固定写 N 条各查一个字,取不满是常态,报错会让整条短语连带消失。 - 写错才报错,而报错等于候选消失。 函数名不存在(含拼写、大小写写错)、参数个数不对、该给数字的位置给了别的,都算错误。错误在显示名里 → 打出编码后候选里什么都没有、也没有提示;错误在动作里 → 选中时提示命令执行失败。所以"编码打出来却看不到候选",第一嫌疑就是函数名或参数写错。
- 显示名里只能用纯函数。
$CC的第一个参数只接受取值 / 文本 / 计算类函数;写open、config.set、key.tap这类动作函数会被判为不纯、整条失效——候选被列出来的时候不该有副作用发生。
显示与执行各求值一次
显示名在候选生成时求值,动作在选中那一刻才求值。所以 type(last()) 取到的是选中时的历史;而 uuid() 在显示名和动作里各写一次会得到两个不同的 ID——要让看到的和打出来的一致,就只在 type() 里写,显示名写死。
取值函数(纯函数)
| 函数 | 说明 |
|---|---|
code() / code(n) | 触发时组合区里的编码(第 n 字符起到末尾) |
tail(s, n) | 字符串 s 从第 n 字符起到末尾 |
last() / last(n) | 最近 / 倒数第 n 次上屏文本 |
clip() | 当前剪贴板文本 |
app() / title() / sel() | 前台进程名 / 窗口标题 / 选中文本——Windows 上均返回空串(仅 macOS 上报;其中 title() 还需 macOS 的辅助功能授权,未授权返回空串) |
date(fmt) / date(fmt, offset) | 日期,见下 |
time() / time(fmt) | 时间,默认 "HH:mm:ss",见下 |
now() | 等价 date("YYYY-MM-DD HH:mm:ss") |
env(name) | 环境变量,见下 |
uuid() / uuid(flags) | 随机 UUID(v4),见下 |
config.get(key) | 读取配置项当前值,key 是配置文件里的路径,如 ui.theme.name |
dict.rev(s) / dict.rev(s, n) 0.117 新增 | 反查:s 中第 n 个字(1 起,默认 1)的编码与读音,见下文 |
日期与时间:date / time / now
fmt 里下面这些字母是占位符,其余字符(- / : 年 等)原样输出。三个函数用的是同一套占位符,time 只是默认值不同、now 是写死的格式。
| 占位符 | 含义 | 2026-06-14 09:05:07 时 |
|---|---|---|
YYYY | 四位年 | 2026 |
YY | 两位年 | 26 |
MM | 月,补零 | 06 |
M | 月,不补零 | 6 |
DD | 日,补零 | 14 |
D | 日,不补零 | 14 |
HH | 时,24 小时制补零 | 09 |
h | 时,12 小时制不补零 | 9 |
mm | 分,补零 | 05 |
m | 分,不补零 | 5 |
ss | 秒,补零 | 07 |
s | 秒,不补零 | 7 |
没有星期、上午/下午、月份英文名的占位符。24 小时制补零只有 HH(没有 H),12 小时制只有 h(没有 hh)。
常用配方:
| 想要 | 写法 | 结果 |
|---|---|---|
| 今天 | date("YYYY-MM-DD") | 2026-06-14 |
| 中文日期 | date("YYYY年M月D日") | 2026年6月14日 |
| 明天 / 昨天 | date("YYYY-MM-DD", "+1d") / date("YYYY-MM-DD", "-1d") | 2026-06-15 / 2026-06-13 |
| 上个月 | date("YYYY-MM", "-1M") | 2026-05 |
| 文件名时间戳 | date("YYYYMMDD-HHmmss") | 20260614-090507 |
| 只要时间 | time() / time("HH:mm") | 09:05:07 / 09:05 |
| 日期加时间 | now() | 2026-06-14 09:05:07 |
codt = $CC("{date("YYYY-MM-DD")}", type(date("YYYY-MM-DD")))
cotm = $CC("{date("YYYY-MM-DD", "+1d")}", type(date("YYYY-MM-DD", "+1d")))大小写就是两个不同的占位符
MM 是月、mm 是分;DD 是日、HH 是时。date("YYYY-mm-DD") 不会报错,它会把分钟填进月份的位置,得到 2026-05-14 这种看着像日期、其实每分钟都在变的东西。
格式串里别混英文单词
单个 M D h m s 也是占位符,所以格式串里写英文会被就地替换:date("Monday") 得到的是 6onday(M → 月份 6)。要固定的英文文字请用 concat() 拼在 date() 外面,或者只在格式串里放中文与标点。
`%` 是保留字符,不要写进格式串
格式串里的 % 会被直接交给底层的时间格式化器:%Y 这类恰好合法的会按 strftime 解释,而落单的 %(或 % 后跟着不认识的字母)会让求值当场崩溃——不是弹个错误,是输入法直接退出。想输出百分号,用 concat(date("YYYY"), "%") 拼在外面。
offset:把日期平移一段。 形如 "+1d",三部分缺一不可:
| 部分 | 规则 |
|---|---|
| 符号 | + 或 -,必须写。"1d" 会报错 |
| 数字 | 整数。"+1.5d" 报错 |
| 单位 | d 天 / w 周 / M 月 / y 年,大小写敏感 |
单位只有这四个:M 是月、小写 m 不是分钟而是非法单位,也没有小时/分钟级的偏移——要平移时间只能靠 date(fmt, offset),而它最小的单位是天。
月和年按自然月平移,落到不存在的日期时夹到当月最后一天:3 月 31 日 -1M 得到 2 月 28 日(闰年 29 日)。
编码、历史与剪贴板:code / tail / last / clip
| 写法 | 取到什么 |
|---|---|
code() | 触发这条候选时组合区里已输入的编码 |
code(n) | 同上,但从第 n 个字符切到末尾;n ≤ 1 等于全部,超出长度得空串 |
tail(s, n) | 与 code(n) 同规则,但作用于任意字符串 |
last() / last(1) | 最近一次上屏的文本 |
last(2) / last(3) | 上上次 / 上上上次;n < 1 或超出历史长度得空串 |
clip() | 当前系统剪贴板里的纯文本;剪的是图片或剪贴板为空时得空串 |
历史只记真正上屏的文本,纯副作用命令(open / proc.run 等)不进历史,所以 last() 不会被命令自己污染(见行为细节)。
last() 与前台信息在命令触发的那一刻冻结成快照,之后动作里再读也是同一份。
环境变量与随机 ID:env / uuid
env(name) 读输入法进程的环境变量,读不到返回空串(不报错):
couser = $CC("{env("USERNAME")}", type(env("USERNAME")))
cohome = $CC("[打开用户主目录]", open(env("USERPROFILE")))新加的环境变量要重启输入法才可见
进程的环境变量在启动时就固定了。刚在系统属性里新建的变量,输入法要退出重开才读得到。输入法自己的目录别用 env() 猜,直接用 ${APP_DIR} 一类的内部目录变量。
uuid() 生成随机 UUID(v4),flags 控制形态,字母不区分大小写:
| 写法 | 结果形如 |
|---|---|
uuid() | 9f1c2b7e-3a4d-4f65-8c21-0d5e7a9b1234 |
uuid("n") | 9f1c2b7e3a4d4f658c210d5e7a9b1234(去横杠) |
uuid("u") | 9F1C2B7E-3A4D-...(转大写) |
uuid("nu") | 两者组合 |
只认 n 和 u,写别的字母(uuid("x"))会报错、候选消失——这是刻意的,静默忽略会让人以为"格式没生效"。短语层的 $uuid 模板变量走的是同一套实现。
文本处理(纯函数)
| 函数 | 说明 |
|---|---|
len(s) | 字符数(按 rune) |
upper(s) / lower(s) | 转大写 / 小写 |
trim(s) / trim(s, chars) | 去首尾空白 / 去首尾的指定字符 |
sub(s, start) / sub(s, start, end) | 切片,索引 1 起,双闭区间,支持负数 |
replace(s, old, new) | 字面替换(全部出现处) |
regex(s, pat, rep) | 正则替换,见下 |
split(s, sep, n) | 按 sep 拆分取第 n 段(1 起,支持负数) |
concat(...) | 拼接任意个片段 |
reverse(s) | 反转(按 rune) |
url(s) / html(s) / json(s) / base64(s) | URL / HTML / JSON / Base64 编码,见下 |
default(s, fallback) | s 为空时返回 fallback |
照着抄的例子(左边是写法,右边是结果):
| 写法 | 结果 |
|---|---|
len("清风输入法") | 5 |
upper("Wind") / lower("Wind") | WIND / wind |
trim(" 两边有空格 ") | 两边有空格 |
trim("**重点**", "*") | 重点 |
sub("abcdef", 3) | cdef |
sub("abcdef", 2, 4) | bcd |
sub("abcdef", -2) | ef |
sub("abcdef", 1, -2) | abcde |
replace("2026/06/14", "/", "-") | 2026-06-14 |
split("张三,李四,王五", ",", 2) | 李四 |
split("a-b-c", "-", -1) | c |
concat("【", clip(), "】") | 剪贴板内容加书名号 |
reverse("abc") | cba |
default(clip(), "(先复制点东西)") | 剪贴板为空时给出提示语 |
trim(s, chars) 的第二个参数是字符集合不是子串:trim("xxabcxx", "x") 与 trim("xxabcxx", "xy") 结果相同,都是 abc。
正则替换:regex(s, pat, rep)
pat 用 Rust regex 语法(与 RE2 同族):\d \w \s、[[:alpha:]]、+ * ? {n,m}、|、分组都支持,不支持反向引用和环视((?<=...)、\1)。
- 模式里的反斜杠按字符串规则写两个:
\\d才是一个\d(见路径里的反斜杠要写两个) - 量词的大括号要转义成
\{\}:{在字符串里是插值语法,"\\d{3}"会被求值成\d3——正则悄悄变成了另一个意思,还不报错 - 替换串里用
${1}${2}引用捕获组。${...}不会被当插值(那是内部目录变量的写法,名字不认识就原样保留),可以照写;$1也认,但后面紧跟字母数字时会被吞进组名,统一写${1}更稳 - 匹配处全部替换,没有"只换第一处"的开关
- 正则本身写错(括号不配对等)→ 求值报错 → 候选消失
匹配换行要用 `\r?\n`,不能用 `\n`
clip() 从 Windows 剪贴板取到的文本,换行通常是 \r\n 两个字符(记事本、Word、浏览器复制都是这样);而 last()、code 以及你自己写在字符串里的换行,往往只有 \n 一个。同一条正则要两种来源都能用,只有 \r?\n 一种写法:
| 模式 | \r\n 来源(剪贴板居多) | \n 来源 |
|---|---|---|
"\n" | ❌ 只吃掉 \n,剩下的 \r 原样上屏 | ✅ |
"\r\n" | ✅ | ❌ 一处都匹配不上,原文照抄 |
"\r?\n" | ✅ | ✅ |
第一行剩下的那个 \r,在多数编辑器里既不显示也不换行,看上去就是「替换好像没生效」或者「行首多了个看不见的东西」。撞上之后最顺手的自救是把 \n 改成 \r\n——那恰好掉进第二行那个更隐蔽的坑:换个来源就一个都匹配不上,而且同样不报错。
这一条不受上面「反斜杠写两个」的约束:"\r?\n" 与 "\\r?\\n" 结果完全相同(前者由字符串转义直接生成真的回车 / 换行字符,后者原样交给正则引擎去解释),写哪个都行。
# 把连续数字换成 #
como = $CC("[数字打码]", type(regex(clip(), "\\d+", "#")))
# 日期换成中文写法(捕获组用 ${1})
cocn = $CC("[日期转中文]", type(regex(clip(), "(\\d+)-(\\d+)-(\\d+)", "${1}年${2}月${3}日")))
# 手机号中间四位打码:注意量词的大括号写成 \{3\}
comask = $CC("[手机号打码]", type(regex(clip(), "(\\d\{3\})\\d\{4\}(\\d\{4\})", "${1}****${2}")))
# 多行粘贴合成一行:\r?\n 才能同时吃掉 \r\n 和 \n
cojoin = $CC("[多行并一行]", type(regex(clip(), "\\r?\\n", "、")))四个编码函数:url / html / json / base64
| 写法 | 结果 | 用在哪 |
|---|---|---|
url("清风 输入") | %E6%B8%85%E9%A3%8E+%E8%BE%93%E5%85%A5 | 拼进查询串。空格变 +(query 编码),拼进路径段要留意 |
html("<b>&") | <b>& | 往 HTML 里贴文本 |
json("a\"b") | "a\"b" | 带外层引号,出来的是一个完整的 JSON 字符串字面量 |
base64("hi") | aGk= | 标准 Base64(带 = 补位) |
url() 正是搜索类命令的关键一环——open("https://www.zdic.net/hans/{url(last())}") 里少了它,含空格或标点的词就会把 URL 拼坏。
计算与内省
算术:calc(expr)
支持 + - * / %(取余)、圆括号、一元正负号、小数与空格。
不支持:乘方 ^、科学计数法 1e3、千分位逗号、全角符号((1+2))、sin 之类的函数、变量。碰到不认识的字符就报错,整条候选消失。
| 写法 | 结果 |
|---|---|
calc("1+2*3") | 7 |
calc("(1+2)*3") | 9 |
calc("6/2") | 3(整数结果不带 .0) |
calc("1/3") | 0.3333333333333333 |
calc("10%3") | 1 |
calc("") | 空串(不报错) |
calc("1/0") | 报错,候选消失 |
最后两行是配剪贴板算式用的:calc(clip()) 在没复制东西时静默给空串,所以出厂那条 coca = $CC("={calc(clip())}", type(calc(clip()))) 只会显示一个 =,而不是整条消失。
进制:num(s, base)
s 的进制由前缀自动识别,base 是输出进制:
| 写法 | 结果 |
|---|---|
num("0xff", 10) | 255 |
num("255", 16) | ff(小写,不带 0x) |
num("10", 2) | 1010 |
num("0b1011", 10) | 11 |
num("0o17", 10) | 15 |
num("-255", 10) | -255 |
输入前缀:0x/0X 十六进制、0o/0O 八进制、0b/0B 二进制、无前缀按十进制,可带正负号,首尾空格自动去掉。base 只能是 2 / 8 / 10 / 16,其它值报错;只处理整数,num("3.5", 10) 报错。
负数只在转十进制时正常
转成 2 / 8 / 16 进制时,负数按 64 位补码输出而不是带负号:num("-255", 16) 得到的是 ffffffffffffff01,num("-10", 2) 是一长串 1。要给负数换进制,请自己把符号拆出来处理。
查手册:help(name)
help("date") 返回 date 的一行内置简介。配上剪贴板就是一条随手可查的手册:
cohelp = $CC("{default(help(clip()), "复制一个函数名再查")}", type(help(clip())))动作函数(有副作用)
| 函数 | 副作用 |
|---|---|
type(s) | 经 TSF 上屏文本,不污染剪贴板 |
open(target) | 打开 URL / 文件 / 程序(http(s) 走浏览器,其他走 ShellExecute) |
proc.run(cmd, ...args, cwd?, verb?, show?) | 异步启动进程,三个具名参数见下 |
proc.shell(cmdline, cwd?) | 执行 shell 命令,cwd 见下 |
key.tap(combo) / key.seq(...combos) | 单次按键 / 按键序列 |
key.hold(combo) / key.release(combo) | 按下 / 抬起(须成对使用) |
key.type(text) | Unicode 直输,绕过键盘布局(走 SendInput,非 TSF)。文本里的换行与制表符会发成回车 / Tab 按键 0.118 新增 |
clip.copy(s) / clip.paste() | 写入剪贴板 / 模拟 Ctrl+V |
dict.add(s) / dict.add(s, code) | 加词到用户库(不给 code 就按当前方案的取码规则推导) |
ime.toggle(target) | 切换状态:cn-en / fullshape / layout / candwin / s2t / preedit / toolbar |
ime.schema(id) | 切换并持久化方案,如 "wubi86" / "pinyin" |
ime.theme(name) / ime.theme_cycle(dir?) | 切换主题 / 循环切换(dir ∈ next / prev) |
ime.undo_commit() | 撤销上屏:删除最近一次上屏的内容,见下文 |
ime.pair(left, right, jump?) | 上屏配对符号:光标落两段之间,可用跳出键越过右段,见下文 |
setting.open(page, args?) | 打开设置的指定页(page 见下;args 为附加命令行参数) |
web.search(engine, q) | 打开搜索页,engine ∈ baidu / bing / google / zdic,见下 |
config.set(key, value) / config.toggle(key) | 写入配置 / 循环切换枚举或翻转布尔(返回新值) |
wind.cli(...) | 调用命令行子命令,见下文 |
ui.toast(text, kind?, color?, pos?, ms?) 0.121 新增 | 弹一条桌面提示,见下文 |
type(s) 是唯一被特殊对待的名字:它只能作为 $CC 的动作直接写出来,不能嵌进别的函数里、也不能出现在显示名中,且只接受 1 个参数、不接受具名参数。help("type") 也查不到它。别和 key.type 搞混——后者是模拟键盘直输,走的是另一条路径。
web.search(engine, q) 的 engine 不区分大小写、首尾空格会被去掉;q 为空时不会打开浏览器,而是报错——避免开出一个空搜索页却让人以为命令没执行。zdic 走的是汉典的词条页而非搜索参数。
工作目录:cwd=
被启动的程序总有一个确定的工作目录,规则如下:
| 写法 | 工作目录 |
|---|---|
proc.run("D:\\Dict\\dict.exe") | D:\Dict(程序自己所在的目录) |
proc.run("notepad.exe") | 用户主目录(%USERPROFILE%) |
proc.run("x.exe", cwd="D:\\Data") | D:\Data |
proc.shell("dict -q 词") | 用户主目录 |
proc.shell("dict -q 词", cwd="D:\\Dict") | D:\Dict |
不写 cwd 时,proc.run 默认取被启动程序所在的目录——等同于你在资源管理器里双击它。靠相对路径找数据文件的程序(各类词典、绿色版工具)正是按这个前提写的,所以多数情况下不需要写 cwd。
proc.shell 是把整条命令行交给 shell,认不出目标程序,所以默认只能落到主目录。命令里带相对路径就必须显式写 cwd。
cwd 支持内部目录变量,也支持内插:
# 用输入法自己的目录
cotool = $CC("[跑工具]", proc.run("${APP_DIR}\\tools\\t.exe", cwd="${APP_DIR}\\tools"))
# 查词:词库在程序目录下,靠相对路径加载
codc = $CC("查 {last(1)}", proc.run("D:\\Dict\\dict.exe", "{last(1)}", cwd="D:\\Dict"))`cwd` 写了个不存在的目录会被忽略
此时程序仍会启动,但工作目录退回默认值,只在日志里留一条警告。之所以不直接失败,是因为你的目的是启动程序而不是校验路径——但这也意味着 cwd 路径写错时表面上一切正常,只是程序又找不到它的数据了。排查这类问题先检查路径拼写和反斜杠。
管理员运行与窗口状态:verb= show=
只对 proc.run 有效,且只在 Windows 生效(macOS 会忽略并在日志留一条说明)。
verb | 效果 |
|---|---|
| 省略 | 该文件类型的默认动作 |
open | 打开 |
runas | 以管理员身份运行(会弹 UAC 提示) |
edit / print | 编辑 / 打印 |
explore / properties | 在资源管理器中浏览 / 打开属性对话框 |
show | 效果 |
|---|---|
省略 / normal | 正常窗口 |
min | 最小化启动,且不抢焦点 |
max | 最大化启动 |
hidden | 不显示窗口 |
# 以管理员身份跑一个维护脚本
coadm = $CC("[管理员运行]", proc.run("D:\\tools\\fix.exe", verb="runas"))
# 后台静默启动,不打断当前打字
cobg = $CC("[后台同步]", proc.run("D:\\tools\\sync.exe", show="hidden", cwd="D:\\tools"))取值必须是上表里的小写词,写错(含大小写不符,如 verb="RUNAS")会直接报错并列出合法取值——这个校验在所有平台一致,所以在 macOS 上写错也会当场发现,不会等到换台 Windows 机器才炸。
`hidden` 不会让程序变成后台服务
它只是不显示窗口。程序本身如果会弹对话框或自己创建窗口,照样会出现;反过来,一个需要你操作的程序设了 hidden 就变成了看不见摸不着的进程,只能去任务管理器结束。
打开设置页:setting.open(page)
page 是目标页的规范 id,直接跳到对应标签页:
page | 页面 |
|---|---|
schema | 方案 |
input | 输入 |
keys | 按键 |
ui | 外观 |
dict | 词库 |
advanced | 高级 |
about | 关于 |
import | 不是独立标签页:切到高级页并弹出导入对话框,同时读一次剪贴板(用于配置分发包) |
""(空串) | 默认页 |
cost = $CC("打开设置", setting.open(""))
codc = $CC("词库管理", setting.open("dict"))未知 id 会被设置端忽略并落到默认页。
附加参数:setting.open(page, args) 0.113 新增
第二个参数是原样直通给设置工具的命令行参数串,写法与设置工具的命令行参数完全一致。最常用的是直接落到某个方案的某类词库数据:
codw = $CC("五笔用户词库", setting.open("dict", "--schema=wubi86 --type=user-dict"))
cods = $CC("五笔候选调整", setting.open("dict", "--schema=wubi86 --type=shadow"))
copy = $CC("拼音词频", setting.open("dict", "--schema=pinyin --type=freq"))输入法只负责转交,不解析这串参数——设置工具支持什么就能写什么,新版本加的参数无需等输入法跟进。参数不认识或方案不存在时,设置工具会退到最接近的位置并给出提示,不会打不开。
含空格的值要自己加引号
参数串会被重新切分成多个参数,--text=你 好 会断在空格处。含空格的值请写成 --text="你 好"。
配对符号:ime.pair(left, right, jump?)
上屏 left + right、光标落在两段之间,并把这一层压入配对状态——之后按跳出键(在设置里配的 Tab / Enter)就能越过右段,和自动配对打出来的括号完全一样。
cojk = $CC("「」", ime.pair("「", "」"))
cozs = $CC("注释", ime.pair("<!--", "-->"))两段都可以是任意文本,不限于单个符号:
输入后: <!--|-->
按 Tab: <!---->|jump:跳出时光标右移的格数。 省略时按右段的字符数推导("-->" → 3),一般不用写。它存在是因为跳出靠合成方向键,而"一次右键越过多少内容"是宿主行为——多数宿主一次跨整个 emoji,少数按编码单元走。若在某个程序里发现跳出后光标位置差了几格,用它手动校准:
cozs = $CC("注释", ime.pair("<!--", "-->", jump=3))分级跳出:同一条词条里写多个 ime.pair 即可,无需额外语法。后写的是内层,跳出时也先出内层:
cokh = $CC("(【】)", ime.pair("(", ")"), ime.pair("【", "】"))
输入后: (【|】)
按 Tab: (【】|) ← 出内层
按 Tab: (【】)| ← 出外层受「标点配对」总开关约束
ime.pair 跟着设置 → 标点配对的开关走(中文模式看中文配对、英文模式看英文配对)。关掉时它退化为纯上屏:整串照常上屏,但光标落在末尾、也不能跳出。
另外,「输入右符号跳出」对它不生效——那条路径只认单个标点按键。ime.pair 压入的配对只能用 Tab / Enter 跳出。
反查剪贴板里的字 0.117 新增
平时的反查要先把字打出来,才能在悬停提示里看它的编码和读音。碰到不认识的字(从网页、 PDF、聊天记录里看到的),这条路走不通——你根本不知道怎么打它。
dict.rev 反过来走:先复制,再查。
出厂已带 cofc,直接可用:
cofc = $CC(default(dict.rev(clip()), "剪贴板反查(需先复制文字)"), type(dict.rev(clip())))复制一个「我」,切回输入法打 cofc,候选就是:
我: q/trn/trnt wǒ编码给的是全部码位(简码在前),因为反查回答的是「这个字怎么打」——q 这样的简码才是
最有用的答案。编码来自码表的反查索引,不是按取码规则算出来的,所以给出的码一定打得出来。
选中即上屏这行文字。
编码为空是正常的
dict.rev 查的是当前主码表。纯拼音方案下没有形码可查,编码段自然是空的,只出读音。
查第几个字
默认查第 1 个字。dict.rev(s, n) 查第 n 个(1 起);n 超出字数返回空串,不报错。
想一次看多个字,用 $SS 列若干条、各查一个:
cofd = $SS("反查", "{dict.rev(clip(),1)}", "{dict.rev(clip(),2)}", "{dict.rev(clip(),3)}")写几条就是最多查几个字。剪贴板不足位数时,多出来的那几条自动不出现,不会留空白候选。
换版式:format=
版式用候选注释的同一套 ${...} 模板语法,
变量名也一样,学一次即可:
| 写法 | 结果 |
|---|---|
dict.rev(clip()) | 我: q/trn/trnt wǒ(默认 ${char}: ${code_all} ${pinyin}) |
dict.rev(clip(), 1, format="${pinyin}") | wǒ |
dict.rev(clip(), 1, format="${code_all:/}") | q/trn/trnt |
dict.rev(clip(), 1, format="${char} ${chaizi}[${chaizi_code}]") | 形如 你 亻尔[wqiy](字根用私用区字形,需装字根字体才显示) |
可用变量:${char}(被查的字)、${code_all}(全部码位)、${code}(只要最长的全码)、
${pinyin}、${chaizi}、${chaizi_code}、${dict}(注释词库释义)。
模板里要写 `${}` 不能写 `{}`
{...} 在命令栏字符串里是求值插值,format="{code}" 会去调用 code() 函数(触发编码),
静默得到完全不相干的结果。变量一律写 ${code} 这种形式。
为什么用 $CC 而不是直接写模板
直接写 {dict.rev(clip())} 也能用,但候选的显示文本就是上屏文本,两者绑死。于是剪贴板
为空时只有两个都不好的结果:整条候选消失(分不清是没复制还是功能坏了),或者把提示语本身
打进文档。
$CC 把两者拆开:display 只管显示(查不到时用 default 回落到提示语),上屏走
type(dict.rev(clip()))(查不到时求值为空,选中什么也不打)。
撤销上屏:ime.undo_commit()
按上屏历史删除最近一次上屏的内容——记录上屏了几个字符,就往前删几个。连续触发可逐条回退。
coun = $CC("撤销上屏", ime.undo_commit())配合快捷键使用更顺手:把这条短语绑一个好按的编码,或用 config.set 给它挂全局热键。
行为细节:
- 正在打字时不动作 —— 输入缓冲非空(组合区有内容)时直接忽略,不会误删
- 无上屏历史时删除 1 个字符
- 每撤销一次就从历史中弹出一条,推送失败会回滚该条记录
撤销不校验光标前的内容
它只按历史记录的字符数往前删,不检查光标前实际是什么。上屏后如果你在输入法之外移动过光标、或改过文本,撤销会删错位置的内容。
另外计数按 UTF-16 单位,在使用退格兜底的宿主里遇到 emoji 可能多删。
调用命令行:wind.cli(...)
把命令行工具的任意子命令挂到短语上,用主程序自身执行。两种传参形式:
# 单参形式:按空白拆分
cocr = $CC("重建词库缓存", wind.cli("schema rebuild"))
codf = $CC("停用辅码库", wind.cli("schema dict disable wubi86 fl"))
# 多参形式:逐个原样传递,用于含空格的路径
cobk = $CC("备份", wind.cli("backup", "create", "D:\\我的 备份\\wind.zip"))
# 配合内部目录变量,免去硬编码路径
cobu = $CC("备份到用户目录", wind.cli("backup", "create", "${USER_DATA}\\backups\\wind.zip"))含空格的参数(尤其是文件路径)必须用多参形式,否则会被空白拆散成多个参数。路径里的反斜杠仍按短语字符串规则写成 \\(见路径里的反斜杠要写两个)——这一层与 CLI 无关,是短语先解析、再把结果交给 CLI。
照搬终端命令的两道坎
从终端里跑通一条命令、再原样贴进 wind.cli(...),是最常见的写法,也最容易连撞两道坎。拿一条真实的命令举例——把竖排候选的注释模板设成 {${chaizi}}{ [${chaizi_code}]}{ ${pinyin}}(这是模板本身的样子,下面写进短语时还要再转义一层):
# ❌ 错误:整条命令行塞进一个参数,大括号也没转义
[[phrases]]
code = 'comm'
text = '$CC1("切注释全", wind.cli("config set ui.candidate.comment_template_vertical {${chaizi}}{ [${chaizi_code}]}{ ${pinyin}}"))'
# ✅ 正确:按空格拆成 4 个参数,大括号写成 \{ \}
[[phrases]]
code = 'comm'
text = '$CC1("切注释全", wind.cli("config", "set", "ui.candidate.comment_template_vertical", "\{${chaizi}\}\{ [${chaizi_code}]\}\{ ${pinyin}\}"))'
weight = 1000
position = 1第一道:参数得你自己拆开。 单参形式只是把整串按空白切一刀(每遇到空白就分出一个参数),它只适合参数本身不含空格的命令。上面那条模板串自己带空格,切完就散成了六七个参数,CLI 收到的东西跟你写的完全不是一回事。只要有任何一个参数可能含空格,就把命令行的每一段写成独立的字符串参数——包括 config、set 这些子命令名。
第二道:\{ 和 \} 要转义。 字符串里的 {...} 是内插语法,{${chaizi}} 会被当成「求值这个表达式」而不是字面的大括号。要输出大括号本身,必须按转义表写成 \{ \}(见路径里的反斜杠要写两个)。
${chaizi} 这类 ${名字} 不用转义:只有内部目录变量那三个名字会被替换,其余原样穿过,正好交给 CLI。
两道坎的失败现象不一样,可以据此判断撞的是哪道
| 症状 | 撞的是 |
|---|---|
| 打出编码后候选里根本没有这条短语,也没有任何报错 | 第二道。大括号没转义 → 整条短语解析失败被静默丢弃 |
| 候选正常出现、选中后也没提示,但配置没变 | 第一道。参数没拆开 → CLI 收到错的参数,而 wind.cli 不回收错误 |
两种都不会弹错误框,所以别按「有没有报错」判断对错,要按上表对症状。
写在 TOML 里请用单引号
上面的 text = '...' 用的是 TOML 的单引号字面串,里面的 \ 原样保留,写法与设置页输入框、词库文件里完全一致。若改用双引号 text = "...",TOML 自己会先吃掉一层转义,而 \{ 在 TOML 里是非法转义,整个文件都会读不进去。
执行完给出反馈 0.121 新增
wind.cli 会等命令跑完,按退出码把结果弹成 toast:成功文案取 CLI 自己打印的最后一行(「✓ 用户词库: 新增 12 · 更新 3」),失败取 stderr 的首行。三个具名参数管这件事,都不影响命令本身:
| 参数 | 默认 | 说明 |
|---|---|---|
toast | on | 执行结果提示。on = 成败都弹;off = 都不弹(此时也不等待);error = 只在失败时弹 |
ok | 省略 | 成功时的自定文案。省略则用命令自身输出的最后一行,不自造第二套说法 |
wait | 10000 | 等待命令结束的毫秒上限。0 = 不等待,此时无结果可报。超时会给一条「命令仍在执行,未能确认结果」的中性提示,迟到的真结果不再补弹 |
# 导入用户词库,失败时才提示
coim = $CC("导入词库", wind.cli("dict import D:\\words.txt", toast="error"))
# 服务侧本来就会弹提示的子命令,把这一层关掉,免得撞车
cort = $CC("重启服务", wind.cli("restart", toast="off"))
# 自定成功文案,并把等待放宽到 30 秒
corb = $CC("重建缓存", wind.cli("schema rebuild", ok="缓存已重建", wait="30000"))哪些子命令该设 toast 为 off
服务侧本身就会弹提示的那些(restart / config set)。撞不撞车只有写词条的人当场知道,输入法不去维护一张子命令名单。
从短语调用时输入法服务必然在线,所以 schema / dict / phrase / backup 这些需要在线的子命令都能正常连上。
改配置优先用 config.set,不要绕 wind.cli
config.set(key, value) 与 wind.cli("config set ...") 效果相同,但前者在进程内直接生效、错误就地返回,后者要另起一个进程再把结果读回来。改配置一律用 config.set / config.toggle;wind.cli 留给没有对应短语函数的子命令。
弹一条提示:ui.toast(...) 0.121 新增
# 最简形式
$CC("提示", ui.toast("已完成"))
# 指定类型与位置
$CC("提示", ui.toast("导入完成", kind="success", pos="top_right", ms="4000"))
# 自定强调色
$CC("提示", ui.toast("注意", color="#FF8800"))除文案外全部具名、与顺序无关:
| 参数 | 默认 | 取值 |
|---|---|---|
kind | info | info / success / error,决定强调条颜色 |
color | 省略 | 自定强调色 #RRGGBB 或 #RRGGBBAA |
pos | bottom_center | center / top / top_center / bottom_center / top_left / top_right / bottom_left / bottom_right |
ms | 2500 | 显示毫秒数 |
底色与文字色仍归主题,不开放到词条——只有强调色可以在这里改。
kind 与 color 只能给一个
两者都是「强调色」的写法,同时出现只能是写错了。此时会直接报错而不是猜一个——静默取一个会让另一个看起来「没生效」。
同理,pos 把 top_right 写成 right、ms 写成 "5秒",都会当场报错,不会静默降级。
外部脚本与计划任务也能弹:命令行 wind_input ui toast "文案",见命令行工具。
按键 combo 格式
key.* 的 combo 为 [修饰键+]...主键,大小写不敏感,+ 分隔。修饰键:Ctrl、Shift、Alt、Win(各有别名如 Control / Menu / Super)。主键支持常见控制键(Enter、Tab、Escape、Home、End、Up 等)、功能键 F1–F24、字母数字键,以及标点键(Comma、Period、Slash 等)。
key.tap("Ctrl+C") # 复制
key.tap("Alt+F4") # 关闭窗口
key.seq("Home", "Shift+End", "Delete") # 删除整行
key.type("Hello, 世界") # Unicode 直输未列出的特殊键用 vk: 前缀指定 Windows 虚拟键值,0x 前缀为十六进制、无前缀为十进制,有效范围 0x01–0xFF:
key.tap("vk:0x5D") # 右键菜单键(VK_APPS)
key.tap("vk:0x60") # 数字小键盘 0
key.tap("Shift+vk:0x5D")标点键 VK 码基于美式布局
标点符号键的 VK 码按美式 US 键盘布局定义,非 US 布局下物理键位可能不同,此时改用 vk: 数值码指定精确虚拟键值。
macOS 上 key.* 需要「辅助功能」授权
key.tap / key.seq / key.hold / key.release 靠向前台应用注入键盘事件实现,macOS 把这类能力归在辅助功能权限下。未授权时系统会静默吞掉事件——命令看起来执行了,按键却没发出去。到 系统设置 → 隐私与安全性 → 辅助功能 打开「清风输入法」即可,见 macOS 版 · 授权辅助功能。
key.type(文本直输)与 clip.paste 走的是输入法自己的文本插入接口,不需要这项授权。
行为细节
- 权重:命令短语与普通短语一样默认权重 1000。命令用的精确英文编码极少与自然候选撞码,默认值通常已足够靠前;拼音方案下更应选一个不与自然词碰撞的编码,而非堆高 weight。
- 自动上屏 / 顶码:纯文本命令(只产出文本)与短语一样可自动上屏、顶码上屏;含副作用的命令不自动上屏,始终等待手动选择,被顶屏时异步执行动作。只有短语的编码(如
date)不参与顶码,也不会被"满码空码清空"误清。 - 不污染历史:
last()只记录真正上屏的文本。有type()的命令进入历史;仅副作用命令(open/proc.run等)不进入,避免显示文字污染下一次last()。 - 显示与执行是两次独立求值:显示名在候选生成时算一遍,动作在选中时再算一遍。所以
uuid()、now()写在两处会得到两个值。想"切一次并显示新状态",让显示名用config.get、动作用config.toggle——把config.toggle写进显示名不会切换两次,而是整条候选直接失效(显示名不允许有副作用)。
临时拼音下不查快捷短语
进入临时拼音模式后不查询快捷短语层,输 zzbd 得到的是拼音候选而非字符组导航。但词库条目内嵌的 $ 语法在临拼下仍会正常展开与执行。
相关阅读
对这篇文档有疑问,或发现内容有误?
欢迎到文档仓库提 issue,写明问题时附上本页链接即可。