Repository navigation
Expand file tree
/
Copy pathcodeLookup_Filter.lua
More file actions
395 lines (370 loc) · 18.3 KB
/
Copy pathcodeLookup_Filter.lua
File metadata and controls
395 lines (370 loc) · 18.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
-- codeLookup_Filter.lua
-- 三生输入法(原五笔词通):编码反查 + 多音字注释(独立滤镜,不改动 inputShow_Filter.lua)
-- 版本:v1.5 (2026-10-16)
--
-- 设计原则(吸取 dsh/doubao 教训 + 本轮反馈):
-- 1. 独立文件,绝不整文件替换 inputShow_Filter,避免「修一个坏一个」。
-- 2. 全程 pcall 保护,任何候选处理出错都不阻断 yield,绝不导致候选中断。
-- 3. comment 一律用 cand:get_genuine().comment 赋值,且先 `or ''` 防 nil 崩溃
-- (dsh 旧版 cand.comment = cand.comment .. extra 在 nil 时崩 coroutine,导致调频/自造词全废)。
-- 4. 只改 comment,不改候选顺序与身份 → 调频/自造词不受影响。
-- 5. 复用 phraseComment 开关(默认开):关闭 commentOn 时本滤镜不输出注释。
--
-- 版本沿革:五笔词通 v1→v3(在 dsh 残局上重建)= 更名前;三生输入法自 v1.0 起重新计版。
-- · v1.0 = 原「五笔词通 v3.0」更名基线:拼音反查恢复(逐字首选读音) + 碰撞检测 + 移除手动词典 + user.txt(tabledb) 正确加载。
-- · v1.1(本轮真机反馈修复):
-- 1) 拼音注释改由上游 pinyinAdding_Filter 负责(其以 ✔ 标注拼音),本滤镜不再追加拼音,
-- 仅对「单字多音字」补充全部读音,消除「打拼音时注释里多出一份拼音」的重复。
-- 2) 五笔反查码前置:[五笔码] 在前,其余在后,保证始终可见不被末尾截断(如 good→好 的 vbrq)。
-- 3) 碰撞隐藏逻辑保留:现算码被另一多字词占用则隐藏(家乡的茶 pxra 与 它找 撞车 → 不显示)。
-- 4) 不手动塞词原则不变:遵循自动组词,海洋生物→iitt 由标准规则计算显示,不动词典。
-- · v1.2(真机反馈·恢复“真反查”语义):
-- 打五笔只回显拼音、打拼音/英文只回显五笔(各显示“对方”那套码,不再两套都显示)。
-- 通过读取 env.engine.context.input 判断输入类型:拼音/英文输入→剥离上游 ✔拼音并回显五笔码;
-- 五笔输入→保留上游 ✔拼音、不再追加五笔码;单字多音字(如 长→zhang/chang)在五笔输入时升级为全部读音。
-- · v1.3(真机反馈·修复反查不对称 + 截断):
-- 1) 五笔输入分支不再“保持上游原样”,改为【主动用本滤镜拼音表把拼音置最前】——
-- 修复上游 pinyinAdding 拼音表覆盖不全(有 大→da/dai 却漏 好→hao、少→shao)导致 vb→好 / it→少 不显拼音。
-- 2) 拼音/英文输入分支把五笔码【置最前】,不再甩在末尾(修复 good→好 的 vbrq 因居末被截断看不到)。
-- 3) 真反查语义不变:仅显示“对方”那套码;单一码即够,不再重复显示。
-- · v1.4(真机反馈·英文反查补回拼音):
-- 1) 根因:v1.3 把「英文/其它」输入归类为「回显五笔码」并剥掉上游拼音 → good→好 只见 [vb] 不见 hao、small→小 只见 [ih] 不见 xiao。
-- 2) 修复:英文/其它输入改为回显「拼音 + 五笔码」(最全),如 good→好 显示「hao [vb]」、small→小 显示「xiao [ih]」。
-- 3) 五笔输入仍只回显拼音、拼音输入仍只回显五笔(真反查两条主轴不变);英文查词作为“查义”场景给最全信息。
-- · v1.5(真机反馈·恢复上游详细注释,不再覆盖):
-- 1) 根因:v1.2~v1.4 用 `cand.comment = 反查串` 整条覆盖,把 phraseComment 的💡词性/释义、pinyinAdding 的✔拼音全抹掉。
-- 2) 修复:改为【保留原 comment,仅前置追加反查码(去重)】—— `新注释 = 反查码 + 原comment`。
-- · 打五笔→想看拼音:base 通常已含 ✔拼音则不补;仅缺失才补(防双份)。
-- · 打拼音→想看五笔:前置 [wubi],base 不含则补(防重复)。
-- · 打英文→最全:base 已有拼音只补 [wubi],缺失再补齐拼音(均防重复)。
-- · 单字多音字打五笔:把 base 里单读 ✔zhang 升级为全读 ✔zhang/chang(去重替换)。
-- 3) 反查语义、碰撞隐藏、不手动塞词等全部能力不变;词性/释义/拼音注释全部回来。
--
-- 插入位置:wubi_pinyin.custom.yaml 的 engine/filters 中,pinyinAdding_Filter 之后插入
-- - lua_filter@codeLookup_Filter
-- 并在 rime.lua 末尾增加:
-- codeLookup_Filter = require("codeLookup_Filter")
local M = {}
local logEnable, log = pcall(require, "runLog")
local sysInfoEnable, sysInfo = pcall(require, 'sysInfo')
local utf8StringEnable, utf8String = pcall(require, 'utf8String')
-- 字符数统计:优先用 Lua 5.4 内建 utf8.len(稳定可靠),fallback 到正则计数
local function utf8Len(s)
if utf8 and utf8.len then
local n = utf8.len(s or "")
if n then return n end
end
local n = 0
for _ in string.gmatch(s or "", "[\1-\127\194-\244][\128-\191]*") do n = n + 1 end
return n
end
-- 计算 Rime 数据目录(dict 文件所在)。优先运行时 API 返回的目录,
-- 其次硬编码已知目录 D:/Program Files/Rime,再次 fallback 到 lua 目录的上一级。
-- 以「该目录下存在 wubi86.dict.yaml」为准。
local function dir_has_dicts(dir)
local f = io.open(dir .. '/wubi86.dict.yaml', 'r')
if f then f:close(); return true end
return false
end
local function resolve_data_dir()
local candidates = {}
local ok_api, api = pcall(function() return rime.get_api() end)
if ok_api and api then
local ok2, dd = pcall(api.get_user_data_dir, api)
if ok2 and dd and dd ~= '' then candidates[#candidates + 1] = dd:gsub('\\', '/') end
end
candidates[#candidates + 1] = 'D:/Program Files/Rime'
if sysInfoEnable and sysInfo.currentDir then
local ok, cd = pcall(sysInfo.currentDir)
if ok and cd and cd ~= '' then
candidates[#candidates + 1] = cd:gsub('/lua$', ''):gsub('\\lua$', '')
end
end
for _, d in ipairs(candidates) do
if dir_has_dicts(d) then return d end
end
return candidates[1] or ''
end
M.data_dir = resolve_data_dir()
-- 五笔码表:text -> 最短五笔码(多条目取最短)
local wubiMap = {}
-- 拼音表:text(单字/词) -> 读音数组(多音字收集全部读音)
local pinyinMap = {}
-- 多字词占用码集合:code -> true 表示该码已被某个「多字词」占用(用于拦截撞车假码)
local wubiMultiOwners = {}
local function split_tab(line)
local fields = {}
local start = 1
while true do
local tabPos = line:find('\t', start, true)
if not tabPos then break end
fields[#fields + 1] = line:sub(start, tabPos - 1)
start = tabPos + 1
end
fields[#fields + 1] = line:sub(start)
return fields
end
-- user.txt 里的 enc* 自动编码条目不是规范五笔码,跳过(不进反查,也不参与撞车判定)
local function is_real_wubi_code(code)
if not code or code == '' then return false end
if code:match('^enc') then return false end -- Rime 自动编码占位码
if #code > 4 then return false end -- 规范五笔码最长 4 位
return true
end
local function add_wubi(text, code)
if not text or text == '' or not is_real_wubi_code(code) then return end
local cur = wubiMap[text]
if not cur or #code < #cur then
wubiMap[text] = code
end
-- 多字词占用该码 → 记入撞车集合
if utf8Len(text) and utf8Len(text) >= 2 then
wubiMultiOwners[code] = true
end
end
local function add_pinyin(text, py)
if not text or not py or text == '' or py == '' then return end
if not pinyinMap[text] then pinyinMap[text] = {} end
for _, v in ipairs(pinyinMap[text]) do
if v == py then return end
end
pinyinMap[text][#pinyinMap[text] + 1] = py
end
local function load_dict_file(path, kind)
local f = io.open(path, 'r')
if not f then
if logEnable then log.writeLog('codeLookup: 跳过字典(不存在) ' .. tostring(path)) end
return 0
end
-- 识别格式:含 YAML 头分隔符 '---' 的是标准词典(数据在 '...' 之后);
-- 否则(如 user.txt 是 tabledb,无 '---'/'...')整文件即数据,直接读取。
local hasHeader = false
local probe = io.open(path, 'r')
if probe then
for line in probe:lines() do
if line:sub(1, 3) == '---' then hasHeader = true; break end
end
probe:close()
end
local inData = not hasHeader
local n = 0
for line in f:lines() do
local c3 = line:sub(1, 3)
if c3 == '---' then
inData = false
elseif c3 == '...' then
inData = true
elseif inData then
if line:sub(1, 1) ~= '#' then
local fields = split_tab(line)
local text, val = fields[1], fields[2]
if text and val and text ~= '' and val ~= '' and #text <= 12 then
if kind == 'wubi' then
add_wubi(text, val)
elseif kind == 'pinyin' then
add_pinyin(text, val)
end
n = n + 1
end
end
end
end
f:close()
if logEnable then log.writeLog('codeLookup: 载入 ' .. tostring(path) .. ' entries=' .. n) end
return n
end
-- 载入全部反查数据源。五笔码优先主词库;拼音全量载入(多音字收集全部读音)。
-- 注意:user.txt 仅取真实五笔码(enc* 自动编码条目已过滤),用作反查与撞车判定。
function M.load(dir)
dir = dir or M.data_dir
M.data_dir = dir
wubiMap = {}
pinyinMap = {}
wubiMultiOwners = {}
load_dict_file(dir .. '/wubi86.dict.yaml', 'wubi') -- 主五笔词库(13.7万条)
load_dict_file(dir .. '/user.txt', 'wubi') -- 运行时用户真实词条(自动组词结果)
load_dict_file(dir .. '/cn_pinyin_wp.dict.yaml', 'pinyin') -- 全量汉字拼音(含多音字多行)
return M
end
-- 标准五笔组词规则:用每个字的「最短码」现算词组码
-- 2字=每字前2码;3字=前2+第1+后2;4字=每字第1码;>4=前三字第1码+末字前2码
local function group_code(codes, L)
local gc
if L == 2 then
gc = string.sub(codes[1], 1, 2) .. string.sub(codes[2], 1, 2)
elseif L == 3 then
gc = string.sub(codes[1], 1, 2) .. string.sub(codes[2], 1, 1) .. string.sub(codes[3], 1, 2)
elseif L == 4 then
gc = string.sub(codes[1], 1, 1) .. string.sub(codes[2], 1, 1)
.. string.sub(codes[3], 1, 1) .. string.sub(codes[4], 1, 1)
else
gc = string.sub(codes[1], 1, 1) .. string.sub(codes[2], 1, 1)
.. string.sub(codes[3], 1, 1) .. string.sub(codes[L], 1, 2)
end
if gc and #gc >= 2 and is_real_wubi_code(gc) then return gc end
return nil
end
-- 取五笔码:优先整词实码(保证与「真能打出」一致);未命中则按标准组词规则现算,
-- 但【若该码被另一个多字词占用则隐藏】,避免误导(如 家乡的茶 pxra→它找)。
local function get_wubi(text)
local w = wubiMap[text]
if w then return w end
local L = utf8Len(text)
if not L or L < 2 then return nil end
local codes = {}
for ch in string.gmatch(text, utf8.charpattern) do
local c = wubiMap[ch]
if not c then return nil end
codes[#codes + 1] = c
end
local gc = group_code(codes, L)
if not gc then return nil end
-- 撞车判定:码已被另一个多字词占用 → 不显示(单字占用如 沙→iitt 视为正常共存,仍显示)
if wubiMultiOwners[gc] then return nil end
return gc
end
-- 取拼音:优先整词读音(多音字词级读法);否则逐字首选读音(多字词反查提示,如 海洋生物→haiyangshengwu)。
-- 单字多音字收集全部读音(如 长→zhang/chang)。
local function get_pinyin(text)
local list = pinyinMap[text]
if list and #list > 0 then
return table.concat(list, '/')
end
local L = utf8Len(text)
if not L or L < 2 then return nil end
local res = ''
for ch in string.gmatch(text, utf8.charpattern) do
local l = pinyinMap[ch]
if l and #l > 0 then
res = res .. l[1] -- 多字词逐字取首选读音,保证输出清爽
else
return nil
end
end
return res ~= '' and res or nil
end
-- 反查结果(自测用):五笔码在前、拼音在后 [wubi] pinyin;正式链路由 codeLookup 处理(含前置与去重)
function M.lookup(text)
local p = get_pinyin(text)
local w = get_wubi(text)
local parts = {}
if w then parts[#parts + 1] = '[' .. w .. ']' end
if p then parts[#parts + 1] = p end
return #parts > 0 and table.concat(parts, ' ') or nil
end
-- 是否含有中日韩表意文字(含扩展A与兼容区),用于只注释中文候选
local function has_cjk(text)
for ch in string.gmatch(text, utf8.charpattern) do
local b = utf8.codepoint(ch)
if (b >= 0x3400 and b <= 0x4DBF) -- 扩展A
or (b >= 0x4E00 and b <= 0x9FFF) -- 基本汉字
or (b >= 0xF900 and b <= 0xFAFF) then -- 兼容汉字
return true
end
end
return false
end
-- 剥离上游 pinyinAdding_Filter 写入的 ✔拼音(其追加在 comment 末尾)
local function strip_upstream_pinyin(base)
return (base:gsub('✔.*$', ''))
end
-- 反查分类:根据「输入串」与候选自身的「五笔码 / 拼音」比对,判断回显哪一套:
-- · 输入 == 候选五笔码 → 用户打的是五笔 → 回显拼音(多音字含全部读音)
-- · 输入 == 候选拼音(任一) → 用户打的是拼音 → 回显五笔
-- · 其它(英文 / 部分码) → 回显「拼音 + 五笔码」(最全,满足英文查词既要音又要码,如 good→好 显示 hao [vb])
-- 该方案精确还原“真反查”语义,且不误伤「五笔码含元音键」的常见字(不=i、有=e、和=a 等)。
local function classify_input(raw, wubi, pinyinStr)
raw = (raw or ''):lower()
if raw == '' then return 'pinyin' end
if wubi and raw == wubi:lower() then return 'pinyin' end
if pinyinStr then
for py in pinyinStr:gmatch('[^/]+') do
if raw == py:lower() then return 'wubi' end
end
end
return 'both'
end
-- 滤镜主体:实现“真反查”——按输入类型【只前置追加对方那套码】,且【完整保留上游注释】(phraseComment 的💡词性/释义 + pinyinAdding 的✔拼音),绝不整体覆盖。
-- 五笔输入 → 想看拼音:base 通常已含 ✔拼音则不补(缺才补),如 vb→好 仍显示 ✔hao + 词性/释义
-- 拼音输入 → 想看五笔:前置 [wubi],如 hao→好 显示 [vb] + 原词性/释义/✔hao
-- 英文/其它输入 → 最全:base 已有拼音只补 [wubi],如 good→好 显示 [vb] + 原词性/释义/✔hao
-- 单字多音字 → 打五笔时把 base 单读 ✔zhang 升级为全读 ✔zhang/chang(去重替换)
-- 全程 pcall,出错也不影响 yield。
local function codeLookup(input, env)
local on = true
pcall(function() on = env.engine.context:get_option("phraseComment") end)
if not on then
for cand in input:iter() do
yield(cand)
end
return
end
local rawInput = ''
pcall(function() rawInput = tostring(env.engine.context.input or '') end)
for cand in input:iter() do
pcall(function()
local txt = cand.text or ''
if has_cjk(txt) then
local w = get_wubi(txt)
local p = get_pinyin(txt)
local L = utf8Len(txt)
local base = cand.comment or '' -- ★ 上游已含:phraseComment 的💡词性/释义 + pinyinAdding 的✔拼音,必须原样保留
local mode = classify_input(rawInput, w, p) -- 'pinyin'(打五笔→显拼音) / 'wubi'(打拼音→显五笔) / 'both'(打英文→最全)
local add = nil
if mode == 'wubi' then
-- 打拼音 → 想看五笔码;base 通常不含五笔码 → 前置追加 [wubi](去重)
if w and not base:find('%[' .. w .. '%]', 1, true) then
add = '[' .. w .. ']'
end
elseif mode == 'pinyin' then
-- 打五笔 → 想看拼音;base 通常已含 ✔拼音;仅当确实缺失才补(去重,避免双份拼音)
if L == 1 and p and p:find('/', 1, true) then
-- 单字多音字:base 若只列单读,升级为全部读音(去重替换,不重复)
if base:find('✔', 1, true) then
local upgraded = base:gsub('✔[%w/]+', '✔' .. p)
if upgraded ~= base then cand:get_genuine().comment = upgraded end
elseif not base:find(p, 1, true) then
add = p
end
elseif p and not base:find('✔', 1, true) and not base:find(p, 1, true) then
add = p
end
else -- 'both' 英文/其它
-- 想看 拼音+五笔码(最全);base 已有拼音则只补五笔码,缺失则补齐(均去重)
if w and not base:find('%[' .. w .. '%]', 1, true) then
add = (add and add .. ' ' or '') .. '[' .. w .. ']'
end
if p and not base:find('✔', 1, true) and not base:find(p, 1, true) then
add = (add and add .. ' ' or '') .. p
end
end
if add then
-- ★ 前置(保证反查码不被末尾截断),base 原样保留在后(词性/释义/✔拼音一律不丢)
local sep = (base ~= '' and base:sub(1, 1) ~= ' ' and add:sub(-1) ~= ' ') and ' ' or ''
cand:get_genuine().comment = add .. sep .. base
end
end
end)
yield(cand)
end
end
M.codeLookup = codeLookup
M.has_cjk = has_cjk
M.get_wubi = get_wubi
M.get_pinyin = get_pinyin
-- 运行时加载(Rime require 本模块时执行一次,建立索引)
M.load()
-- 独立运行自测(仅在非 Rime 环境下:lua54 直接执行本文件时)
if not (sysInfoEnable and sysInfo and sysInfo.currentDir) then
local testDir = M.data_dir ~= '' and M.data_dir or 'D:/Program Files/Rime'
M.load(testDir)
print('三生输入法 codeLookup self-test, data_dir=' .. testDir)
print('wubi entries=' .. (function() local n=0 for _ in pairs(wubiMap) do n=n+1 end return n end)())
print('pinyin entries=' .. (function() local n=0 for _ in pairs(pinyinMap) do n=n+1 end return n end)())
for _, t in ipairs({ '电', '长', '你', '好', '你好', '苹果', '划', '中国', '人民', '银行',
'海洋生物', '春夏秋冬', '打印机', '中华人民共和国', '中国人',
'家乡的茶', '它找', '𧟴' }) do
print(t .. ' -> ' .. tostring(M.lookup(t)))
end
end
return codeLookup