由pr: !21:添加功能和修复漏洞
引入了自定义字典的方案, 我认为是非常实用的想法.
并且在这里提到了一些输入法, 通过使用这些自定义字典, 做到一些创新的输入方法.
我已经合并了这个pr, 并且做了一些修改和重构后, 提交到了master分支.(还没有发布新版本): f16cf6c
在整理代码的过程中, 我有一些想法, 也许可以一并实现然后更新个大的:
现在的实现中, 有一个核心的转换函数, 它输入一个拼写方式(例如"全拼","五笔98全码"), 和一个中文(例如"你好"), 返回该中文的拼写表示, 在这里, 会得到["nihao"]
(返回数组是因为可能有多音字).
在这个转换的过程中, 现在的实现是, 对于全拼, 我们调用了库, 对于五笔, 我们也自己写了对应的库.
现在在加入"自定义字典"功能的过程中, 实现的方法是查询给定的字典文件.
那么也许可以统一, 只保留一种"查询字典"来转换拼写的方式.
我们可以预设不同的五笔, 拼音模式, 他们都是不同的字典.
用户也可以编写自己的字典.
最终, 用户可以选择一个或多个字典.
这是一种更通用的方法, 并且也能简化代码的逻辑.
这个字典的方案似乎是这个叫rime的输入法的方案.
而这些字典也不需要我们自己编写, 这个项目中很多现成的, 例如:
即使这些字典不全, 基于现有的生成算法, 生成对应的字典也不难.
这些项目的协议是LGPL, 只要不修改这些代码, 我们的协议就不需要修改.
我注意到这些字典里不止包含单字, 有些还包含着常用的词语, 应该可以用这些实现本地的输入法.
之前提到过自定义词库功能: #I1NZWJ:基于项目内标识符积累词库
这也可以作为一个字典, 通过某种算法确定用户输入的高频词后, 将其写入到这个字典文件中即可.
综上, 我觉得这是一个很有搞头的事, 可以大大增加可配置性和输入法体验.
不知各位觉得如何?
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。
支持。
关于一确认一下,当前虽然调用不同输入法的库,个人理解仍是基于字/词典数据。那么改用rime的字典的优势主要是数据格式更一致因而转换算法可以简化?
关于二,打算移植一部分rime的输入法代码吗?另外不知常用词典有多大?
另外,记录高频词和 #I5RJXG:补全项先后顺序自动调整 似乎也相关?
1 是的, 主要目的是使用统一的模型, 这是实现更强大功能的基础.
如果软件的功能是几个不相关的无法连接的小功能拼凑的, 那么这几个小功能都不能得到更好的发展, 扩展到一定程度后, 他们就会互相冲突. 如果能用一个统一的模型解释他们, 就可以继续扩展了. (当然, 模型也会带来僵化, 对模型涵盖范围之外的功能就比较难加进来, 但我认为建模依然是利大于弊的, 任何复杂的功能都有模型做基础.)
2 输入法的核心机制就是输入给定的字符串后, 将这个字符串转换成一组候选的汉字, 然后由用户选择一个.
如果只考虑这一点, 那么在本插件中, 可以很容易的实现, 只需要读取用户现在输入的字符串, 然后通过比对字典获得候选词, 加入vscode的补全项中即可.
这其中的核心算法, 也就是比对和选择候选词的算法, 这非常简单, 只需要简单的对比输入字符串和字典的开头部分做筛选即可.
所以不太需要移植rime的代码, 因为这段逻辑足够简单.
不过, 如果我们要做更好的输入法, 那么就要考虑更多的事, 我想到的有几点:
要实现这些高级功能, 需要考虑更多的算法, 也许参考rime的代码也可以有一些启发, 不过任何输入法应该都可以.
上面提到的"更好的输入法"的第三点其实就是 #I1NZWJ:基于项目内标识符积累词库 所说的功能.
而 #I5RJXG:补全项先后顺序自动调整 也可以看作这个"更好的输入法"是一种扩展, 即"参考上下文提供更好的候选项". 只是现在的输入法还没有做到这一点, 可能是受限于操作系统, 也可能这是个馊主意, 或者也有输入法实现了只是我没有注意到?
这样一看, 甚至可以说现在实现的补全功能本身, 也是一种实现了"参考上下文提供更好的候选项"的输入法. 只是这个参考的上下文更大一点, 不但参考了当前文本, 还参考了vscode提供的信息.
这又回到了1上, 目前的补全功能, 自定义字典功能, 输入法功能, 以及构想中的"更好的输入法"功能, 这些功能其实全都可以统一为一个"基于vscode实现的输入法"这样一个模型上, 我们可以先实现这个模型的部分功能, 以确保现有的功能不会退化, 而剩下的"高级功能"也可以慢慢补上.
更重要的是, 转换了模型之后, 我们能做的事情就更多了, 从最开始的"给补全项加拼音"变成了"输入法". 以前我们局限于"给补全项加拼音", 所以如果vscode不返回合理的补全项(例如c++的情况), 我们就束手无策, 虽然我们也可以引入"自己分析代码结构"这样的手段, 但无法自圆其说, 不能解释为什么"给补全项加拼音"的软件里会加入"分析代码结构"的功能, 以及这个功能的定位是什么, 放在哪里, 怎样调用. 但如果考虑我们是"输入法"的话, 我们就可以解释"分析代码结构"的定位: 一种输入法依赖的上下文. 这样就可以清晰的实现这个功能而不产生混乱.
现在的模糊匹配是倚靠vsc的匹配机制吧?比如输入 nh 匹配到 你好
。
感觉这个方向会在某种程度上代替vsc的匹配机制和代码分析功能,空间的确很大。
也许先从几个实例开始作些需求用例分析?
关注下进展,实现一个内置输入法的想法很有意思,也确实能很大地增强体验,但是分析代码上下文结构带来的阻力是相当大的 ,依附于现有 IDE 的生态体系可能还是会省心些
进展: 我实现了字典树来查找字典, 以此封装了一个依赖本地字典的输入法, 效果:
看起来还不错, 也没有很慢, 这还没做优化, 可以限制结果返回数量, 还有很大优化空间.
可以先在字典方案
分支上体验一下.
个人感觉速度已够用,不大确定的是多出来的那些补全项对用户感官上有多大影响。
刚试了 这里 的“消耗气力”,发现如果先输入了“消耗”,继续输qili没有触发补全:
另外,如果已经输入过“消耗气力”,也许以后输入qili时,就可以把“气力”优先级上升,现在是这样:
这大概就和积累词库和调整优先级那两个issue相关了。
另外,如果输入“shifangf”(释放范围),因为匹配不到词补全弹窗会消失(不意外),但此时退格删除最后的f也不会再次触发弹窗。
进入输入法领域,感觉很多工作量会在细节上。个人也倾向步子跨小些,其实从自己需求出发就挺好,先做到最基本功能。
有几个要讨论的点.
首先先整理一下思路.
理想的工作模式是:
计算补全项 :: 用户输入字符 -> 上下文 -> Array<中文>
.排序算法 :: 中文 -> 中文 -> Bool
.插入文本
插入到光标位置.这个过程中有很多细节:
计算补全项
的数据来源包括很多部分:
vsc的api
表现不稳定:
B.
的时候, 可以从vsc的api中得到你好
这个补全项.B.n
的时候, 依然可以从vsc的api中得到你好
这个补全项, 因为在js中返回结果与输入无关.跨文件
的引用其他文件中的中文变量.A.py
和B.py
, B.py中有一个你好
的变量.B.
的时候, vsc的api提供了你好
这个补全项.B.n
的时候, vsc的api就不再提供这个补全项了, 因为py中返回结果和输入有关, 而n
和你好
没有交集.跨文件
的情况下中文变量补全体验不好.将中文列表作为补全项显示出来
的过程中, 原理是设置补全项的filterText
字段.
本次正在输入的字符
和filterText
的开头一致, 则这个补全项就会出现.本次正在输入的字符
是vsc决定的, 是实际本次输入的, 不会考虑前后文, 比如输入在已有的ni
字符后面输入h
的时候, 就会被算成h
, 对于英文, 就是每次都是输入的那个字符, 对于中文, 如果你用输入发直接输入你好
就会是你好
, 不会分解成'n''i''h''a''o', 只有当按空格或回车将中文实际打上去的时候才会触发这个过程.某个补全项是否要显示
的.sortText
属性, 这个属性会决定补全项的顺序, 当然, 前提是补全项已经通过了filterText
的检查, 不然就不会显示.insertText
属性, 这个属性决定补全项实际插入的结果.
中文
并不一定就是最终插入的东西.片段
, 例如可以提供一个加法片段
的补全项, 你可以输入jiafapianduan
来看到它, 但你选择后并不会插入加法片段
, 而是插入var add = a => b => a + b
.这其中需要决定一些问题:
ni
, 此时用户在这后面紧接着输入h
, 我们应该认为输入字符串是nih
还是h
?你
的后面输入h
ni
的后面输入好
get投入产出
的后面输入b
B.
的时候, 缓存vsc提供的项, 并且在输入到B.n
的时候加入计算.计算补全项
中的字典是否要用同一组字典?中文
一致吗?目前只想到这些问题, 也许在实现的过程中还会有其他问题.
我先按我个人的想法处理这些问题, 对于这些问题有什么想法可以在这里讨论.
基本完成, 有一些内容需要你确认或讨论或说明:
关于文档和说明:
我修改了readme.md
主要原因是里面的图片大小不一, 看起来有点怪.
其次, 里面的一些截图已经和当前版本不一致了.
另外我觉得演示不同语言的运行情况似乎意义不大, 我觉得有些偏离重点. (也许可以放在单独的文件里?)
所以就修改了, 写的比较简单, 也许你可以补充.
我在readme.md
里加入了关于配置的说明
你可以试试以一个普通用户
的角度看, 能不能看懂. (主要是关于字典的配置)
并且尝试配置一下?
在文件src\extension.ts
中, 我加入了一大段注释
你可以试试以一个普通开发者
的角度看, 能不能看懂.
插件的配置选项进行了彻底的修改
你可以看看是否合理.
你可以体验一下现在这个分支的效果
对于本地输入法, 主要是在"你"的后面输入"hao"这种情况, 即"中文+拼写"的情况, 看体验如何.
vsc补全和本地分词和网络输入法基本没变, 应该没啥区别.
计算拼写的方式也变了, 但看起来正常.
具体的处理方式我写在了那一大段注释里, 你也可以看一下是否合理.
有几点还没有完成:
有几点打算以后完成:
有其他问题也可以在这里讨论.
谢谢告知。
首页说明变动很大。
本以为只会对修改或增加的功能进行说明。像snippet中文补全功能应该还有吧?
另外一些用语如“中文拼写”和“符号分词”感觉有待商榷。
建议渐进式修改,可以更清楚看出哪些功能有变动,也便于讨论。
snippet的功能并没有删, 文档漏写了, 我之后补上.
我主要修改的地方就是那些对某个语言的测试图片, 上面也说了.
我觉得演示不同语言的运行情况似乎意义不大, 我觉得有些偏离重点. (也许可以放在单独的文件里?)
删了后基本就没啥东西了.
主要是我觉得作为首页文档应该干净整洁有逻辑, 贴太多图片影响对整体的观感, 而且这些图还大小不一...
我觉得对不同语言的测试图片意义不大, 如果你觉得很重要的话, 我把它整理到单独的文件里, 然后在主页放个连接怎么样?
本来是想写拼音
的, 但想到还可能有五笔或其他输入中文的方法, 就用了中文拼写
, 有更好的建议吗?
一般我们说的分词是比较智能的分词, 比如你好世界
会分成你好
和世界
, 但现在我们的分词实现不是这样, 只是简单通过标点符号做分词标记, 比如类型包含: 字符串, 数字, 布尔
这样, 才会分出来字符串``数字``布尔
这些词, 所以我用了符号分词
这个说法, 有更好的建议吗?
@吴烜 我完善了文档, 先看看, 还有什么建议吗.
然后帮忙看看文档, 对于一般用户而言是否能看懂呢? 主要是关于字典
的说明.
另外请体验一下现在的效果, 看看是否有问题, 我感觉不错, 但也许有我忽略的地方.
(目前只支持全拼, 效果不错的话我再把其他字典搬过来.)
代码还有一些细节没有处理, 但现在应该是可以正常使用的.
好的, 改成"可以在配置中调整"怎样?
字典有两个, 一个是"拼写字典", 一个是"本地输入法字典".
拼写字典是决定"如何把中文翻译成拼写"的, 输入法字典是提供"输入法词组"的.
中文
, 然后将这些中文转换成拼写, 如果转换出来的拼写和用户实际输入的字母有重叠, vscode就会显示这个补全项, 这个转换的过程使用的是"拼写字典".总的来说, "拼写字典"是核心的, 决定着我们用拼音还是五笔还是什么输入方法来输入中文.
而"本地输入法"只是一个扩展功能, "本地输入法字典"只是为这个功能提供词组.
对于"拼写字典"而言, 比如同时开启了拼音和五笔, 那么就既可以输入拼音, 又可以输入五笔, 都可以得到正确的结果.
对于"输入法字典"而言, 同时提供多个字典, 意味着同时有多个词组的来源, 比如如果同时提供的简体拼音和繁体拼音, 那么本地输入法里就即有简体又有繁体.
我这里的导入操作并不复杂, 只要提供字典的路径即可.
3 我预想的默认配置是使用简体全拼, 就是字典方案
分支现在的样子. 这应该和当前商店的版本体验区别不大.
另外会提供几个其他的字典, 例如各种双拼, 五笔之类的, 用户如果要使用这些, 则需要自己去vscode的配置中配置一下, 方法是:
可以在配置中调整
嗯好些。
则需要自己去vscode的配置中配置一下
之前那样的下拉选择感觉更友好些。
感觉一些使用上的细节还有改进空间,比如:
发布版的是这样:
要不以你的使用感受为准,在可以接受的范围内就可以发布,以后根据进一步使用和用户反馈慢慢打磨。
另一种比较稳妥的方式是提供新旧两种模式,用户在新版本有使用问题时可以选择切回老模式。不过这样维护成本比较高。
@吴烜 前一阵比较忙, 现在我又回来搞了orz
我在整理码表了, 有一个问题请教一下, 我注意到之前的版本支持四种五笔:
但我看了之前版本实现的代码:
import { 首字母大写 } from '../扩展/字符串扩展'
const 码表 = require('wubi-code-data')
function 五笔(文字: string, 提示方式: string) {
if (!文字) return ''
var 键码 = ''
var 非汉字字符计数 = 0
var 版本 = 提示方式.substr(2, 2)
for (var i = 0, len = 文字.length; i < len; i++) {
var 字 = 文字.substr(i, 1)
var unicode = 字.charCodeAt(0)
//如果unicode在符号,英文,数字或其他语系,则直接返回
if (unicode > 40869 || unicode < 19968) {
键码 += 字
非汉字字符计数++
} else if (提示方式.indexOf('全码') != -1) {
键码 += 首字母大写(码表.取码(字, 版本))
} else if (提示方式.indexOf('四码') != -1) {
if (文字.length - 非汉字字符计数 == 1) {
键码 += 码表.取码(字, 版本)
} else if (文字.length - 非汉字字符计数 == 2) {
键码 += 码表.取码(字, 版本).slice(0, 2)
} else if (文字.length - 非汉字字符计数 == 3) {
键码 += 码表.取码(字, 版本).slice(0, i - 非汉字字符计数 < 2 ? 1 : 2)
} else if (
文字.length - 非汉字字符计数 >= 4 &&
(i - 非汉字字符计数 < 3 || i + 1 - 非汉字字符计数 == 文字.length)
) {
键码 += 码表.取码(字, 版本).slice(0, 1)
}
}
}
return 键码
}
export default 五笔
根本就没有判断是98还是86, 只判断了是全码还是四码.
因为我不会五笔, 所以我不知道是是否正常.
如果我想通过这个算法生成上面提到的四种五笔的字典该怎么做呢?
在项目rime中, 我只找到了一份五笔的字典: https://raw.githubusercontent.com/rime/rime-wubi/master/wubi86.dict.yaml
看起来是86版的, 是全码还是四码呢?
码表.取码(字, 版本)
里的 版本 应该就是 98/86。另见 此测试:
it('86牺', function() {
assert.equal(演示.取码("牺", 86), "trsg");
});
it('98牺', function() {
assert.equal(演示.取码("牺"), "csg");
});
你我都不用五笔的情况下,个人建议改版的第一步迈小些,比如先做拼音字典。
@吴烜 我有一点问题, 五笔的全码和四码到底有什么区别呢? 代码没看明白...
比如我现在要形成一个字典, 格式是
汉字\t字符表示
那么全码和四码会有不同吗?
之前你的项目"wubi-code-data"(https://www.npmjs.com/package/wubi-code-data)中的码表是全码还是四码呢?
全码和四码的码表可以相互转换吗
差不多搞完啦, 这几天我再做一些测试就发布!🎉
1.5.0版本已发布!
登录 后才可以发表评论