🖤

cmdheight=0 in neovim

2022/06/16に公開

始めに

2022 年 6 月 13 日、neovim の nightly (0.8) にある一つの機能がマージされました。
これこそが私が長年開発していた cmdheight = 0 パッチです。

https://github.com/neovim/neovim/pull/16251

今回は cmdheight = 0 とはなんなのか、それによる利点、パッチ開発の苦労についてまとめていければと思っています。

cmdheight = 0 とはなんなのか

cmdheight = 0 とは、つまり cmdheight の値を 0 に設定できるということです。
cmdheight の値を 0 に設定した場合、コマンドライン領域は表示されなくなり、コマンドライン入力のときだけ自動的に表示されます。

この機能は Vim/neovim 双方に昔から要望があったようです。

https://github.com/neovim/neovim/issues/1004

https://github.com/vim/vim/issues/940

なぜ一部の人にこの機能が必要とされているのか、答えは単純で美しいからです。
それは以下の画像を見れば一目瞭然でしょう。

cmdheight = 0

設定によりステータスラインなど他の表示も全て無効化することで、完全にテキストのみが表示されるようになります。
私は全てをテキストエディタとともに過ごしたい、余計な表示は必要ないと考えているので、この機能が必要でした。

他には cmdheight = 0 でコマンドラインを無効化し、既存のコマンドラインを以下のようなプラグインで置き換えてしまうという手もあるかもしれません。
必要ないときにコマンドラインを消せるということはあなたが自由にコマンドラインの挙動をカスタマイズできることを意味します。応用例は無限なのです。

https://github.com/VonHeikemen/fine-cmdline.nvim

もう一つの実装について

cmdheight = 0 には私が実装したもの以外にも、もう一つの実装がありました。

https://github.com/neovim/neovim/pull/4382

こちらの実装ではコマンドラインをステータスラインの上に表示することによって、従来のコマンドライン領域を削除しています。

statuslineframe

私も手元で試したのですが、パッチがあまりに古く手元では動作しませんでした。更に以下の問題があります。

  • ウインドウ分割時にどうするのか

  • laststatus への対応をどうするのか

  • コマンドラインが複数行になったらどうなるのか

他には「ステータス行とコマンドライン行を一体化する」というアイディアもありました。
私はステータス行を表示しない主義なので、ステータス行に依存したアプローチはとりたくありませんでした。
こちらもウインドウ分割時や複数行コマンドラインに問題が出ると思います。アイディアとしては面白くても既存機能との互換性に問題があるのです。

私がとったアプローチは単純でつまらないかもしれませんが、ちゃんと動作します。動作するというのはとても価値が高いです。

パッチ開発における苦労

cmdheight = 0 を実装するには、以下のプルリクエストのように cmdheight の範囲チェックを変更すればよいだけだと考える人がいるかもしれません。

https://github.com/neovim/neovim/pull/6732

それはとても甘いです。Vim/neovim のコマンドライン領域がどれほど広範囲の機能で使われているか理解をしていません。
ざっと以下のパターンがあります。

  • ruler

  • macro メッセージ

  • input プロンプト

  • 各種メッセージ出力

当然適当に cmdheight の値をセットするだけでは既存の機能が壊れてしまいます。
地道に全てのメッセージ出力に対応する必要があるのです。

今回のパッチに開発期間がかかったのも、あらゆる状況でのテストと単体テストの追加、本体の更新の追従にとても時間がかかったのが理由として挙げられます。
私がパッチを開発している間、neovim 本体には laststatus = 3winbar といった cmdheight の挙動にも関連する機能が次々と追加されています。
UI 部分の機能追加はとても難しいです。

本体にマージするというのはお遊びで「自分の手元でとりあえず動けばよい」という態度ではだめなのです。
あらゆる問題を自分で責任をもつという覚悟が必要です。

パッチがマージされたその後について

パッチがマージされた後、多くの人からお褒めの言葉を貰っており、広く要望された機能なのだと実感しています。
なかには「この機能が実装されるまで苦節 16 年だった」という人までいました。

cmdheight = 0 と tmux status line の連携例についても以下で紹介されています。
tmux の status line で表示することにより、cmdheight = 0 のときの表示を改善できるようです。

https://www.reddit.com/r/neovim/comments/vc75v7/cmdheight0_vimtpipeline_a_match_made_in_heaven/

しかし、本体にマージされテストする人が増えたことによる不具合の報告も貰っており現在はその対応に追われている状態です。
ここからが本当に大変です。マージして終わりなんかではなく、むしろここからが本当の始まりです。

neovim 0.8 が正式リリースされる前には安定化させないといけないと思います。
もちろん自分でも日常的に cmdheight = 0 機能を使うようにしています。

全てのメッセージ出力に「Hit enter プロンプト」が出てくることについて

cmdheight = 0 にはもちろん副作用があります。コマンドライン領域はメッセージ出力にも使われているのです。
それを無効化したらどうなるのか容易に想像つくのではないでしょうか。
そうです、全てのメッセージ出力において「Hit enterプロンプト」が出力されます。

これは煩いということで、私は msgfunc というパッチを実装しました。
msgfunc は以下のように全てのメッセージ出力を関数に渡し、メッセージ出力を時前で処理できるようにする仕組みです。

set msgfunc=Func

function! Func(method, kind, chunks, overwrite) abort
  echomsg a:method
  if a:kind !=# ''
    echomsg a:kind
  endif
  if !empty(a:chunks)
    echomsg a:chunks
  endif
endfunction

一応機能は動作するようになったものの「既存の ext_messages 機能を TUI でも使えるように拡張して実装したい、同じ機能は複数必要がない」ということで却下されました。

https://github.com/neovim/neovim/pull/16480

ちなみに、Vim に msgfunc を実装したいという議論が以下にあります。

https://github.com/vim/vim/issues/10571

これからの動きに注目です。

Vim への移植について

この機能を Vim に移植することについて、考えていないわけではありません。

https://github.com/vim/vim/issues/940#issuecomment-1155088682

しかし、そこには neovim 以上の困難があるだろうと思っています。

なぜならば neovim にはベースとなる機能があったものの、Vim にはなかったからです。
neovim は ext_cmdline という機能を持っています。これは組み込みのコマンドラインを隠し、GUI 独自のコマンドラインを作成することができます。
コマンドラインを隠す機能が元々存在していたので、neovim だとまだ実装が楽でした。

neovim でもここまで苦労しているのに Vim で同じ機能を実装する場合、より多くの困難が待ちうけていることは想像に固くありません。

追記: cmdheight = 0 の未来

Vim に一度マージされた cmdheight = 0 の機能はなぜ revert してしまったのでしょうか。
なぜ neovim では好意的に受け入れられ、Vim では受け入れられなかったのかについて説明しようと思います。

そもそも、cmdheight = 0 はただコマンドラインを隠すための機能ではないということを留意する必要があります。
これは Vim/neovim の UI をユーザーがプラグインにより拡張するための機能なのです。プラグインにより拡張するためには当然既存のコマンドライン領域を無効にできる必要があります。

Vim のユーザーや開発者はよくも悪くも保守的です。cmdheight = 0 が目指している未来は非常に魅力的ではありますが、そのような UI の拡張に興味がなければ
なぜわざわざコストをかけてそのような機能を実装するのだということになるでしょう。
実際に、私が cmdheight = 0 の提案をしても Vim の開発者やユーザーから反対意見が多く寄せられていました。賛成する声は本当に少ないなと思いました。

neovim が cmdheight = 0 に前向きであったのは、neovim は昔から UI の分離に積極的であり neovim GUI で独自の UI を実装できるようになっており思想が一致していたためです。

今の cmdheight = 0 の機能はあくまで過渡的なものであり、問題点は多く残っています。

  • コマンドラインを出す際にコマンドラインが出現するが、その際に副作用がある

  • 既存プラグインと一部相性がある

  • メッセージが出力されると入力待ちになってしまう

  • モードライン、ruler 等メッセージ領域に表示されていた情報が表示されない

これらの問題を根本的に対応するには、コマンドライン機能、メッセージ機能を外に出す他ないと思われます。neovim は UI の拡張をプラグインで実装できるようにする予定です。
Vim では残念ながらそこまでやるモチベーションが開発者にありませんでした。

GitHub sponsors について

私は Github sponsors の支援を通じてプラグインの開発だけでなく、本体へのパッチやプルリクエストの作成といった活動も行っています。
私にとってプラグインの開発と本体の修正は価値が等価であり、プラグインにとって必要なら本体を修正することを厭いません。

今回の変更は誇張抜きに今までのパッチ開発のなかでも最難関であり、半年以上の月日がかかっています。
皆さんからの支援がなければ、ここまで情熱をかけて一つの機能に向きあうこともなかっただろうと思います。
全ての支援者に感謝しています。

https://zenn.dev/shougo/articles/github-sponsors

https://github.com/sponsors/Shougo/

Discussion