ステータスラインのアイコンの右側で、桁が 1 つずれます。カーソルの位置と画面の表示が食い違い、それ以降ずっと直りません。
原因は 2 つの幅が別々に決まることです。Neovim は Unicode の East Asian Width プロパティから幅を計算します。端末が実際に何桁ぶん塗るかは、フォントが決めます。 Nerd Font は、この 2 つが一致しない場所にグリフを置いています。
どれだけずれているか
nvim/src/lua/my/fonts.lua は 21 レンジ、合わせて 9,636 文字を幅 1 と宣言しています。Box Drawing、Powerline、Devicons、Material Design Icons、Codicons。
そのうち、宣言しないと幅 1 にならない文字が何個あるかを数えました。
ambiwidth=single 9,636 文字中 45
ambiwidth=double 9,636 文字中 9,456
この設定は ambiwidth を触っていないので既定の single です。つまり今のところこの一覧が働いているのは 45 文字ぶん。全部 U+2600–U+26FF にいて、Unicode が
East Asian Wide と分類しているものです。⛔ や ⚽ の類が、Nerd Font では 1 桁で描かれます。
default : U+26BD=2 U+26CE=2 U+26D4=2 U+26EA=2 U+26F5=2
after override : U+26BD=1 U+26CE=1 U+26D4=1 U+26EA=1 U+26F5=1
残り 9,591 文字は保険です。ambiwidth=double にした瞬間に効きはじめます。日本語を書いていると罫線や約物のために double にしたくなるので、そのときアイコンが全部 2 桁になって崩れる、という順序で踏みます。
教える先は Neovim だけ
vim.fn.setcellwidths() が上書きするのは Neovim の計算だけです。端末には何も伝わりません。だから宣言する値は「こうしたい幅」ではなく「端末が既にそう描いている幅」です。
Nerd Font には mono と propo の 2 系統があり、mono はアイコンを 1 セルに収めるほうです。この設定の blink.cmp も nerd_font_variant = "mono" を指定しています。propo を使っているなら、同じ一覧が今度は逆にずれます。
レンジ単位で書く
コードポイント単位ではなく、Nerd Font の出自ごとにレンジで書いてあります。
-- Devicons
table.insert(cellwidths, { 0xe700, 0xe7c5, 1 })
-- Material Design Icons
table.insert(cellwidths, { 0xf0001, 0xf1af0, 1 })
Material Design Icons だけで 110,000 文字あり、実際に使うのはそのうち数十です。使うぶんだけ書くと、アイコンを 1 つ足すたびに幅が壊れます。レンジで宣言しておけば、フォントが埋めている範囲は先に全部押さえられます。