Obsidian(Vimモード)のIME制御をAutoHotkeyでハックしていい感じにする

マークダウンベースの知識管理アプリObsidianが昨今盛り上がりを見せつつある。Obsidianがどんなアプリかは多くの紹介記事を見ていただくとして、ここでは日本語入力の問題について考えてみたい。

Obsidianでパブサをしていると日本語入力で困ってる人を見かける。IME関連をいじるには、WindowsならばAutoHotkeyという方法もある。一例として日本語入力を快適にするIME制御を取り上げる。


# ObsidianのVimモード+各種プラグインの強力さはあまりにも異常

Obsidianのイケてる面の1つにVimモードがあり、VS Codeのvimプラグインみたいな具合に"Vimモドキ"の操作が行える。

さらに、Vimrc Supportプラグインでは"vimrcモドキ"の設定ファイルを用いてそこそこカスタマイズすることもできる。このプラグインにはjscommand, jsfileコマンドが付属しており、ワンライナーJavaScriptや外部JavaScriptでカスタマイズすることもできる。もちろんそれを特定のキーストロークで呼び出すマッピングだって定義できる。

```obsidian.vimrc
" 次のリンクに飛ぶ
" vimrc.jsはvault(DBフォルダ)直下に置く。そこにパターンで検索してジャンプする関数を書く。中身は割愛。
exmap nextlink jsfile vimrc.js { jumpByPattern(true, "(?<=\\[\\[).") }
map ]l :nextlink
exmap prevlink jsfile vimrc.js { jumpByPattern(false, "(?<=\\[\\[).") }
map [l :prevlink

" 現在行のヘッダーを切り替える
exmap switchHeader jscommand { let lnum = editor.getCursor().line; let line = editor.getLine(lnum); line = line.slice(0, 2) == "# " ? line.slice(2) : "# " + line; editor.setLine(lnum, line); }
map <C-q> :switchHeader

```


# 日本語入力がいい感じにオンオフしてくれない問題に喝！

そんな快適なVimモードにおいて、どうにも困った快適ではない現象がある。日本語入力の問題だ。

例えば、タイトルを日本語で編集してEnterで確定すると日本語入力オンのままエディタにフォーカスが移るため、Vimライクアプリ特有の修飾キーのないキーバインドが利かずに日本語が入力されてしまう。例えば、iでインサートモードに入ろうとすると、「に」が入力される(かな入力の場合)。

Obsidianは日本語ユーザーも増えてきており日本人開発者もいるのでこの問題はいずれ修正されるかもしれないが、ワークアラウンドとしてAutoHotkeyを使ってみよう。


# AutoHotkey準備編

必要なものは次のとおり。
- AutoHotkeyのACC+COMライブラリ。キャレット位置を取得するのに必要。
- IME.ahk。IME制御の定番。
- ノーマルモードを示す右下の緑色の🟢マークの画像。緑色の🟢マークはVimrc Supportプラグインを入れてその設定から表示させてスクショする。
- IMEの設定でCtrl+]で変換中の内容がクリアされるようにしておく(ATOKでは可能だがIMEによっては不可能かも)

大まかな仕様は「編集エリアでEnterを押した時にノーマルモードかつ日本語入力モードであれば、EnterでIMEで変換中だった内容を入力する代わりに、その内容をクリアしてIMEを切る」。

編集エリアにいるかの判定として、ACCライブラリでキャレット位置を取得してウィンドウ左端600px(環境依存)の幅にいなければ編集エリアにいるとみなす(クソ雑な)判定にした。
- ここで「ウィンドウ中央で入力時でも編集エリアにいるとは限らんのでは？」と思った読者もいるだろう。しかしこれはうまく機能する。何故なら後で述べるようにノーマルモードを🟢マークの画像とのマッチで判定しているからだ。例えば、コマンドバーで入力時にはウィンドウの他領域が全体に暗くなるためノーマルモードのマークが表示されていてもノーマルモードとは判定されない。
- もう1点。AutoHotkeyでは標準でA_CaretX, A_CaretYという変数を利用してキャレットの位置が取得できるが、これは限られたキャレットのみで、Obsidianでは使えない。だからアクセシビリティを強力にサポートするACCライブラリを用いる。

日本語入力モードかはIME.ahkのIME_GET()値を見ればよい。

ノーマルモードにいるかは前述の画像を用いてImageSearchコマンドで画像検索する。


# 完成したコード

実際のコードは次のようである。


```%A_MyDocuments%\AutoHotkey\main.ahk
#Include %A_MyDocuments%\AutoHotkey\ACC+COM\ACC\ACCV2.ahk
; 「#Include %A_MyDocuments%\AutoHotkey\Lib\IME.ahk」の略記
#Include <IME>



; Autoexec Sectionはここまで。(最初にラベルやホットキーの定義、スクリプトの末尾、return、exit
; のいずれかが出てきた所までの領域をAutoexec Sectionと言い、その内容はスクリプト読み込み時に実行される。)
#Include %A_MyDocuments%\AutoHotkey\app\obsidian.ahk

```

```%A_MyDocuments%\AutoHotkey\app\obsidian.ahk

#If WinActive("ahk_exe Obsidian.exe")
; Enter時に編集エリアにいてノーマルモードで日本語入力モードであればIMEを切る
Enter::
Acc_Caret := Acc_ObjectFromWindow(WinExist("A"), OBJID_CARET := 0xFFFFFFF8)
caretloc := Acc_Location(Acc_Caret)
If(IME_GET() = 1){
    CoordMode, Pixel, Screen
    WinGetPos, x, y, w, h, A
    x1 := x + w - 80, x2 := x + w, y1 := y + h -30, y2 := y + h
    ; 🟢マークの画像の名前
    file := "obsidian_mode_normal.bmp"
    ImageSearch, x, y, x1, y1, x2, y2, *20 %A_MyDocuments%/AutoHotkey/img/%file%

    ; 日本語入力中で、編集エリアにキャレットがあり、ノーマルモードのとき
    If(caretloc.x > 600 && ErrorLevel = 0){
        ; ATOKの設定で入力内容をクリアする
        SendInput, ^]
        ; 日本語入力をオフ
        IME_SET(0)
        return()
    }
}
SendInput, {Enter}
return()
#If
```



## より細やかに編集中か判定するには

複数のペインを開いている時やSliding Panes(タイトルバーを縦にして左右端に並べてスライド切り替え可能にするプラグイン)使用時などに編集エリアにいるかをきめ細やかに判定するには、新規ペインを開いたり閉じたりする度にglobal/static変数を加減したり、キャレット位置でカーソル形状を画像判定したりするとうまくいくかも？