From e3bdffad2919515a74d7dd7fe7880ad93e0e9f5b Mon Sep 17 00:00:00 2001 From: amzxyz Date: Wed, 14 Jan 2026 19:34:00 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=9B=86=E4=B8=AD=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E4=B8=89=E4=B8=AAlua=20bug=EF=BC=8C=E4=B8=BA=E6=9B=BF=E6=8D=A2?= =?UTF-8?q?=E5=99=A8=E5=A2=9E=E5=8A=A0FMM=E5=88=86=E8=AF=8D=E7=AE=97?= =?UTF-8?q?=E6=B3=95=E8=A1=A5=E9=BD=90=E5=8F=A5=E5=AD=90=E8=BD=AC=E6=8D=A2?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- custom/wanxiang_pro.schema.yaml | 5 +- lua/lib/bit.lua | 68 ------------- lua/super_lookup.lua | 40 ++++---- lua/super_replacer.lua | 159 +++++++++++++++++++---------- lua/super_tips.lua | 174 +++++++++++--------------------- wanxiang.schema.yaml | 5 +- wanxiang_t9.schema.yaml | 5 +- 7 files changed, 199 insertions(+), 257 deletions(-) delete mode 100644 lua/lib/bit.lua diff --git a/custom/wanxiang_pro.schema.yaml b/custom/wanxiang_pro.schema.yaml index 5e2289f..e2a5b40 100644 --- a/custom/wanxiang_pro.schema.yaml +++ b/custom/wanxiang_pro.schema.yaml @@ -149,7 +149,7 @@ super_replacer: # 场景1:输入 '哈哈' -> 变成 '1.哈哈 2.😄' - option: emoji # 开关名称与上面开关名称保持一致 mode: append # 新增候选append 替换原候选replace 替换注释comment - comment_mode: none # 注释模式: "append"(默认), "text"(原文), "none"(无) + comment_mode: none # 注释模式: "append"(原候选注释继承), "text"(原候选文本放在注释), "none"(空,默认) tags: [abc] # 生效的tag prefix: "_em_" # 前缀用于区分同一个数据库的不同用途数据 files: @@ -175,6 +175,7 @@ super_replacer: - option: [ s2t, s2hk, s2tw ] #后面依赖这条流水线有一个开关为true这条流水线就能工作 mode: replace # <--- 替换原候选模式 comment_mode: append + sentence: true # <--- 句子级别替换 tags: [abc] prefix: "_s2t_" files: @@ -183,6 +184,7 @@ super_replacer: - option: s2hk mode: replace # <--- 替换原候选模式 comment_mode: append + sentence: true # <--- 句子级别替换 tags: [abc] prefix: "_s2hk_" files: @@ -191,6 +193,7 @@ super_replacer: - option: s2tw mode: replace # <--- 替换原候选模式 comment_mode: append + sentence: true # <--- 句子级别替换 tags: [abc] prefix: "_s2tw_" files: diff --git a/lua/lib/bit.lua b/lua/lib/bit.lua deleted file mode 100644 index bdd66a5..0000000 --- a/lua/lib/bit.lua +++ /dev/null @@ -1,68 +0,0 @@ -local bit_ok, bit_ = pcall(require, "bit") -- LuaJIT 内置 bit 库 -local bit32_ok, bit32_ = pcall(require, "bit32") -- Lua 5.2 内置 bit32 库 - ----@alias fn_band fun(a: integer, b: integer): integer ----@alias fn_bxor fun(a: integer, b: integer): integer ----@type nil | { band: fn_band, bxor: fn_bxor } -local bit53_ = nil -- Lua 5.3 引入的原生位运算操作符 - ----@diagnostic disable-next-line: deprecated -local load_func = load or loadstring -if load_func then - ---将新语法放入字符串中,避免在旧版 Lua 中导致语法错误 - local bit53_func, bit53_err = load_func("return {" .. - "band = function(a, b) return a & b end," .. - "bxor = function(a, b) return a ~ b end," .. - "}") - if bit53_func and not bit53_err then - bit53_ = bit53_func() - end -end - -local bit = {} - ----@return integer -function bit.bxor(a, b) - if bit_ok then - return bit_.bxor(a, b) - elseif bit32_ok then - return bit32_.bxor(a, b) - elseif bit53_ then - return bit53_.bxor(a, b) - end - - local p, c = 1, 0 - while a > 0 and b > 0 do - local ra, rb = a % 2, b % 2 - if ra ~= rb then c = c + p end - a, b, p = (a - ra) / 2, (b - rb) / 2, p * 2 - end - if a < b then a = b end - while a > 0 do - local ra = a % 2 - if ra > 0 then c = c + p end - a, p = (a - ra) / 2, p * 2 - end - return c -end - ----@return integer -function bit.band(a, b) - if bit_ok then - return bit_.band(a, b) - elseif bit32_ok then - return bit32_.band(a, b) - elseif bit53_ then - return bit53_.band(a, b) - end - - local p, c = 1, 0 - while a > 0 and b > 0 do - local ra, rb = a % 2, b % 2 - if ra + rb > 1 then c = c + p end - a, b, p = (a - ra) / 2, (b - rb) / 2, p * 2 - end - return c -end - -return bit diff --git a/lua/super_lookup.lua b/lua/super_lookup.lua index d1425d9..bc048c7 100644 --- a/lua/super_lookup.lua +++ b/lua/super_lookup.lua @@ -25,7 +25,7 @@ local function get_utf8_len(s) local _, count = string.gsub(s, "[^\128-\193]", "") return count end --- 提取声调数字 (无声调/轻声 -> 默认归为 4) +-- 提取声调数字 (无声调/轻声 -> 默认归为 0) local function get_tone_from_pinyin(pinyin) if not pinyin or #pinyin == 0 then return nil end for char, tone in pairs(tones_map) do @@ -35,6 +35,7 @@ local function get_tone_from_pinyin(pinyin) end return "0" end + -- 规则加载 local function parse_and_separate_rules(schema_id) if not schema_id or #schema_id == 0 then return nil, nil end @@ -171,6 +172,7 @@ local function match_fuzzy_recursive(codes_sequence, idx, input_str, input_idx, for _, code in ipairs(codes) do local skip = false if is_phrase_mode and #code > 3 then skip = true end + if code:match("^%d+$") then skip = true end if not skip then local i_curr = input_idx @@ -220,22 +222,22 @@ local function parse_comment_codes(comment, pattern, target_len, enable_tone) local codes_part if p1 then - -- 有分号:前面是音/拼音,后面是码 pinyin_part = part:sub(1, p1 - 1) codes_part = part:sub(p2 + 1) else - -- 无分号:整体是音/拼音,无码 pinyin_part = part codes_part = "" end local codes_list = {} + -- 1. 提取辅码 if #codes_part > 0 then for c in codes_part:gmatch("[^,]+") do local trimmed = c:gsub("^%s+", ""):gsub("%s+$", "") if #trimmed > 0 then table.insert(codes_list, trimmed) end end end + -- 2. 提取声调 (如果开启) if enable_tone then local tone = get_tone_from_pinyin(pinyin_part) if tone then @@ -252,33 +254,32 @@ local f = {} function f.init(env) local config = env.engine.schema.config - -- 1. 先读取是否开启声调过滤 (默认为 true) + -- 1. 开启声调 env.enable_tone = config:get_bool('wanxiang_lookup/enable_tone') if env.enable_tone == nil then env.enable_tone = true end - -- 2. 读取数据源 data_source + -- 2. 读取数据源 local sources_list = config:get_list('wanxiang_lookup/data_source') env.data_sources = {} - -- 临时标记,判断配置里是否显式包含了 comment - local config_has_comment_source = false + local config_has_aux_source = false env.has_db = false if sources_list and sources_list.size > 0 then for i = 0, sources_list.size - 1 do local s = sources_list:get_value_at(i).value table.insert(env.data_sources, s) - if s == 'aux' then config_has_comment_source = true end + if s == 'aux' then config_has_aux_source = true end if s == 'db' then env.has_db = true end end else env.data_sources = { 'aux', 'db' } - config_has_comment_source = true + config_has_aux_source = true env.has_db = true end - -- 只要配置里用了 comment 做数据源,或者开启了 enable_tone (需要从注释借声调),都必须解析注释。 - env.has_comment = config_has_comment_source or env.enable_tone + -- 核心逻辑:只要配置了 aux 源,或者开启了 enable_tone (需要借声调),就必须解析注释 + env.has_comment = config_has_aux_source or env.enable_tone env.db_table = nil if env.has_db then @@ -394,7 +395,7 @@ function f.func(input, env) local raw_data = {} - -- A: Comment Data + -- 数据加载 A: Aux Data (From Comment) if env.has_comment then local genuine = cand:get_genuine() local comment_text = genuine and genuine.comment or "" @@ -405,12 +406,14 @@ function f.func(input, env) env.cache_size = env.cache_size + 1 end if comment_cache[cache_key] then - raw_data.comment = comment_cache[cache_key] + raw_data.aux = comment_cache[cache_key] + -- 同时赋给 _comment_internal,用于 data_source: [db] 借用声调 + raw_data._comment_internal = comment_cache[cache_key] end end end - -- B: DB Data + -- 数据加载 B: DB Data if env.has_db then raw_data.db = {} local i = 0 @@ -425,12 +428,13 @@ function f.func(input, env) end end - -- 提取借用声调 (总是尝试从 raw_data.comment 提取,即使 data_source 只有 db) + -- 提取借用声调 (即使 config 里只写了 db,这里也要尝试从注释里借声调) local borrowed_tones = {} - if raw_data.comment then - for k, codes in ipairs(raw_data.comment) do + if raw_data._comment_internal then + for k, codes in ipairs(raw_data._comment_internal) do borrowed_tones[k] = {} for _, c in ipairs(codes) do + -- parse_comment_codes 会把声调数字也放入 list,这里提取出来备用 if c:match("^%d+$") then borrowed_tones[k][c] = true end end end @@ -446,7 +450,7 @@ function f.func(input, env) for k, tone_input in ipairs(tone_filter_seq) do if k > #codes_seq then break end local has_tone = list_contains(codes_seq[k], tone_input) - -- 如果是 db 源且自身没匹配到声调,尝试查阅 borrowed_tones + -- 如果是 db 源且自身没匹配到声调,尝试查阅 borrowed_tones (从注释借来的) if not has_tone and source_type == 'db' then if borrowed_tones[k] and borrowed_tones[k][tone_input] then has_tone = true end end diff --git a/lua/super_replacer.lua b/lua/super_replacer.lua index 6a8b793..fb54b2f 100644 --- a/lua/super_replacer.lua +++ b/lua/super_replacer.lua @@ -71,9 +71,11 @@ local s_match = string.match local s_gmatch = string.gmatch local s_format = string.format local s_byte = string.byte +local s_sub = string.sub local s_gsub = string.gsub local open = io.open local type = type +local tonumber = tonumber -- 基础依赖 local function safe_require(name) @@ -83,33 +85,9 @@ local function safe_require(name) end local userdb = safe_require("lib/userdb") or safe_require("userdb") -local bit = safe_require("lib/bit") or safe_require("bit") - --- 核心工具函数 - -local function get_file_hash(path) - local f = open(path, "rb") - if not f then return "NIL" end - if not bit then local s=f:seek("end"); f:close(); return tostring(s) end - local h = 0x811C9DC5 - while true do - local chunk = f:read(4096) - if not chunk then break end - for i = 1, #chunk do h=bit.bxor(h,s_byte(chunk,i)); h=(h*0x01000193)%0x100000000; h=bit.band(h,0xFFFFFFFF) end - end - f:close() - return s_format("%08x", h) -end - -local function calculate_tasks_signature(tasks) - local sig_parts = {} - for _, task in ipairs(tasks) do - local file_hash = get_file_hash(task.path) - insert(sig_parts, task.prefix .. "@" .. file_hash) - end - return concat(sig_parts, "|") -end +local wanxiang = safe_require("wanxiang") +-- 重建数据库 (仅在 wanxiang 版本变更时运行) local function rebuild(tasks, db) if db.empty then db:empty() end for _, task in ipairs(tasks) do @@ -134,6 +112,68 @@ local function rebuild(tasks, db) return true end +-- UTF-8 辅助 +local function get_utf8_offsets(text) + local offsets = {} + local len = #text + local i = 1 + while i <= len do + insert(offsets, i) + local b = s_byte(text, i) + if b < 128 then i = i + 1 + elseif b < 224 then i = i + 2 + elseif b < 240 then i = i + 3 + else i = i + 4 end + end + insert(offsets, len + 1) + return offsets +end + +-- FMM 分词转换算法 +local function segment_convert(text, db, prefix, split_pat) + local offsets = get_utf8_offsets(text) + local char_count = #offsets - 1 + local result_parts = {} + local i = 1 + local MAX_LOOKAHEAD = 6 + + while i <= char_count do + local matched = false + local max_j = i + MAX_LOOKAHEAD + if max_j > char_count + 1 then max_j = char_count + 1 end + + for j = max_j - 1, i + 1, -1 do + local start_byte = offsets[i] + local end_byte = offsets[j] - 1 + local sub_text = s_sub(text, start_byte, end_byte) + + local val = db:fetch(prefix .. sub_text) + if val then + local first_val = s_match(val, split_pat) + insert(result_parts, first_val or sub_text) + i = j - 1 + matched = true + break + end + end + + if not matched then + local start_byte = offsets[i] + local end_byte = offsets[i+1] - 1 + local char = s_sub(text, start_byte, end_byte) + local val = db:fetch(prefix .. char) + if val then + local first_val = s_match(val, split_pat) + insert(result_parts, first_val or char) + else + insert(result_parts, char) + end + end + i = i + 1 + end + return concat(result_parts) +end + -- 模块接口 function M.init(env) @@ -150,6 +190,12 @@ function M.init(env) env.delimiter = delim env.comment_format = config:get_string(ns .. "/comment_format") or "〔%s〕" + -- 获取全局版本号 + local current_version = "v0.0.0" + if wanxiang and wanxiang.version then + current_version = wanxiang.version + end + env.chain = config:get_bool(ns .. "/chain") if env.chain == nil then env.chain = false end @@ -158,7 +204,7 @@ function M.init(env) -- 2. 解析 Types env.types = {} - local tasks = {} + local tasks = {} -- 仅在需要重建时使用 local function resolve_path(relative) if not relative then return nil end @@ -221,20 +267,21 @@ function M.init(env) if #triggers > 0 then local prefix = config:get_string(entry_path .. "/prefix") or "" local mode = config:get_string(entry_path .. "/mode") or "append" - - -- 模式: "append"(默认), "text"(原文), "none"(无) local comment_mode = config:get_string(entry_path .. "/comment_mode") - if not comment_mode then comment_mode = "none" end + if not comment_mode then comment_mode = "comment" end + local fmm = config:get_bool(entry_path .. "/fmm") + if fmm == nil then fmm = false end insert(env.types, { triggers = triggers, tags = target_tags, prefix = prefix, mode = mode, - comment_mode = comment_mode + comment_mode = comment_mode, + fmm = fmm }) - -- 解析文件 + -- 收集文件路径 (用于重建) local keys_to_check = {"files", "file"} for _, key in ipairs(keys_to_check) do local d_path = entry_path .. "/" .. key @@ -259,13 +306,27 @@ function M.init(env) if ok and db then env.db = db - local cur_sig = calculate_tasks_signature(tasks) - local old_sig = db:meta_fetch("_sig") + + -- Wanxiang 版本控制核心逻辑 ★★★ + local db_version = db:meta_fetch("_wanxiang_ver") or "" local old_delim = db:meta_fetch("_delim") - if cur_sig ~= old_sig or env.delimiter ~= old_delim then + + local need_rebuild = false + + -- 1. 检查版本号字符串是否一致 (例如 "v14.0.5" vs "v14.0.6") + if current_version ~= db_version then need_rebuild = true end + + -- 2. 分隔符变了也需要重建 + if env.delimiter ~= old_delim then need_rebuild = true end + + if need_rebuild then if rebuild(tasks, db) then - db:meta_update("_sig", cur_sig) + -- 写入新的版本号字符串 + db:meta_update("_wanxiang_ver", current_version) db:meta_update("_delim", env.delimiter) + if log and log.info then + log.info("super_replacer: 检测到版本变更 (" .. db_version .. " -> " .. current_version .. "),数据已重建。") + end end end else @@ -321,18 +382,16 @@ function M.func(input, env) local key = t.prefix .. query_text local val = db:fetch(key) + if not val and t.fmm then + local seg_result = segment_convert(query_text, db, t.prefix, split_pat) + if seg_result ~= query_text then val = seg_result end + end + if val then local mode = t.mode - - -- 计算注释内容 local rule_comment = "" - if t.comment_mode == "text" then - rule_comment = cand.text - elseif t.comment_mode == "append" then - rule_comment = cand.comment - else - rule_comment = "" - end + if t.comment_mode == "text" then rule_comment = cand.text + elseif t.comment_mode == "comment" then rule_comment = cand.comment end if mode == "comment" then local parts = {} @@ -345,12 +404,8 @@ function M.func(input, env) for p in s_gmatch(val, split_pat) do if first then current_text = p - -- 链式替换时更新主候选注释 - if t.comment_mode == "none" then - current_main_comment = "" - elseif t.comment_mode == "text" then - current_main_comment = cand.text - end + if t.comment_mode == "none" then current_main_comment = "" + elseif t.comment_mode == "text" then current_main_comment = cand.text end first = false else insert(pending_candidates, { text=p, comment=rule_comment }) @@ -384,6 +439,7 @@ function M.func(input, env) if show_main then if is_chain and current_text ~= cand.text then local nc = Candidate("kv", cand.start, cand._end, current_text, current_main_comment) + nc.preedit = cand.preedit nc.quality = cand.quality yield(nc) else @@ -394,6 +450,7 @@ function M.func(input, env) for _, item in ipairs(pending_candidates) do if not (show_main and item.text == current_text) then local nc = Candidate("kv", cand.start, cand._end, item.text, item.comment) + nc.preedit = cand.preedit nc.quality = cand.quality yield(nc) end diff --git a/lua/super_tips.lua b/lua/super_tips.lua index 6508544..998aa7a 100644 --- a/lua/super_tips.lua +++ b/lua/super_tips.lua @@ -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 \ No newline at end of file diff --git a/wanxiang.schema.yaml b/wanxiang.schema.yaml index 35ef1d3..581a8a6 100644 --- a/wanxiang.schema.yaml +++ b/wanxiang.schema.yaml @@ -128,7 +128,7 @@ super_replacer: # 场景1:输入 '哈哈' -> 变成 '1.哈哈 2.😄' - option: emoji # 开关名称与上面开关名称保持一致 mode: append # 新增候选append 替换原候选replace 替换注释comment - comment_mode: none # 注释模式: "append"(默认), "text"(原文), "none"(无) + comment_mode: none # 注释模式: "append"(原候选注释继承), "text"(原候选文本放在注释), "none"(空,默认) tags: [abc] # 生效的tag prefix: "_em_" # 前缀用于区分同一个数据库的不同用途数据 files: @@ -154,6 +154,7 @@ super_replacer: - option: [ s2t, s2hk, s2tw ] #后面依赖这条流水线有一个开关为true这条流水线就能工作 mode: replace # <--- 替换原候选模式 comment_mode: append + sentence: true # <--- 句子级别替换 tags: [abc] prefix: "_s2t_" files: @@ -162,6 +163,7 @@ super_replacer: - option: s2hk mode: replace # <--- 替换原候选模式 comment_mode: append + sentence: true # <--- 句子级别替换 tags: [abc] prefix: "_s2hk_" files: @@ -170,6 +172,7 @@ super_replacer: - option: s2tw mode: replace # <--- 替换原候选模式 comment_mode: append + sentence: true # <--- 句子级别替换 tags: [abc] prefix: "_s2tw_" files: diff --git a/wanxiang_t9.schema.yaml b/wanxiang_t9.schema.yaml index f0772a4..eaf14b2 100644 --- a/wanxiang_t9.schema.yaml +++ b/wanxiang_t9.schema.yaml @@ -228,7 +228,7 @@ super_replacer: # 场景1:输入 '哈哈' -> 变成 '1.哈哈 2.😄' - option: emoji # 开关名称与上面开关名称保持一致 mode: append # 新增候选append 替换原候选replace 替换注释comment - comment_mode: none # 注释模式: "append"(默认), "text"(原文), "none"(无) + comment_mode: none # 注释模式: "append"(原候选注释继承), "text"(原候选文本放在注释), "none"(空,默认) tags: [abc] # 生效的tag prefix: "_em_" # 前缀用于区分同一个数据库的不同用途数据 files: @@ -254,6 +254,7 @@ super_replacer: - option: [ s2t, s2hk, s2tw ] #后面依赖这条流水线有一个开关为true这条流水线就能工作 mode: replace # <--- 替换原候选模式 comment_mode: append + sentence: true # <--- 句子级别替换 tags: [abc] prefix: "_s2t_" files: @@ -262,6 +263,7 @@ super_replacer: - option: s2hk mode: replace # <--- 替换原候选模式 comment_mode: append + sentence: true # <--- 句子级别替换 tags: [abc] prefix: "_s2hk_" files: @@ -270,6 +272,7 @@ super_replacer: - option: s2tw mode: replace # <--- 替换原候选模式 comment_mode: append + sentence: true # <--- 句子级别替换 tags: [abc] prefix: "_s2tw_" files: