diff --git a/README.md b/README.md index 809fb98..9b6acde 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,7 @@ require("skkelua").config({ enabled = true, insertOnSelect = true, -- 候補にフォーカスした時点で本文へ挿入する deferOkuri = true, -- 送り仮名確定でも自動変換せず、第一候補を自動選択する + abbrevWithSpace = false, -- abbrev モードの候補から半角スペースを外す (既定は true) }, pureSpace = true, -- Space を変換に使わず「変換中なら確定 + 空白」にする }) @@ -133,7 +134,10 @@ require("skkelua").config({ 漢字を含む単語も pum で変換しながら登録できます。辞書に無い読みでも この項目だけのメニューが開くので、変換キーを使わずに新しい単語を登録できます - abbrev モード (`/`) の入力中は、「[辞書登録]」の下に入力したアルファベットの - 前に半角スペースを足したものが並びます (▽overall なら ` overall`) + 前に半角スペースを足したものが並びます (▽overall なら ` overall`)。 + `completion.abbrevWithSpace = false` にすると半角スペースを足さない + 入力そのもの (▽overall なら `overall`) が並びます。 + どちらも確定ではユーザー辞書に登録されません - skkelua の有効・無効に連動して補完も付いたり外れたりします ## skkeleton との関係 diff --git a/doc/skkelua.txt b/doc/skkelua.txt index 4be2e09..ba7a81c 100644 --- a/doc/skkelua.txt +++ b/doc/skkelua.txt @@ -289,6 +289,11 @@ in-process の LSP サーバー (|vim.lsp.start()| の cmd 関数) として実 ▽おく*る のまま補完メニューの第一候補を自動選択・挿入します。 CTRL-P で選択を外すと変換前のひらがなに戻ります。 insertOnSelect と併用してください +- abbrevWithSpace (true): abbrev モード (/) の入力そのままの候補 (raw 候補) + の前に半角スペースを足すかどうか。true なら ▽overall で " overall"、 + false なら "overall" が [辞書登録] の下に並びます。 + 他の SKK では abbrev モードにこの候補が出ないため、それに合わせる場合は + false にします 変換の開始・確定を補完メニューだけで行う場合は、Space を変換に使わない pureSpace (|skkelua-options|) も合わせて有効にすると快適です。 @@ -323,8 +328,11 @@ pureSpace (|skkelua-options|) も合わせて有効にすると快適です。 (送りあり入力では送り仮名が確定するまで表示されません) - abbrev モード (/) の入力中は、[辞書登録] の下に入力したアルファベットの 前に半角スペースを足したものが並びます (▽overall なら " overall")。 - この候補の確定ではユーザー辞書に登録されません。入力そのものは - の無変換確定で入ります + abbrevWithSpace (上記) を false にすると半角スペースを足さない + 入力そのもの (▽overall なら "overall") が並びます。 + いずれの候補の確定でもユーザー辞書には登録されません。 + 入力そのものは abbrevWithSpace が true の場合でも の + 無変換確定で入力できます - 登録プロンプトは insert モードのバッファなので、中でも skkelua の かな入力と補完メニューがそのまま使えます (プロンプト内の補完に [辞書登録] は出ません)。 で登録、 / CTRL-C でキャンセル。 diff --git a/lua/skkelua/config.lua b/lua/skkelua/config.lua index dc43262..7a44571 100644 --- a/lua/skkelua/config.lua +++ b/lua/skkelua/config.lua @@ -58,10 +58,12 @@ M.default_indicator = default_indicator ---@field enabled boolean 変換候補を builtin LSP 補完として表示するかどうか ---@field insertOnSelect boolean 候補の選択 (フォーカス) と同時に本文へ挿入するかどうか ---@field deferOkuri boolean 送り仮名確定時に自動変換せず、補完メニューでの選択に委ねるかどうか +---@field abbrevWithSpace boolean abbrev モードの raw 候補 (入力そのもの) の前に半角スペースを足すかどうか local default_completion = { enabled = false, insertOnSelect = false, deferOkuri = false, + abbrevWithSpace = true, } M.default_completion = default_completion @@ -169,6 +171,7 @@ local validators = { ensure_type(merged.enabled, "boolean", "completion.enabled") ensure_type(merged.insertOnSelect, "boolean", "completion.insertOnSelect") ensure_type(merged.deferOkuri, "boolean", "completion.deferOkuri") + ensure_type(merged.abbrevWithSpace, "boolean", "completion.abbrevWithSpace") return merged end, completionRankFile = ensure_string("completionRankFile"), diff --git a/lua/skkelua/function/common.lua b/lua/skkelua/function/common.lua index 0d63f7b..5d820bc 100644 --- a/lua/skkelua/function/common.lua +++ b/lua/skkelua/function/common.lua @@ -181,7 +181,7 @@ function M.purge_candidate(context, key) elseif state.type == "input" then if state.mode == "direct" then local sel_word, data = require("skkelua.lsp").selected_word() - -- raw 候補 (abbrev の半角スペース + 入力) は辞書エントリではないので対象外 + -- raw 候補 (abbrev の入力そのまま) は辞書エントリではないので対象外 if data and not data.raw then type_, word, candidate = data.type, data.midasi, data.word -- pum が直接書き換えたバッファのテキストを表示中として認識させ、 diff --git a/lua/skkelua/lsp.lua b/lua/skkelua/lsp.lua index c0923ed..0ae730b 100644 --- a/lua/skkelua/lsp.lua +++ b/lua/skkelua/lsp.lua @@ -72,13 +72,13 @@ end ---@field type skkelua.HenkanType ---@field affix? skkelua.AffixType ---@field rank? number 並び順の決定に使うランク (大きいほど上) ----@field raw? boolean 辞書由来でない候補 (abbrev の半角スペース + 入力。登録・purge の対象外) +---@field raw? boolean 辞書由来でない候補 (abbrev の入力そのまま。completion.abbrevWithSpace で半角スペースを前置できる。登録・purge の対象外) --- 送りなし変換入力 (▽かんじ) の候補: 見出しの前方一致検索 --- (@ddc-sources/skkeleton の gather に相当)。 --- ユーザー辞書で確定済みの候補 (ランク持ち) を確定が新しい順に先頭へ置き、 --- 残りは見出しの辞書順で並べる。 ---- abbrev モードでは入力の半角スペース前置形を末尾に足す +--- abbrev モードでは入力の raw 候補を末尾に足す --- (補完リストでは [辞書登録] の後ろに並ぶ) ---@return skkelua.LspCandidate[] local function okurinasi_candidates() @@ -111,11 +111,13 @@ local function okurinasi_candidates() end) local context = require("skkelua.store").get_context() if context.mode == "abbrev" then - -- 入力したアルファベットの前に半角スペースを足したもの (英単語を - -- 和文の中に空けて入れる用)。入力そのものは の無変換確定で - -- 入るので候補には並べない + -- 入力そのものの raw 候補。completion.abbrevWithSpace (デフォルト true) だと + -- 前に半角スペースを足す (英単語を和文の中に空けて入れる用)。false なら + -- 入力そのまま (▽overall なら "overall") local feed = context.state.henkanFeed - result[#result + 1] = { word = " " .. feed, midasi = feed, okuri = "", type = "okurinasi", raw = true } + local with_space = require("skkelua.config").config.completion.abbrevWithSpace + local word = with_space and (" " .. feed) or feed + result[#result + 1] = { word = word, midasi = feed, okuri = "", type = "okurinasi", raw = true } end return result end @@ -327,7 +329,7 @@ local function make_completion_list() set_completeopt(buf, auto_select) local items = {} - -- abbrev の raw 候補 (半角スペース + 入力) は辞書候補ではないので、 + -- abbrev の raw 候補 (入力そのもの) は辞書候補ではないので、 -- [辞書登録] より後ろに回す local trailing = {} local seen = {} @@ -516,7 +518,7 @@ function M._on_complete_done(reason, completed_item) end) return end - -- abbrev の raw 候補 (半角スペース + 入力) は辞書へ登録しない + -- abbrev の raw 候補 (入力そのもの) は辞書へ登録しない if data.raw then return end diff --git a/tests/config_spec.lua b/tests/config_spec.lua index 4d14785..173d95d 100644 --- a/tests/config_spec.lua +++ b/tests/config_spec.lua @@ -100,6 +100,14 @@ t.test("set_config validation", function() skkelua.config({ useGoogleJapaneseInput = true }) end) + -- completion の部分指定はデフォルトへマージされる + skkelua.config({ completion = { abbrevWithSpace = false } }) + t.assert_equals(false, config.completion.abbrevWithSpace) + t.assert_equals(false, config.completion.enabled) + t.assert_error(function() + skkelua.config({ completion = { abbrevWithSpace = "no" } }) + end) + -- userDictionary の ~ 展開 skkelua.config({ userDictionary = "~/.test-skkeleton" }) t.assert_true(not vim.startswith(config.userDictionary, "~")) diff --git a/tests/lsp_spec.lua b/tests/lsp_spec.lua index eb3e39c..c7d2cd8 100644 --- a/tests/lsp_spec.lua +++ b/tests/lsp_spec.lua @@ -653,7 +653,7 @@ t.test("abbrev input adds a space-prefixed raw candidate below [辞書登録]", labels[#labels + 1] = item.label end -- 辞書候補、[辞書登録] の後ろに、スペース前置形だけが並ぶ - -- (入力そのものは の無変換確定で入るので候補にしない) + -- (completion.abbrevWithSpace = false だと半角スペース無しの入力そのままになる) t.assert_equals({ "全体", "[辞書登録]", " overall" }, labels) -- 並び順は sortText で保たれる (クライアントは sortText で並べ替える) t.assert_equals({ "00001", "00002", "00003" }, vim.tbl_map(function(item) @@ -697,6 +697,62 @@ t.test("abbrev keeps a dictionary candidate equal to the input and adds only the vim.cmd.bwipeout({ bang = true }) end) +t.test("completion.abbrevWithSpace=false uses the raw input without a leading space", function() + local skkelua = require("skkelua") + local lib = require("skkelua.store").get_library() + lib:register_henkan_result("okurinasi", "overall", "全体") + skkelua.config({ completion = { enabled = true, abbrevWithSpace = false } }) + skkelua._handle_request("enable", {}, vim_status) + + local pre_edit = setup_state({ "/", "o", "v", "e", "r", "a", "l", "l" }) + t.assert_equals("▽overall", pre_edit) + + local list = require("skkelua.lsp")._make_completion_list() + local labels = {} + for _, item in ipairs(list.items) do + labels[#labels + 1] = item.label + end + t.assert_equals({ "全体", "[辞書登録]", "overall" }, labels) + t.assert_equals({ "00001", "00002", "00003" }, vim.tbl_map(function(item) + return item.sortText + end, list.items)) + + local raw = list.items[3] + t.assert_equals("overall", raw.textEdit.newText) + t.assert_equals(true, raw.data.raw) + + -- 入力そのものの候補を確定しても辞書には登録されない + require("skkelua.lsp")._on_complete_done("accept", { + user_data = { nvim = { lsp = { completion_item = { data = raw.data } } } }, + }) + t.assert_equals({ "全体" }, lib:get_henkan_result("okurinasi", "overall")) + + vim.cmd.bwipeout({ bang = true }) +end) + +t.test("completion.abbrevWithSpace=false drops a raw candidate duplicated by a dictionary candidate", function() + local skkelua = require("skkelua") + local lib = require("skkelua.store").get_library() + -- 辞書候補が入力そのものと同じ場合は dedup され、raw 候補は並ばない + lib:register_henkan_result("okurinasi", "ov", "ov") + skkelua.config({ completion = { enabled = true, abbrevWithSpace = false } }) + skkelua._handle_request("enable", {}, vim_status) + + local pre_edit = setup_state({ "/", "o", "v" }) + t.assert_equals("▽ov", pre_edit) + + local list = require("skkelua.lsp")._make_completion_list() + local labels = {} + for _, item in ipairs(list.items) do + labels[#labels + 1] = item.label + end + t.assert_equals({ "ov", "[辞書登録]" }, labels) + -- 先頭は辞書候補なので確定時に登録される (raw ではない) + t.assert_equals(nil, list.items[1].data.raw) + + vim.cmd.bwipeout({ bang = true }) +end) + t.test("kana input does not add raw candidates", function() setup_library() local skkelua = require("skkelua")