🇯🇵

新しいVim用日本語入力プラグインを作った

2021/12/24に公開約6,500字

skkeleton の実演に便利なので書くぞーという気持ちでこの記事を書いています。全て Vim 上で skkeleton を使って書いています。

はじめに

我々は日本人であるからには日本語を扱います、もちろんテキストエディタの上でも扱います。
しかしながら Vim はあまり IM の扱いが得意ではありません。[1]
そこで私は Vim の中で動く日本語入力環境として eskk.vim を長いこと使っていましたが、どうしても自分で作りたくなったので skkeleton というプラグインを作ってみました。この記事では、そのプラグインを紹介していきます。

skkeleton

skkeleton

eskk.vim と同じく SKK と呼ばれる IM の移植になります。SKK に関しては本家のドキュメントもしくはニコニコ大百科の記事に詳しく載っているので興味がある方は読んでみて貰えたらと思います。

skkeleton という名前については、試作版を作った際に「骨組みしかない SKK」という意味で付けていた洒落を気に入ってそのまま使いました。

denops.vim を利用して実装しています。その恩恵として Vim/Neovim 双方で動作する他、一度起動してしまえば高速に変換や補完などの動作が走ります。

使い方

denops.vimskkeleton をお好みのプラグインマネージャで導入してください。

denops.vim は Deno に依存しているので環境に入っていない場合はそれも導入してください。

変換に別途辞書ファイルも必要になるので SKK Openlab より L と付いている辞書をダウンロード、展開し配置しておいてください。(設定は私が使っている関係上 Arch Linuxのパッケージのパスにデフォルトで向けてあります。そのため一部の Linux ディストリビューションでは OS のパッケージマネージャで辞書をインストールするだけで使えます)

call skkeleton#config({ 'globalJisyo': '~/.skk/SKK-JISYO.L' })

また、eskk.vim と違ってグローバルのマッピングを提供していないため、マッピングをする必要があります。

" 例
imap <C-j> <Plug>(skkeleton-enable)
cmap <C-j> <Plug>(skkeleton-enable)

この状態で <C-j> を入力すると起動します。(statusline が出ている場合、右下に <skkeleton> の表示が出ていると思います)

起動した後適当にキー入力をすると、かなで入力されると思います。この状態で何も入力されていない時に Nihongo と打つと ▽にほんご とバッファに入力されます。

そして <Space> を打つと、辞書が正しく導入できている場合 ▼日本語 に表示が変わると思います。そのまま変換に関わる動作(ここでは <Space> を更に打つと辞書登録モード[2]に入り、x を打つと ▽にほんご の状態に戻ります)以外を行うと 日本語 が確定されます。

ここではあらゆる操作が自然に行えるので、上手く操作すると流れるように日本語を入力できるようになります。試しに Nyuuryoku と打ってみると 日本語▽にゅうりょく のような表示になり、上記の操作を繰り返すと 日本語▼入力 という状態になります。

送り仮名付きの変換も似たやり方で行えます。そのまま TanoSii と入力すると Tano が入力された時点で 入力 が確定された上で ▽たの が入り、Si が入力された時点で変換が走り ▼楽し になります。そして i が入力されると 楽しが確定された後 が入り、全体としては 日本語入力楽しい となります。

SKK ではこの2つのパターン(それぞれ送りなし変換、送りあり変換と呼ばれます)を駆使して日本語を入力していくことになります。

使い終わったら l を押すか、<Esc> で挿入モードを抜けると skkeleton モードが解除されます。

補完

ddc.vim を導入した上で設定を書くと自動補完が働くようになります。

skkeleton-completion

入力した単語と前方一致する変換候補を全て表示して補完機能で直接変換できます。eskk.vim の補完機能の移植になりますが、ddc.vim も denops.vim を使っており双方とも高速なため軽快に動作します。[3]
あくまで例になりますが、次のような設定を書けば最低限の動作はすると思います。

call ddc#custom#patch_global('sources', ['skkeleton'])
call ddc#custom#patch_global('sourceOptions', {
    \   '_': {
    \     'matchers': ['matcher_head'],
    \     'sorters': ['sorter_rank']
    \   },
    \   'skkeleton': {
    \     'mark': 'skkeleton',
    \     'matchers': ['skkeleton'],
    \     'sorters': [],
    \     'minAutoCompleteLength': 2,
    \   },
    \ })
call ddc#enable()

また、Vim の補完機能の制約により skkeleton ソースを使用している場合どうしても補完ウィンドウがちらつくため気になる方は pum.vim(作者による紹介記事)の導入をおすすめします。(現状私の怠慢のせいで pum.vim を使用している場合 <Tab> での補完ハンドリングを除く skkeleton が上書きするマッピングと被っている場合機能しなくなります、すみません)

作った経緯

ここからは実装の話になります、興味の無い方は読み飛ばしてください。

動機はいくつかありますが、直接的には長いこと利用させて貰っていた eskk.vim の内部構造が複雑で機能追加が困難だったためです。AZIK、直接かな入力などの入力方式をサポートさせようと中を覗いた所、ローマ字入力に強く依存していた構造になっていて[4]諦めた時から自分で何か作りたいと漠然と思っていました。[5]

そして、ひらがな入力するスクリプトを適当に書いた所、意外と反響が貰えたのでそのまま勢いで SKK のように振る舞う何かを作り上げました。何をどうやったら eskk.vim のようにテキストエディタの中で IM として振る舞わせられるのかを大体理解できてよかったと思います。[6]

こちらは SKK の作り方が想像もできなかったこともあり、捨てるつもりのプロトタイプとして作ったのでそのまま捨てて、新しく作り直しました。この段階で denops.vim をバランスよく使う方法や正しい(と思う)設計に辿り付けたので、時間が必要ではありますが、プロトタイプを作るのは綺麗にプラグインを作る上で効果的だと感じました。

こちらは公開するつもりで作っていたので、最低限の機能が完成した時点で出すことにしました。自分の中では実装した機能については仕上がっていると思っていたのですが、いざ公開してみると、設定が上手く行かない、エラーが分かりにくい、ここが変、などのフィードバックを頂けました。(特に thinca さん、Shougo さん、蒲生辰巳 さんからは初期段階から多数のフィードバックを頂きました、ありがとうございます)
このことから、公開するつもりのあるプラグインであれば早い段階から公開してしまうのがいいと感じました。

denops について

私が skkeleton を作ることを決めた頃、vim-jp slackで Deno という JavaScript/TypeScript ランタイムが流行っていて、その流れで denops.vim(作者による紹介記事) というプラグイン基盤が生まれ、お祭りのように盛り上がっていました。

私もそれぞれ少しずつ触ってみてとても面白かったのと、実験的・実用的含め色々なプラグインが出てくる中で自分も何か作ってみたいと思うようになりました。

結果的に、それは正解だったと思います。Deno(V8) はとても高速で、SKK Openlab の L 辞書くらいであれば現実的な速度[7]で全てパースしてメモリに取り込めます。そのおかげで軽快な動作を実現できました。また、表には出ないですが TypeScript はとても書きやすい言語で、Language Server Protocol を介してチェックもその場で走るので軽微なミスをすぐに取り除けて気持ちよく開発できました。

開発していく中で、全てを denops.vim 上でやろうとせず Vim に近い部分は素直に Vim script で書いた方がよかったり[8]といった事に気付いたり(実際 skkeleton には Vim script で書かれた部分がそれなりにあります)、バグを直してもらったり機能を追加してもらったりもしました。

嬉しい事ばかりではなく辛い事もありました。外部にランタイムがあるため常に非同期で動いており、他のプラグインとの干渉が発生したり[9]、同期処理では上手く行くコードが動作しなかったり(特に非同期処理周りの思想がプラグイン向きではない Vim で顕著でした)と言ったこともありました。

しかし、総合的に見ると denops.vim を使ったことでいい結果に繋がったと思います。特に TypeScript という優れた(かつ慣れた人も多いと思われる[10])言語で Vim のプラグインを書けるのはいい体験だと思います。興味がある方は是非触ってみてください。

まだまだ未完成で粗も多いプラグインではありますが、私は日常的に Vim の上で日本語を入力するので、日常的に自分が作ったプラグインを使っている状態に持っていけたのはよかったと思います。[11]

普通に使うのに必要な機能は大方実装してしまったため開発が少し停滞してしまってはいるものの、常に使ってはいるのでぼちぼち改善していきたいと思います。

ここまで読んで頂きありがとうございました。

:qa!

脚注
  1. 端末上で動く Vim では汎用的に IM を制御する方法が無い、端末自体がマルチバイト文字や IM の扱いが不得手(私が使っている Alacritty ではまともにインライン入力が効きません)、 gVim であれば IM 制御は効くがあまり便利ではない等色々あります ↩︎

  2. 辞書登録モードでも、辞書登録モードのネストを含めた SKK が働くため再帰的な辞書登録を自然な流れで行えます。SKK の辞書登録のしやすさはどの IM にも負けないと思っています。 ↩︎

  3. 余談ですが、ddc.vim も denops.vim で実装されているのをいい事に Vim を一切介さずにデータを送信しています。 ↩︎

  4. SKK では辞書上で送りあり変換の定義は「おくr」のように登録されていますが、eskk.vim ではこの処理に入力しているローマ字をそのまま使用しており、他の方式のテーブルをそのまま定義しても使えません。 ↩︎

  5. なお、入力テーブルの切り替えは不要不急の機能であるが故に未だに実装されていません、おかしいですね。 ↩︎

  6. dps-skkeleton に当時の実装をそのまま置いています。
    また、見れた物ではないと思いますがゴリラ.vimでその時の話をしました。 ↩︎

  7. 手元で約0.4秒程度で読み込めます。 ↩︎

  8. denops.vim 作者のありすえさんも同じことを言われてました ↩︎

  9. Vim では expr マッピングが実行されている途中などの一部の状態でロックがかかりますが、Vim の非同期処理はそんなことを全く考慮してくれないため容赦なく割り込みがかかります。特に補完で利用した ddc.vim との干渉が顕著で、こちらではどうにもできずに直してもらったりといった事がありました。 ↩︎

  10. 少なくとも Vim script よりは書ける人口が多いと思われます。 ↩︎

  11. Vim を好きで居続けるには一番の方法だと思います。 ↩︎

GitHubで編集を提案

Discussion

ログインするとコメントできます