Commit Graph
28 Commits
Author SHA1 Message Date
AkaneIwasaandfanlusky 568d07f513 feat(windows): 添加中日英三语轮换 (#717)
* feat(windows): 添加中日英三语轮换

* fix(windows): 三语轮换改为快捷键开关并修复两端状态不同步

- 开关移到「快捷键 → 中英文切换」下的「允许中日英循环切换」,配置键改为
  [keybindings] trilingual_cycle;输入模式恢复为中文/日文,二者互不影响
- 循环请求带显式标记位和请求方自身的中英/日文状态,Server 据此计算目标语言;
  普通 VK_SHIFT 仍是中英切换,不再被误当成循环
- Server 开关在请求途中被关闭时仍回复,按中英切换处理,TSF 只丢弃后续排队按键,
  不再超时重连
- 已发出的循环回复未被应用(含 Superseded)时走完整重连,避免日文状态两端不一致
- 循环切换补上光标处状态提示和标点同步
- 切换语言时复用暂存的另一语言会话,不在按键路径上重建引擎和打开 msime.db
- 配置读取的语言与开关改为原子量;TrilingualCycleChanged 的能力过滤移到发送方
- 中日切换不再重发 TrilingualCycleChanged;结束组合被拒绝时不计入统计

---------

Co-authored-by: fanlusky <1730976608@qq.com>
2026-10-09 15:44:41 +08:00
fanlusky 06fb8f87ba fix(server): 关闭顶字且混输关闭时第五码仍走顶字,避免丢字与两端分叉
混输关闭时码表答得出的完整四码保留四码上限,关掉顶字后第五个字母会被 Server
裁掉,而 raw 预编辑样式的 TSF 已经显示了它,两边缓冲就此分叉。顶字开关改为只在
混输开启时生效(字母并入混输串),混输关闭时仍按顶字上屏首选。

同时修正第四键注释(两个开关作用在不同按键上)、设置页与配置模板的说明文字,
以及 settings_app.cpp 新增行的缩进。
2026-10-08 14:10:36 +08:00
Jia Sui 3d31a40a74 feat(server): 五笔四码自动上屏与顶字各加一个设置开关
「四码唯一时自动上屏」和「第五码将首选上屏」此前都是无条件行为,用户遇到
误上屏没有任何出口。四家主流五笔(搜狗/QQ/百度/微软)都提供这两个开关且
默认开启,这里补上并保持默认开,升级用户零行为变化。

- input.wubi_four_code_auto_commit(默认 true)门控四码唯一自动上屏
- input.wubi_fifth_code_top_commit(默认 true)门控真顶字
- 开关只落在 Server 判定层,引擎完全不知道:WubiInputOptions 不加字段,
  避免测试里大量聚合初始化静默取默认值
- 顶字关闭后第五个字母落回引擎既有四码上限(混输关=裁掉,混输开=并入混输
  串),与搜狗 PC 一致,不补新分支
- 顺带修正 input_key_policy.h 与 event_listener_keys.cpp 中把不存在的开关
  当既成事实引用的注释,以及两个把「无条件」写进名字的用例
2026-10-08 13:24:25 +08:00
Ehtasham-Yasin 0fe601e259 feat(server): submit converted shuangpin with Shift+Enter 2026-10-06 15:09:29 +02:00
Lu Fan(SonnyCalcr) b391fd616b feat: T 模式支持输入指定的日期和时间 (#626)
Shift+T 后面除了 rq / sj / xq 唤醒词,还可以直接写数字:

- YYYYMMDD、YYYY/M/D、M/D(年份取今年)出那一天的日期格式,星期按那一天算
- YYYY/M 出年月格式(新增 date:ym_* 五种)
- h:mm、h:mm:ss 出时间格式,写了秒才给带秒的格式
- YYYYMMDDhh[:mm[:ss]] 出日期加时间的格式

指定的日期时间沿用唤醒词的格式 ID,学到的顺序、置顶、固定位置跟着走。

输入形状放在 engine/contracts/date_time_input.h,TSF、Server、引擎共用:
数字、/ 和 : 只在还能接成某种形状时才进输入串,接不上的数字仍是选词键,
/ 和 : 仍是标点,Shift+数字始终选词。小键盘数字同样可以输入。

Refs #531
2026-10-05 03:42:12 +08:00
fanlusky 995174af47 feat(server): 造词时 Esc 可只清除未选拼音、保留已选词
新增 input.escape_keeps_selected_word(默认 false)。开启后,造词中按 Esc
只清掉还没选的拼音,保留已选的词,候选窗只显示该词作为 preedit;
再按一次 Esc 整体取消(小狼毫风格)。R 模式下仍整体取消。

- server:HandleCreatingWordEscape 以 CompositionRestored 帧回复
- windows:_HandleEscapeCancel 读取回复并应用,超时或不支持时回退到原取消
- ui-html:设置页「输入」增加开关
- 配置模板、两个设置宿主同步新增该键
2026-09-29 01:58:38 +08:00
Jia Sui 6506a7a07f fix(server): 退格删空 raw 不再吞掉已选词
输入 nihaoya、选「你好」、再敲一个字符后连按退格,删掉最后一个 raw 字符
的那一次会把造词态连同已选词一起清掉(尾部「raw 空即清」规则),用户看到
「你好」被直接删掉。这条规则的前提是「客户端会随空 raw 取消组合」,但协商
过 CompositionRestored 的客户端会应用帧、把组合留在「只有已选词」的状态
(与 R3 段删除一致),此时清态反而丢字。

- 新增 ShouldKeepCreatingWordAfterRawEmptied:仅协商过组合恢复、非 UILess
  且有快照时保留造词态与快照;普通退格与 Ctrl+Backspace 段删除共用该谓词。
- 普通退格删空 raw 时置 keep:帧里 word 非空,DLL 走
  _ApplyCreatingWordPayload 的「只有已选词」分支继续组合,不再 _HandleCancel;
  下一次退格由「raw 为空时退格只能是撤销」把最近一次选词撤回,而不是丢整句。
- 未协商与 UILess 客户端维持原行为(本地取消 + 清态),避免两侧状态分叉。
- 测试补 deleting_the_last_raw_character_keeps_the_created_word。
2026-09-25 15:59:05 +08:00
Jia Sui a596f8b1bc fix(server): 撤销重高亮只在候选页前缀未变时应用
选词快照记的候选绝对位置对应的是选词那一刻用户眼前的候选页,而撤销
重建的页面由恢复后的 raw 与光标前缀决定:选词之后用 Del 或光标编辑改
动过后缀时,重建页装着另一批候选,旧位置会把高亮落到不相干的项上。

- 新增 FanyImeIpc::NormalizeCandidatePagePrefix,按引擎的页身份把光标
  前缀折成小写(refresh_prefix_candidates 的同一套归一化)并夹到 raw 长度。
- 快照多带一个 selected_page_prefix,取在 advance_composition_after_selection
  之前(那之后引擎已经换成结算后的 raw,不再是用户看到的那一页)。
- 一次性交接从裸 int 改成 RestoredSelectionHighlight(位置 + 页前缀),
  HandleImeKey 重建页面后先比较前缀再套用位置,不一致就只发布新页不高亮。
- 测试更新位置交接用例并覆盖归一化谓词的空串、越界、大小写与改短后缀。
2026-09-25 09:43:38 +08:00
Jia Sui eb6c6f84ce fix(server): 造词态被退格清空时也回帧,避免 TSF 挂起烧满超时
答帧判定用的是按键之后的 creating_word.active,而退格删掉最后一个
raw 字符时尾部会清掉造词态与快照,形态随之消失:这样一次退格不发
帧,而 DLL 的 hold 只看按键前的镜像 word_for_creating_word,仍然
非空,于是白等满 50ms 才回退本地删除,正是本次改动想消除的那类等待。

- HandleImeKey 在 ApplyCompositionEditKey 之前抓一份客户端可见的
  形态,答帧条件改为「撤销过 或 按键前形态 或 按键后形态」,用新的
  ShouldAnswerRetreatBackspace 把这条不变量写成可测谓词。
- 该结局的 payload 是空 raw + 空 word,DLL 侧按 _ApplyCreatingWordPayload
  的空 word 分支立即 _HandleCancel,语义与超时后一致,只是不再等。
- 候选窗收起的判据从「raw 空且造词态还在」放宽为「raw 空」:造词态
  被这次退格终结时同样不能落入重建分支,否则会给空输入发布兜底假候选。
- 测试补 retreat_backspace_reply_survives_the_key_that_ends_the_word。
2026-09-25 09:37:57 +08:00
Jia Sui c06dc17ae1 feat(server,windows): 造词中的退格优先撤销最近一次选词
把"退格撤销上次选词"从旧的两条窄触发(剩余输入只剩一个字符、或光标
正好停在这个词后面)放宽为造词态下的默认行为,对齐 Rime/微信:只要
造词还活着且有可选回的快照,首个退格先撤销最近一次选词,剩余输入
长度与光标位置不再参与判定。

- 选后编辑锁(selected_before_editing):选词后又输入了新字符,退格
  继续删除这些新输入,而不是把选词从脚下抽走;只标记最新快照,与
  Rime 一致。空输入例外——没有字符可删时退格只能是撤销,否则会退回
  #35 的整句丢弃。
- 恢复改为前插:被选词消耗的拼音插回未删除的后缀之前,光标随恢复
  长度平移("输入变长、光标不动"),不再整串覆盖 raw 而丢掉后缀。
- 撤销后重新高亮:选词快照记下所选候选的绝对位置,撤销重建候选页
  后把高亮放回同一项(一次性消费),下次按键可直接改选,不用再找。
- 客户端契约拆成两层:DLL 只凭造词镜像(word_for_creating_word)
  挂起等待回复,看不到 Server 侧的快照与编辑锁,因此新增
  HasRetreatBackspaceShape 描述客户端可见形态,Server 对形态内每个
  退格都必须答帧(撤销、锁定删除或空历史空操作),否则挂起白烧满
  50ms 超时;ShouldRetreatCreatingWordSelection 只在真能改写状态时
  为真。
- 撤销改为纯状态操作,引擎序列、候选重建与光标前缀由尾部统一执行
  一次,替换原先每次撤销都额外跑一遍完整候选查询的 helper。
- DLL 侧:已消耗请求槽位的回复(或光标 0 处的 Normal 应答)之后
  丢弃 request id,让 worker 回退本地读取,而不是为已消失的槽位
  再烧一个 50ms 超时。

Signed-off-by: Jia Sui <jsfaint@gmail.com>
2026-09-24 20:52:12 +08:00
Jia Sui 3821f3ede8 fix(server): 补回合并时丢失的测试用例花括号 2026-09-24 09:04:27 +08:00
Jia Sui 5b0be4e71d Merge branch 'develop' into feat/caret-driven-resegmentation 2026-09-24 08:45:54 +08:00
Jia Sui 4f26f35ac3 fix(server): 光标移回串尾时从引擎重建候选页
RefreshCandidatePageUi 只重排旧 candidate_ui.items、不重读引擎,而结算读的
正是 items。回串尾时引擎已整串重算,页面却停在旧前缀候选:选词只消费前缀,
ni'hao'ya 回串尾得「你好ya」而非「你好呀」。提取
ResolveCaretArrowCandidatePublish(前缀空收起;门控启用含回串尾一律
PrepareCandidateList 重读并复活窗口;未启用维持只刷页面,R7/AC8 零差异)。
2026-09-22 18:49:37 +08:00
Jia Sui fceb0692cb fix(server): 造词帧的 caret 字段只发给协商过组合恢复的客户端
未协商的旧版 DLL 会把第 2 个制表符后的整个尾部当 display_preedit,
追加的 caret 字段会污染一次 inline preedit。提取
ShouldCreateWordFrameCarryCaret 谓词(协商 && caret<剩余串),
未协商时帧输出与旧 Server 字节一致;CompositionRestored 帧本就只在
协商路径产生,无需改。
2026-09-22 16:39:50 +08:00
Jia Sui 096c0ccb10 feat(server): 前缀重算接线与仅前缀选词结算
编辑键后按门控(协商 composition-restore 且非 UILess、非英文/特殊模式)
把 caret 喂给会话做前缀重解;caret 回到串尾显式清 caret_,串尾行为
与现状一致。前缀选词复用 advance_composition_after_selection 的头部
消费语义:剩余 raw 即后缀、caret 归 0,NeedToCreateWord 帧带契约既有的
可选 caret 字段(协议零新增);学习/调频/AI 上下文走既有链路。
空前缀时收起 Server 自有候选窗并守卫假候选兜底与 ShowCandidate。
门控谓词与边界补真值表单测;回归测试钉住「移回串尾恢复整串解码」。
2026-09-22 16:09:05 +08:00
Jia Sui 536d0a95ef feat(windows): 五笔四码后继续输入不丢字(顶字)
四码(不论是否唯一)之后继续敲字母时,先上屏该码的首选候选,再把这个字母
保留为下一次组合的开头。根因是两侧对「组合里有几个字母」认知不一致:TSF 的
本地按键缓冲跟着敲键涨(raw 预编辑样式下 preedit 就是它),服务端的五笔 raw
被引擎裁在四码;空格提交时组合文本被整段替换成候选并清掉本地缓冲,多敲的
字母随之丢失。

- 新增 worker 消息 CommitCandidateAndContinue(27),payload "<consumed>\t<text>":
  TSF 按服务端给出的消费数裁**自己**的组合缓冲,而不是套用服务端按旧视图算好
  的剩余串——快打时用户已多敲的字母因此不会被覆盖
- TSF 处理序列全部复用既有原语:_InsertTextToComposition + _HandleCompleteCommitFirst
  真提交,_StartComposition + AddVirtualKey + _HandleCompositionInputWorker(NO_REQUEST_ID)
  重建余下字母的组合;无组合时退化为直写
- 服务端顶字不看自动上屏开关:开关只决定唯一码要不要多敲一键才上屏,
  不决定丢不丢输入;自动上屏那条路也改用同一消息,顺带收掉快打竞态
- 引擎新增 wubi_four_code_is_complete(完整四码、码表作答、不要求唯一;
  空码不算——没有候选可提交时不触发顶字)

已知限制:光标在组合中间时的改码行为维持现状;空码继续敲字母仍会被忽略。
2026-09-22 09:23:25 +08:00
Jia Sui 5d1a9a6cc2 feat(server): 五笔四码唯一自动上屏
五笔敲满四码且码表只答一个候选时不再需要空格:第四个字母落下即上屏,下一个
字母直接开新组合。行为无条件生效,不设配置开关:业界默认开启(微软五笔、
搜狗五笔、极点、Rime 主流五笔方案),唯一候选没有第二选择,确认空格是纯冗余;
而一条开关要牵动配置五处同步,换不来可形成预期的行为差异,不值。

- engine 只报告事实(wubi_unique_four_code:完整四码、码表作答、候选恰好一个),
  是否上屏由 Server 决定;判定收在 input_key_policy.h 的纯函数里,随 portable
  目标进 ASan/UBSan;组词(creating_word)激活时组合保持打开,唯一四码不会把
  正在拼的词截断上屏
- 提交复用 ProcessSelectionKey,与空格上屏同一套记账;但只能走 worker 管道推送
  CommitCandidate:字母键在默认 raw 预编辑样式下不读请求-回复管道,回一帧 Normal
  既不会上屏,还会在 TSF 侧留下永不释放的回复帧,而 Server 已按 Normal 清掉组合
- 上屏前按当前组合同步重建候选页:候选页异步发布,提交路径读的 ui.items /
  page_words 否则可能还停在第三码那一拍
- 非唯一四码不做顶字上屏:一个按键只能回一条管道消息,真顶字需要扩 IPC 协议
2026-09-22 09:23:24 +08:00
Jia Sui 2576986881 feat(windows): Ctrl+左右键按分词移动光标
组词中 Ctrl+Left/Right 按一个字的拼音(音节段)移动光标,
与 Ctrl+Backspace 的删除粒度共用同一套引擎分段边界。

- 闸门归纳为 SegmentEditFunction(code, modifiers):Ctrl-only + 组词存在,
  VK_BACK/LEFT/RIGHT 分别映射到 segment 函数;三处判据与延迟投影共用
- 新增 FUNCTION_MOVE_LEFT_SEGMENT / FUNCTION_MOVE_RIGHT_SEGMENT,
  编辑会话只按 CompositionRestored 的 caret 移动本地光标,不重建 buffer
- Server 用 segment_raw_boundaries() 取前/后边界,能力位与非 UiLess 时回包;
  非拼音/本地模式/UiLess/旧端仍按单字符 ±1 回退

Refs #187
2026-09-17 23:36:34 +08:00
Jia Sui b1ec3ab1e0 feat(server): Ctrl+Backspace 按分词删除前置拼音(#187)
组词中 Ctrl+Backspace 删除光标前的一个分词单元:剩余拼音非空时删
最后一个字的拼音(音节段),剩余拼音为空时删除最后一段已选分词。

engine:
- InputSession::segment_raw_boundaries() 作为按段删除/移动的唯一分段来源:
  全拼把纠错显示串的分隔符映回 raw 下标,双拼与 pinyin_segmentation 共用贪心谓词,
  2 字辅助码自成一段;非拼音方案与本地模式返回空表示无单位模型
server:
- IsSegmentBackspaceKey(Ctrl-only)、整段丢弃 / 边界截断 / 单字符回退三分支,
  截断点残留分隔符清理
- 复用 CompositionRestored 回包(含 caret),仅协商能力位且非 UiLess 时发送;
  新出现「只剩已选汉字、无拼音」的中间态
windows:
- 两处 Ctrl 闸门对组词中的 Ctrl+VK_BACK 放行,新增 FUNCTION_BACKSPACE_SEGMENT
  与 _HandleCompositionBackspaceSegment(读回包重建,失败回退单字符)
- 空 raw 状态渲染、终态清理 word_for_creating_word,延迟投影失效处理

Closes #187
2026-09-17 23:01:31 +08:00
Jia Sui e64195731e feat(server): Backspace 删空剩余拼音后退选已选分词(#35)
已选分词是造词中间态:删空剩余拼音时不再整条 composition 丢弃,
而是退选最后一段并恢复其原始拼音与候选。

engine:
- SelectionTransition 暴露本次选择消费掉的原始输入(保留大小写与双拼按键)
engine/contracts:
- 新增回复类型 CompositionRestored=14 与能力位 CompositionRestore;
  MaxKnown 同步抬高,Server 按协商能力位门控发送,旧 DLL 不会收到未知帧
server:
- 造词中间态压 (consumed_raw, previous_creating_word) 快照,边界 Backspace 弹栈退选
- 任何清空 raw 的路径同时清 creating_word 与快照历史,避免跨 composition 泄漏
windows:
- _HandleCompositionBackspace 提示退选时读回包重建 keystroke buffer / word / 预编辑 / 光标
- NeedToCreateWord 与 CompositionRestored 共用载荷解析;旧 Server 与 UiLess 回退旧行为

Closes #35
2026-09-17 18:45:01 +08:00
fanlusky 5a5dea6b7e feat(japanese): 日语模式禁用 -/= 翻页并用 - 打长音符
日语模式下把 - 让给长音符(ー)输入,同时禁掉 -/= 的候选翻页:

- 罗马字方案接受 VK_OEM_MINUS,set_raw_input 不再把 - 过滤掉;
  罗马字表里本来就有 - 到 ー 的条目,只缺按键准入。
- n 后面接 - 时按辅音处理,让前面的 n 收成 ん,避免 n- 卡在 pending。
- 空编码时单独按 - 也起头组合,候选框第一项是 ー、第二项是普通 -。
- 组词流程同样适用:判据只看日语模式加组合状态,不再走翻页和词转字。
- -/= 在日语模式下不翻页,_ = + 这些没有假名写法的字符退回标点上屏,
  并把 = _ 补进服务端的标点集合,和 TSF 侧保持一致,否则会等不到回包。

TSF 侧的影子分类、IsVirtualKeyNeed(ForFreshComposition) 和服务端按键策略
三处同时改,统一以配置项 input_mode == japanese 为判据(R 模式不在范围内,
TSF 侧看不到临时会话,用会话类型判断会让两侧分类打架)。
2026-09-15 01:57:50 +08:00
Aegon8719 6ff1cff42d issue-52 加入简繁切换快捷键;增加候选词翻页快捷键组合、增加以词定字快捷键组合;
issue-55 候选词与辅助码间保留一个半角空格。
2026-09-08 14:07:48 +08:00
fanlusky 38fe99be62 Add a function that commits the raw string when the Enter key is pressed and records the string into the dictionary. 2026-08-25 19:05:25 +08:00
fanlusky e42cc92b8e Attempt to fix the flickering issue with Microsoft Shuangpin. Add an R mode for temporary Japanese input. 2026-08-22 16:54:29 +08:00
fanlusky aad3992541 Migrate some shortcuts to keyeventsink. 2026-08-02 22:57:21 +08:00
fanlusky 9fa82d8878 Add English input mode. 2026-08-01 23:15:02 +08:00
fanlusky 4848ac1cff Fix the issue where selecting candidates with the numeric keypad fails to commit and causes the interface to flicker and freeze. 2026-08-01 17:45:12 +08:00
fanlusky 7775b97c09 Fix the issue where the candidate window cannot be pulled up when switching back from Microsoft's emoji panel, and deeply optimize the stability of IPC connections. 2026-07-16 21:41:09 +08:00