-- 输入统计:上屏字数 + 数按键数 local M = {} -- 存储路径(根据操作系统修改路径,当前为Windows路径) local db_path = "D:\\%APPDATA%\\Rime\\dist\\input_count.txt" -- 输入统计触发按键(可以改成其他不常用的按键组合) local command_key = "sS" local save_stat local function format_count(n) local num = tonumber(n) or 0 local function trunc(v, digits) local p = 10 ^ digits return math.floor(v * p) / p end if num < 1000 then return string.format("%d", math.floor(num)) elseif num < 10000 then return string.format("%.2f千", trunc(num / 1000, 2)) else return string.format("%.2f万", trunc(num / 10000, 2)) end end local function load_stat() if not _G.my_stat then _G.my_stat = { total_keys = 0, total_words = 0, day_keys = 0, day_words = 0, day = os.date("%Y%m%d"), last_code = "", start_iso = os.date("%Y-%m-%d %H:%M:%S"), history = {} } local f = io.open(db_path, "r") if f then local history = {} for line in f:lines() do local day1, day_words1, day_keys1 = line:match("^day=(%d+)%s+day_words=(%d+)%s+day_keys=(%d+)$") if day1 then table.insert(history, { day = day1, day_words = tonumber(day_words1) or 0, day_keys = tonumber(day_keys1) or 0 }) else local total_words1, total_keys1 = line:match("^total_words=(%d+)%s+total_keys=(%d+)$") if total_words1 and total_keys1 then _G.my_stat.total_words = tonumber(total_words1) or _G.my_stat.total_words _G.my_stat.total_keys = tonumber(total_keys1) or _G.my_stat.total_keys else local k, v = line:match("([%w_]+)=(.+)") if k and v then if k == "day" then _G.my_stat.day = v elseif k == "start_iso" then _G.my_stat.start_iso = v elseif k == "last_code" then _G.my_stat.last_code = v elseif k == "total_words" then _G.my_stat.total_words = tonumber(v) or _G.my_stat.total_words elseif k == "total_keys" then _G.my_stat.total_keys = tonumber(v) or _G.my_stat.total_keys elseif k == "day_words" then _G.my_stat.day_words = tonumber(v) or _G.my_stat.day_words elseif k == "day_keys" then _G.my_stat.day_keys = tonumber(v) or _G.my_stat.day_keys else _G.my_stat[k] = tonumber(v) or 0 end end end end end f:close() if #history > 0 then table.sort(history, function(a, b) return a.day < b.day end) local last = history[#history] _G.my_stat.day = last.day _G.my_stat.day_keys = last.day_keys _G.my_stat.day_words = last.day_words _G.my_stat.history = history else _G.my_stat.history = {} end end end -- 如果文件不存在,清空内存历史,避免删文件后旧记录残留 do local f = io.open(db_path, "r") if f then f:close() else _G.my_stat.history = {} end end -- 按天重置今日统计 local today = os.date("%Y%m%d") if _G.my_stat.day ~= today then local history = _G.my_stat.history or {} local prev_day = _G.my_stat.day local found = false for i = 1, #history do if history[i].day == prev_day then history[i].day_words = tonumber(_G.my_stat.day_words) or 0 history[i].day_keys = tonumber(_G.my_stat.day_keys) or 0 found = true break end end if not found and prev_day ~= nil and prev_day ~= "" then table.insert(history, { day = prev_day, day_words = tonumber(_G.my_stat.day_words) or 0, day_keys = tonumber(_G.my_stat.day_keys) or 0 }) end _G.my_stat.history = history _G.my_stat.day = today _G.my_stat.day_words = 0 _G.my_stat.day_keys = 0 _G.my_stat.last_code = "" save_stat() end return _G.my_stat end function save_stat() local f = io.open(db_path, "w") if f then -- 首行写入起始日期和时间(ISO 8601 格式) local start_iso = _G.my_stat.start_iso or os.date("%Y-%m-%d %H:%M:%S") _G.my_stat.start_iso = start_iso f:write("start_iso=" .. tostring(start_iso) .. "\n") -- 第二行写入累计统计 f:write(string.format( "total_words=%d total_keys=%d\n", tonumber(_G.my_stat.total_words) or 0, tonumber(_G.my_stat.total_keys) or 0 )) -- 按天一行输出统计项 local history = _G.my_stat.history or {} local today = _G.my_stat.day local found = false for i = 1, #history do if history[i].day == today then history[i].day_words = tonumber(_G.my_stat.day_words) or 0 history[i].day_keys = tonumber(_G.my_stat.day_keys) or 0 found = true break end end if not found then table.insert(history, { day = today, day_words = tonumber(_G.my_stat.day_words) or 0, day_keys = tonumber(_G.my_stat.day_keys) or 0 }) end table.sort(history, function(a, b) return a.day < b.day end) _G.my_stat.history = history for i = 1, #history do local h = history[i] f:write(string.format( "day=%s day_words=%d day_keys=%d\n", h.day, h.day_words or 0, h.day_keys or 0 )) end -- 其他字段保持兼容 if _G.my_stat.last_code ~= nil then f:write("last_code=" .. tostring(_G.my_stat.last_code) .. "\n") end f:close() end end -- 核心:通过 Translator 逆向统计(新版 librime Lua 入口使用 func) function M.func(input, seg, env) local s = load_stat() if input == "" then s.last_code = "" return end if #input < #s.last_code then s.last_code = "" end -- 统计逻辑:如果当前的输入码比上次长,说明按了键 if input == command_key then if s.last_code ~= "" and command_key:sub(1, #s.last_code) == s.last_code then local rollback = #s.last_code if rollback > 0 then s.total_keys = math.max(0, s.total_keys - rollback) s.day_keys = math.max(0, s.day_keys - rollback) end end s.last_code = input elseif input ~= "" and input ~= s.last_code then if #input > #s.last_code then local diff = #input - #s.last_code s.total_keys = s.total_keys + diff s.day_keys = s.day_keys + diff end s.last_code = input end -- 显示逻辑 if input == command_key then save_stat() local day_words = format_count(s.day_words) local day_keys = format_count(s.day_keys) local total_words = format_count(s.total_words) local total_keys = format_count(s.total_keys) local from_date = (s.start_iso or ""):match("^(%d%d%d%d%-%d%d%-%d%d)") or "" local text = string.format("今日:%s字/%s键 | 总计:%s字/%s键", day_words, day_keys, total_words, total_keys) local comment = string.format("[%s—至今]", from_date) yield(Candidate("stat", seg.start, seg._end, text, comment)) end end -- 捕捉上屏(利用 init 挂载监听,如果监听失效,至少按键数能动) function M.init(env) load_stat() pcall(function() env.conn = env.engine.context.commit_notifier:connect(function(ctx) local text = ctx:get_commit_text() if text ~= "" and text ~= command_key then local _, count = text:gsub("[^\128-\193]", "") _G.my_stat.total_words = _G.my_stat.total_words + count _G.my_stat.day_words = _G.my_stat.day_words + count _G.my_stat.last_code = "" -- 上屏后重置编码追踪 save_stat() end end) end) end function M.processor(key, env) return 2 -- 彻底停用此组件的逻辑 end return M