Commit Graph
6 Commits
Author SHA1 Message Date
Jia Sui d4a0796400 feat(windows): 重构智能标点
原「智能标点」是小狼毫 digit_separators 的语义:字母或数字后的 , . : 直出英文,
2 秒内再按同一标点转回中文——后半段靠 SendInput 往应用里补一个 Backspace 再打
字符,落点全靠光标当时在哪。这次换掉主路径:输入中文标点后按空格,用 TSF 编辑
会话就地把它改写成对应英文(。,!?;:、 → . , ! ? ; : /),2 秒内再按同一标点
键撤回中文;原来的直出降为子开关,整个家族默认全关、改为显式开启。

- 按键与会话:转换/撤回是本地文档改写,不进 IPC、不进延迟队列;判据落在
  _IsKeyEaten,OnTestKeyDown 的预判与 _DispatchKeyDown 的第二遍预判共用同一份,
  声称吃掉的键不交还宿主。改写走编辑会话,请求不上时另起一个最小会话把键本身
  的字符写回去,避免已吃掉的键静默丢失。
- 状态:原来散落的九个成员收成一个 SmartPunctuationAction(None / ChineseCommitted
  / AsciiConverted),带焦点令牌、前台窗口和 beforeChar 指纹;清理以分类结果为准,
  丢焦点与上下文栈变化一并清掉。
- 转换范围:。,!?;:、 与单独出现的 “”‘’【】《》() 都是映射表里的单字符
  条目,路径完全一致;成对补全产生的一对(autoClosedPair)不 arm,按空格只落空格,
  不会留下 <》 这类孤儿。
- 开关:总开关 + 中文标点后按空格转换 + 同键转回中文 + 数字后直出 + 字母后直出,
  五键全部默认关;直出拆成数字/字母两个独立子开关(IPC 24 收窄为数字、25 追加为
  字母),TSF 按前字符类型分别查键。
- 命名:子开关「独立符号」改名「中文标点后按空格转换」,配置键
  smart_punctuation_standalone → smart_punctuation_space_convert,IPC 22 只改常量名
  (SmartPunctuationSpaceConvertChanged),编号与语义不变、新旧测试包互通;设置页
  文案同步重写,五处配置链路与死宿主一并跟改。
- 兼容:旧键未出厂、不迁移(升级合并按模板缺键丢弃、取默认 false);编号 23 随
  「按空格整体转换一对」的设计退役并留空不复用。
- 验证:TSF x86/x64 与 Server Release 编译、Server 335 单测、windows_ipc_contract
  x64/x86、设置页 build + 27 测试、clang-format 18.1.8 全绿。真机未跑。

Signed-off-by: Jia Sui <jsfaint@gmail.com>
2026-09-17 09:29:57 +08:00
fanlusky c9a5d0fd2b feat(tsf): 成对标点补全加修饰键闸门与配对栈
问题:成对标点上屏后靠 PostMessage + SendInput(VK_LEFT) 把光标送回中
间。(〈《""' 全是 Shift 组合键,而 PostMessage 是异步的,合成的
VK_LEFT 常常在物理 Shift 仍按住时抵达宿主,于是变成 Shift+Left——右
半边标点被选中。机器差异只是竞态窗口大小的差异,【】这类不需要 Shift
的键从不复现,可作对照。另外按右半边标点会再插一个,得到(内容));
而引号方向由左右交替维护,右引号根本打不出来。

改动:
- 修饰键闸门:送键前用 GetAsyncKeyState 查 Shift/Ctrl/Alt/Win 是否物
  理按下,按住就挂 15ms 重试定时器推迟,而不是合成一次 Shift 抬起
  (WindInput 的源码注释记录了那条路走不通)。Win 键同样要拦,Win+Left
  是贴靠窗口的快捷键,箭头落进按住的 Win 组合会重排桌面。带 2s 超时
  和焦点会话校验,过期即放弃并清栈。
- 自生成按键标记:合成的 INPUT 带 dwExtraInfo =
  PAIRED_PUNCTUATION_SENDINPUT_EXTRA_INFO,四个按键 sink 入口和
  _BareShiftKeyboardHookProc 在开头识别后放行,IME 不会把自己发出去
  的键再处理一遍,跳过配对时也就不会清掉正在走的那个栈。
- 配对栈:参照 WindInput 的 pair_tracker,记录 {左半, 右半, 焦点令牌},
  深度上限 16。按右半边标点时先读文档确认光标右边确实是那个字符,确
  认后跳过而不是再插一个;焦点切换、中英与标点模式切换、任何结构性
  按键(方向键、Backspace、回车等)都清栈。
- 引号在成对模式下每次按都开一对新的,不再走 GetPunctuation 的左右交
  替,右引号因此可达。
- 消息窗口销毁时走 _CancelPairedPunctuationCaretMove 而不是裸
  KillTimer:重试定时器额外挂着 _pairedCaretRetryTimerActive,只杀定
  时器会把标志停在 true,消息窗口重建后就再也装不上定时器,等于延迟
  送键这条路被静默废掉。

已知限制:连续按住 Shift 跨两个成对标点键(如 " 接 ()会得到 ""()
而不是 "()"。这是延迟送键设计的固有取舍,WindInput 同样如此;要消
掉需走 TSF SetSelection 兜底,或合成一次 Shift 抬起。

Backspace 整对删除未实现:判断左右两半必须读文档,而读文档需要编辑
cookie,不吃键的 Backspace 路径没有。不验证就合成 VK_DELETE,在栈因
鼠标点击而失效时会删掉无关字符。

验证:Debug 与 Release 的 x64、x86 四种组合均编译通过;clang-format
对改动文件无差异;本机实测成对标点上屏后光标落在成对标点中间。
2026-09-13 17:48:41 +08:00
fanlusky 0febd575ac refactor(windows): clear the TSF /W4 warnings
The bulk of them were left behind when the keystroke latency logging was
removed: PerfTimer objects and *ElapsedMs doubles that nothing reads any more
(C4189). Dropping them also takes the QueryPerformanceCounter calls back out of
the keystroke path. The timers that still feed DebugTsfKeyLatency (Ipc.cpp,
KeyEventSink.cpp, KeyHandlerEditSession.cpp) are untouched.

The rest, all behaviour-preserving:

- C4463: the four CompositionProcessorEngine configuration flags were one-bit
  BOOL fields, so every `= TRUE` stored -1. They are plain BOOLs now.
- C4456: renamed the shadowing inner locals (pLI -> pItem, focusToken ->
  expectedFocusToken).
- C4100: commented out the parameter names that are part of a TSF interface but
  unused in the implementation.
- C4702: dropped the unreachable `return 0;` after FreeLibraryAndExitThread,
  which is __declspec(noreturn).
- C4102: removed the two vestigial NoPresenter labels and the empty branches
  that used to jump to them.
- C4267/C4244: made the narrowing conversions in the pipe write and the offline
  key handler explicit.
- C4996: SharedConfigPath now uses GetEnvironmentVariableW instead of the
  deprecated _wgetenv.

x64 and x86 Release both build clean; ctest passes in both.
2026-09-12 00:55:56 +08:00
Evan 997fdfd9cb chore: settle line endings in .gitattributes (#310)
* chore: settle line endings in .gitattributes

Nothing declared line endings, so each file kept whatever the contributor's core.autocrlf produced when it was added: 382 text files ended up LF and 50 CRLF. The CRLF ones are the hazard. Editing one on macOS or Linux writes LF lines into it, and clang-format rejects a file that mixes both -- which is how `quality / Workflow validation` went red on KeyStateCategory.h, where three lines out of a CRLF file had arrived as LF. #309 converted those three lines back and left the other 49 files loaded.

The repository now declares LF and the working trees are renormalized to match, so the failure mode is gone rather than deferred to whichever of those files is edited next.

The four binary types are declared rather than left to git's heuristic, which decides from the first 8000 bytes alone. user_dict.dat is caught by that sample today only because the 40-byte seed is almost entirely NUL.

Verified: the 56 renormalized files are byte-identical to their old contents once CR is stripped, no text file in the index still carries CRLF, and scripts/format.sh --check passes.

* chore: skip the line-ending renormalization in blame

The commit before this one rewrote every line of 56 files without changing a character of them, which is what this file exists to hide.
2026-09-10 03:04:23 +00:00
Evan 42238a2af6 style: 用 .clang-format 重排 server / windows / ui / log
一次性重排,无任何行为改动。四个目录各自的 .clang-format 放在仓库里已久,但全组织只有
MSIME-Linux 在 CI 里执行 clang-format 检查,这边 149 个文件里有 149 个不符合自己声明的风格。

已逐文件核对语义等价:去掉空白、注释与相邻字符串字面量拆分之后,全部 149 个文件与重排前逐字节
相同。差异只有三类——缩进与换行、clang-format 补的 } // namespace 结尾注释、以及长字符串被拆成
相邻字面量(C++ 保证自动拼接)。

这个提交会写进 .git-blame-ignore-revs。
2026-09-06 12:13:47 +09:00
Evan Hu 253b068984 refactor: move the Windows TSF frontend under windows/
Preparation for folding Server, UI, UiHtml, Installer, ImeLog and TsfEditControl into this repository. The Windows tip's own sources, build files and local build scripts move under windows/ so every component sits at the same level. Product-level files stay at the root: the lock, the release manifest, the shared CI scripts, the integration tests, the docs and the Engine submodule.
2026-09-06 02:24:27 +09:00