fix: 集中修复三个lua bug,为替换器增加FMM分词算法补齐句子转换功能

This commit is contained in:
amzxyz
2026-01-14 19:34:00 +08:00
parent 8d03ab3506
commit e3bdffad29
7 changed files with 199 additions and 257 deletions

View File

@@ -5,37 +5,13 @@
-- - lua_processor@*super_tips
-- key_binder/tips_key: "slash" # 上屏按键配置
-- tips/disabled_types: [] # 禁用的 tips 类型
local wanxiang = require("wanxiang")
local bit = require("lib/bit")
local userdb = require("lib/userdb")
-- 尝试打开数据库
local tips_db = userdb.LevelDb("lua/tips")
-- 获取文件内容哈希值,使用 FNV-1a 哈希算法
local function calculate_file_hash(filepath)
local file = io.open(filepath, "rb")
if not file then return nil end
-- FNV-1a 哈希参数32位
local FNV_OFFSET_BASIS = 0x811C9DC5
local FNV_PRIME = 0x01000193
local hash = FNV_OFFSET_BASIS
while true do
local chunk = file:read(4096)
if not chunk then break end
for i = 1, #chunk do
local byte = string.byte(chunk, i)
hash = bit.bxor(hash, byte)
hash = (hash * FNV_PRIME) % 0x100000000
hash = bit.band(hash, 0xFFFFFFFF)
end
end
file:close()
return string.format("%08x", hash)
end
local tips = {}
---@type "pending" | "initialing" | "done"
@@ -46,171 +22,145 @@ tips.disabled_types = {}
tips.preset_file_path = wanxiang.get_filename_with_fallback("lua/data/tips_show.txt")
tips.user_override_path = rime_api.get_user_data_dir() .. "/lua/data/tips_user.txt"
-- 元数据 Key
local META_KEY = {
version = "wanxiang_version",
user_file_hash = "user_tips_file_hash",
disabled_types = "disabled_types",
disabled_types = "disabled_types_fingerprint", -- 改名:配置指纹
}
---判断某个类型是否被禁用
---@param tip string
function tips.is_disabled(tip)
local type = tip:match("^(..-):")
or tip:match("^(..-)")
local type = tip:match("^(..-):") or tip:match("^(..-)")
if not type then return false end
return tips.disabled_types[type] == true
end
---从文件加载数据到 DB
function tips.init_db_from_file(path)
local file = io.open(path, "r")
if not file then return end
for line in file:lines() do
-- 格式:值 [tab] 键
local value, key = line:match("([^\t]+)\t([^\t]+)")
if key and value
and not tips.is_disabled(value)
then
if key and value and not tips.is_disabled(value) then
tips_db:update(key, value)
end
end
file:close()
end
function tips.ensure_dir_exist(dir)
-- 获取系统路径分隔符
local sep = package.config:sub(1, 1)
dir = dir:gsub([["]], [[\"]]) -- 处理双引号
if sep == "/" then
local cmd = 'mkdir -p "' .. dir .. '" 2>/dev/null'
os.execute(cmd)
os.execute('mkdir -p "' .. dir .. '" 2>/dev/null')
end
end
---初始化核心逻辑
---@param config Config
function tips.init(config)
if tips.status ~= "pending" then return end
-- 1. 确保目录存在 (仅非特定发行版)
local dist = rime_api.get_distribution_code_name() or ""
local user_lua_dir = rime_api.get_user_data_dir() .. "/lua"
if dist ~= "hamster" and dist ~= "hamster3" and dist ~= "Weasel" then
tips.ensure_dir_exist(user_lua_dir)
tips.ensure_dir_exist(user_lua_dir .. "/tips")
local user_lua_dir = rime_api.get_user_data_dir() .. "/lua"
tips.ensure_dir_exist(user_lua_dir .. "/data")
end
-- 读取配置
-- 2. 读取 disabled_types 配置
-- 这是轻量级操作,必须每次读取以生成指纹
local disabled_keys = {}
local disabled_types_list = config:get_list("tips/disabled_types")
if disabled_types_list then
for i = 1, disabled_types_list.size do
local item = disabled_types_list:get_value_at(i - 1)
if item and #item.value > 0 then
tips.disabled_types[item.value] = true
table.insert(disabled_keys, item.value)
end
end
end
table.sort(disabled_keys) -- 排序,确保指纹唯一
local current_disabled_fingerprint = table.concat(disabled_keys, "|")
-- 检查是否需要重建数据库
-- 3. 检查是否需要重建
tips_db:open()
local needs_rebuild = false
-- 检查 1: 万象版本号
if tips_db:meta_fetch(META_KEY.version) ~= wanxiang.version then
-- A. 检查全局版本号
local db_ver = tips_db:meta_fetch(META_KEY.version)
if db_ver ~= wanxiang.version then
needs_rebuild = true
end
-- 检查 2: 用户文件哈希 (仅在版本号相同时检查)
local user_file_hash = calculate_file_hash(tips.user_override_path) or ""
if not needs_rebuild
and (tips_db:meta_fetch(META_KEY.user_file_hash) or "") ~= user_file_hash
then
needs_rebuild = true
-- B. 检查配置指纹 (如果版本号没变,但用户修改了禁用类型,也需要重建以清洗数据)
if not needs_rebuild then
local db_fingerprint = tips_db:meta_fetch(META_KEY.disabled_types) or ""
if db_fingerprint ~= current_disabled_fingerprint then
needs_rebuild = true
end
end
-- 检查 3: 禁用类型 (仅在前两者都相同时检查)
local disabled_keys = {}
for k, _ in pairs(tips.disabled_types) do
table.insert(disabled_keys, k)
end
table.sort(disabled_keys) -- 排序以确保顺序一致
local disabled_types_str = table.concat(disabled_keys, ",")
if not needs_rebuild
and (tips_db:meta_fetch(META_KEY.disabled_types) or "") ~= disabled_types_str
then
needs_rebuild = true
end
-- 如果需要,则执行重建
-- 4. 执行重建
if needs_rebuild then
tips_db:empty()
tips.init_db_from_file(tips.preset_file_path)
tips.init_db_from_file(tips.user_override_path)
-- 重建成功后,再更新所有元数据,确保操作的原子性
-- 更新元数据
tips_db:meta_update(META_KEY.version, wanxiang.version)
tips_db:meta_update(META_KEY.user_file_hash, user_file_hash)
tips_db:meta_update(META_KEY.disabled_types, disabled_types_str)
tips_db:meta_update(META_KEY.disabled_types, current_disabled_fingerprint)
end
-- 关闭并以只读模式重新打开
-- 5. 切换为只读模式 (查询更快)
tips_db:close()
tips_db:open_read_only()
tips.status = "done"
end
---从数据库中查询 tips
---@param keys string | string[] 接受一个字符串或一个字符串数组作为键,使用数组时会挨个查询,直到获得有效值
---@return string | nil
function tips.get_tip(keys)
-- 输入归一化:如果输入是 string将其包装成单元素的 table
if type(keys) == 'string' then
keys = { keys }
end
if type(keys) == 'string' then keys = { keys } end
for _, key in ipairs(keys) do
if key and key ~= "" then
local tip = tips_db:fetch(key)
if tip and #tip > 0 then
return tip
end
if tip and #tip > 0 then return tip end
end
end
return nil
end
---@class Env
---@field current_tip string | nil 当前 tips 值
---@field last_prompt string 最后一次设置的 prompt 值
---@field current_tip string | nil
---@field last_prompt string
---@field tips_update_connection Connection
---tips prompt 处理
---@param context Context
---@param env Env
local function update_tips_prompt(context, env)
env.current_tip = nil
local is_tips_enabled = context:get_option("super_tips")
if not is_tips_enabled then return end
if not context:get_option("super_tips") then return end
local segment = context.composition:back()
if not segment then return end
local cand = context:get_selected_candidate() or {}
if segment.selected_index == 0 then
env.current_tip = tips.get_tip({ context.input, cand.text })
else
env.current_tip = tips.get_tip(cand.text)
end
if env.current_tip ~= nil and env.current_tip ~= "" then
-- 有 tips 则直接设置 prompt
if env.current_tip and env.current_tip ~= "" then
segment.prompt = "" .. env.current_tip .. ""
env.last_prompt = segment.prompt
elseif segment.prompt ~= "" and env.last_prompt == segment.prompt then
-- 没有 tips且当前 prompt 不为空,且是由 super_tips 设置的,则重置
segment.prompt = ""
env.last_prompt = segment.prompt
end
@@ -218,55 +168,45 @@ end
local P = {}
-- Processor按键触发上屏 (S)
---@param env Env
function P.init(env)
local config = env.engine.schema.config
tips.init(config)
P.tips_key = config:get_string("key_binder/tips_key")
-- 注册 tips 查找监听器
local context = env.engine.context
env.tips_update_connection = context.update_notifier:connect(
function(context)
update_tips_prompt(context, env)
end
)
env.tips_update_connection = context.update_notifier:connect(function(ctx)
update_tips_prompt(ctx, env)
end)
end
function P.fini(env)
-- 清理连接
if env.tips_update_connection then
env.tips_update_connection:disconnect()
env.tips_update_connection = nil
end
end
---@param key KeyEvent
---@param env Env
---@return ProcessResult
function P.func(key, env)
local context = env.engine.context
local is_tips_enabled = context:get_option("super_tips")
if not is_tips_enabled then
return wanxiang.RIME_PROCESS_RESULTS.kNoop
end
-- 以下处理 tips 上屏逻辑
if not P.tips_key -- 未设置上屏键
or P.tips_key ~= key:repr() -- 或者当前按下的不是上屏键
or wanxiang.is_function_mode_active(context) -- 或者是功能模式不用上屏
or not env.current_tip or env.current_tip == "" -- 或匹配的 tips 为空/空字符串
if not P.tips_key
or P.tips_key ~= key:repr()
or wanxiang.is_function_mode_active(context)
or not env.current_tip
or env.current_tip == ""
then
return wanxiang.RIME_PROCESS_RESULTS.kNoop
end
---@type string 从 tips 内容中获取上屏文本
local commit_txt = env.current_tip:match("%s*(.*)%s*") -- 优先匹配常规的全角冒号
or env.current_tip:match(":%s*(.*)%s*") -- 没有匹配则回落到半角冒号
-- 提取上屏文本 (支持全角/半角冒号)
local commit_txt = env.current_tip:match("%s*(.*)%s*")
or env.current_tip:match(":%s*(.*)%s*")
if commit_txt and #commit_txt > 0 then
env.engine:commit_text(commit_txt)
context:clear()
@@ -276,4 +216,4 @@ function P.func(key, env)
return wanxiang.RIME_PROCESS_RESULTS.kNoop
end
return P
return P