Set up rime-frost basic configuration

This commit is contained in:
2026-03-08 19:03:31 +08:00
commit 90bdf8464d
93 changed files with 2406514 additions and 0 deletions

62
lua/autocap_filter.lua Normal file
View File

@@ -0,0 +1,62 @@
--[[
#302@abcdefg233 #305@Mirtle
自动大写英文词汇:
- 部分规则不做转换
- 输入首字母大写,候选词转换为首字母大写: Hello → Hello
- 输入至少前 2 个字母大写,候选词转换为全部大写: HEllo → HELLO
大写时无法动态调整词频
--]]
local function autocap_filter(input, env)
local code = env.engine.context.input -- 输入码
local codeLen = #code
local codeAllUCase = false
local codeUCase = false
-- 不转换:
if codeLen == 1 or -- 码长为 1
code:find("^[%l%p]") -- 输入码首位为小写字母或标点
then -- 输入码不满足条件不判断候选项
for cand in input:iter() do
yield(cand)
end
return
---- 输入码全大写
-- elseif code == code:upper() then
-- codeAllUCase = true
-- 输入码前 2 - n 位大写
elseif code:find("^%u%u+.*") then
codeAllUCase = true
-- 输入码首位大写
elseif code:find("^%u.*") then
codeUCase = true
end
local pureCode = code:gsub("[%s%p]", "") -- 删除标点和空格的输入码
for cand in input:iter() do
local text = cand.text -- 候选词
local pureText = text:gsub("[%s%p]", "") -- 删除标点和空格的候选词
-- 不转换:
if
text:find("[^%w%p%s]") or -- 候选词包含非字母和数字、非标点符号、非空格的字符
text:find("%s") or -- 候选词中包含空格
pureText:find("^" .. code) or -- 输入码完全匹配候选词
(cand.type ~= "completion" and -- 单词与其对应的编码不一致
pureCode:lower() ~= pureText:lower()) -- 例如 PS - Photoshop
then
yield(cand)
-- 输入码前 2~10 位大写,候选词转换为全大写
elseif codeAllUCase then
text = text:upper()
yield(Candidate(cand.type, 0, codeLen, text, cand.comment))
-- 输入码首位大写,候选词转换为首位大写
elseif codeUCase then
text = text:gsub("^%a", string.upper)
yield(Candidate(cand.type, 0, codeLen, text, cand.comment))
else
yield(cand)
end
end
end
return autocap_filter

275
lua/aux_code.lua Normal file
View File

@@ -0,0 +1,275 @@
-- https://github.com/HowcanoeWang/rime-lua-aux-code
local AuxFilter = {}
-- local log = require 'log'
-- log.outfile = "aux_code.log"
function AuxFilter.init(env)
-- log.info("** AuxCode filter", env.name_space)
AuxFilter.aux_code = AuxFilter.readAuxTxt(env.name_space)
local engine = env.engine
local config = engine.schema.config
-- 設定預設觸發鍵為分號,並從配置中讀取自訂的觸發鍵
env.trigger_key = config:get_string("key_binder/aux_code_trigger") or "`"
-- 设定是否显示辅助码,默认为显示
env.show_aux_notice = config:get_string("key_binder/show_aux_notice") or 'true'
if env.show_aux_notice == "false" then
env.show_aux_notice = false
else
env.show_aux_notice = true
end
----------------------------
-- 持續選詞上屏,保持輔助碼分隔符存在 --
----------------------------
env.notifier = engine.context.select_notifier:connect(function(ctx)
-- 含有輔助碼分隔符才處理
if not string.find(ctx.input, env.trigger_key) then
return
end
local preedit = ctx:get_preedit()
local removeAuxInput = ctx.input:match("([^,]+)" .. env.trigger_key)
local reeditTextFront = preedit.text:match("([^,]+)" .. env.trigger_key)
-- ctx.text 隨著選字的進行oaoaoa 有如下的輸出:
-- ---- 有輔助碼 ----
-- >>> 啊 oaoaau
-- >>> 啊吖 oaau
-- >>> 啊吖啊au
-- ---- 無輔助碼 ----
-- >>> 啊 oaoa
-- >>> 啊吖 oa
-- >>> 啊吖啊;
-- 這邊把已經上屏的字段 (preedit:text) 進行分割;
-- 如果已經全部選完了,分割後的結果就是 nil否則都是 吖卡 a 這種字符串
-- 驗證方式:
-- log.info('select_notifier', ctx.input, removeAuxInput, preedit.text, reeditTextFront)
-- 當最終不含有任何字母時 (候選),就跳出分割模式,並把輔助碼分隔符刪掉
ctx.input = removeAuxInput
if reeditTextFront and reeditTextFront:match("[a-z]") then
-- 給詞尾自動添加分隔符,上面的 re.match 會把分隔符刪掉
ctx.input = ctx.input .. env.trigger_key
else
-- 剩下的直接上屏
ctx:commit()
end
end)
end
----------------
-- 閱讀輔碼文件 --
----------------
function AuxFilter.readAuxTxt(txtpath)
-- log.info("** AuxCode filter", 'read Aux code txt:', txtpath)
if AuxFilter.cache then
return AuxFilter.cache
end
local defaultFile = 'moqi_aux_code.txt'
local userPath = rime_api.get_user_data_dir() .. "/lua/aux_code/"
local fileAbsolutePath = userPath .. txtpath .. ".txt"
local file = io.open(fileAbsolutePath, "r") or io.open(userPath .. defaultFile, "r")
if not file then
log.info("Unable to open auxiliary code file.")
return {}
end
local auxCodes = {}
for line in file:lines() do
if line ~= nil and line ~= "" then -- 检查 line 是否为空
line = line:match("[^\r\n]+") -- 去掉換行符,不然 value 是帶著 \n 的
local key, value = line:match("([^=]+)=(.+)") -- 分割 = 左右的變數
if key and value then
auxCodes[key] = auxCodes[key] or {}
table.insert(auxCodes[key], value)
end
end
end
file:close()
-- 確認 code 能打印出來
-- for key, value in pairs(AuxFilter.aux_code) do
-- log.info(key, table.concat(value, ','))
-- end
AuxFilter.cache = auxCodes
return AuxFilter.cache
end
-- local function getUtf8CharLength(byte)
-- if byte < 128 then
-- return 1
-- elseif byte < 224 then
-- return 2
-- elseif byte < 240 then
-- return 3
-- else
-- return 4
-- end
-- end
-- 輔助函數,用於獲取表格的所有鍵
local function table_keys(t)
local keys = {}
for key, _ in pairs(t) do
table.insert(keys, key)
end
return keys
end
-----------------------------------------------
-- 計算詞語整體的輔助碼
-- 目前定義為
-- 把字或词组的所有辅码,第一个键堆到一起,第二个键堆到一起
-- 例子:
-- 候选(word) = 拜日
-- 【拜】 的辅码有 charAuxCodes=
-- p a
-- p u
-- u a
-- u f
-- u u
-- 【日】 的辅码有 charAuxCodes=
-- o r
-- r i
-- a a
-- u h
-- (竖着拍成左右两个字符串)
-- 第一个辅码键的不重复列表为fullAuxCodes[1]= urpao
-- 第二个辅码键的不重复列表为fullAuxCodes[2]= urhafi
-- -----------------------------------------------
function AuxFilter.fullAux(env, word)
local fullAuxCodes = {}
-- log.info('候选词:', word)
for _, codePoint in utf8.codes(word) do
local char = utf8.char(codePoint)
local charAuxCodes = AuxFilter.aux_code[char] -- 每個字的輔助碼組
if charAuxCodes then -- 輔助碼存在
for _, code in ipairs(charAuxCodes) do
for i = 1, #code do
fullAuxCodes[i] = fullAuxCodes[i] or {}
fullAuxCodes[i][code:sub(i, i)] = true
end
end
end
end
-- 將表格轉換為字符串
for i, chars in pairs(fullAuxCodes) do
fullAuxCodes[i] = table.concat(table_keys(chars), "")
end
return fullAuxCodes
end
-----------------------------------------------
-- 判斷 auxStr 是否匹配 fullAux
-----------------------------------------------
function AuxFilter.match(fullAux, auxStr)
if #fullAux == 0 then
return false
end
local firstKeyMatched = fullAux[1]:find(auxStr:sub(1, 1)) ~= nil
-- 如果辅助码只有一个键,且第一个键匹配,则返回 true
if #auxStr == 1 then
return firstKeyMatched
end
-- 如果辅助码有两个或更多键,检查第二个键是否匹配
local secondKeyMatched = fullAux[2] and fullAux[2]:find(auxStr:sub(2, 2)) ~= nil
-- 只有当第一个键和第二个键都匹配时,才返回 true
return firstKeyMatched and secondKeyMatched
end
------------------
-- filter 主函數 --
------------------
function AuxFilter.func(input, env)
local context = env.engine.context
local inputCode = context.input
-- 分割部分正式開始
local auxStr = ''
-- 判断字符串中是否包含輔助碼分隔符
if not string.find(inputCode, env.trigger_key) then
-- 没有输入辅助码引导符则直接yield所有待选项不进入后续迭代提升性能
for cand in input:iter() do
yield(cand)
end
return
else
-- 字符串中包含輔助碼分隔符
local trigger_pattern = env.trigger_key:gsub("%W", "%%%1") -- 處理特殊字符
local localSplit = inputCode:match(trigger_pattern .. "([^,]+)")
if localSplit then
auxStr = string.sub(localSplit, 1, 2)
-- log.info('re.match ' .. local_split)
end
-- 更新逻辑:没有匹配上就不出现再候选框里,提升性能
-- local insertLater = {}
-- 遍歷每一個待選項
for cand in input:iter() do
local auxCodes = AuxFilter.aux_code[cand.text] -- 僅單字非 nil
local fullAuxCodes = AuxFilter.fullAux(env, cand.text)
-- 查看 auxCodes
-- log.info(cand.text, #auxCodes)
-- for i, cl in ipairs(auxCodes) do
-- log.info(i, table.concat(cl, ',', 1, #cl))
-- end
-- 給待選項加上輔助碼提示
if env.show_aux_notice and auxCodes and #auxCodes > 0 then
local codeComment = table.concat(auxCodes, ',')
-- 處理 simplifier
if cand:get_dynamic_type() == "Shadow" then
local shadowText = cand.text
local shadowComment = cand.comment
local originalCand = cand:get_genuine()
cand = ShadowCandidate(originalCand, originalCand.type, shadowText,
originalCand.comment .. shadowComment .. '(' .. codeComment .. ')')
else
cand.comment = '(' .. codeComment .. ')'
end
end
-- 過濾輔助碼
if #auxStr == 0 then
-- 沒有輔助碼、不需篩選,直接返回待選項
yield(cand)
elseif #auxStr > 0 and fullAuxCodes and (cand.type == 'user_phrase' or cand.type == 'phrase') and
AuxFilter.match(fullAuxCodes, auxStr) then
-- 匹配到辅助码的待选项,直接插入到候选框中( 获得靠前的位置 )
yield(cand)
else
-- 待选项字词 没有 匹配到当前的辅助码,插入到列表中,最后插入到候选框里( 获得靠后的位置 )
-- table.insert(insertLater, cand)
-- 更新逻辑:没有匹配上就不出现再候选框里,提升性能
end
end
-- 把沒有匹配上的待選給添加上
-- for _, cand in ipairs(insertLater) do
-- yield(cand)
-- end
-- 更新逻辑:没有匹配上就不出现再候选框里,提升性能
end
end
function AuxFilter.fini(env)
env.notifier:disconnect()
end
return AuxFilter

90
lua/aux_code/log.lua Normal file
View File

@@ -0,0 +1,90 @@
--
-- log.lua
--
-- Copyright (c) 2016 rxi
--
-- This library is free software; you can redistribute it and/or modify it
-- under the terms of the MIT license. See LICENSE for details.
--
local log = { _version = "0.1.0" }
log.usecolor = true
log.outfile = nil
log.level = "trace"
local modes = {
{ name = "trace", color = "\27[34m", },
{ name = "debug", color = "\27[36m", },
{ name = "info", color = "\27[32m", },
{ name = "warn", color = "\27[33m", },
{ name = "error", color = "\27[31m", },
{ name = "fatal", color = "\27[35m", },
}
local levels = {}
for i, v in ipairs(modes) do
levels[v.name] = i
end
local round = function(x, increment)
increment = increment or 1
x = x / increment
return (x > 0 and math.floor(x + .5) or math.ceil(x - .5)) * increment
end
local _tostring = tostring
local tostring = function(...)
local t = {}
for i = 1, select('#', ...) do
local x = select(i, ...)
if type(x) == "number" then
x = round(x, .01)
end
t[#t + 1] = _tostring(x)
end
return table.concat(t, " ")
end
for i, x in ipairs(modes) do
local nameupper = x.name:upper()
log[x.name] = function(...)
-- Return early if we're below the log level
if i < levels[log.level] then
return
end
local msg = tostring(...)
local info = debug.getinfo(2, "Sl")
local lineinfo = info.short_src .. ":" .. info.currentline
-- Output to console
print(string.format("%s[%-6s%s]%s %s: %s",
log.usecolor and x.color or "",
nameupper,
os.date("%H:%M:%S"),
log.usecolor and "\27[0m" or "",
lineinfo,
msg))
-- Output to log file
if log.outfile then
local fp = io.open(log.outfile, "a")
local str = string.format("[%-6s%s] %s: %s\n",
nameupper, os.date(), lineinfo, msg)
fp:write(str)
fp:close()
end
end
end
return log

40502
lua/aux_code/moqi_aux_code.txt Normal file

File diff suppressed because it is too large Load Diff

209
lua/calc_translator.lua Normal file
View File

@@ -0,0 +1,209 @@
-- 计算器插件
-- author: https://github.com/ChaosAlphard
local calc = {}
function calc.init( env )
local config = env.engine.schema.config
env.prefix = config:get_string( 'calculator/prefix' ) or 'cC'
env.show_prefix = config:get_bool( 'calculator/show_prefix' ) -- set to true to show prefix in preedit area
-- env.decimalPlaces = config:get_string('calculator/decimalPlaces') or '4'
end
local function startsWith( str, start ) return string.sub( str, 1, string.len( start ) ) == start end
local function truncateFromStart( str, truncateStr ) return string.sub( str, string.len( truncateStr ) + 1 ) end
local function yield_calc_cand( seg, cand_text, cand_comment, cand_preedit, show_prefix )
local cand = Candidate( 'calc', seg.start, seg._end, cand_text, cand_comment )
cand.quality = 99999
if not show_prefix then cand.preedit = cand_preedit end
yield( cand )
end
-- 函数表
local calcPlugin = {
-- e, exp(1) = e^1 = e
e = math.exp( 1 ),
-- π
pi = math.pi,
}
-- random([m [,n ]]) 返回m-n之间的随机数, n为空则返回1-m之间, 都为空则返回0-1之间的小数
local function random( ... ) return math.random( ... ) end
-- 注册到函数表中
calcPlugin['rdm'] = random
calcPlugin['random'] = random
-- 正弦
local function sin( x ) return math.sin( x ) end
calcPlugin['sin'] = sin
-- 双曲正弦
local function sinh( x ) return math.sinh( x ) end
calcPlugin['sinh'] = sinh
-- 反正弦
local function asin( x ) return math.asin( x ) end
calcPlugin['asin'] = asin
-- 余弦
local function cos( x ) return math.cos( x ) end
calcPlugin['cos'] = cos
-- 双曲余弦
local function cosh( x ) return math.cosh( x ) end
calcPlugin['cosh'] = cosh
-- 反余弦
local function acos( x ) return math.acos( x ) end
calcPlugin['acos'] = acos
-- 正切
local function tan( x ) return math.tan( x ) end
calcPlugin['tan'] = tan
-- 双曲正切
local function tanh( x ) return math.tanh( x ) end
calcPlugin['tanh'] = tanh
-- 反正切
-- 返回以弧度为单位的点相对于x轴的逆时针角度。x是点的横纵坐标比值
-- 返回范围从−π到π (以弧度为单位),其中负角度表示向下旋转,正角度表示向上旋转
local function atan( x ) return math.atan( x ) end
calcPlugin['atan'] = atan
-- 反正切
-- atan( y/x ) = atan2(y, x)
-- 返回以弧度为单位的点相对于x轴的逆时针角度。y是点的纵坐标x是点的横坐标
-- 返回范围从−π到π (以弧度为单位),其中负角度表示向下旋转,正角度表示向上旋转
-- 与 math.atan(y/x) 函数相比具有更好的数学定义因为它能够正确处理边界情况例如x=0
local function atan2( y, x ) return math.atan2( y, x ) end
calcPlugin['atan2'] = atan2
-- 将角度从弧度转换为度 e.g. deg(π) = 180
local function deg( x ) return math.deg( x ) end
calcPlugin['deg'] = deg
-- 将角度从度转换为弧度 e.g. rad(180) = π
local function rad( x ) return math.rad( x ) end
calcPlugin['rad'] = rad
-- 返回两个值, 无法参与运算后续, 只能单独使用
-- 返回m,e 使得x = m * 2^e
local function frexp( x )
local m, e = math.frexp( x )
return m .. ' * 2^' .. e
end
calcPlugin['frexp'] = frexp
-- 返回 x * 2^y
local function ldexp( x, y ) return math.ldexp( x, y ) end
calcPlugin['ldexp'] = ldexp
-- 返回 e^x
local function exp( x ) return math.exp( x ) end
calcPlugin['exp'] = exp
-- 返回x的平方根 e.g. sqrt(x) = x^0.5
local function sqrt( x ) return math.sqrt( x ) end
calcPlugin['sqrt'] = sqrt
-- y为底x的对数, 使用换底公式实现
local function log( y, x )
-- 不能为负数或0
if x <= 0 or y <= 0 then return nil end
return math.log( x ) / math.log( y )
end
calcPlugin['log'] = log
-- e为底x的对数
local function loge( x )
-- 不能为负数或0
if x <= 0 then return nil end
return math.log( x )
end
calcPlugin['loge'] = loge
-- 10为底x的对数
local function log10( x )
-- 不能为负数或0
if x <= 0 then return nil end
return math.log10( x )
end
calcPlugin['log10'] = log10
-- 平均值
local function avg( ... )
local data = { ... }
local n = select( '#', ... )
-- 样本数量不能为0
if n == 0 then return nil end
-- 计算总和
local sum = 0
for _, value in ipairs( data ) do sum = sum + value end
return sum / n
end
calcPlugin['avg'] = avg
-- 方差
local function variance( ... )
local data = { ... }
local n = select( '#', ... )
-- 样本数量不能为0
if n == 0 then return nil end
-- 计算均值
local sum = 0
for _, value in ipairs( data ) do sum = sum + value end
local mean = sum / n
-- 计算方差
local sum_squared_diff = 0
for _, value in ipairs( data ) do sum_squared_diff = sum_squared_diff + (value - mean) ^ 2 end
return sum_squared_diff / n
end
calcPlugin['var'] = variance
-- 阶乘
local function factorial( x )
-- 不能为负数
if x < 0 then return nil end
if x == 0 or x == 1 then return 1 end
local result = 1
for i = 1, x do result = result * i end
return result
end
calcPlugin['fact'] = factorial
-- 实现阶乘计算(!)
local function replaceToFactorial( str )
-- 替换[0-9]!字符为fact([0-9])以实现阶乘
return str:gsub( '([0-9]+)!', 'fact(%1)' )
end
-- 简单计算器
function calc.func( input, seg, env )
if not seg:has_tag( 'calculator' ) or input == '' then return end
-- 提取算式
local express = truncateFromStart( input, env.prefix )
if express == '' then return end -- 防止用户写错了正则表达式造成错误
local code = replaceToFactorial( express )
local success, result = pcall( load( 'return ' .. code, 'calculate', 't', calcPlugin ) )
if success and result and (type( result ) == 'string' or type( result ) == 'number') and #tostring( result ) > 0 then
yield_calc_cand( seg, result, '', express, env.show_prefix )
yield_calc_cand( seg, express .. '=' .. result, '', express, env.show_prefix )
else
yield_calc_cand( seg, express, '解析失败', express, env.show_prefix )
yield_calc_cand( seg, code, '入参', express, env.show_prefix )
end
end
return calc

251
lua/calculator.lua Normal file
View File

@@ -0,0 +1,251 @@
-- author: https://github.com/ChaosAlphard
-- 说明 https://github.com/gaboolic/rime-shuangpin-fuzhuma/pull/41
local M = {}
function M.init(env)
local config = env.engine.schema.config
env.name_space = env.name_space:gsub('^*', '')
M.prefix = config:get_string(env.name_space .. '/trigger') or 'V'
end
local function startsWith(str, start)
return string.sub(str, 1, string.len(start)) == start
end
local function truncateFromStart(str, truncateStr)
return string.sub(str, string.len(truncateStr) + 1)
end
-- 函数表
local calcPlugin = {
-- e, exp(1) = e^1 = e
e = math.exp(1),
-- π
pi = math.pi
}
-- random([m [,n ]]) 返回m-n之间的随机数, n为空则返回1-m之间, 都为空则返回0-1之间的小数
local function random(...)
return math.random(...)
end
-- 注册到函数表中
calcPlugin["rdm"] = random
-- 正弦
local function sin(x)
return math.sin(x)
end
calcPlugin["sin"] = sin
-- 双曲正弦
local function sinh(x)
return math.sinh(x)
end
calcPlugin["sinh"] = sinh
-- 反正弦
local function asin(x)
return math.asin(x)
end
calcPlugin["asin"] = asin
-- 余弦
local function cos(x)
return math.cos(x)
end
calcPlugin["cos"] = cos
-- 双曲余弦
local function cosh(x)
return math.cosh(x)
end
calcPlugin["cosh"] = cosh
-- 反余弦
local function acos(x)
return math.acos(x)
end
calcPlugin["acos"] = acos
-- 正切
local function tan(x)
return math.tan(x)
end
calcPlugin["tan"] = tan
-- 双曲正切
local function tanh(x)
return math.tanh(x)
end
calcPlugin["tanh"] = tanh
-- 反正切
local function atan(x)
return math.atan(x)
end
calcPlugin["atan"] = atan
-- 返回以弧度为单位的点(x,y)相对于x轴的逆时针角度。y是点的纵坐标x是点的横坐标
-- 返回范围从−π到π (以弧度为单位),其中负角度表示向下旋转,正角度表示向上旋转
-- 它与传统的 math.atan(y/x) 函数相比具有更好的数学定义因为它能够正确处理边界情况例如x=0
local function atan2(y, x)
return math.atan2(y, x)
end
calcPlugin["atan2"] = atan2
-- 将角度从弧度转换为度 e.g. deg(π) = 180
local function deg(x)
return math.deg(x)
end
calcPlugin["deg"] = deg
-- 将角度从度转换为弧度 e.g. rad(180) = π
local function rad(x)
return math.rad(x)
end
calcPlugin["rad"] = rad
-- 返回两个值, 无法参与运算后续
-- 返回m,e 使得x = m*2^e
-- local function frexp(x)
-- return math.frexp(x)
-- end
-- calcPlugin["frexp"] = frexp
-- 返回 x*2^y
local function ldexp(x, y)
return math.ldexp(x, y)
end
calcPlugin["ldexp"] = ldexp
-- 返回 e^x
local function exp(x)
return math.exp(x)
end
calcPlugin["exp"] = exp
-- 返回x的平方根 e.g. sqrt(x) = x^0.5
local function sqrt(x)
return math.sqrt(x)
end
calcPlugin["sqrt"] = sqrt
-- x为底的对数, log(10, 100) = log(100) / log(10) = 2
local function log(x, y)
-- 不能为负数或0
if x <= 0 or y <= 0 then return nil end
return math.log(y) / math.log(x)
end
calcPlugin["log"] = log
-- 自然数e为底的对数
local function loge(x)
-- 不能为负数或0
if x <= 0 then return nil end
return math.log(x)
end
calcPlugin["loge"] = loge
-- 10为底的对数
local function log10(x)
-- 不能为负数或0
if x <= 0 then return nil end
return math.log10(x)
end
calcPlugin["log10"] = log10
-- 平均值
local function avg(...)
local data = {...}
local n = select("#", ...)
-- 样本数量不能为0
if n == 0 then return nil end
-- 计算总和
local sum = 0
for _, value in ipairs(data) do
sum = sum + value
end
return sum / n
end
calcPlugin["avg"] = avg
-- 方差
local function variance(...)
local data = {...}
local n = select("#", ...)
-- 样本数量不能为0
if n == 0 then return nil end
-- 计算均值
local sum = 0
for _, value in ipairs(data) do
sum = sum + value
end
local mean = sum / n
-- 计算方差
local sum_squared_diff = 0
for _, value in ipairs(data) do
sum_squared_diff = sum_squared_diff + (value - mean)^2
end
return sum_squared_diff / n
end
calcPlugin["var"] = variance
-- 阶乘
local function factorial(x)
-- 不能为负数
if x < 0 then return nil end
if x == 0 or x == 1 then return 1 end
local result = 1
for i = 1, x do
result = result * i
end
return result
end
calcPlugin["fact"] = factorial
-- 实现阶乘计算(!)
local function replaceToFactorial(str)
-- 替换[0-9]!字符为fact([0-9])以实现阶乘
return str:gsub("([0-9]+)!", "fact(%1)")
end
-- 简单计算器
function M.func(input, seg, env)
if not startsWith(input, M.prefix) then return end
-- 提取算式
local express = truncateFromStart(input, M.prefix)
-- 算式长度 < 2 直接终止(没有计算意义)
if (string.len(express) < 2) then return end
-- pcall()的原因需要控制一下 . 符号的位置
-- 现在不需要了
-- if (string.match(express, "[^0-9]%.")) then
-- yield(Candidate(input, seg.start, seg._end, express, "小数点不能在非数字字符后面"))
-- return
-- end
local code = replaceToFactorial(express)
local success, result = pcall(load("return " .. code, "calculate", "t", calcPlugin))
if success then
yield(Candidate(input, seg.start, seg._end, result, ""))
yield(Candidate(input, seg.start, seg._end, express .. "=" .. result, ""))
else
yield(Candidate(input, seg.start, seg._end, express, "解析失败"))
yield(Candidate(input, seg.start, seg._end, code, "入参"))
-- TODO: 错误信息记录到日志中
-- print("express: " .. express)
-- print("code: " .. code)
-- print("result: " .. result)
end
end
return M

31
lua/cn_en_spacer.lua Normal file
View File

@@ -0,0 +1,31 @@
-- 中英混输词条自动空格
-- 在 engine/filters 增加 - lua_filter@cn_en_spacer
--
-- 为中英混输词条cn_en.dict.yaml自动空格
-- 示例:`VIP中P` → `VIP 中 P`
--
-- ChatGPT 写的
local function add_spaces(s)
-- 在中文字符后和英文字符前插入空格
s = s:gsub("([\228-\233][\128-\191]-)([%w%p])", "%1 %2")
-- 在英文字符后和中文字符前插入空格
s = s:gsub("([%w%p])([\228-\233][\128-\191]-)", "%1 %2")
return s
end
-- 是否同时包含中文和英文数字
local function is_mixed_cn_en_num(s)
return s:find("([\228-\233][\128-\191]-)") and s:find("[%a%d]")
end
local function cn_en_spacer(input, env)
for cand in input:iter() do
if is_mixed_cn_en_num(cand.text) then
cand = cand:to_shadow_candidate(cand.type, add_spaces(cand.text), cand.comment)
end
yield(cand)
end
end
return cn_en_spacer

View File

@@ -0,0 +1,11 @@
-- 临时用的
function debug_checker(input, env)
for cand in input:iter() do
yield(ShadowCandidate(
cand,
cand.type,
cand.text,
env.engine.context.input .. " - " .. env.engine.context:get_preedit().text .. " - " .. cand.preedit
))
end
end

View File

@@ -0,0 +1,92 @@
#! /usr/bin/env lua
--
-- debugtool.lua
-- Copyright (C) 2021 Shewer Lu <shewer@gmail.com>
--
-- Distributed under terms of the MIT license.
--
-- puts(tag,...)
-- DEBUG --> log.error
-- WARN --> log.warning
-- INFO --> log.info
-- CONSOLE --> print
--
-- ex:
-- test.lua
--
-- local puts = require 'tools/debugtool'
-- --set tag D103 C102
-- local D103= DEBUG .. "103"
-- local C102= CONSOLE .. "102"
-- local C103= nil
--
--
-- puts(ERROR,__FILE__(),__LINE__(),__FUNC__(), 1, 2 , 3 )
-- --> log.error( "error" .. tran_msg(...))
--
-- puts(DEBUG,__FILE__(),__LINE__(),__FUNC__(), 1, 2 , 3 )
-- --> log.error( DEBUG .. tran_msg(...))
--
-- puts(D103,__FILE__(),__LINE__(),__FUNC__(), 1 2 3)
-- --> log.error("trace103" .. tran_msg(...)
--
-- puts(C102,__FILE__(),__LINE__(),__FUNC__(), 1 2 3)
-- --> print("console103" .. tran_msg(...)
--
-- puts(C103,__FILE__(),__LINE__(),__FUNC__(), 1 2 3)
-- --> pass
--
--
--
-- puts(DEBUG,__FILE__(),__LINE__(),__FUNC__() , ...)
-- puts(INFO,__FILE__(),__LINE__(),__FUNC__() , ...)
--
-- global variable
function __FILE__(n)
n = n or 2
return debug.getinfo(n, 'S').source
end
function __LINE__(n)
n = n or 2
return debug.getinfo(n, 'l').currentline
end
function __FUNC__(n)
n = n or 2
return debug.getinfo(n, 'n').name
end
INFO = "log"
WARN = "warn"
ERROR = "error"
DEBUG = "trace"
CONSOLE = "console"
local function tran_msg(...)
local msg = "\t"
for i, k in next, { ... } do msg = msg .. ": " .. tostring(k) end
return msg
end
local function puts(tag, ...)
if type(tag) ~= "string" then return end
if INFO and tag:match("^" .. INFO) then
(log and log.info or print)(tag .. tran_msg(...))
elseif WARN and tag:match("^" .. WARN) then
(log and log.warning or print)(tag .. tran_msg(...))
elseif ERROR and tag:match("^" .. ERROR) then
(log and log.error or print)(tag .. tran_msg(...))
elseif DEBUG and tag:match("^" .. DEBUG) then
(log and log.error or print)(tag .. tran_msg(...))
elseif CONSOLE and tag:match("^" .. CONSOLE) then
(print)(tag .. tran_msg(...))
else
return
end
end
return puts

View File

@@ -0,0 +1,4 @@
local drop_words =
{ "示~例~",
}
return drop_words

View File

@@ -0,0 +1,54 @@
local drop_list = require("cold_word_drop.drop_words")
local hide_list = require("cold_word_drop.hide_words")
local turndown_freq_list = require("cold_word_drop.turndown_freq_words")
local function filter(input, env)
local idx = 3 -- 降频的词条放到第三个后面, 即第四位, 可在 yaml 里配置
local i = 1
local cands = {}
local context = env.engine.context
local preedit_code = context.input
for cand in input:iter() do
local cpreedit_code = string.gsub(cand.preedit, ' ', '')
if (i <= idx) then
local tfl = turndown_freq_list[cand.text] or nil
-- 前三个 候选项排除 要调整词频的词条, 要删的(实际假性删词, 彻底隐藏罢了) 和要隐藏的词条
if not
((tfl and table.find_index(tfl, cpreedit_code)) or
table.find_index(drop_list, cand.text) or
(hide_list[cand.text] and table.find_index(hide_list[cand.text], cpreedit_code))
)
then
i = i + 1
---@diagnostic disable-next-line: undefined-global
yield(cand)
else
table.insert(cands, cand)
end
else
table.insert(cands, cand)
end
if (#cands > 50) then
break
end
end
for _, cand in ipairs(cands) do
local cpreedit_code = string.gsub(cand.preedit, ' ', '')
if not
-- 要删的 和要隐藏的词条不显示
(
table.find_index(drop_list, cand.text) or
(hide_list[cand.text] and table.find_index(hide_list[cand.text], cpreedit_code))
)
then
---@diagnostic disable-next-line: undefined-global
yield(cand)
end
end
for cand in input:iter() do
yield(cand)
end
end
return filter

View File

@@ -0,0 +1,4 @@
local hide_words =
{ ["示~例~"] = { "shil", "shili", },
}
return hide_words

View File

@@ -0,0 +1,163 @@
-- create metatable
orgtype = type
function type(obj)
local _type = orgtype(obj)
if "table" == _type and obj._cname then
return obj._cname
end
return _type
end
function metatable(...)
if ... and type(...) == "table" then
return setmetatable(..., { __index = table })
else
return setmetatable({ ... }, { __index = table })
end
end
-- chech metatble
function metatable_chk(tab)
if "table" == type(tab)
then
return (tab.each and tab) or metatable(tab)
else
return tab
end
end
table.eachi = function(tab, func)
for i = 1, #tab do
func(tab[i], i)
end
return tab
end
table.eacha = function(tab, func)
for i, v in ipairs(tab) do
func(v, i)
end
return tab
end
table.each = function(tab, func)
for k, v in pairs(tab) do
func(v, k)
end
return tab
end
table.find_index = function(tab, elm, ...)
local _, i = table.find(tab, elm, ...)
return i
end
table.find = function(tab, elm, func)
for i, v in ipairs(tab) do
if elm == v then
return v, i
end
end
end
table.find_with_func = function(tab, elm, ...)
local i, v = table.find(tab, elm)
end
table.delete = function(tab, elm, ...)
local index = table.find_index(tab, elm)
return index and table.remove(tab, index)
end
table.find_all = function(tab, elm, ...)
local tmptab = setmetatable({}, { __index = table })
local _func = (type(elm) == "function" and elm) or function(v, k, ...) return v == elm end
for k, v in pairs(tab) do
if _func(v, k, ...) then
tmptab:insert(v)
end
end
return tmptab
end
table.select = table.find_all
table.reduce = function(tab, func, arg)
local new, old = arg, arg
for i, v in ipairs(tab) do
new, old = func(v, new)
end
return new, arg
end
table.map = function(tab, func)
local newtab = setmetatable({}, { __index = table })
func = func or function(v, i) return v, i end
for i, v in ipairs(tab) do
newtab[i] = func(v, i)
end
return newtab
end
table.map_hash = function(tab, func) -- table to list of array { key, v}
local newtab = setmetatable({}, { __index = table })
func = func or function(k, v) return { k, v } end
for k, v in pairs(tab) do
newtab:insert(func(k, v))
end
return newtab
end
function table:push(elm)
self:insert(elm)
end
table.append = table.push
function table:pop()
return self:remove(#self)
end
function table:shift()
self:remove(1)
end
function table:unshift(elm)
self:insert(1, elm)
end
function table.len(t)
local leng = 0
for k, v in pairs(t) do
leng = leng + 1
end
return leng;
end
-- table to string 序列化
function table.serialize(obj)
local serialize_str = ""
local t = type(obj)
if t == "number" then
serialize_str = serialize_str .. obj
elseif t == "boolean" then
serialize_str = serialize_str .. tostring(obj)
elseif t == "string" then
serialize_str = serialize_str .. string.format("%q", obj)
elseif t == "table" then
serialize_str = serialize_str .. "{ "
local record_sep = #obj < 4 and ", " or ",\n"
local record_prefix = #obj < 4 and "" or "\t"
for k, v in pairs(obj) do
if type(k) == "number" then
serialize_str = serialize_str .. record_prefix .. '"' .. v .. '"' .. record_sep
else
serialize_str = serialize_str .. "\t[" .. table.serialize(k) .. "] = " .. table.serialize(v) .. ",\n"
end
end
-- local metatable = getmetatable(obj)
-- if metatable ~= nil and type(metatable.__index) == "table" then
-- for k, v in pairs(metatable.__index) do
-- serialize_str = serialize_str .. "[" .. table.serialize(k) .. "]=" .. table.serialize(v) .. ",\n"
-- end
-- end
serialize_str = serialize_str .. "}"
elseif t == "nil" then
return nil
else
error("can not serialize a " .. t .. " type.")
end
return serialize_str
end

View File

@@ -0,0 +1,151 @@
require('cold_word_drop.string')
require("cold_word_drop.metatable")
-- local puts = require("tools/debugtool")
local drop_list = require("cold_word_drop.drop_words")
local hide_list = require("cold_word_drop.hide_words")
local turndown_freq_list = require("cold_word_drop.turndown_freq_words")
local tbls = {
['drop_list'] = drop_list,
['hide_list'] = hide_list,
['turndown_freq_list'] = turndown_freq_list
}
-- local cold_word_drop = {}
local function get_record_filername(record_type)
local user_distribute_name = rime_api:get_distribution_name()
if user_distribute_name == '小狼毫' then
return string.format("%s\\Rime\\lua\\cold_word_drop\\%s_words.lua", os.getenv("APPDATA"), record_type)
end
local system = io.popen("uname -s"):read("*l")
local filename = nil
-- body
if system == "Darwin" then
filename = string.format("%s/Library/Rime/lua/cold_word_drop/%s_words.lua", os.getenv('HOME'), record_type)
elseif system == "Linux" then
filename = string.format("%s/%s/rime/lua/cold_word_drop/%s_words.lua",
os.getenv('HOME'),
(string.find(os.getenv('GTK_IM_MODULE'), 'fcitx') and '.local/share/fcitx5' or '.config/ibus'),
record_type)
end
return filename
end
local function write_word_to_file(record_type)
-- local filename = string.format("%s/Library/Rime/lua/cold_word_drop/%s_words.lua", os.getenv('HOME'), record_type)
local filename = get_record_filername(record_type)
local record_header = string.format("local %s_words =\n", record_type)
local record_tailer = string.format("\nreturn %s_words", record_type)
local fd = assert(io.open(filename, "w")) --打开
fd:setvbuf("line")
fd:write(record_header) --写入文件头部
-- df:flush() --刷新
local x = string.format("%s_list", record_type)
local record = table.serialize(tbls[x]) -- lua 的 table 对象 序列化为字符串
fd:write(record) --写入 序列化的字符串
fd:write(record_tailer) --写入文件尾部, 结束记录
fd:close() --关闭
end
local function check_encode_matched(cand_code, word, input_code_tbl, reversedb)
if #cand_code < 1 and utf8.len(word) > 1 then -- 二字词以上的词条反查, 需要逐个字去反查
local word_cand_code = string.split(word, "")
for i, v in ipairs(word_cand_code) do
-- 如有 `[` 引导的辅助码情况, 去掉引导符及之后的所有形码字符
local char_code = string.gsub(reversedb:lookup(v), '%[%l%l', '')
local _char_preedit_code = input_code_tbl[i] or " "
-- 如有 `[` 引导的辅助码情况, 同上, 去掉之
local char_preedit_code = string.gsub(_char_preedit_code, '%[%l+', '')
if not string.match(char_code, char_preedit_code) then
-- 输入编码串和词条反查结果不匹配(考虑到多音字, 开启了模糊音, 纠错音), 返回false, 表示隐藏这个词条
return false
end
end
end
-- 输入编码串和词条反查结果匹配, 返回true, 表示对这个词条降频
return true
end
local function append_word_to_droplist(ctx, action_type, reversedb)
local word = ctx.word
local input_code = ctx.code
if action_type == 'drop' then
table.insert(drop_list, word) -- 高亮选中的词条插入到 drop_list
return true
end
local input_code_tbl = string.split(input_code, " ")
local cand_code = reversedb:lookup(word) or "" -- 反查候选项文字编码
-- 二字词 的匹配检查, 匹配返回true, 不匹配返回false
local match_result = check_encode_matched(cand_code, word, input_code_tbl, reversedb)
local ccand_code = string.gsub(cand_code, '%[%l%l', '')
-- 如有 `[` 引导的辅助码情况, 去掉引导符及之后的所有形码字符
local input_str = string.gsub(input_code, '%[%l+', '')
local input_code_str = table.concat(input_code_tbl, '')
-- 单字和二字词 的匹配检查, 如果匹配, 降频
if string.match(ccand_code, input_str) or match_result then
if turndown_freq_list[word] then
table.insert(turndown_freq_list[word], input_code_str)
else
turndown_freq_list[word] = { input_code_str }
end
return 'turndown_freq'
end
-- 单字和二字词 如果不匹配 就隐藏
if not hide_list[word] then
hide_list[word] = { input_code_str }
return true
else
-- 隐藏的词条如果已经在 hide_list 中, 则将输入串追加到 值表中, 如: ['藏'] = {'chang', 'zhang'}
if not table.find_index(hide_list[word], input_code_str) then
table.insert(hide_list[word], input_code_str)
return true
else
return false
end
end
end
local function processor(key, env)
local engine = env.engine
local config = engine.schema.config
local context = engine.context
-- local top_cand_text = context:get_commit_text()
-- local preedit_code = context.input
local preedit_code = context:get_script_text()
local turndown_cand_key = config:get_string("key_binder/turn_down_cand") or "Control+j"
local drop_cand_key = config:get_string("key_binder/drop_cand") or "Control+d"
local action_map = {
[turndown_cand_key] = 'hide',
[drop_cand_key] = 'drop'
}
-- local schema_id = config:get_string("schema/schema_id")
local schema_id = config:get_string("translator/dictionary") -- 多方案共用字典取主方案名称
---@diagnostic disable-next-line: undefined-global
local reversedb = ReverseLookup(schema_id)
if key:repr() == turndown_cand_key or key:repr() == drop_cand_key then
local cand = context:get_selected_candidate()
local action_type = action_map[key:repr()]
local ctx_map = {
['word'] = cand.text,
['code'] = preedit_code
}
local res = append_word_to_droplist(ctx_map, action_type, reversedb)
context:refresh_non_confirmed_composition() -- 刷新当前输入法候选菜单, 实现看到实时效果
if type(res) == "boolean" then
-- 期望被删的词和隐藏的词条写入文件(drop_words.lua, hide_words.lua)
write_word_to_file(action_type)
else
-- 期望 要调整词频的词条写入 turndown_freq_words.lua 文件
write_word_to_file(res)
end
return 1 -- kAccept
end
return 2 -- kNoop, 不做任何操作, 交给下个组件处理
end
return processor

View File

@@ -0,0 +1,41 @@
-- wrap utf8.sub(str,head_index, tail_index)
-- wrap string.split(str,sp,sp1)
-- string.utf8_len = utf8.len
-- string.utf8_offset= utf8.offset
-- string.utf8_sub= utf8.sub
function string.split(str, sp, sp1)
sp = type(sp) == "string" and sp or " "
if #sp == 0 then
sp = "([%z\1-\127\194-\244][\128-\191]*)"
elseif #sp == 1 then
sp = "[^" .. (sp == "%" and "%%" or sp) .. "]*"
else
sp1 = sp1 or "^"
str = str:gsub(sp, sp1)
sp = "[^" .. sp1 .. "]*"
end
local tab = {}
for v in str:gmatch(sp) do
table.insert(tab, v)
end
return tab
end
function utf8.gsub(str, si, ei)
local function index(ustr, i)
return i >= 0 and (ustr:utf8_offset(i) or ustr:len() + 1)
or (ustr:utf8_offset(i) or 1)
end
local u_si = index(str, si)
ei = ei or str:utf8_len()
ei = ei >= 0 and ei + 1 or ei
local u_ei = index(str, ei) - 1
return str:sub(u_si, u_ei)
end
string.utf8_len = utf8.len
string.utf8_offset = utf8.offset
string.utf8_sub = utf8.gsub
return true

View File

@@ -0,0 +1,4 @@
local turndown_freq_words =
{ ["示~例~"] = { "shili", },
}
return turndown_freq_words

188
lua/corrector.lua Normal file
View File

@@ -0,0 +1,188 @@
--[[
错音错字提示。
示例:「给予」的正确读音是 ji yu当用户输入 gei yu 时,在候选项的 comment 显示正确读音
示例:「按耐」的正确写法是「按捺」,当用户输入「按耐」时,在候选项的 comment 显示正确写法
关闭此 Lua 时,同时需要关闭 translator/spelling_hints否则 comment 里都是拼音
为了让这个 Lua 同时适配全拼与双拼,使用 `spelling_hints` 生成的 comment全拼拼音作为通用的判断条件。
感谢大佬@[Shewer Lu](https://github.com/shewer)提供的思路。
容错词在 cn_dicts/others.dict.yaml 中,有新增建议可以提个 issue
--]]
local M = {}
function M.init(env)
local config = env.engine.schema.config
env.keep_comment = config:get_bool('translator/keep_comments')
local delimiter = config:get_string('speller/delimiter')
if delimiter and #delimiter > 0 and delimiter:sub(1,1) ~= ' ' then
env.delimiter = delimiter:sub(1,1)
end
env.name_space = env.name_space:gsub('^*', '')
M.style = config:get_string(env.name_space) or '{comment}'
M.corrections = {
-- 错音
["hun dun"] = { text = "馄饨", comment = "hún tun" },
["zhu jiao"] = { text = "主角", comment = "zhǔ jué" },
["jiao se"] = { text = "角色", comment = "jué sè" },
["chi pi sa"] = { text = "吃比萨", comment = "chī bǐ sà" },
["pi sa bing"] = { text = "比萨饼", comment = "bǐ sà bǐng" },
["shui fu"] = { text = "说服", comment = "shuō fú" },
["dao hang"] = { text = "道行", comment = "dào heng" },
["mo yang"] = { text = "模样", comment = "mú yàng" },
["you mo you yang"] = { text = "有模有样", comment = "yǒu mú yǒu yàng" },
["yi mo yi yang"] = { text = "一模一样", comment = "yī mú yī yàng" },
["zhuang mo zuo yang"] = { text = "装模作样", comment = "zhuāng mú zuò yàng" },
["ren mo gou yang"] = { text = "人模狗样", comment = "rén mú gǒu yàng" },
["mo ban"] = { text = "模板", comment = "mú bǎn" },
["a mi tuo fo"] = { text = "阿弥陀佛", comment = "ē mí tuó fó" },
["na mo a mi tuo fo"] = { text = "南无阿弥陀佛", comment = "nā mó ē mí tuó fó" },
["nan wu a mi tuo fo"] = { text = "南无阿弥陀佛", comment = "nā mó ē mí tuó fó" },
["nan wu e mi tuo fo"] = { text = "南无阿弥陀佛", comment = "nā mó ē mí tuó fó" },
["gei yu"] = { text = "给予", comment = "jǐ yǔ" },
["bin lang"] = { text = "槟榔", comment = "bīng láng" },
["zhang bai zhi"] = { text = "张柏芝", comment = "zhāng bó zhī" },
["teng man"] = { text = "藤蔓", comment = "téng wàn" },
["nong tang"] = { text = "弄堂", comment = "lòng táng" },
["xin kuan ti pang"] = { text = "心宽体胖", comment = "xīn kuān tǐ pán" },
["mai yuan"] = { text = "埋怨", comment = "mán yuàn" },
["xu yu wei she"] = { text = "虚与委蛇", comment = "xū yǔ wēi yí" },
["mu na"] = { text = "木讷", comment = "mù nè" },
["du le le"] = { text = "独乐乐", comment = "dú yuè lè" },
["zhong le le"] = { text = "众乐乐", comment = "zhòng yuè lè" },
["xun ma"] = { text = "荨麻", comment = "qián má" },
["qian ma zhen"] = { text = "荨麻疹", comment = "xún má zhěn" },
["mo ju"] = { text = "模具", comment = "mú jù" },
["cao zhi"] = { text = "草薙", comment = "cǎo tì" },
["cao zhi jing"] = { text = "草薙京", comment = "cǎo tì jīng" },
["cao zhi jian"] = { text = "草薙剑", comment = "cǎo tì jiàn" },
["jia ping ao"] = { text = "贾平凹", comment = "jiǎ píng wā" },
["xue fo lan"] = { text = "雪佛兰", comment = "xuě fú lán" },
["qiang jin"] = { text = "强劲", comment = "qiáng jìng" },
["tong ti"] = { text = "胴体", comment = "dòng tǐ" },
["li neng kang ding"] = { text = "力能扛鼎", comment = "lì néng gāng dǐng" },
["ya lv jiang"] = { text = "鸭绿江", comment = "yā lù jiāng" },
["da fu bian bian"] = { text = "大腹便便", comment = "dà fù pián pián" },
["ka bo zi"] = { text = "卡脖子", comment = "qiǎ bó zi" },
["zhi sheng"] = { text = "吱声", comment = "zī shēng" },
["chan he"] = { text = "掺和", comment = "chān huo" },
["can huo"] = { text = "掺和", comment = "chān huo" },
["can he"] = { text = "掺和", comment = "chān huo" },
["cheng zhi"] = { text = "称职", comment = "chèn zhí" },
["luo shi fen"] = { text = "螺蛳粉", comment = "luó sī fěn" },
["tiao huan"] = { text = "调换", comment = "diào huàn" },
["tai xing shan"] = { text = "太行山", comment = "tài háng shān" },
["jie si di li"] = { text = "歇斯底里", comment = "xiē sī dǐ lǐ" },
["fa xiao"] = { text = "发酵", comment = "fā jiào" },
["xiao mu jun"] = { text = "酵母菌", comment = "jiào mǔ jūn" },
["yin hong"] = { text = "殷红", comment = "yān hóng" },
["nuan he"] = { text = "暖和", comment = "nuǎn huo" },
["mo ling liang ke"] = { text = "模棱两可", comment = "mó léng liǎng kě" },
["pan yang hu"] = { text = "鄱阳湖", comment = "pó yáng hú" },
["bo jing"] = { text = "脖颈", comment = "bó gěng" },
["bo jing er"] = { text = "脖颈儿", comment = "bó gěng er" },
["niu pi xian"] = { text = "牛皮癣", comment = "niú pí xuǎn" },
["hua ban xian"] = { text = "花斑癣", comment = "huā bān xuǎn" },
["ti xian"] = { text = "体癣", comment = "tǐ xuǎn" },
["gu xian"] = { text = "股癣", comment = "gǔ xuǎn" },
["jiao xian"] = { text = "脚癣", comment = "jiǎo xuǎn" },
["zu xian"] = { text = "足癣", comment = "zú xuǎn" },
["jie zha"] = { text = "结扎", comment = "jié zā" },
["hai shen wei"] = { text = "海参崴", comment = "hǎi shēn wǎi" },
["hou pu"] = { text = "厚朴", comment = "hòu pò " },
["da wan ma"] = { text = "大宛马", comment = "dà yuān mǎ" },
["ci ya"] = { text = "龇牙", comment = "zī yá" },
["ci zhe ya"] = { text = "龇着牙", comment = "zī zhe yá" },
["ci ya lie zui"] = { text = "龇牙咧嘴", comment = "zī yá liě zuǐ" },
["tou pi xue"] = { text = "头皮屑", comment = "tóu pí xiè" },
["liu an shi"] = { text = "六安市", comment = "lù ān shì" },
["liu an xian"] = { text = "六安县", comment = "lù ān xiàn" },
["an hui sheng liu an shi"] = { text = "安徽省六安市", comment = "ān huī shěng lù ān shì" },
["an hui liu an"] = { text = "安徽六安", comment = "ān huī lù ān" },
["an hui liu an shi"] = { text = "安徽六安市", comment = "ān huī lù ān shì" },
["nan jing liu he"] = { text = "南京六合", comment = "nán jīng lù hé" },
["nan jing shi liu he"] = { text = "南京六合区", comment = "nán jīng lù hé qū" },
["nan jing shi liu he qu"] = { text = "南京市六合区", comment = "nán jīng shì lù hé qū" },
["nuo da"] = { text = "偌大", comment = "偌(ruò)大" },
["yin jiu zhi ke"] = { text = "饮鸩止渴", comment = "饮鸩(zhèn)止渴" },
["yin jiu jie ke"] = { text = "饮鸩解渴", comment = "饮鸩(zhèn)解渴" },
["gong shang jiao zhi yu"] = { text = "宫商角徵羽", comment = "宫商角(jué)徵羽" },
["shan qi deng"] = { text = "氙气灯", comment = "氙(xiān)气灯" },
["shan qi da deng"] = { text = "氙气大灯", comment = "氙(xiān)气大灯" },
["shan qi shou dian tong"] = { text = "氙气手电筒", comment = "氙(xiān)气手电筒" },
["yin gai"] = { text = "应该", comment = "应(yīng)该" },
["nian tie"] = { text = "粘贴", comment = "粘(zhān)贴" },
["gao ju li"] = { text = "高句丽", comment = "高句(gōu)丽" },
["jiao dou shi"] = { text = "角斗士", comment = "角(jué)斗士" },
["suo sha mi"] = { text = "缩砂密", comment = "缩(sù)砂密" },
["po ji pao"] = { text = "迫击炮", comment = "迫(pǎi)击炮" },
["wen bo"] = { text = "榅桲", comment = "wēn po" },
["bi ji"] = { text = "荸荠", comment = "bí qi" },
["rou yi"] = { text = "柔荑", comment = "柔荑(tí)" },
["rou yi hua xu"] = { text = "柔荑花序", comment = "柔荑(tí)花序" },
["shou ru rou yi"] = { text = "手如柔荑", comment = "手如柔荑(tí)" },
["wen ting jun"] = { text = "温庭筠", comment = "温庭筠(yún)" },
["guan ka"] = { text = "关卡", comment = "guān qiǎ" },
["san wei zhen huo"] = { text = "三昧真火", comment = "三昧(mèi)真火" },
["qing ping zhi mo"] = { text = "青𬞟之末", comment = "青𬞟(pín)之末" },
["qi yu qing ping zhi mo"] = { text = "起于青𬞟之末", comment = "起于青𬞟(pín)之末" },
["feng qi yu qing ping zhi mo"] = { text = "风起于青𬞟之末", comment = "风起于青𬞟(pín)之末" },
["you hui juan"] = { text = "优惠券", comment = "优惠券(quàn)" },
["gong quan"] = { text = "拱券", comment = "gǒng xuàn" },
["pu ru"] = { text = "哺乳", comment = "bǔ rǔ" },
["nao zu zhong"] = { text = "脑卒中", comment = "nǎo cù zhòng" },
["xie hu"] = { text = "潟湖", comment = "xì hú" },
["guo pu"] = { text = "果脯", comment = "guǒ fǔ" },
["rou pu"] = { text = "肉脯", comment = "ròu fǔ" },
["bai qi tun"] = { text = "白𬶨豚", comment = "bái jì tún" },
-- 错字
["pu jie"] = { text = "扑街", comment = "仆街" },
["pu gai"] = { text = "扑街", comment = "仆街" },
["pu jie zai"] = { text = "扑街仔", comment = "仆街仔" },
["pu gai zai"] = { text = "扑街仔", comment = "仆街仔" },
["ceng jin"] = { text = "曾今", comment = "曾经" },
["an nai"] = { text = "按耐", comment = "按捺(nà)" },
["an nai bu zhu"] = { text = "按耐不住", comment = "按捺(nà)不住" },
["bie jie"] = { text = "别介", comment = "别价(jie)" },
["beng jie"] = { text = "甭介", comment = "甭价(jie)" },
["xue mai pen zhang"] = { text = "血脉喷张", comment = "血脉贲(bēn)张 | 血脉偾(fèn)张" },
["qi ke fu"] = { text = "契科夫", comment = "契诃(hē)夫" },
["zhao cha"] = { text = "找茬", comment = "找碴" },
["zhao cha er"] = { text = "找茬儿", comment = "找碴儿" },
["da jia lai zhao cha"] = { text = "大家来找茬", comment = "大家来找碴" },
["da jia lai zhao cha er"] = { text = "大家来找茬儿", comment = "大家来找碴儿" },
["cou huo"] = { text = "凑活", comment = "凑合(he)" },
["ju hui"] = { text = "钜惠", comment = "巨惠" },
["mo xie zuo"] = { text = "魔蝎座", comment = "摩羯(jié)座" },
["pi sa"] = { text = "披萨", comment = "比(bǐ)萨" },
["geng quan"] = { text = "梗犬", comment = "㹴犬" },
}
end
function M.func(input, env)
for cand in input:iter() do
-- cand.comment 是目前输入的词汇的完整拼音
local pinyin = cand.comment:match("^(.-)$")
if pinyin and #pinyin > 0 then
local correction_pinyin = pinyin
if env.delimiter then
correction_pinyin = correction_pinyin:gsub(env.delimiter,' ')
end
local c = M.corrections[correction_pinyin]
if c and cand.text == c.text then
cand:get_genuine().comment = string.gsub(M.style, "{comment}", c.comment)
else
if env.keep_comment then
cand:get_genuine().comment = string.gsub(M.style, "{comment}", pinyin)
else
cand:get_genuine().comment = ""
end
end
end
yield(cand)
end
end
return M

72
lua/date_translator.lua Normal file
View File

@@ -0,0 +1,72 @@
-- 日期时间,可在方案中配置触发关键字。
-- 提高权重的原因:因为在方案中设置了大于 1 的 initial_quality导致 rq sj xq dt ts 产出的候选项在所有词语的最后。
local function yield_cand(seg, text)
local cand = Candidate('', seg.start, seg._end, text, '')
cand.quality = 100
yield(cand)
end
local M = {}
function M.init(env)
local config = env.engine.schema.config
env.name_space = env.name_space:gsub('^*', '')
M.date = config:get_string(env.name_space .. '/date') or 'rq'
M.time = config:get_string(env.name_space .. '/time') or 'sj'
M.week = config:get_string(env.name_space .. '/week') or 'xq'
M.datetime = config:get_string(env.name_space .. '/datetime') or 'dt'
M.timestamp = config:get_string(env.name_space .. '/timestamp') or 'ts'
end
function M.func(input, seg, env)
-- 日期
if (input == M.date) then
local current_time = os.time()
yield_cand(seg, os.date('%Y-%m-%d', current_time))
yield_cand(seg, os.date('%Y/%m/%d', current_time))
yield_cand(seg, os.date('%Y.%m.%d', current_time))
yield_cand(seg, os.date('%Y%m%d', current_time))
yield_cand(seg, os.date('%Y年%m月%d日', current_time):gsub('年0', ''):gsub('月0',''))
-- 时间
elseif (input == M.time) then
local current_time = os.time()
yield_cand(seg, os.date('%H:%M', current_time))
yield_cand(seg, os.date('%H:%M:%S', current_time))
-- 星期
elseif (input == M.week) then
local current_time = os.time()
local week_tab = {'', '', '', '', '', '', ''}
local text = week_tab[tonumber(os.date('%w', current_time) + 1)]
yield_cand(seg, '星期' .. text)
yield_cand(seg, '礼拜' .. text)
yield_cand(seg, '' .. text)
-- ISO 8601/RFC 3339 的时间格式 (固定东八区)(示例 2022-01-07T20:42:51+08:00
elseif (input == M.datetime) then
local current_time = os.time()
yield_cand(seg, os.date('%Y-%m-%dT%H:%M:%S+08:00', current_time))
yield_cand(seg, os.date('%Y-%m-%d %H:%M:%S', current_time))
yield_cand(seg, os.date('%Y%m%d%H%M%S', current_time))
-- 时间戳(十位数,到秒,示例 1650861664
elseif (input == M.timestamp) then
local current_time = os.time()
yield_cand(seg, string.format('%d', current_time))
end
-- -- 显示内存
-- local cand = Candidate("date", seg.start, seg._end, ("%.f"):format(collectgarbage('count')), "")
-- cand.quality = 100
-- yield(cand)
-- if input == "xxx" then
-- collectgarbage()
-- local cand = Candidate("date", seg.start, seg._end, "collectgarbage()", "")
-- cand.quality = 100
-- yield(cand)
-- end
end
return M

12
lua/debuger.lua Normal file
View File

@@ -0,0 +1,12 @@
local function debuger(input, env)
for cand in input:iter() do
yield(ShadowCandidate(
cand,
cand.type,
cand.text,
env.engine.context.input .. " - " .. env.engine.context:get_preedit().text .. " - " .. cand.preedit
))
end
end
return debuger

18
lua/en_spacer.lua Normal file
View File

@@ -0,0 +1,18 @@
-- 英文词条上屏自动添加空格
-- 在 engine/filters 的倒数第二个位置,增加 - lua_filter@en_spacer
--
-- 英文后,再输入英文单词(必须为候选项)自动添加空格
local F = {}
function F.func( input, env )
local latest_text = env.engine.context.commit_history:latest_text()
for cand in input:iter() do
if cand.text:match( '^[%a\']+[%a\']*$' ) and latest_text and #latest_text > 0 and
latest_text:find( '^ ?[%a\']+[%a\']*$' ) then
cand = cand:to_shadow_candidate( 'en_spacer', cand.text:gsub( '(%a+\'?%a*)', ' %1' ), cand.comment )
end
yield( cand )
end
end
return F

11
lua/force_gc.lua Normal file
View File

@@ -0,0 +1,11 @@
-- 暴力 GC
-- 详情 https://github.com/hchunhui/librime-lua/issues/307
-- collectgarbage():默认调用,等同于 collectgarbage("collect"),触发完整的垃圾回收。
-- collectgarbage("step"):执行垃圾回收的一小步。这个函数会返回一个布尔值,表示这一步是否完成了整个收集周期。
-- 这样也不会导致卡顿,那就每次都调用一下吧,内存稳稳的
local function force_gc()
-- collectgarbage()
collectgarbage("step")
end
return force_gc

32
lua/is_in_user_dict.lua Normal file
View File

@@ -0,0 +1,32 @@
-- 根据是否在用户词典,在结尾加上一个星号 *
-- is_in_user_dict: true 输入过的内容
-- is_in_user_dict: false 或不写 未输入过的内容
local M = {}
function M.init(env)
local config = env.engine.schema.config
env.name_space = env.name_space:gsub('^*', '')
M.is_in_user_dict = config:get_bool(env.name_space) or true
end
function M.func(input, env)
for cand in input:iter() do
-- 用户词库,加上*号
if cand.type == "user_phrase" then
cand.comment = '*'
end
-- 用户置顶词
-- if cand.type == "user_table" then
-- cand.comment = cand.comment .. '⚡️'
-- end
-- 整句联想,加上𑄗符号
if cand.type == 'sentence' then
cand.comment = ''
end
yield(cand)
end
end
return M

52
lua/long_word_filter.lua Normal file
View File

@@ -0,0 +1,52 @@
-- 长词优先(提升「西安」「提案」「图案」「饥饿」等词汇的优先级)
-- 感谢&参考于: https://github.com/tumuyan/rime-melt
-- 不提升包含英文、数字的候选项
-- 不提升包含 emoji、假名的候选项通过将此 Lua 放到 simplifier@emoji 前面来实现)
local M = {}
function M.init(env)
-- 提升 count 个词语,插入到第 idx 个位置,默认 2、4。
local config = env.engine.schema.config
env.name_space = env.name_space:gsub("^*", "")
M.count = config:get_int(env.name_space .. "/count") or 2
M.idx = config:get_int(env.name_space .. "/idx") or 4
end
function M.func(input)
local l = {}
local firstWordLength = 0 -- 记录第一个候选词的长度,提前的候选词至少要比第一个候选词长
local done = 0 -- 记录筛选了多少个词条(只提升 count 个词的权重)
local i = 1
for cand in input:iter() do
local leng = utf8.len(cand.text)
-- 只以第一个候选项的长度作为参考
if firstWordLength < 1 then
firstWordLength = leng
end
-- 不处理 M.idx 之前的候选项
if i < M.idx then
i = i + 1
yield(cand)
-- 长词直接 yield其余的放到 l 里
elseif leng <= firstWordLength or cand.text:find("[%a%d]") then
table.insert(l, cand)
else
yield(cand)
done = done + 1
end
-- 找齐了或者 l 太大了,就不找了,一般前 50 个就够了
if done == M.count or #l > 50 then
break
end
end
-- yield l 及后续的候选项
for _, cand in ipairs(l) do
yield(cand)
end
for cand in input:iter() do
yield(cand)
end
end
return M

689
lua/lunar.lua Executable file
View File

@@ -0,0 +1,689 @@
--[[
Lua 阿拉伯数字转中文实现 https://blog.csdn.net/lp12345678910/article/details/121396243
农历功能复制自 https://github.com/boomker/rime-fast-xhup
--]]
--
-- 农历,可在方案中配置触发关键字。
-- 数字转中文:
local numerical_units = {
"",
"",
"",
"",
"",
"",
"",
"",
"亿",
"",
"",
"",
"",
"",
"",
"",
}
local numerical_names = {
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
}
local function convert_arab_to_chinese(number)
local n_number = tonumber(number)
assert(n_number, "传入参数非正确number类型!")
-- 0 ~ 9
if n_number < 10 then
return numerical_names[n_number + 1]
end
-- 一十九 => 十九
if n_number < 20 then
local digit = string.sub(n_number, 2, 2)
if digit == "0" then
return ""
else
return "" .. numerical_names[digit + 1]
end
end
--[[
1. 最大输入9位
超过9位string的len加2位因为有.0的两位)
零 ~ 九亿九千九百九十九万九千九百九十九
0 ~ 999999999
2. 最大输入14位超过14位会四舍五入
零 ~ 九十九兆九千九百九十九亿九千九百九十九万九千九百九十九万
0 ~ 99999999999999
--]]
local len_max = 9
local len_number = string.len(number)
assert(
len_number > 0 and len_number <= len_max,
"传入参数位数" .. len_number .. "必须在(0, " .. len_max .. "]之间!"
)
-- 01数字转成表结构存储
local numerical_tbl = {}
for i = 1, len_number do
numerical_tbl[i] = tonumber(string.sub(n_number, i, i))
end
local pre_zero = false
local result = ""
for index, digit in ipairs(numerical_tbl) do
local curr_unit = numerical_units[len_number - index + 1]
local curr_name = numerical_names[digit + 1]
if digit == 0 then
if not pre_zero then
result = result .. curr_name
end
pre_zero = true
else
result = result .. curr_name .. curr_unit
pre_zero = false
end
end
result = string.gsub(result, "零+$", "")
return result
end
-- 农历:
-- 天干名称
local cTianGan = {
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
}
-- 地支名称
local cDiZhi = {
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
}
-- 属相名称
local cShuXiang = {
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
}
-- 农历日期名
local cDayName = {
"初一",
"初二",
"初三",
"初四",
"初五",
"初六",
"初七",
"初八",
"初九",
"初十",
"十一",
"十二",
"十三",
"十四",
"十五",
"十六",
"十七",
"十八",
"十九",
"二十",
"廿一",
"廿二",
"廿三",
"廿四",
"廿五",
"廿六",
"廿七",
"廿八",
"廿九",
"三十",
}
-- 农历月份名
local cMonName = {
"正月",
"二月",
"三月",
"四月",
"五月",
"六月",
"七月",
"八月",
"九月",
"十月",
"冬月",
"腊月",
}
-- 农历数据
local wNongliData = {
"AB500D2",
"4BD0883",
"4AE00DB",
"A5700D0",
"54D0581",
"D2600D8",
"D9500CC",
"655147D",
"56A00D5",
"9AD00CA",
"55D027A",
"4AE00D2",
"A5B0682",
"A4D00DA",
"D2500CE",
"D25157E",
"B5500D6",
"56A00CC",
"ADA027B",
"95B00D3",
"49717C9",
"49B00DC",
"A4B00D0",
"B4B0580",
"6A500D8",
"6D400CD",
"AB5147C",
"2B600D5",
"95700CA",
"52F027B",
"49700D2",
"6560682",
"D4A00D9",
"EA500CE",
"6A9157E",
"5AD00D6",
"2B600CC",
"86E137C",
"92E00D3",
"C8D1783",
"C9500DB",
"D4A00D0",
"D8A167F",
"B5500D7",
"56A00CD",
"A5B147D",
"25D00D5",
"92D00CA",
"D2B027A",
"A9500D2",
"B550781",
"6CA00D9",
"B5500CE",
"535157F",
"4DA00D6",
"A5B00CB",
"457037C",
"52B00D4",
"A9A0883",
"E9500DA",
"6AA00D0",
"AEA0680",
"AB500D7",
"4B600CD",
"AAE047D",
"A5700D5",
"52600CA",
"F260379",
"D9500D1",
"5B50782",
"56A00D9",
"96D00CE",
"4DD057F",
"4AD00D7",
"A4D00CB",
"D4D047B",
"D2500D3",
"D550883",
"B5400DA",
"B6A00CF",
"95A1680",
"95B00D8",
"49B00CD",
"A97047D",
"A4B00D5",
"B270ACA",
"6A500DC",
"6D400D1",
"AF40681",
"AB600D9",
"93700CE",
"4AF057F",
"49700D7",
"64B00CC",
"74A037B",
"EA500D2",
"6B50883",
"5AC00DB",
"AB600CF",
"96D0580",
"92E00D8",
"C9600CD",
"D95047C",
"D4A00D4",
"DA500C9",
"755027A",
"56A00D1",
"ABB0781",
"25D00DA",
"92D00CF",
"CAB057E",
"A9500D6",
"B4A00CB",
"BAA047B",
"AD500D2",
"55D0983",
"4BA00DB",
"A5B00D0",
"5171680",
"52B00D8",
"A9300CD",
"795047D",
"6AA00D4",
"AD500C9",
"5B5027A",
"4B600D2",
"96E0681",
"A4E00D9",
"D2600CE",
"EA6057E",
"D5300D5",
"5AA00CB",
"76A037B",
"96D00D3",
"4AB0B83",
"4AD00DB",
"A4D00D0",
"D0B1680",
"D2500D7",
"D5200CC",
"DD4057C",
"B5A00D4",
"56D00C9",
"55B027A",
"49B00D2",
"A570782",
"A4B00D9",
"AA500CE",
"B25157E",
"6D200D6",
"ADA00CA",
"4B6137B",
"93700D3",
"49F08C9",
"49700DB",
"64B00D0",
"68A1680",
"EA500D7",
"6AA00CC",
"A6C147C",
"AAE00D4",
"92E00CA",
"D2E0379",
"C9600D1",
"D550781",
"D4A00D9",
"DA400CD",
"5D5057E",
"56A00D6",
"A6C00CB",
"55D047B",
"52D00D3",
"A9B0883",
"A9500DB",
"B4A00CF",
"B6A067F",
"AD500D7",
"55A00CD",
"ABA047C",
"A5A00D4",
"52B00CA",
"B27037A",
"69300D1",
"7330781",
"6AA00D9",
"AD500CE",
"4B5157E",
"4B600D6",
"A5700CB",
"54E047C",
"D1600D2",
"E960882",
"D5200DA",
"DAA00CF",
"6AA167F",
"56D00D7",
"4AE00CD",
"A9D047D",
"A2D00D4",
"D1500C9",
"F250279",
"D5200D1",
}
-- 十进制转二进制
local function Dec2bin(n)
local t, t1
local tables = {}
t = tonumber(n)
while math.floor(t / 2) >= 1 do
t1 = t and math.fmod(t, 2)
if t1 > 0 then
if #tables > 0 then
table.insert(tables, 1, 1)
else
tables[1] = 1
end
else
if #tables > 0 then
table.insert(tables, 1, 0)
else
tables[1] = 0
end
end
t = math.floor(t / 2)
if t == 1 then
if #tables > 0 then
table.insert(tables, 1, 1)
else
tables[1] = 1
end
end
end
return string.gsub(table.concat(tables), "^[0]+", "")
end
-- 2/10/16进制互转
local function Atoi(x, inPuttype, outputtype)
local r
if tonumber(inPuttype) == 2 then
if tonumber(outputtype) == 10 then -- 2进制-->10进制
r = tonumber(tostring(x), 2)
-- elseif tonumber(outputtype) == 16 then -- 2进制-->16进制
-- r = bin2hex(tostring(x))
end
elseif tonumber(inPuttype) == 10 then
if tonumber(outputtype) == 2 then -- 10进制-->2进制
r = Dec2bin(tonumber(x))
elseif tonumber(outputtype) == 16 then -- 10进制-->16进制
r = string.format("%x", x)
end
elseif tonumber(inPuttype) == 16 then
if tonumber(outputtype) == 2 then -- 16进制-->2进制
r = Dec2bin(tonumber(tostring(x), 16))
elseif tonumber(outputtype) == 10 then -- 16进制-->10进制
r = tonumber(tostring(x), 16)
end
end
return r
end
-- 农历16进制数据分解
local function Analyze(Data)
local rtn1, rtn2, rtn3, rtn4
rtn1 = Atoi(string.sub(Data, 1, 3), 16, 2)
if string.len(rtn1) < 12 then
rtn1 = "0" .. rtn1
end
rtn2 = string.sub(Data, 4, 4)
rtn3 = Atoi(string.sub(Data, 5, 5), 16, 10)
rtn4 = Atoi(string.sub(Data, -2, -1), 16, 10)
if string.len(rtn4) == 3 then
rtn4 = "0" .. Atoi(string.sub(Data, -2, -1), 16, 10)
end
-- string.gsub(rtn1, "^[0]*", "")
return { rtn1, rtn2, rtn3, rtn4 }
end
-- 年天数判断
local function IsLeap(y)
local year = tonumber(y)
if not year then
return nil
end
if math.fmod(year, 400) ~= 0 and math.fmod(year, 4) == 0 or math.fmod(year, 400) == 0 then
return 366
else
return 365
end
end
-- 返回当年过了多少天
local function leaveDate(y)
local day, total
total = 0
if IsLeap(tonumber(string.sub(y, 1, 4))) > 365 then
day = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
else
day = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
end
if tonumber(string.sub(y, 5, 6)) > 1 then
for i = 1, tonumber(string.sub(y, 5, 6)) - 1 do
total = total + day[i]
end
total = total + tonumber(string.sub(y, 7, 8))
else
return tonumber(string.sub(y, 7, 8))
end
return tonumber(total)
end
-- 计算日期差两个8位数日期之间相隔的天数date2>date1
local function diffDate(date1, date2)
local n, total
total = 0
date1 = tostring(date1)
date2 = tostring(date2)
if tonumber(date2) > tonumber(date1) then
n = tonumber(string.sub(date2, 1, 4)) - tonumber(string.sub(date1, 1, 4))
if n > 1 then
for i = 1, n - 1 do
total = total + IsLeap(tonumber(string.sub(date1, 1, 4)) + i)
end
total = total
+ leaveDate(tonumber(string.sub(date2, 1, 8)))
+ IsLeap(tonumber(string.sub(date1, 1, 4)))
- leaveDate(tonumber(string.sub(date1, 1, 8)))
elseif n == 1 then
total = IsLeap(tonumber(string.sub(date1, 1, 4)))
- leaveDate(tonumber(string.sub(date1, 1, 8)))
+ leaveDate(tonumber(string.sub(date2, 1, 8)))
else
total = leaveDate(tonumber(string.sub(date2, 1, 8))) - leaveDate(tonumber(string.sub(date1, 1, 8)))
-- print(date1 .. "-" .. date2)
end
elseif tonumber(date2) == tonumber(date1) then
return 0
else
return -1
end
return total
end
-- 公历转农历支持转化范围公元1900-2100年
-- 公历日期 Gregorian:格式 YYYYMMDD
-- <返回值>农历日期 中文 天干地支属相
local function Date2LunarDate(Gregorian)
Gregorian = tostring(Gregorian)
local Year, Month, Day, Pos, Data0, Data1, MonthInfo, LeapInfo, Leap, Newyear, LYear, thisMonthInfo
Year = tonumber(Gregorian.sub(Gregorian, 1, 4))
Month = tonumber(Gregorian.sub(Gregorian, 5, 6))
Day = tonumber(Gregorian.sub(Gregorian, 7, 8))
LunarDate3 = Year .. "" .. Month .."".. Day .. ""
if Year > 2100 or Year < 1899 or Month > 12 or Month < 1 or Day < 1 or Day > 31 or string.len(Gregorian) < 8 then
-- 2024.07.27 这个不能判断不存在的日期,例如 02.31 04.30 等,会显示农历,但不存在 by Mirtle
return "无效日期", "无效日期"
end
-- 获取两百年内的农历数据
Pos = Year - 1900 + 2
Data0 = wNongliData[Pos - 1]
Data1 = wNongliData[Pos]
-- 判断农历年份
local tb1 = Analyze(Data1)
MonthInfo = tb1[1]
LeapInfo = tb1[2]
Leap = tb1[3]
Newyear = tb1[4]
local Date1 = Year .. Newyear
local Date2 = Gregorian
local Date3 = diffDate(Date1, Date2) -- 和当年农历新年相差的天数
if Date3 < 0 then
-- print(Data0 .. "-2")
tb1 = Analyze(Data0)
Year = Year - 1
MonthInfo = tb1[1]
LeapInfo = tb1[2]
Leap = tb1[3]
Newyear = tb1[4]
Date1 = Year .. Newyear
Date2 = Gregorian
Date3 = diffDate(Date1, Date2)
-- print(Date2 .. "--" .. Date1 .. "--" .. Date3)
end
Date3 = Date3 + 1
LYear = Year -- 农历年份,就是上面计算后的值
if Leap > 0 then -- 有闰月
thisMonthInfo = string.sub(MonthInfo, 1, tonumber(Leap)) .. LeapInfo .. string.sub(MonthInfo, Leap + 1)
else
thisMonthInfo = MonthInfo
end
local thisMonth, thisDays, LMonth, LDay, Isleap, LunarDate, LunarDate2, LunarYear, LunarMonth
for i = 1, 13 do
thisMonth = string.sub(thisMonthInfo, i, i)
thisDays = 29 + thisMonth
if Date3 > thisDays then
Date3 = Date3 - thisDays
else
if Leap > 0 then
if Leap >= i then
LMonth = i
Isleap = 0
else
LMonth = i - 1
if i - Leap == 1 then
Isleap = 1
else
Isleap = 0
end
end
else
LMonth = i
Isleap = 0
end
LDay = math.floor(Date3)
break
end
end
if Isleap > 0 then
LunarMonth = "" .. cMonName[LMonth]
else
LunarMonth = cMonName[LMonth]
end
local _nis = tostring(LYear)
local _LunarYears = ""
for i = 1, _nis:len() do
local _ni_digit = tonumber(_nis:sub(i, i))
_LunarYears = _LunarYears .. convert_arab_to_chinese(_ni_digit)
end
LunarYear = string.gsub(_LunarYears, "", "")
LunarDate = cTianGan[math.fmod(LYear - 4, 10) + 1]
.. cDiZhi[math.fmod(LYear - 4, 12) + 1]
.. "年("
.. cShuXiang[math.fmod(LYear - 4, 12) + 1]
.. ""
.. LunarMonth
.. cDayName[LDay]
LunarDate2 = LunarYear .. "" .. LunarMonth .. cDayName[LDay]
return LunarDate, LunarDate2, LunarDate3
end
-- 农历
-- 从 lunar: nl 获取农历触发关键字(双拼默认为 lunar
-- 从 recognizer/patterns/gregorian_to_lunar 获取第 2 个字符作为公历转农历的触发前缀,默认为 N
local function translator(input, seg, env)
env.lunar_key_word = env.lunar_key_word or
(env.engine.schema.config:get_string(env.name_space:gsub('^*', '')) or 'nl')
env.gregorian_to_lunar = env.gregorian_to_lunar or
(env.engine.schema.config:get_string('recognizer/patterns/gregorian_to_lunar'):sub(2, 2) or 'N')
if input == env.lunar_key_word then
local date1, date2,date3 = Date2LunarDate(os.date("%Y%m%d"))
local lunar_ymd = (Candidate("", seg.start, seg._end, date2, ""))
lunar_ymd.quality = 999
yield(lunar_ymd)
local lunar_date = Candidate("", seg.start, seg._end, date1, "")
lunar_date.quality = 999
yield(lunar_date)
local date = Candidate("", seg.start, seg._end, date3, "")
date.quality = 999
yield(date)
elseif env.gregorian_to_lunar ~= '' and input:sub(1, 1) == env.gregorian_to_lunar then
local date1, date2, date3 = Date2LunarDate(input:sub(2))
local lunar_ymd = (Candidate("", seg.start, seg._end, date2, ""))
lunar_ymd.quality = 999
yield(lunar_ymd)
local lunar_date = Candidate("", seg.start, seg._end, date1, "")
lunar_date.quality = 999
yield(lunar_date)
local date = Candidate("", seg.start, seg._end, date3, "")
date.quality = 999
yield(date)
end
end
local function yield_cand(seg, text)
local cand = Candidate('', seg.start, seg._end, text, '')
cand.quality = 1000000
yield(cand)
end
return translator

163
lua/number_translator.lua Normal file
View File

@@ -0,0 +1,163 @@
-- 来源 https://github.com/yanhuacuo/98wubi-tables > http://98wb.ysepan.com/
-- 数字、金额大写
-- 触发前缀默认为 recognizer/patterns/number 的第 2 个字符,即 R
local function splitNumPart(str)
local part = {}
part.int, part.dot, part.dec = string.match(str, "^(%d*)(%.?)(%d*)")
return part
end
local function GetPreciseDecimal(nNum, n)
if type(nNum) ~= "number" then nNum = tonumber(nNum) end
n = n or 0;
n = math.floor(n)
if n < 0 then n = 0 end
local nDecimal = 10 ^ n
local nTemp = math.floor(nNum * nDecimal);
local nRet = nTemp / nDecimal;
return nRet;
end
local function decimal_func(str, posMap, valMap)
local dec
posMap = posMap or { [1] = "", [2] = "", [3] = "", [4] = "" }
valMap = valMap or { [0] = "", "", "", "", "", "", "", "", "", "" }
if #str > 4 then dec = string.sub(tostring(str), 1, 4) else dec = tostring(str) end
dec = string.gsub(dec, "0+$", "")
if dec == "" then return "" end
local result = ""
for pos = 1, #dec do
local val = tonumber(string.sub(dec, pos, pos))
if val ~= 0 then result = result .. valMap[val] .. posMap[pos] else result = result .. valMap[val] end
end
result = result:gsub(valMap[0] .. valMap[0], valMap[0])
return result:gsub(valMap[0] .. valMap[0], valMap[0])
end
-- 把数字串按千分位四位数分割,进行转换为中文
local function formatNum(num, t)
local digitUnit, wordFigure
local result = ""
num = tostring(num)
if tonumber(t) < 1 then digitUnit = { "", "", "", "" } else digitUnit = { "", "", "", "" } end
if tonumber(t) < 1 then
wordFigure = { "", "", "", "", "", "", "", "", "", "" }
else
wordFigure = { "", "", "", "", "", "", "", "", "", "" }
end
if string.len(num) > 4 or tonumber(num) == 0 then return wordFigure[1] end
local lens = string.len(num)
for i = 1, lens do
local n = wordFigure[tonumber(string.sub(num, -i, -i)) + 1]
if n ~= wordFigure[1] then result = n .. digitUnit[i] .. result else result = n .. result end
end
result = result:gsub(wordFigure[1] .. wordFigure[1], wordFigure[1])
result = result:gsub(wordFigure[1] .. "$", "")
result = result:gsub(wordFigure[1] .. "$", "")
return result
end
-- 数值转换为中文
local function number2cnChar(num, flag, digitUnit, wordFigure) --flag=0中文小写反之为大写
local result = ""
if tonumber(flag) < 1 then
digitUnit = digitUnit or { [1] = "", [2] = "亿" }
wordFigure = wordFigure or { [1] = "", [2] = "", [3] = "", [4] = "" }
else
digitUnit = digitUnit or { [1] = "", [2] = "亿" }
wordFigure = wordFigure or { [1] = "", [2] = "", [3] = "", [4] = "" }
end
local lens = string.len(num)
if lens < 5 then
result = formatNum(num, flag)
elseif lens < 9 then
result = formatNum(string.sub(num, 1, -5), flag) .. digitUnit[1] .. formatNum(string.sub(num, -4, -1), flag)
elseif lens < 13 then
result = formatNum(string.sub(num, 1, -9), flag) ..
digitUnit[2] ..
formatNum(string.sub(num, -8, -5), flag) .. digitUnit[1] .. formatNum(string.sub(num, -4, -1), flag)
else
result = ""
end
result = result:gsub("^" .. wordFigure[1], "")
result = result:gsub(wordFigure[1] .. digitUnit[1], "")
result = result:gsub(wordFigure[1] .. digitUnit[2], "")
result = result:gsub(wordFigure[1] .. wordFigure[1], wordFigure[1])
result = result:gsub(wordFigure[1] .. "$", "")
if lens > 4 then result = result:gsub("^" .. wordFigure[2] .. wordFigure[3], wordFigure[3]) end
if result ~= "" then result = result .. wordFigure[4] else result = "数值超限!" end
return result
end
local function number2zh(num, t)
local result, wordFigure
result = ""
if tonumber(t) < 1 then
wordFigure = { "", "", "", "", "", "", "", "", "", "" }
else
wordFigure = { "", "", "", "", "", "", "", "", "", "" }
end
if tostring(num) == nil then return "" end
for pos = 1, string.len(num) do
result = result .. wordFigure[tonumber(string.sub(num, pos, pos) + 1)]
end
result = result:gsub(wordFigure[1] .. wordFigure[1], wordFigure[1])
return result:gsub(wordFigure[1] .. wordFigure[1], wordFigure[1])
end
local function number_translatorFunc(num)
local numberPart = splitNumPart(num)
local result = {}
if numberPart.dot ~= "" then
table.insert(result,
{ number2cnChar(numberPart.int, 0, { "", "亿" }, { "", "", "", "" }) .. number2zh(numberPart.dec, 0),
"〔数字小写〕" })
table.insert(result,
{ number2cnChar(numberPart.int, 1, { "", "" }, { "", "", "", "" }) .. number2zh(numberPart.dec, 1),
"〔数字大写〕" })
else
table.insert(result, { number2cnChar(numberPart.int, 0, { "", "亿" }, { "", "", "", "" }), "〔数字小写〕" })
table.insert(result, { number2cnChar(numberPart.int, 1, { "", "" }, { "", "", "", "" }), "〔数字大写〕" })
end
table.insert(result,
{ number2cnChar(numberPart.int, 0) ..
decimal_func(numberPart.dec, { [1] = "", [2] = "", [3] = "", [4] = "" },
{ [0] = "", "", "", "", "", "", "", "", "", "" }), "〔金额小写〕" })
local number2cnCharInt = number2cnChar(numberPart.int, 1)
local number2cnCharDec = decimal_func(numberPart.dec, { [1] = "", [2] = "", [3] = "", [4] = "" }, { [0] = "", "", "", "", "", "", "", "", "", "" })
if string.len(numberPart.int) > 4 and number2cnCharInt:find('^拾[壹贰叁肆伍陆柒捌玖]?') and number2cnCharInt:find('[万亿]') then -- 简易地规避 utf8 匹配问题
local number2cnCharInt_var = number2cnCharInt:gsub('^拾', '壹拾')
table.insert(result, { number2cnCharInt_var .. number2cnCharDec , "〔金额大写〕"})
-- 会计书写要求 https://github.com/iDvel/rime-ice/issues/989
else
table.insert(result, { number2cnCharInt .. number2cnCharDec , "〔金额大写〕"})
end
return result
end
local function number_translator(input, seg, env)
-- 获取 recognizer/patterns/number 的第 2 个字符作为触发前缀
env.number_keyword = env.number_keyword or
env.engine.schema.config:get_string('recognizer/patterns/number'):sub(2, 2)
local str, num, numberPart
if env.number_keyword ~= '' and input:sub(1, 1) == env.number_keyword then
str = string.gsub(input, "^(%a+)", "")
numberPart = number_translatorFunc(str)
if str and #str > 0 and #numberPart > 0 then
for i = 1, #numberPart do
yield(Candidate(input, seg.start, seg._end, numberPart[i][1], numberPart[i][2]))
end
end
end
end
-- print(#number_translatorFunc(3355.433))
return number_translator

238
lua/pin_cand_filter.lua Normal file
View File

@@ -0,0 +1,238 @@
-- 置顶候选项
--[[
《说明书》
符合左边的编码(preedit)时,按顺序置顶右边的候选项。只是提升已有候选项的顺序,没有自创编码的功能。
脚本对比的是去掉空格的 cand.preedit配置里写空格可以生成额外的编码参考示例。
cand.preedit 是经过 translator/preedit_format 转换后的编码
⚠️ 注意方案的 preedit_format 设定,如果 v 显示为 ü,那么左边也要写 ü
⚠️ 双拼:显示为全拼拼写就要写全拼,如 'shuang pin',显示为双拼拼写就要写双拼,如 'ul pb'
格式:编码<Tab>字词1<Space>字词2……
按照 YAML 语法,加不加引号都行,也可以这么写 pin_cand_filter: [l 了, 'de 的', "ni hao 你好"]
示例:
- 'le 了' # 输入 le 时,置顶「了」
- 'ta 他 她 它' # 可以置顶多个字,按顺序排列
- 'l 了 啦' # 支持单编码,输入 l 时,置顶「了、啦」
- 'l 了 > 啦' # 右边的字词如果包含空格,用 > 分割也行(大于号左右必须有空格)
- 'ta 啊' # ❌ 编码不会产生的字词,不会生效且影响查找效率。自创编码的字词句可以写到 custom_phrase 中。
- 'hao 好 👌' # ❌ 不要写 emoji
### 简拼
支持简拼,简拼加不加空格都行。但需要方案开启简拼,雾凇全拼是默认开启的,双拼默认没开启
- s m 什么
- wsm 为什么
### 空格的作用:
- nihao 你好
无空格,生成原样;
生成 nihao输入 nihao 时首位是「你好」,但输入 nih 时首位可能是「你会 你还」等其他词语。
- ni hao 你好
包含空格,额外生成最后一个空格后的拼音的首字母简码;
生成 nihao nih ,现在输入 nih 时首位也会是「你好」。
- bu hao chi 不好吃
包含空格且结尾以 zh ch sh 开头,再额外生成最后一个空格后的拼音的 zh ch sh 简码;
生成 buhaochi buhaoc buhaoch
### 优先级:
- da zhuang 大专
- da zhong 大众
上面两行,会额外生成 'da z' 'da zh' 的置顶,前两个候选项是「大专、大众」,先写的排在前面
- da z 打字
如果明确定义了简码形式,则完全使用简码形式
此时输入 daz 首位为「打字」,输入 dazh 首位仍为「大专、大众」
--]]
local function find_index(list, str)
for i, v in ipairs(list) do
if v == str then
return i
end
end
return 0
end
local M = {}
function M.init(env)
env.name_space = env.name_space:gsub("^*", "")
if env.pin_cands ~= nil then return end
local list = env.engine.schema.config:get_list(env.name_space)
if not list or list.size == 0 then return end
-- 如果定义了 'da zhuan' 或 'da zhong' ,会自动生成 'daz' 和 'dazh' 的键。
-- 然而,如果明确定义了 'da z' 或 'da zh',则会优先使用这些明确自定义的简码,用 set 来做判断。
local set = {}
for i = 0, list.size - 1 do
local preedit, texts = list:get_value_at(i).value:match("([^\t]+)\t(.+)")
if #preedit > 0 and #texts > 0 then
set[preedit:gsub(" ", "")] = true
end
end
-- 遍历要置顶的候选项列表,将其转换为 table 存储到 env.pin_cands
-- 'l 了 啦' → env.pin_cands["l"] = {"了", "啦"}
-- 'ta 他 她 它' → env.pin_cands["ta"] = {"他", "她", "它"}
--
-- 无空格的键,如 `nihao 你好` → env.pin_cands["nihao"] = {"你好"}
--
-- 包含空格的的键,同时生成简码的拼写(最后一个空格后的首字母),如:
-- 'ni hao 你好 拟好' → env.pin_cands["nihao"] = {"你好", "拟好"}
-- → env.pin_cands["nih"] = {"你好", "拟好"}
--
-- 如果最后一个空格后以 zh ch sh 开头,额外再生成 zh, ch, sh 的拼写,如:
-- 'zhi chi 支持' → env.pin_cands["zhichi"] = {"支持"}
-- → env.pin_cands["zhic"] = {"支持"}
-- → env.pin_cands["zhich"] = {"支持"}
--
-- 如果同时定义了 'da zhuan 大专' 'da zhong 大众',会生成:
-- env.pin_cands["dazhuan"] = {"大专"}
-- env.pin_cands["dazhong"] = {"大众"}
-- env.pin_cands["daz"] = {"大专", "大众"} -- 先写的排在前面
-- env.pin_cands["dazh"] = {"大专", "大众"} -- 先写的排在前面
--
-- 如果同时定义了 'da zhuan 大专' 'da zhong 大众' 且明确定义了简码形式 'da z 打字',会生成:
-- env.pin_cands["dazhuan"] = {"大专"}
-- env.pin_cands["dazhong"] = {"大众"}
-- env.pin_cands["daz"] = {"打字"} -- 明确定义的优先级更高
-- env.pin_cands["dazh"] = {"大专", "大众"} -- 没明确定义的,仍然按上面的方式,先写的排在前面
env.pin_cands = {}
for i = 0, list.size - 1 do
local preedit, texts = list:get_value_at(i).value:match("([^\t]+)\t(.+)")
if #preedit > 0 and #texts > 0 then
-- 按照 " > " 或 " " 分割词汇
local delimiter = "\0"
if texts:find(" > ") then
texts = texts:gsub(" > ", delimiter)
else
texts = texts:gsub(" ", delimiter)
end
-- 按照键生成完整的拼写
local preedit_no_spaces = preedit:gsub(" ", "")
env.pin_cands[preedit_no_spaces] = {}
for text in texts:gmatch("[^" .. delimiter .. "]+") do
table.insert(env.pin_cands[preedit_no_spaces], text)
end
-- 额外处理包含空格的 preedit增加最后一个拼音的首字母和 zh, ch, sh 的简码
if preedit:find(" ") then
local preceding_part, last_part = preedit:match("^(.+)%s(%S+)$")
local p1, p2 = "", ""
-- p1 生成最后一个拼音的首字母简码拼写(最后一个空格后的首字母),如 ni hao 生成 nih
p1 = preceding_part:gsub(" ", "") .. last_part:sub(1, 1)
-- p2 生成最后一个拼音的 zh, ch, sh 的简码拼写(最后一个空格后以 zh ch sh 开头),如 zhi chi 生成 zhich
if last_part:match("^[zcs]h") then
p2 = preceding_part:gsub(" ", "") .. last_part:sub(1, 2)
end
for _, p in ipairs({ p1, p2 }) do
-- 只在没有明确定义此简码时才生成,已有的追加,没有的直接赋值
if p ~= "" and not set[p] then
if env.pin_cands[p] ~= nil then
for text in texts:gmatch("[^" .. delimiter .. "]+") do
table.insert(env.pin_cands[p], text)
end
else
env.pin_cands[p] = env.pin_cands[preedit_no_spaces]
end
end
end
end
end
end
end
function M.func(input, env)
-- 当前输入框的 preedit未经过方案 translator/preedit_format 转换
-- 输入 nihaoshij 则为 nihaoshij选择了「你好」后变成 你好shij
local full_preedit = env.engine.context:get_preedit().text
-- 非汉字部分的 preedit如 shij
local letter_only_preedit = string.gsub(full_preedit, "[^a-zA-Z]", "")
if env.pin_cands == nil or next(env.pin_cands) == nil or #letter_only_preedit == 0 then
for cand in input:iter() do yield(cand) end
return
end
--[[
full_preedit 与候选项的情况
hao 好、号、毫 ... 哈、蛤、铪
你hao 好、号、毫 ... 哈、蛤、铪
haobu 好不、毫不 ... 好、号、毫 ... 哈、蛤、铪
你haobu 好不、毫不 ... 好、号、毫 ... 哈、蛤、铪
简化为 letter_only_preedit 与候选项的情况
hao 好、号、毫 ... 哈、蛤、铪
haobu 好不、毫不 ... 好、号、毫 ... 哈、蛤、铪
在循环中随着候选项的变化cand.preedit 也跟着变化:
letter_only_preedit cand.preedit
--------------------------------------------------
dian dian ... di
ha ha
hao hao ... ha
haobu hao bu ... hao ... ha
--]]
-- 用 pined 和 others 调整顺序,找齐后先遍历 pined 再遍历 others
local pined = {} -- 提升的候选项
local others = {} -- 其余候选项
local pined_count = 0
for cand in input:iter() do
local preedit = cand.preedit:gsub(" ", "") -- 对比去掉空格的 cand.preedit
local texts = env.pin_cands[preedit]
if texts == nil then
-- 当前候选项无须排序,直接 yield 并结束循环
-- 当前候选项正在排序,例如要置顶某个 `hao`,但从 `hao` 查到 `ha` 了还没找齐,不能直接 yield要先输出 pined 和 others 中的 `hao`
if letter_only_preedit == preedit then
yield(cand)
else
table.insert(others, cand)
end
break
else
-- 给 pined 几个空字符串占位元素,后面直接 pined[idx] = cand 确保 pined 与 texts 顺序一致
if #pined < #texts then
for _ = 1, #texts do
table.insert(pined, "")
end
end
-- 要置顶的放到 pined 中,其余的放到 others
local idx = find_index(texts, cand.text)
if idx ~= 0 then
pined[idx] = cand
pined_count = pined_count + 1
else
table.insert(others, cand)
end
-- 找齐了或查询超过 100 个就不找了(如果要提升的候选项不在前 100 则不会被提升)
if pined_count == #texts or #others > 100 then
break
end
end
end
-- yield pined others 及后续的候选项
for _, cand in ipairs(pined) do
if cand ~= "" then
yield(cand)
end
end
for _, cand in ipairs(others) do
yield(cand)
end
for cand in input:iter() do
yield(cand)
end
end
return M

View File

@@ -0,0 +1,114 @@
-- 降低部分英语单词在候选项的位置,可在方案中配置要降低的模式和单词
-- https://dvel.me/posts/make-rime-en-better/#短单词置顶的问题
-- 感谢大佬 @[Shewer Lu](https://github.com/shewer) 指点
-- Mintimate 修改:
-- 1. 在不设置 mode 情况下,调整为默认全降模式(原本为 none 模式);
-- 2. all 会合并默认全降内容和自定义内容。
local M = {}
function M.init(env)
local config = env.engine.schema.config
env.name_space = env.name_space:gsub("^*", "")
-- 要降低到的位置
M.idx = config:get_int(env.name_space .. "/idx")
-- 所有 3~4 位长度、前 2~3 位是完整拼音、最后一位是声母的单词
local all = { "aid", "aim", "air", "and", "ann", "ant", "any", "bad", "bag", "bail", "bait", "bam", "ban", "band",
"bang", "bank", "bans", "bar", "bat", "bay", "bend", "benq", "bent", "benz", "bib", "bid", "bien", "big", "bin",
"bind", "bit", "biz", "bob", "boc", "bop", "bos", "bot", "bow", "box", "boy", "bud", "buf", "bug", "bus",
"but", "buy", "cab", "cad", "cain", "cam", "can", "cans", "cant", "cap", "car", "cat", "cef", "cen",
"cent", "chad", "chan", "chap", "char", "chat", "chef", "chen", "cher", "chew", "chic", "chin", "chip", "chit",
"coup", "cum", "cunt", "cup", "cur", "cut", "dab", "dad", "dag", "dal", "dam", "day", "def", "del", "den",
"dent", "deny", "der", "dew", "dial", "did", "died", "dies", "diet", "dig", "dim", "din", "dip", "dir", "dis",
"dit", "diy", "doug", "dub", "dug", "dun", "dunn", "don", "end", "err", "fab", "fan", "fans", "faq", "far", "fat",
"fax", "fob", "fog", "for", "foul", "four", "fox", "fun", "fur", "gag", "gail", "gain", "gal", "gam", "gan",
"gang", "gank", "gaol", "gap", "gas", "gay", "ged", "gel", "gem", "gen", "ger", "get", "guam", "guid", "gum",
"gun", "guns", "gus", "gut", "guy", "had", "hail", "hair", "ham", "han", "hand", "hang", "hank", "hans", "has",
"hat", "hay", "heil", "heir", "hem", "hen", "hep", "her", "hex", "hey", "hour", "hub", "hud", "hug", "huh",
"hum", "hung", "hunk", "hunt", "hut", "jim", "jug", "junk", "kat", "kent", "key", "lab", "lad", "lag", "laid",
"lam", "lan", "land", "lang", "laos", "lap", "lat", "law", "lax", "lay", "led", "leg", "len", "let", "lex",
"liam", "liar", "lib", "lid", "lied", "lien", "lies", "ling", "link", "linn", "lip", "lit", "liz", "lob", "log",
"lol", "lot", "loud", "low", "lug", "lund", "lung", "lux", "mac", "mad", "mag", "maid", "mail", "main", "man",
"mann", "many", "map", "mar", "mat", "max", "may", "med", "mel", "men", "mend", "mens", "ment", "met", "mic",
"mid", "mil", "min", "mind", "ming", "mins", "mint", "mit", "mix", "mob", "moc", "mod", "mom", "mop", "mos",
"mot", "mud", "mug", "mum", "nad", "nail", "nan", "nap", "nas", "nat", "nay", "neil", "net", "new", "nib", "nil",
"nip", "noun", "nous", "nun", "nut", "nvm", "our", "out", "pac", "pad", "paid", "pail", "pain", "pair", "pak", "pal",
"pam", "pan", "pans", "pant", "pap", "par", "pat", "paw", "pax", "pay", "pens", "pic", "pier", "pies", "pig",
"pin", "ping", "pink", "pins", "pint", "pit", "pix", "pod", "pop", "por", "pos", "pot", "pour", "pow", "pub",
"put", "rand", "rang", "rank", "rant", "red", "rent", "rep", "res", "ret", "rex", "rib", "rid", "rig", "rim",
"rip", "rub", "rug", "ruin", "rum", "run", "runc", "runs", "sac", "sad", "said", "sail", "sal", "sam", "san",
"sand", "sang", "sans", "sap", "sat", "saw", "sax", "say", "sec", "send", "sent", "set", "sew", "sex", "sham",
"shaw", "shed", "shin", "ship", "shit", "shut", "sig", "sim", "sin", "sip", "sir", "sis", "sit", "six", "soul",
"soup", "sour", "sub", "suit", "sum", "sun", "sung", "suns", "sup", "sur", "sus", "tab", "tad", "tag", "tail",
"taj", "tan", "tang", "tank", "tap", "tar", "tax", "tec", "ted", "tel", "ten", "ter", "tex", "tic", "tied",
"tier", "ties", "tim", "tin", "tip", "tit", "tour", "tout", "tum", "wag", "wait", "wail", "wan", "wand", "womens",
"want", "wap", "war", "was", "wax", "way", "weir", "went", "won", "wow", "yan", "yang", "yen", "yep", "yes",
"yet", "yin", "your", "yum", "zen", "zip",
-- 后面是 zh ch sh 的
"bach", "bash", "bench", "bush", "cash", "couch", "dash", "dish", "hash", "hush", "lash", "loch", "lunch",
"lush", "mesh", "much", "nash", "pinch", "pouch", "push", "ranch", "rich", "rush", "such", "tech", "touch",
"wash", "zach",
-- 其他
"eg",
"my", "mt", "dj", "as", "js", "cs", "ak", "ps", "cd", "cn", "hk", "bt", "pk", "ml"
}
M.all = {}
for _, v in ipairs(all) do
M.all[v] = true
end
-- 自定义
M.words = {}
local list = config:get_list(env.name_space .. "/words")
local listSize = list and list.size or 0
for i = 0, listSize - 1 do
local word = list:get_value_at(i).value
M.words[word] = true
end
-- 模式
local mode = config:get_string(env.name_space .. "/mode")
if mode == "custom" then
M.map = M.words
elseif mode == "none" then
M.map = {}
else -- 默认 mode 为 all 且合并 M.all 和 words
for key in pairs(M.words) do
M.all[key] = true
end
M.map = M.all
end
end
function M.func(input, env)
-- filter start
local code = env.engine.context.input
if M.map[code] then
local pending_cands = {}
local index = 0
for cand in input:iter() do
index = index + 1
-- 找到要降低的英文词,加入 pending_cands
if cand.preedit:find(" ") or not cand.text:match("[a-zA-Z]") or cand.type == "user_table" then
yield(cand)
else
table.insert(pending_cands, cand)
end
if index >= M.idx + #pending_cands - 1 then
for _, cand in ipairs(pending_cands) do
yield(cand)
end
break
end
end
end
-- yield other
for cand in input:iter() do
yield(cand)
end
end
return M

311
lua/search.lua Executable file
View File

@@ -0,0 +1,311 @@
-- 辅码https://github.com/mirtlecn/rime-radical-pinyin/blob/master/search.lua.md
--
-- Copyright (C) [Mirtle](https://github.com/mirtlecn)
-- License: CC BY-SA 4.0 (https://creativecommons.org/licenses/by-sa/4.0/)
-- 使用说明:<https://github.com/mirtlecn/rime-radical-pinyin/blob/master/search.lua.md>
-- 处理 lua 中的特殊字符用于匹配
local function alt_lua_punc( s )
if s then
return s:gsub( '([%.%+%-%*%?%[%]%^%$%(%)%%])', '%%%1' )
else
return ''
end
end
-- 获取指定字符在文本中的位置
local function get_pos( text, char )
local pos = {}
if text:find( char ) then
local tmp = text
for i = 1, utf8.len( tmp ) do
local first_char = tmp:sub( 1, utf8.offset( tmp, 2 ) - 1 )
if first_char == char then pos[i] = true end
tmp = tmp:gsub( '^' .. first_char, '' )
i = i + 1
end
end
return pos
end
-- 此函数用于手动写入用户词库,目前仅对双拼有效
local function update_dict_entry( s, code, mem, proj )
if #s == 0 or utf8.len( s ) == #s or (#code % 2 ~= 0) then
log.info( '[search.lua]: Ignored' .. s )
return 0
end
local e = DictEntry()
s = s:gsub( '^%s+', '' ):gsub( '%s+$', '' )
e.text = s
local pos = {}
if s:find( '·' ) and (utf8.len( s ) > 1) then pos = get_pos( s, '·' ) end
local custom_code = {}
local loop = 1
for i = 1, #code, 2 do
local code_convert = code:sub( i, i + 1 )
local p = proj:apply( code_convert, true )
if p and #p > 0 then code_convert = p end
if code_convert == 'dian' and pos[loop] then
-- Ignored
else
table.insert( custom_code, code_convert )
end
loop = loop + 1
end
e.custom_code = table.concat( custom_code, ' ' ) .. ' '
if mem.start_session then mem:start_session() end -- new on librime 2024.05
mem:update_userdict( e, 1, '' )
if mem.finish_session then mem:finish_session() end -- new on librime 2024.05
end
-- 通过 schema 的方式查询(以辅码查字,然后对比候选,慢,但能够匹配到算法转换过的码)
-- 查询方案中的匹配项,并返回字表
local function dict_init( search_string, mem, search_limit, code_projection )
local dict_table = {}
if code_projection then
-- old librime(<= 2023.06) do not return original string when apply failed
local p = code_projection:apply( search_string, true )
if p and #p > 0 then search_string = p end
end
if mem:dict_lookup( search_string, true, search_limit ) then
for entry in mem:iter_dict() do dict_table[entry.text] = true end
end
return dict_table
end
-- 匹配候选
local function dict_match( table, text )
if table[text] == true then return true end
return false
end
-- 通过 reverse db 查询(以字查码,然后比对辅码是否相同,快,但只能匹配未经算法转换的码)
local function reverse_lookup( code_projection, db_table, wildcard, text, s, global_match )
if wildcard then s = s:gsub( wildcard, '.*' ) end
if code_projection then
-- old librime do not return original string when apply failed
local p = code_projection:apply( s, true )
if p and #p > 0 then s = p end
end
-- log.error(s)
for _, db in ipairs( db_table ) do
local code = db:lookup( text )
if code and #code > 0 then
for part in code:gmatch( '%S+' ) do
if global_match then
if part:find( s ) then return true end
else
if part:find( '^' .. s ) then return true end -- an error pointing at this line. do not know why. so I'll keep an eye.
end
end
end
end
return false
end
-- 处理单字优先
local function handle_long_cand( if_single_char_first, cand, long_word_cands )
if if_single_char_first and utf8.len( cand.text ) > 1 then
table.insert( long_word_cands, cand )
else
yield( cand )
end
end
local f = {}
function f.init( env )
local config = env.engine.schema.config
local ns = 'search'
env.if_schema_lookup = false
env.if_reverse_lookup = false
-- 配置:仅限 script_translator 引擎
-- local engine = config:get_list( 'engine/translators' )
-- local engine_table = {}
-- for i = 0, engine.size - 1 do engine_table[engine:get_value_at( i ).value] = true end
-- if not engine_table['script_translator'] then
-- log.error( '[search.lua]: script_translator not found in engine/translators, search.lua will not work' )
-- return
-- end
-- 配置:辅码查字方法
-- --
-- 当在 engine 出直接指定了 namespace 则使用该 namespace 进行 schema 匹配
-- 当在 search_in_cand 节点下指定了 schema 和 db 则进行相应匹配
-- 当该节点下 schema 为 0 或者 false或者不存在时不进行相应匹配
-- --
local schema_name = config:get_string( ns .. '/schema' )
if not env.name_space:find( '^%*' ) then schema_name = env.name_space end
if not schema_name or schema_name == 'false' or schema_name == '0' or #schema_name == 0 then goto checkdb end
env.search = Memory( env.engine, Schema( schema_name ) )
if schema_name and env.search then
env.if_schema_lookup = true
env.search_limit = config:get_int( ns .. '/schema_search_limit' ) or 1000
end
::checkdb::
local db = config:get_list( ns .. '/db' )
if db and db.size > 0 then
env.wildcard = alt_lua_punc( config:get_string( ns .. '/wildcard' ) ) or '*'
env.db_table = {}
for i = 0, db.size - 1 do table.insert( env.db_table, ReverseLookup( db:get_value_at( i ).value ) ) end
env.if_reverse_lookup = true
end
if not env.if_reverse_lookup and not env.if_schema_lookup then return end
-- 配置:辅码转换规则
-- --
-- 例如:- xlit/ABCD/1234/ 就可以用 ABCD 来输入 1234地球拼音音调
local fuma_format = config:get_list( ns .. '/fuma_format' )
if fuma_format and fuma_format.size > 0 then
env.code_projection = Projection()
env.code_projection:load( fuma_format )
else
env.code_projection = nil
end
-- 配置:是否显示不符合辅码的候选
env.show_other_cands = config:get_bool( ns .. '/show_other_cands' )
-- 配置:辅码引导符号,默认为反引号 `
local search_key = config:get_string( 'key_binder/search' ) or config:get_string( ns .. '/key' ) or '`'
env.search_key_alt = alt_lua_punc( search_key )
local code_pattern = config:get_string( ns .. '/code_pattern' ) or '[a-z;]'
-- 配置seg tag
local tag = config:get_list( ns .. '/tags' )
if tag and tag.size > 0 then
env.tag = {}
for i = 0, tag.size - 1 do table.insert( env.tag, tag:get_value_at( i ).value ) end
else
env.tag = { 'abc' }
end
-- 配置:手动写入用户词库
local rules = config:get_list( ns .. '/input2code_format' )
if rules and rules.size > 0 then
env.projection = Projection()
env.projection:load( rules )
env.mem = Memory( env.engine, env.engine.schema )
end
-- 推入输入历史,并手动(如果设定了按键到编码的转换规则)写入用户词库
env.commit_notifier = env.engine.context.commit_notifier:connect(
function( ctx )
if env.have_select_commit and env.commit_code then
local commit_text = ctx:get_commit_text()
if env.mem then
update_dict_entry( commit_text, env.commit_code, env.mem, env.projection )
end
ctx.commit_history:push( 'search.lua', commit_text )
env.have_select_commit = false
else
return
end
end
)
-- 接管选词逻辑,是词组则始终保留引导码,否则直接上屏
env.notifier = env.engine.context.select_notifier:connect(
function( ctx )
local input = ctx.input
local code = input:match( '^(.-)' .. env.search_key_alt )
if (not code or #code == 0) then return end
local preedit = ctx:get_preedit()
local no_search_string = ctx.input:match( '^(.-)' .. env.search_key_alt )
local edit = preedit.text:match( '^(.-)' .. env.search_key_alt )
env.have_select_commit = true
if edit and edit:match( code_pattern ) then
ctx.input = no_search_string .. search_key
else
ctx.input = no_search_string
env.commit_code = no_search_string
ctx:commit()
end
end
)
end
function f.func( input, env )
-- 当且仅当当输入码中含有辅码引导符号,并有有辅码存在,进入匹配逻辑
local code, fuma = env.engine.context.input:match( '^(.-)' .. env.search_key_alt .. '(.+)$' )
if (not code or #code == 0) or (not fuma or #fuma == 0) or (not env.if_reverse_lookup and not env.if_schema_lookup) then
for cand in input:iter() do yield( cand ) end
return
end
local if_single_char_first = env.engine.context:get_option( 'search_single_char' )
local dict_table
local fuma_2
local other_cand = {}
local long_word_cands = {}
if env.if_schema_lookup then dict_table = dict_init( fuma, env.search, env.search_limit, env.code_projection ) end
if fuma:find( env.search_key_alt ) then fuma, fuma_2 = fuma:match( '^(.-)' .. env.search_key_alt .. '(.*)$' ) end
for cand in input:iter() do
if cand.type == 'sentence' then goto skip end
local cand_text = cand.text
local text = cand_text
local text_2 = nil
-- 当候选多于一个字,则取第一个匹配
if utf8.len( cand_text ) and utf8.len( cand_text ) > 1 then
text = cand_text:sub( 1, utf8.offset( cand_text, 2 ) - 1 )
local cand_text_2 = cand_text:gsub( '^' .. text, '' )
text_2 = cand_text_2:sub( 1, utf8.offset( cand_text_2, 2 ) - 1 )
end
if fuma_2 and #fuma_2 > 0 and env.if_reverse_lookup and not env.if_schema_lookup then
if -- 第一个辅码匹配第一个字,第二个辅码正则匹配第一个字**或者**匹配第二个字
reverse_lookup( env.code_projection, env.db_table, env.wildcard, text, fuma ) and
((text_2 and reverse_lookup( env.code_projection, env.db_table, env.wildcard, text_2, fuma_2 )) or
reverse_lookup( env.code_projection, env.db_table, env.wildcard, text, fuma_2, true )) then
handle_long_cand( if_single_char_first, cand, long_word_cands )
else
table.insert( other_cand, cand )
end
else
if -- 用辅码匹配第一个字
(env.if_reverse_lookup and reverse_lookup( env.code_projection, env.db_table, env.wildcard, text, fuma )) or
(env.if_schema_lookup and dict_match( dict_table, text )) then
handle_long_cand( if_single_char_first, cand, long_word_cands )
else
table.insert( other_cand, cand )
end
end
::skip::
end
-- 上屏其余的候选
for i, cand in ipairs( long_word_cands ) do yield( cand ) end
if env.show_other_cands then for i, cand in ipairs( other_cand ) do yield( cand ) end end
end
function f.tags_match( seg, env )
for i, v in ipairs( env.tag ) do if seg.tags[v] then return true end end
return false
end
function f.fini( env )
if env.if_reverse_lookup or env.if_schema_lookup then
env.notifier:disconnect()
env.commit_notifier:disconnect()
if env.mem and env.mem.disconnect then env.mem:disconnect() end
if env.search and env.search.disconnect then env.search:disconnect() end
if env.mem or env.search or env.db_table then
env.db_table = nil
env.mem = nil
env.search = nil
collectgarbage( 'collect' )
end
end
end
return f

50
lua/select_character.lua Normal file
View File

@@ -0,0 +1,50 @@
-- 以词定字
-- 原脚本 https://github.com/BlindingDark/rime-lua-select-character
-- 删除了默认按键 [ ],和方括号翻页冲突,需要在 key_binder 下指定才能生效
-- 20230526195910 不再错误地获取commit_text而是直接获取get_selected_candidate().text
-- 20240128141207 重写:将读取设置移动到 init 方法中;简化中文取字方法;预先判断候选存在与否,无候选取 input
-- 20240508111725 当候选字数为 1 时,快捷键使该字上屏
local select = {}
function select.init(env)
local config = env.engine.schema.config
env.first_key = config:get_string('key_binder/select_first_character')
env.last_key = config:get_string('key_binder/select_last_character')
end
function select.func(key, env)
local engine = env.engine
local context = env.engine.context
if
not key:release()
and (context:is_composing() or context:has_menu())
and (env.first_key or env.last_key)
then
local text = context.input
if context:get_selected_candidate() then
text = context:get_selected_candidate().text
end
if utf8.len(text) > 1 then
if (key:repr() == env.first_key) then
engine:commit_text(text:sub(1, utf8.offset(text, 2) - 1))
context:clear()
return 1
elseif (key:repr() == env.last_key) then
engine:commit_text(text:sub(utf8.offset(text, -1)))
context:clear()
return 1
end
else
if key:repr() == env.first_key or key:repr() == env.last_key then
engine:commit_text(text)
context:clear()
return 1
end
end
end
return 2
end
return select

18
lua/t9_preedit.lua Normal file
View File

@@ -0,0 +1,18 @@
-- 九宫格将输入框的数字转为对应的拼音或英文iRime 用Hamster 不需要。
-- 在 engine/filters 增加 - lua_filter@t9_preedit
--
-- 九宫格专用iRime 用,仓输入法不用
-- 拼写规则通过 xlit 转写: xlit/abcdefghijklmnopqrstuvwxyz/22233344455566677778889999/
-- 然后通过此 Lua 将输入框的数字转为对应的拼音或英文
local function t9_preedit(input, env)
for cand in input:iter() do
if (string.find(cand.text, "%w+") ~= nil) then
cand:get_genuine().preedit = cand.text
else
cand:get_genuine().preedit = cand.comment
end
yield(cand)
end
end
return t9_preedit

36
lua/unicode.lua Normal file
View File

@@ -0,0 +1,36 @@
-- Unicode
-- 复制自: https://github.com/shewer/librime-lua-script/blob/main/lua/component/unicode.lua
-- 示例:输入 U62fc 得到「拼」
-- 触发前缀默认为 recognizer/patterns/unicode 的第 2 个字符,即 U
-- 2024.02.26: 限定编码最大值
-- 2024.06.01: 部分变量初始化,条件语句调整。
local path = 'recognizer/patterns/unicode'
local function unicode(input, seg, env)
if not seg:has_tag("unicode") or input == '' then return end
-- 获取 recognizer/patterns/unicode 的第 2 个字符作为触发前缀
-- config:get_string(path) 可能取得 nil 造成error
if not env.unicode_keyword then
local pattern = env.engine.schema.config:get_string(path) or "UU"
env.unicode_keyword = pattern:sub(2,2)
end
local ucodestr = input:match(env.unicode_keyword .. "(%x+)")
if ucodestr and #ucodestr > 1 then
local code = tonumber(ucodestr, 16)
if code > 0x10FFFF then
yield(Candidate("unicode", seg.start, seg._end, "数值超限!", ""))
return
end
local text = utf8.char(code)
yield(Candidate("unicode", seg.start, seg._end, text, string.format("U%x", code)))
if code < 0x10000 then
for i = 0, 15 do
local text = utf8.char(code * 16 + i)
yield(Candidate("unicode", seg.start, seg._end, text, string.format("U%x~%x", code, i)))
end
end
end
end
return unicode

32
lua/v_filter.lua Normal file
View File

@@ -0,0 +1,32 @@
-- v 模式,单个字符优先
-- 因为设置了英文翻译器的 initial_quality 大于 1导致输入「va」时候选项是「van vain …… ā á ǎ à」
-- 把候选项应改为「ā á ǎ à …… van vain」让单个字符的排在前面
-- 感谢改进 @[t123yh](https://github.com/t123yh) @[Shewer Lu](https://github.com/shewer)
local function v_filter(input, env)
local code = env.engine.context.input -- 当前编码
env.v_spec_arr = env.v_spec_arr or Set({ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "Vs." })
-- 仅当当前输入以 v 开头,并且编码长度为 2才进行处理
if (string.len(code) == 2 and string.find(code, "^v")) then
local l = {}
for cand in input:iter() do
-- 特殊情况处理
if (env.v_spec_arr[cand.text]) then
yield(cand)
-- 候选项为单个字符的,提到前面来。
elseif (utf8.len(cand.text) == 1) then
yield(cand)
else
table.insert(l, cand)
end
end
for _, cand in ipairs(l) do
yield(cand)
end
else
for cand in input:iter() do
yield(cand)
end
end
end
return v_filter