重力に縋るな

千種夜羽です

久しぶりに dotfiles を弄った

たまには雑なブログを書きたい.

要約はこれ.

Neovim

テキストエディタには長いこと Neovim を使っている. 「そういえばこれはいつからなのだろう,dotfiles の first commit は2018年の4月なのでまあそのぐらいなのかな」と思って自分のツイートを検索してみたらこのブログの過去記事が出てきた.なるほど.

sksat.hatenablog.com

この記事を書いた後に Vim と Neovim の間で揺れ動く,みたいなことをした覚えは無いので,2017年11月からずっと Neovim を使っているのだろう.

というか apt-get とか書いてあるし,この頃はまだ Ubuntu で生活していたんだなー.懐かしい. Neovim も Ubuntu の公式リポジトリ入りしてなかったっぽくて PPA 入れてるし. 5年前とかってそんなかんじだったんだな.

やったことは大体この Pull Request. github.com

lazy.nvim

Neovim の plugin manager には長いこと(というか,さっきの記事を見る限り最初から)dein.vim を使っていた. github.com

これは設定ファイルが TOML で書けたり,プラグイン遅延読み込みができたり,依存関係を設定できたりして便利だった. 別に dein にそんなに不満があったわけではないのだけど,せっかく色々見直す気持ちになっているのだから他の実装を調べてみるか,と思って色々見てみたところ,lazy.nvim が気に入ったので乗り換えることにした.

github.com

一応の背景としては2年前ぐらいから Neovim の設定を Lua 化しようとしていたというのがある.

github.com

僕はほとんど Vim script が書けないし書けるようになるつもりもあまりない*1ので,できるだけ Lua に設定を寄せたいと思っている. で,その上で dein の設定を見直してみると,各 plugin の設定をする時に結局 hook_add とかで Vim script 書かないといけないじゃんか~,という問題があった.

dotfiles/dein.toml at 473f9a8082a5a8a5971711e079e89f40aa098eda · sksat/dotfiles · GitHub

hook_add = '''
    autocmd BufEnter,BufWinEnter,TabEnter *.rs :lua require'lsp_extensions'.inlay_hints{}
    nnoremap <Leader>h :lua require'lsp_extensions'.inlay_hints()<CR>
'''

はてなブログ,Gist はコード埋め込みできるのに GitHub リポジトリはできないのか......)

まあここで lua してしまえば Lua で書けるのだけど,そうか?とはなる*2シンタックスハイライトとか効かないし(当然).

lua require() してしまえば .lua ファイルを読み込むこともできるけれど,ここで書きたいような設定の多くは気持ちの面では「このリポジトリから持ってくる」とか「遅延読み込みする」とかと似通っているし,同じような気持ちで設定しているのだから同じ場所で記述した方が認知コストが小さくて済む. 「どういった類のプラグインか」という文脈をもって分割したいことはあれど,同じプラグインという1つの文脈を分割したいことは個人的にはあまりない. 認知コストの面で言うと,init.lua -> dein -> TOML -> Vim script -> Lua という設定の読み込みフローも大概複雑だ. *3

ここで,Lua で完結する plugin manager を使うことで,すべての設定を init.lua に書くことができる.

もちろん,なんでもかんでも init.lua に書いたら init.lua がすごい行数になってそれはそれで体験が悪くなってしまうのだけど,その時は「keymap の設定」とか「プラグインの設定」みたいな文脈毎に Lua のモジュールとして設定を分割してしまえばよい. そうしたらこれはもうただのいつものプログラミングだ. なんなら,いつでも分割できることによって最初はベタッと1つにまとめておいた方が後で適切な分割が行いやすくなるまである.*4

あと,式年遷宮してしまえば手元の環境がなんだかんだで dotfiles リポジトリと同期してなくて diff まみれになっているやつの処理の気持ち的な手間が省ける,みたいな事情もあった. よし,乗り換えよう.

Lua で完結する plugin manager」の具体的な実装としては,packer.nvim や lazy.nvim などがある.

github.com

github.com

どちらを使うかはちょっと悩んだが,Lockfile(lazy-lock.json)があるのが気に入ったので lazy.nvim を使ってみることにした. README にもデカデカとあるけど,わかりやすいダッシュボードがあるのも面白い.

lazy.nvim のダッシュボード

LSP

Neovim 0.5.0 のリリース前から意地で Neovim builtin LSP client を使っていたので,久しぶりに見てみたら builtin LSP client でもエコシステムが整っていたしちゃんと動くので感動した.しました.

nvim-lspconfig を入れるだけでスッと動くわけ.素晴らしい.

github.com

あと,trouble.nvim と rust-tools.nvim の体験がだいぶよかった.これでまだまだ Neovim に引き篭もることができそうだ.

github.com github.com

tree-sitter

あと,今回は前から気になっていた tree-sitter による syntax highlighting も導入してみた.

tree-sitter はインクリメンタルに,かつ高速*5にパースを行うためのパーサジェネレータだ.

github.com

「syntax highlight なんてキーワードの一覧並べて正規表現とかでマッチしたやつに色付ければいいんじゃないか」みたいな気もするし,実際多くの(ほとんどの?) syntax highlighting はそのように実装されている. 実際それで十分なことがほとんどだし,コンパイラを作るわけでもないのにいちいちパーサを書くのは面倒だし,みなさんが思い思いにパーサを実装していく*6と管理できなくなっていくし,技術記事とかならともかく手元のエディタでは1文字書き換える度に色がなくなってバカ真面目にパースが走って色が付き直していたら体験が悪すぎる.

しかし,ある程度ちゃんとパースした上で syntax highlight されるとうれしいことは結構ある. まあ細かいことは置いておいて,雑に比較してみたので見てみてほしい.

syntax highlight の tree-sittter と素の VIm の比較

左が tree-sitter で右が素の Vim(つまり,keyword 登録しての雑マッチ)だ. 比較対象がC言語という syntax がおしまいすぎてマトモにパースすることすら面倒な言語というのはあるのだけど,差は歴然だ. もちろん左は2行目のコメントを戻したら色が変化する.偉すぎ. あと,引数/変数と型を区別できるところとかはコードをパッと見て内容を把握する時の負荷に結構寄与する,気がする.

そして,tree-sitter の一番偉いところは単なるパーサ実装,単なるパーサジェネレータ,というだけでなく,(テキストエディタ向けの syntax highlighting を強く意識した上で)1つのエコシステムを作っている点だろう. GitHub organization を見てみると,tree-sitter を使った各言語向けの"grammar"がたくさんあるし,それらがちゃんとメンテナンスされていることがわかる. github.com

Neovim では 0.5 から tree-sitter を使うためのサポートが入った. 実際使うためには nvim-treesitter というプラグインを使うのが手っ取り早く確実だ.

github.com

このプラグインには色々な tree-sitter grammar が登録されているので,このプラグインの設定で統一的に tree-sitter の恩恵を受けることができる.便利.

あと,tree-sitter による恩恵は syntax highlight にとどまらず,例えば nvim-treesitter-context プラグインなどでは「今この関数にいる」みたいなのを上部に表示することができる.便利.

github.com

wezterm

今回の dotfiles 盆栽の主な目的は Neovim の設定が腐っているのをどうにかすることだったのだけど,ついでに terminal emulator も変えた.

terminal emulator としては,ここ2,3年は Alacritty が顕著に人気だと思う. github.com

昔は「terminal emulator なんてバーンと黒い背景で terminal を emulate してくれればええねん,人気も何もあるかい」,みたいな気持ちもあった. しかし騙されたと思って Alacritty を動かしてみると,なんかこう,露骨に速い.terminal emulator に"速い"とかあるんだ,となる.

まあ文字列の表示って他の普通の計算・メモリコピーとかと比較すると一般に遅い処理だというのはよく知られた事実だけれど*7,それって terminal emulator 部分の律速もあったのかー,というか. これは(概ね OS 側寄りの気持ちで)OS・アプリケーションの2択(とそもそも OS が無い環境)で物事を認識しがちなところがあるからだろうな,とは思う.

それはそれとして最近は GPU の気持ちになることも増えてきたので,今考えてみると「まあうまくやったら色々速くできるわなー」という納得はある.納得. 納得ついでにちょっと思ったけど,これからは Electron 物体とかでも WebGPU でバーン(オノマトペ),みたいなかんじのやつが出てくるんだろうか(知らず).

さてこんなに Alacritty の話をしたので Alacritty を使い始めたのかというと,実はそんなことはない(オイ). これはなんだかんだ言いつつ今使っている Tilix に手が依存してしまっているからだ.

元々,「バーンと黒い背景で terminal を emulate してくれればええねん」と思っていた時期は sakura というシンプルな terminal emulator を使っていた.

github.com

これはとにかくシンプルでいい.黒いし.

ちなみに今見てみたら実装は sakura.c 1ファイルだけでめちゃめちゃびっくりした.そうなんだ. あとタブ機能が存在したことも今知った.そうなんだ2.

Alacritty もシンプル志向なので,このまま sakura をずっと使っていたならなんの支障もなく Alacritty に移行できていたと思うのだが,3年前から Tilix に乗り換えていたのでそうもいかなくなってしまっていた.

github.com

まずはこれを見てほしい

Tilix の実装言語

Dだ.

......D?

............D言語

https://raw.githubusercontent.com/dlang-community/d-mans/gh-pages/dman-original.svg

そう.D言語で実装されているのだ. D言語で実装されたプロダクトってあるんだ(超失礼).

というわけで「D言語で実装されている」という驚きによってこの Tilix を使っていた*8のだけど,使い始めた理由はともかくとして,これが非常に手に馴染んでいた.

その理由はまさにこれが"A tiling terminal emulator"だからだ*9. window manager にずっと i3wm を使っているような人間なので,あらゆるウィンドウ的なモノはタイル状に並んでいてほしい. そういう欲求がある.Tilix はそれを叶えてくれる. 1つのウィンドウでタイル状に terminal のセッションを切っていける.僕のプログラミングスタイルは完全にこれに依存してしまった.

「どうせタイル型 WM 使ってるんだから terminal emulator のウィンドウを開いて並べればいいだろ」?

いや違う,そうじゃないんだ. terminal は同時にたくさん開きたいけど,その上で他のアプリケーションのウィンドウ(主にブラウザ)と「terminal の集合」を同等に扱いたいんだ.

「それって tmux とかの terminal multiplexer でやればいいんじゃないの」?

そう思います!!!!!!でも僕の手に馴染まないんだあれらは*10

で,まあ何が問題かって alacritty はタイル状にセッションを切れないのだ. それ以前に,タブ機能みたいなものすらない. なんと sakura にすらあったというのに*11

これは単に無いだけでなく,そもそも実装する気がビタいち無いらしい.

github.com

まあそういうことなら仕方ない.仕方ないのだけど,少なくとも今は Not for me であることも事実だ.

というわけで,「救いはないんですか?」となるわけだが,意外と救いはある.それが wezterm だ.

github.com

GPU-acceleratted だし,horizontal/vertical split もできるし,設定は Lua で書ける. こういうのでいいんだよこういうので.

というところまでちょうど2年前にまったく同じことを考えて,wezterm に移行しようとしていたことがある.

ただし,この移行はこの後無事に失敗していた. 理由はいくつかあるが,一番致命的なのは日本語入力がろくすっぽ動かなかったからだ. 当時は IME 対応がまったく無かったわけではなく,「動くこともあるらしい」みたいな怪情報が出回っているもののまあ結局安定してはいない,みたいな状態だったと思う. 少なくとも自分の手元では動かなかった.ざんねん.

そして,最近はどうなのかなと思って見てみたらここらへんの問題が大体解決していたのでリベンジしてみるか,というわけだ. 特に IME 周りはどうにも去年あたりに色々対応が入ったようだ.

設定は一旦こんなかんじだ. github.com

大した設定はまだしていないけれど,概ねいいかんじに使えているので今度は長続きするんじゃないだろうか.

強いて言えば,Tilix でいうところの "Add terminal automatically" がとても欲しい. split したい領域が縦長だったら縦に,横長だったら横に分割するやつだ.

しかし,まあ一旦 horizontal/vertical split の keybind が分かれていても思ったよりはダメージはなかった. 1秒くらいのディレイは発生するけれど,split は個人的にはそこそこの頻度でやるので意外と慣れる. それはそれとして気持ちがあるときにパッチを書いてもいいかもしれない.

== 記事とゴールデンウィークの終わり

2日ほどこの環境でやっているけど結構快適に生活できている.

ちなみに,今クソデカ find を各 terminal emulator で雑に戦わせてみたら,

  • Alacritty: 30s
  • wezterm: 1m
  • kitty*12: 1m
  • Tilix: 1m15s

みたいなかんじだった.

悔しい!!!!!!

あとなんか連休とかいうやつも終わるらしい!!!!!!!!!!

*1:単にプログラミング言語的にどうこうとか syntax がどうこうみたいな問題ではなくて,そもそも僕が気合を入れて Neovim の設定を弄るようなことが1年ぐらいの間隔でしか無いのでメリットがあまりないこと,設定がろくすっぽ構造化されていないので渋い気持ちになる,などが理由

*2:ヒアドキュメントで複数行も書けるけど,少なくともなんか負けた気持ちにはなる

*3:ちゃんと計測とかしてないけど,ここの読み込みコストも気にならなくはない.まあ LuaJIT が速すぎることによって大して問題になっていない,みたいなのもありそうだが......

*4:monorepo と同じ利点

*5:Fast enough to parse on every keystroke in a text editor とのこと

*6:思い思いにパーサを実装するのは,楽しい

*7:これによって「printf を挟むと動く」みたいなやつが発生しがち

*8:本当に失礼

*9:なお,それにめちゃめちゃ依存しておいて tiling を意識している上に名前の由来もそれなことに今気付いた

*10:ちゃんと理由を考えてみる必要があるなとは思っている

*11:いやまあ,使ってなかったというか存在に気付いていなかったけれども

*12:そういえばちゃんと試してなかったので検討する価値はある