cmdheight=0がrevertされたので無理矢理Pluginで頑張ってみた
Vimのcmdheight=0
はrevertされてしまいました
でも一度味わってしまったあの感覚を諦めきれなくてプラグインを作ってみました。
こんなかんじになります
実現方法
ゴリゴリにウィドウの配置を計算して疑似statuslineをechoで表示しています。
Install
vim9script
⋮
dein# add('utubo/vim-cmdheight0')
⋮
g:cmdheight0 = get(g:, 'cmdheight0', {})
g:cmdheight0.format = '%t %m%r %=%`%3l:%-2c%`%{&ff} %{&fenc} %L'
# require nerd fonts
g:cmdheight0.tail = "\ue0be"
g:cmdheight0.tail_style = "reverse"
g:cmdheight0.sep = "\ue0bc"
g:cmdheight0.sub = "\ue0bb"
nnoremap ZZ <ScriptCmd>cmdheight0#ToggleZen()<CR>
# (デバッグ用)以下を指定するとVimEnter時に起動しなくなります。
#g:cmdheight0.at_start = 0
カスタマイズ
表示形式
- 一部ですが
set statusline
と同じように表示内容を設定できます。g:cmdline.format = '%t%=%{&fenc} %{&ff} %l:%c'
- モード表記は
g:cmdline.mode
でカスタマイズできます。g:cmdline.mode.n = 'N' # ノーマル
- 当然区切り線も変更できます。
# NERD FONTSが使えるならこんな感じ g:cmdheight0.tail = "\ue0be" g:cmdheight0.tail_style = "reverse" g:cmdheight0.sep = "\ue0bc" g:cmdheight0.sub = "\ue0bb"
色
ベースとなるステータスラインと各モード表記の色の2色だけですが、hilight
コマンドで設定できます。(ハイライトグループについてはREADME.mdを見てください)
&statusline
の%#foo#
にも対応させたいのですが、斜め区切りのハイライトとechoを1行に収めるための切捨て処理が大変そうで妥協してます…
また、特に設定しなくてもcolorschemeからそれっぽい色を拾ってくるようにしています。
これについてはlightlineのようにプリセットを用意したいところです。(仕組みだけは組み込んであります)
問題点
statuslineをechoで再現するにあたって以下の問題がありました。
- 右端まで表示できない(改行されてENTER待ちになってしまう)
- プラグインとの相性でチラつく
- statuslineのコピーを自前実装するのは手間
ZEN-mode
画面の次の行を表示してstatuslineを無くすZEN-modeも作ってみました。
:call cmdheight0#ToggleZen()
で切り替えられます。
このモードはかなり粗削りで、以下の問題が残っています。
- 再現した次の行にはカーソルを移動できない
- 再現した次の行は各プラグインに認識されない(easy-motion等)
- signやconcealが再現できていない
- 画面一番下の行が改行されていると表示がダブってしまう(バグ🐞)
popupwinでの実装の問題と可能性
vim-cmdheight0
は最初からechoで行こうと考えて実際echoで実装したのですが、
途中チラつきと格闘する羽目になりpopupwinなら問題クリアできるのでは?
と試してみました。
が、以下の難点があり諦めました。
- popupwinを隠すタイミングを判断できず本来のechoを隠ぺいしてしまう
(echoでの実装なら本来のechoは上書き表示されるので考えることが少ない) - 疑似statuslineのハイライトの調整が以外と面倒
逆に上記をクリアすれば以下の利点が見込めます。
- 多分echoでやるより安定して表示される(チラつかない)
- ZEN modeでのシンタックスハイライトとタブストップを再現できる
(&filetype
や&tabstop
を元のウィンドウと合わせてやれば再現できると思います) - 画面右端まで表示できる
- 画面右端を超えそうでも勝手に切り捨てられるので処理が楽
- 分割ウインドウでもechoのように1行に無理矢理結合表示せずに複数のpopupwinを使えばOK
- 頑張れば元のcmdlineはpopupwinで隠して新たにcmdlineを独自実装できるかも?
苦労したところ
分割ウインドウ
疑似statusラインは一番下にあるウインドウの情報を表示するのですが、
縦2つに分割されているとそれは2つになります。
縦横縦横縦縦横横…と分割されていると…
対象のウインドウのリストを取得する処理は数分でサクッとつくりましたがエレガントなコードにできなくて悔しい。
autocmdとモードの表示
モードを変更したりウィンドウを作ったり閉じたり移動したりタブを移動したりすると変わるので色々こねくり回していたら、
もうautocmd
の設定がごっちゃごちゃです!ヤバイ!
チラつき
gvimだととにかくチラついて大変でした。
redraw
やredrawstatus
で画面全体がチラついてしまいます。
前者は\r
に変更、後者は実行するタイミングを出来るだけ少なくしました。
それでもチラついてしまい最終的には
SafeState
イベントでも再描画するようにしたら解決しました。
:echo
チラつかなくできたと思ったら今度は:echo foo
と打っても即上書き表示するようになってしまいました。
これは、以前cmdheight=0
を使って自動でcmdlineを隠したり表示したりするようにした経験を生かして&updatetime
の時間だけ再描画を待つようにしました。
%{expr}
の展開
基本vim9scriptでつくりましたが、%{expr}
の展開はlegacyで行っています。
例えばstatuslineなら、
vim9script
g:foo = 'bar'
def Foo(): stirng
return 'bar'
enddef
set statusline=%{Bar()}
のようにかけますが、vim9script内でexecute()で
bar展開すると、 スクリプトローカルな
Bar()を探してしまいエラーになってしまいます。 ここは
%{g:Bar()}と書かなければなりません。 冗長ですしstatuslineと違うと混乱もするのでlegacyで展開するようにしました。 それでも
g:buzz = 0は
%{g:buzz}`ってかかなきゃだめみたい。なんで?
silent!
できない!
CursorMoved
やModeChanged
は頻繁に呼びだされるので、エラーになるとほぼ操作不能になってしまいます。
なので
autocmd CursorMoved * silent! Hoge()
のように予防したかったのですがecho
も出なくなってしまいました。
結局def Silent(F: func)
を作ってtry-catchしてどうにかしました。
def Silent(F: func)
try
F() # この中で疑似statuslineをecho
catch
# エラー処理
endtry
enddef
autocmd CursorMoved * Silent(Hoge)
ZEN-mode
「次の行を表示するだけ」と思ってたらSignColumnやLineNrの存在を忘れていました。
特にSignColumnは現在表示されているかを直接判断できませんので、
&textoff
と&number
と&numberwidth
から逆算するようにしました。
やっぱりlightline.vimはすごい
今回疑似statuslineを作ってみて分かったことはやっぱりligihtline.vimは素晴らしいプラグインということです。
サクッと設定して簡単におしゃれでカッコイイstatuslineを作れる手軽さ素晴らしいです。
設定方法とかカラースキームのプリセットとかとてもよく考えられているなぁと思いました。
終わりに
冒頭にあるように、このプラグインは自分用に作ったもののお裾分けです。
ですが、cmdheight=0
再現の可能性は示せたと思います。
※どこまで雑なコードを上げていいのか問題はちょっと気になり始めています。
Discussion