早在去年,落格输入法的用户就有报告说落格输入法 macOS 在 有道云笔记 的 MarkDown 模式下无法正常键入中文,经过测试证明确实如此,体现为打中文字的时候,buffer的刷新会奇怪的删除掉光标前的一个字符——对,不多不少,就删一个。
捣鼓了很久未果,最后我没招了打印出了所有内容,发现了谜团:
当我把输入法获取到的光标左边的文字打印出来后,我发现文字的后边被追加了一个奇怪的字符:
你可能看不见,它实际上也是一个无法表达的字符,一般来说文本编辑器都用来表示一个坏掉的字符。不过,经过字符编码转换后,我还是找到了它的原本,这是一个 ASCII 字符,编码是 0x01 ……嗯,无法表达的字符——那么答案就明显了,显然是在系统在设定 buffer 的时候判断到了这个字符的数量(也就是1),但在替换的时候这个字符没了,于是把末尾的字给替换了。
于是,我就根据有道云笔记的 BundleID 进行判断,一旦找到这个字符,那说明就是在它的 MarkDown 模式里,然后在键入 buffer 之前,手动把这个字符给它补回去——好吧它至少真的管用:
1 2 3 4 5 6 7 8 9 |
//修复有道云笔记 MD 模式的奇葩问题 if let id = self.bundleIdentifier(), id == "com.youdao.note.YoudaoNoteMac" { if self.selectedRange().length == 1 { if self.attributedSubstring(from: self.selectedRange()).string == "\u{01}" { self.insert(text: "\u{01}") } } } //补丁结束 |
不过好景不长,时隔半年,在另一个 app, Quiver 中又遇到了这个问题,这次就没那么容易解决了,同样的办法会导致无限递归……好吧,是时候使用真正的技术了。
经过询问大佬(是的是问出来的……),macOS 的 InputMethodKit 中 public func setMarkedText(_ string: Any!, selectionRange: NSRange, replacementRange: NSRange) 这个方法,虽然在 Swift 中声明的是接收 Any 类型,也就是说不论是 String 还是 NSAttributedString 均可(声明中也是如此说明),但实际上它需要的是后者,很简单,生成一个 NSAttributedString 再写入 buffer ,问题解决。
同时,细心的我还发现了一个有趣的问题,落格输入法的 buffer 下划线总是比系统的下划线要粗那么一点(这里我用 QIM 做比较):
原来, IMKInputController 中是有这么一个方法的:
1 |
func mark(forStyle style: Int, at range: NSRange) -> [AnyHashable : Any]! |
它可以返回一个包含富文本标签的字典,这个字典可以标记某条 buffer,从而让系统认识这个 buffer。
那么大概用法是这样:
1 |
let attr = mark(forStyle: kTSMHiliteConvertedText, at: NSMakeRange(NSNotFound,0)) |
然后把这个属性附加到设置 buffer 时生成的 NSAttributedString 即可,这才是 macOS 输入法正确设置 buffer 的姿势:
1 2 |
let att = NSAttributedString(string: t, attributes: attr) setMarkedText(att, selectionRange: NSMakeRange(NSNotFound, NSNotFound), replacementRange: NSMakeRange(NSNotFound, NSNotFound)) |
这样,输入法的 buffer 区域就和系统的一模一样了:
本文由 落格博客 原创撰写:落格博客 » 落格输入法 macOS 是如何处理 ascii 0x01 的兼容问题的-macOS 输入法如何正确设置 buffer
转载请保留出处和原文链接:https://www.logcg.com/archives/2959.html