diff --git a/lua/skkelua/guard.lua b/lua/skkelua/guard.lua index 3c1700b..e5cdcfa 100644 --- a/lua/skkelua/guard.lua +++ b/lua/skkelua/guard.lua @@ -75,6 +75,13 @@ end -- insert モードでのみ通す local completion_keys = key_set({ "", "" }) +-- 補完 UI が feedkeys するキー (タイプ由来でなければ通す)。外部の補完 UI は +-- 入力を候補で置き換えるために noremap で を feedkeys することがある。 +-- これは物理キーではなく、捨てると候補が pre-edit の後ろに追記されてしまう。 +-- 置き換えでずれた pre-edit の追跡は、選択挿入と同じく既存の補完リカバリ +-- (prevInput 不一致リセット) が面倒を見る +local fed_keys = key_set({ "" }) + -- pum 表示中の候補操作キー (cmdline の wildmenu も含む) -- Note: は skkelua 自身も feed する: [辞書登録] 項目の確定では -- native_confirm_key が raw を feedkeys し、しかも登録フローへ @@ -141,11 +148,17 @@ end --- 現れない (:lmap はその結果だけが、Lua コールバック --- マッピングは KE_LUA 擬似キーが来る) ため、ここに生の --- 特殊キーが来る = 未マップで素通りする直前ということ +---@param typed string マッピング適用前のキー。feedkeys などタイプ以外で +--- 生じたキーでは空になる ---@return string? "" を返すとそのキーは破棄される -function M._on_key(key, _) +function M._on_key(key, typed) if not M._is_physical_special(key) and not M._is_hostile_control(key) then return end + -- マクロ再生もタイプ以外で生じるキーだが、ユーザー入力の再現なので守る + if typed == "" and vim.fn.reg_executing() == "" and fed_keys()[key] then + return + end local store = require("skkelua.store") if not store.status.enabled then return diff --git a/tests/integration_spec.lua b/tests/integration_spec.lua index 9dd1753..96c36e6 100644 --- a/tests/integration_spec.lua +++ b/tests/integration_spec.lua @@ -78,6 +78,40 @@ t.test("hostile control keys are discarded during pre-edit", function() end) end) +t.test("fed from a completion UI replaces the pre-edit", function() + with_buffer(function() + -- 外部の補完 UI は、入力を候補で置き換えるために noremap で と + -- 候補を feedkeys することがある。 + -- 物理キーではないので guard は破棄しない + vim.keymap.set("i", "", function() + local keys = vim.api.nvim_replace_termcodes("漢字", true, true, true) + vim.api.nvim_feedkeys(keys, "in", false) + end, { buffer = true }) + feed("iJKanji") + t.assert_equals({ "漢字" }, vim.fn.getline(1, "$")) + end) +end) + +t.test(" reaching the guard unmapped is discarded unless fed", function() + local guard = require("skkelua.guard") + local bs = vim.api.nvim_replace_termcodes("", true, true, true) + with_buffer(function() + local results + vim.keymap.set("i", "", function() + local reg_executing = vim.fn.reg_executing + vim.fn.reg_executing = function() + return "q" + end + local replayed = guard._on_key(bs, "") + vim.fn.reg_executing = reg_executing + results = { typed = guard._on_key(bs, bs), fed = guard._on_key(bs, ""), replayed = replayed } + end, { buffer = true }) + feed("iJKanji") + -- 物理キーとマクロ再生の は従来どおり破棄する + t.assert_equals({ typed = "", replayed = "" }, results) + end) +end) + t.test(" still navigates pum during pre-edit", function() with_buffer(function() vim.opt_local.completeopt = "menuone,noselect"