这是一个“Rust 重写 Rime”的起步骨架:先做跨平台 CLI(逐键实时刷新),核心逻辑与 CLI 严格解耦。
rime_core: 纯逻辑层(Session/Engine/InputEvent/UiState),不做任何 I/Orime_pinyin: 全拼切分(从仓库的test/generate_pinyin_syllables.js构建期生成音节+频次表)rime_dict: TSV 词典加载与查询(精确匹配 + 前缀补全;TSV 采用text<TAB>key<TAB>weight)rime_cli: 交互式 CLI(crossterm raw mode)
在仓库根目录:
cd rime_rs
cargo run -p rime_cli -- --dict ./rime_cli/asset/dict.tsv也可以用一个很小的 demo 词典验证“简拼”效果(例如输入 qs):
cargo run -p rime_cli -- --dict ./rime_cli/asset/dict.tsv交互说明(按行提交,std-only):
- 输入一行拼音后回车:展示 preedit + 候选列表
- 再输入
1-9选择候选;直接回车默认选1 - 如果有多个音节段,会进入多步选词:每次选择会推进
confirmed,直到全部段确认后一次性上屏 - 输入
0:上屏原串 - 输入
q:放弃本次选择 - 输入
:q:退出程序
engine:
# 一、处理各类按键消息
processor:
- ascii_composer # 处理英文模式及中英文切换
- recognizer # 与 matcher 搭配,处理符合特定规则的输入码,如网址、反查等 tag
- key_binder # 在特定条件下将按键绑定到其他按键,如重定义逗号、句号为候选翻页、开关快捷键等
- speller # 拼写处理器,接受字符按键,编辑输入
- punctuator # 符号处理器,将单个字符按键直接映射为标点符号或文字
- selector # 选字处理器,处理数字选字键、上、下切换候选定位、换页
- navigator # 处理输入栏内的光标移动
- express_editor # 编辑器,处理空格、回车上屏、回退键
# 二、识别不同内容类型,将输入代码分段
segmenter:
- ascii_segmenter # 标识西文段落
- matcher # 标识符合特定规则的段落,如网址、反查等
- abc_segmenter # 标识常规的文字段落
- punct_segmenter # 标识句读段落
- fallback_segmenter # 标识其他未标识段落
# 三、翻译特定类型的编码段为一组候选文字
translator:
- echo_translator # 没有候选字时,显示输入的原始代码
- punct_translator # 转换标点符号
- script_translator # 脚本翻译器,用于拼音等基于音节表的输入方案
- reverse_lookup_translator # 反查翻译器,用另一种编码方案查码
# 四、过滤输出结果
filter:
- simplify # 简繁转换
- unique # 过滤重复的候选字,有可能来自繁简转换处理各类按键消息
- 接收:由引擎响应按键
- 拒绝:输入引擎向操作系统反馈不做按键事件响应,由系统对该按键执行默认处理;
- 未识别:该按键不在当前
processor的处理范围内,请交由下一个processor继续处理。
优先级按照 processor 列表的顺序排列,接收按键的 processor 会针对该按键消息开展具体处理。
虽然看起来 processor 接点可以通过组合不同的 processor 实现输入引擎的全部功能,但为了将逻辑继续细分,RIME 又为引擎设计了另外三类功能组件。这些主键都可以访问引擎中的数据对象(输入上下文),并将各自处理的阶段结果存储在其中。
processor 最常见的处理方式,就是将按键对应的字符录入到上下文的输入码序列中。当输入码发生变化时,下一类组件 segmenter 便会启动一轮新的处理流程。
从输入引擎整体来看,以按键消息作为输入,输出内容包含三个部分:
识别不同内容类型,将输入代码分段
segmenter 即分段器,将用户连续输入的文字、数字、符号等不同内容,按照需要识别为不同格式的输入码,将输入码分成若干段分而治之。 这通过数轮代码段划分操作完成。每一轮操作中、一众 segmenter 分别给出起始于某一处、符合特定格式的代码段,识别到的最长代码段成为本轮划分的结果,而给出这一划分的一个或多个 segmenter 组件则可为该代码段打上「类型标签」;从这一新代码段的结束位置,开始下一轮划分,直到整个输入码序列划分完毕。
举例来说,【朙月拼音】中,输入 2012nian\,划分为三个编码段:2012 (贴 number 标签)、nian(贴 abc 标签)、\ (贴 punct 标签)。
那些标签是初步划分后判定的类型,也可能有一个编码段贴多个标签的情况。下一个阶段中,translator 会把特定类型的编码段翻译为文字。
translator 完成由编码到文字的翻译。
- 一是翻译的对象是分隔好的一个代码段。
- 二是某个
translator组件往往只翻译具有特别标签的代码段。 - 三是翻译的结果可能有多条,每条结果成为一个展现给用的候选词。
- 四是代码段可由几种
translator分别翻译、翻译结果按一定规则合并成一列候选。 - 五是候选项所对应的编码未必是整个代码段。用拼音敲一个词组时,词组后面继续列出单字候选,即是此例。
| input | tag | translator |
|---|---|---|
| 2012 | number | "2012" 、"二〇一二" |
| nian | abc | "年", "念", "拈", ... 、"nian" |
| \ | punct | "、", "" |
输入的代码划分为多个代码段、每段代码又可具有多组翻译结果;取各代码段的首选结果连接起来,就是预备上屏的文字「2012年、」。
且将以上所示的数据称为「作文」。这是一篇未定稿的作文,输入法界面此时显示预备上屏的文字「2012年、」,并列出最末一个代码段上的候选「、」及「\」以供选择。
有两款主力 translator 完成主要文字内容的翻译,其实现的方式很不一样:
script_translator也叫做r10n_translator修炼罗马字分析法,以「固定音节表」为算法的基础,识别输入码的音节构成,推敲排列组合、完成遣词造句。table_translator修炼传承自上世纪的码表功夫,基于规则的动态码表,构成编码空间内一个开放的编码集合。
拼音、注音、方言拼音,皆是以固定音节表上的拼写排列组合的方式产生编码,故适用罗马字分析法。仓颉、五笔字型这类则是传统的码表输入法。
如果以码表方式来写拼音输入方案,是怎样的效果呢?虽然仍可完成输入,但无法完全实现支持简拼、模糊拼音、使用隔音符号的动态调频、智能语句等有用的特性。
反之,以罗马字方式使用码表输入法,则无法实现定长编码顶字上屏、按编码规则构词等功能。在Rime 各发行版预设输入方案中,有一款「速成」输入方案,即是以 script_translator 翻译仓颉码,实现全、简码混合的语句输入。
概括起来,这是两种构造新编码的方式:罗马字式输入方案以一组固定的基本音节码创造新的组合而构词,而码表式输入方案则以一定码长为限创造新的编码映像而构词。
上一步已经收集到各个代码段的翻译结果,当输入法需要在界面呈现一页候选项时,就从最末一个代码段的结果集中挑选、直至取够方案中设定的页最大候选数。
每从结果集选出一条字词、会经过一组 filter 过滤。多个 filter 串行工作,最终产出的结果进入候选序列。