Neovim で日本語入力の確定時に文字化けする症状の直し方
Neovim を使用していて日本語入力時の文字化けに長いこと苦しんでいたのですが、解決の目処が立ったのでブログに残しておきます。
日本語が普通に打てるって幸せ…!
そもそものバグ
Neovim で IME ( Google 日本語入力を使用しています)からの入力時を確定したタイミングでバーンっと文字化けするという症状がありました。
Neovimで日本語入力文字化けする。入れ直せば入るが結構な高頻度で困ってる。
— ゆーいち (@u1tnk) June 26, 2017
SKKとの相性かなんかかと思ったけど、Googleでも発生。
pluginも設定ファイルも全部無効でも発生(頻度はすげー下がるけど)するし、iTermとかtmuxとか関係ありそうなの切ってもダメ。 pic.twitter.com/1vtFNlFeSH
NeoVimで「使ってみたかったので」の入力を確定(Enterキー)したらこのぶっ壊れである。
— hori (@hori_ryota) June 11, 2017
改行が入ったりコピペっぽい動きをするのだけど全然わからぬ…! pic.twitter.com/ls4dye2FRC
直し方
詳細は後述しますが、 ttmeout
と ttimeoutlen
が適切に設定されていないと生じる症状でした。
init.nvim に以下を追記すれば直ります。
set ttimeout
set ttimeoutlen=50
原因
Neovim に issue があります。
2015 年の古い closed issue ですが、実はまだ直っていなかったという。
下の方に私の(拙い)英語コメントが新しく追加されていますが、概要をブログにも記しておきます。
Neovim の input の挙動ですが、 Neovim ではキー入力( input )を受け取った際、一定の処理量ごとにバッファリングして処理します。
neovim/input.c at 685ca180f7c96f77a79c78d3b26bd003f8cd834c · neovim/neovim
一回のループでは
size_t consumed = termkey_push_bytes(input->tk, ptr, MIN(count, len));
で input 用のバッファから input->tk
のサイズ分だけ抽出して処理を行います。
抽出した input->tk
のバッファを
tk_getkeys(input, false);
で処理するのですが、先ほどの termkey_push_bytes
で取得したバッファはバイト数で切り取っただけなのでマルチバイト文字を考慮できていません。
tk_getkeys
はタイムアウトした場合、もしくはタイムアウトが設定されていない場合に、第二引数を true
で呼び直し、すぐに解釈できない(確定できない)バイト列でも確定する、という挙動をします。
neovim/input.c at 685ca180f7c96f77a79c78d3b26bd003f8cd834c · neovim/neovim
よってマルチバイト文字を入力した際、分割されてしまったマルチバイトが上手く解釈できず悪さをし、文字化けする場合があるというのが主旨です。
tk_getkeys
のタイムアウトの設定が先述のオプション ttimeout
と ttimeoutlen
なので、これらを適切に設定すると直りますが、デフォルト値が無効とする設定だったため文字化けに遭遇した日本ユーザーが続出したという経緯でした。
なお、 2017-07-06 にデフォルト値を設定するプルリクがマージされたため、現在の master ブランチでは直っています。
options: Default to ‘ttimeout’ and ‘ttimeoutlen=50’ by jamessan · Pull Request #6969 · neovim/neovim
日本語以外でもマルチバイトな入力を行うと症状が出るため、カーソルキーの連打などで変な文字が入ってしまう症状もおなじ原因と考えています。今のところ私の環境では併せて改善しました。
ちなみに、 ttimeout
と timeout
の違いが混乱しがちなのですが、以下の区別です。
ttimeout
: キーコードのタイムアウト- 入力文字の確定に対するタイムアウト
- 流れ込んでくるバイト列を文字として判定するときに使われるタイムアウトなので、本来はとくに体感するようなタイムアウトではない
- Esc キーのような、後ろにバイト列が付くと別のキーになるキーの入力時に、確定するまでの待ちとして体感する
timeout
: マッピングのタイムアウト- たとえばキーマッピングで
gg
とg
を設定したとき、g
だけ入力してもマッピングは確定しない。マッピングを確定するまでのタイムアウト
- たとえばキーマッピングで
以上
Neovim のコードは C なので、初めて gdb
を使ってがっつりデバッグしてみました。
デバッガは IDE でしかやったことなかったのですが、意外となんとかなるもんですね。
文字化けを言い訳にブログを滞らせていたので、更新再開します。なおってよかった。