From 8ca2c8b59e887e1b483b0c5472487c5ec9af8507 Mon Sep 17 00:00:00 2001 From: yoshphys Date: Thu, 19 Mar 2026 14:35:21 +0900 Subject: [PATCH 1/3] =?UTF-8?q?fix:=20abbrev=E3=83=A2=E3=83=BC=E3=83=89?= =?UTF-8?q?=E3=81=A7=E3=81=AE=E3=82=AD=E3=83=A3=E3=83=B3=E3=82=BB=E3=83=AB?= =?UTF-8?q?=E5=BE=8C=E3=81=AB=E3=83=A2=E3=83=BC=E3=83=89=E3=82=92hira?= =?UTF-8?q?=E3=81=AB=E6=88=BB=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cancel()はinitializeState()を直接呼んでいたため、state.modeは "direct"にリセットされるがmodeChange()は呼ばれず、context.mode (およびvim.g["skkeleton#mode"])が"abbrev"のまま残るバグがあった。 initializeStateWithAbbrev()を使うよう変更することで、abbrevモードから 抜ける際にmodeChange("hira")が呼ばれ、表示上のモードが正しく同期される。 Co-Authored-By: Claude Sonnet 4.6 --- denops/skkeleton/function/common.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/denops/skkeleton/function/common.ts b/denops/skkeleton/function/common.ts index a74865571..16b8764a1 100644 --- a/denops/skkeleton/function/common.ts +++ b/denops/skkeleton/function/common.ts @@ -73,7 +73,7 @@ export async function newline(context: Context) { } } -export function cancel(context: Context) { +export async function cancel(context: Context) { const state = context.state; if ( state.type === "input" && @@ -83,12 +83,12 @@ export function cancel(context: Context) { context.kakutei("\x03"); } if (config.immediatelyCancel) { - initializeState(context.state); + await initializeStateWithAbbrev(context); return; } switch (state.type) { case "input": - initializeState(state); + await initializeStateWithAbbrev(context); break; case "henkan": context.state.type = "input"; From b6824567f3e65ed659860cf6de37d777744fa44a Mon Sep 17 00:00:00 2001 From: yoshphys Date: Thu, 19 Mar 2026 14:36:49 +0900 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20direct=E5=85=A5=E5=8A=9B=E7=8A=B6?= =?UTF-8?q?=E6=85=8B=E3=81=8B=E3=82=89abbrev=E3=83=A2=E3=83=BC=E3=83=89?= =?UTF-8?q?=E3=82=92=E4=B8=80=E6=99=82=E7=9A=84=E3=81=AB=E4=BD=BF=E3=81=86?= =?UTF-8?q?abbrevFromDirect=20API=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit skkeleton無効(direct入力)状態から直接abbrevモードに入り、 変換確定またはキャンセル後に自動的にdisableに戻る機能を追加する。 実装内容: - Context.onAbbrevDone: abbrevセッション完了時のコールバックフィールドを追加 - initializeStateWithAbbrev(): onAbbrevDoneがある場合はmodeChange("hira")を スキップしてコールバックを実行(hiraへの一瞬の遷移を防ぐ) - abbrevFromDirect(): enable後にabbrevモードに入り、onAbbrevDoneにdisableを設定 - handle()にaabbrevFromDirectルーティングを追加 - completeCallback(): ddc経由での確定時もonAbbrevDoneを処理 使用例: vim.fn["skkeleton#handle"]("abbrevFromDirect", vim.empty_dict()) Co-Authored-By: Claude Sonnet 4.6 --- denops/skkeleton/context.ts | 2 ++ denops/skkeleton/main.ts | 19 +++++++++++++++++++ denops/skkeleton/mode.ts | 12 +++++++++++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/denops/skkeleton/context.ts b/denops/skkeleton/context.ts index 6fab4b07c..0f212e5d4 100644 --- a/denops/skkeleton/context.ts +++ b/denops/skkeleton/context.ts @@ -24,6 +24,8 @@ export class Context { word: "", candidate: "", }; + // abbrevFromDirect用: abbrevセッション完了時に呼ばれるコールバック + onAbbrevDone?: () => Promise; kakutei(str: string) { this.preEdit.doKakutei(str); diff --git a/denops/skkeleton/main.ts b/denops/skkeleton/main.ts index 13a428175..a9e4b3017 100644 --- a/denops/skkeleton/main.ts +++ b/denops/skkeleton/main.ts @@ -144,6 +144,19 @@ async function enable(opts: unknown, vimStatus: unknown): Promise { return ""; } +async function abbrevFromDirect( + opts: unknown, + vimStatus: unknown, +): Promise { + await enable(opts, vimStatus); + const context = currentContext.get(); + context.onAbbrevDone = async () => { + await context.denops!.call("skkeleton#disable"); + }; + await modeFunctions.get()["abbrev"]?.(context, ""); + return context.preEdit.output(context.toString()); +} + async function disable(opts: unknown, vimStatus: unknown): Promise { const context = currentContext.get(); const state = currentContext.get().state; @@ -303,6 +316,8 @@ export const main: Entrypoint = async (denops) => { return buildResult(await enable(opts, vimStatus)); } else if (func === "enable") { return buildResult(await enable(opts, vimStatus)); + } else if (func === "abbrevFromDirect") { + return buildResult(await abbrevFromDirect(opts, vimStatus)); } else if (func === "disable") { return buildResult(await disable(opts, vimStatus)); } else if (func === "toggle") { @@ -374,6 +389,10 @@ export const main: Entrypoint = async (denops) => { word: midasi, candidate: word, }; + // abbrevFromDirectセッション中にddc経由で確定された場合の後処理 + if (context.onAbbrevDone && context.mode === "abbrev") { + await initializeStateWithAbbrev(context, ["converter"]); + } }, // deno-lint-ignore require-await async getConfig() { diff --git a/denops/skkeleton/mode.ts b/denops/skkeleton/mode.ts index 546f38acb..54cb3e65f 100644 --- a/denops/skkeleton/mode.ts +++ b/denops/skkeleton/mode.ts @@ -29,8 +29,18 @@ export async function initializeStateWithAbbrev( ignore: string[] = [], ) { if (context.mode === "abbrev") { - await modeChange(context, "hira"); ignore = ignore.filter((key) => key !== "converter" && key !== "table"); + initializeState(context.state, ignore); + if (context.onAbbrevDone) { + // onAbbrevDoneがある場合はmodeChange("hira")をスキップし + // コールバックに後処理を委ねる(例: disable) + const callback = context.onAbbrevDone; + context.onAbbrevDone = undefined; + await callback(); + } else { + await modeChange(context, "hira"); + } + return; } initializeState(context.state, ignore); } From 1e8612149024b385ff4de6aaa440e8b90be50167 Mon Sep 17 00:00:00 2001 From: yoshphys Date: Fri, 20 Mar 2026 00:04:23 +0900 Subject: [PATCH 3/3] fix: output next key directly after abbrevFromDirect session ends --- denops/skkeleton/function/henkan.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/denops/skkeleton/function/henkan.ts b/denops/skkeleton/function/henkan.ts index 3927007ca..0005924f5 100644 --- a/denops/skkeleton/function/henkan.ts +++ b/denops/skkeleton/function/henkan.ts @@ -135,8 +135,13 @@ export async function henkanInput(context: Context, key: string) { } } + const wasAbbrevSession = context.onAbbrevDone !== undefined; await kakutei(context); - await handleKey(context, keyToNotation[key] ?? key); + if (wasAbbrevSession) { + context.kakutei(key); + } else { + await handleKey(context, keyToNotation[key] ?? key); + } } export async function suffix(context: Context) {