Files
rime_wanxiang/lua/tone_fallback.lua
2026-01-21 17:43:36 +08:00

129 lines
4.1 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- 欢迎使用万象拼音方案
-- @amzxyz
-- https://github.com/amzxyz/rime_wanxiang
-- 用来在声调辅助的时候当你输入 2 个数字的时候自动将声调替换为第二个数字,
-- 也就是说你发现输入错误声调你可以手动轮巡输入而不用回退删除直接输入下一个即可
-- 兼容小键盘输入中不回退
local wanxiang = require("wanxiang")
-- 将目标字符的连续段压缩为“最后一个字符”
local function compress_runs_keep_last(text)
local changed = false
local out = text:gsub('([:"<>7890])([:"<>7890]+)', function(_, tail)
changed = true
return tail:sub(-1)
end)
return out, changed
end
local function should_ignore(ctx)
return wanxiang.is_function_mode_active(ctx) or ctx.input == ""
end
-- 小键盘 keycode 判定0xFFB0 ~ 0xFFB9
local function is_kp_digit_keycode(kc)
return kc >= 0xFFB0 and kc <= 0xFFB9
end
---@class Env
---@field tone_state "idle"|"skip"|"compress"
---@field tone_fallback_update_connection Connection|nil
local P = {}
function P.init(env)
env.tone_state = "idle"
local ctx = env.engine and env.engine.context
if not ctx or not ctx.update_notifier then return end
env.tone_fallback_update_connection = ctx.update_notifier:connect(function(c)
if should_ignore(c) then
env.tone_state = "idle"
return
end
-- 只在当前按键对应的一次更新里消费 state
local state = env.tone_state or "idle"
env.tone_state = "idle" -- 立刻消费,避免“下一次才生效”
-- 小键盘数字:标记为 skip本次直接不压缩
if state == "skip" then
return
end
-- 非压缩状态:直接不动
if state ~= "compress" then
return
end
-- 压缩逻辑
local input = c.input
local caret = (c.caret_pos ~= nil) and c.caret_pos or #input
if caret < 0 then caret = 0 end
if caret > #input then caret = #input end
-- 仅处理光标左侧;右侧保持不变
local left = (caret > 0) and input:sub(1, caret) or ""
local right = (caret < #input) and input:sub(caret + 1) or ""
local left_new, changed = compress_runs_keep_last(left)
if not changed then return end
-- 只改左侧,避免干扰右侧;并精确设置 caret_pos
if caret > 0 then c:pop_input(caret) end
if #left_new > 0 then c:push_input(left_new) end
if c.caret_pos ~= nil then c.caret_pos = #left_new end
-- 右侧 right 不需处理Rime 会保持不变
end)
end
function P.fini(env)
if env.tone_fallback_update_connection then
env.tone_fallback_update_connection:disconnect()
env.tone_fallback_update_connection = nil
end
env.tone_state = "idle"
end
---@return ProcessResult
function P.func(key, env)
local ctx = env.engine.context
if should_ignore(ctx) then
env.tone_state = "idle"
return wanxiang.RIME_PROCESS_RESULTS.kNoop
end
-- 小键盘数字:标记这次按键为 skip本轮 update_notifier 不压缩
local kc = key.keycode
if is_kp_digit_keycode(kc) then
env.tone_state = "skip"
return wanxiang.RIME_PROCESS_RESULTS.kNoop
end
-- 主键盘数字 09标记为 compress
local r = key:repr() or ""
if r:match("^[0-9]$") then
env.tone_state = "compress"
-- 这里用“预测压缩是否会发生”来决定要不要告诉 Rime “我处理了这个按键”
local input = ctx.input or ""
local caret = (ctx.caret_pos ~= nil) and ctx.caret_pos or #input
if caret < 0 then caret = 0 end
if caret > #input then caret = #input end
local left = (caret > 0) and input:sub(1, caret) or ""
local _, changed = compress_runs_keep_last(left)
return changed and wanxiang.RIME_PROCESS_RESULTS.kAccepted
or wanxiang.RIME_PROCESS_RESULTS.kNoop
end
-- 其它按键:不触发压缩,也不跳过
env.tone_state = "idle"
return wanxiang.RIME_PROCESS_RESULTS.kNoop
end
return P