TL;DR
denite.nvimのdenite-filterをこんな感じにできます。
はじめに
Vim/Neovimにおける高機能なファジーファインダーShougo/denite.nvim。
拡張性の高さが大きな特徴で、さまざまな記事で多彩なdenite.nvimの「使い方」が解説されています。
きっとみなさんも、.vimrc
やdein.toml
にゴリゴリと設定を書き込んで使っておられることでしょう。
参考:
【vim】denite.nvim v3のマイベスト設定 - Qiita
そこでこの記事では、denite.nvimの「見た目」、中でも最近新しく追加されたdenite-filterウィンドウをかっこよくした話をしようと思います。
denite.nvimのfilterについて
もともと非常に高い拡張性を誇っていたdenite.nvimですが、2019年にはプロンプトを排し、Vimのネイティブなバッファを使用するdenite v3へとアップデートされました。
現在のdenite.nvimでは一般的なファジーファインダーと異なり、このように絞り込みを「denite-filter」と呼ばれる別ウィンドウで行うようになっています。
参考:
【Vim】新しい Denite に爆速で対応する - Qiita
filterからできる操作が限定されてしまったので操作感は大きく変わりましたが、動作はより警戒に、キーマッピングはvimと共通したより自然な形で設定できるようになり、フィルタ中で補完が効くようになるなど、慣れればアドバンテージの方が多いかと思います。
しかし、気になる点がひとつ。
filterがvimのウィンドウとして開くようになったゆえに、statusline
までついてきてしまうのです。
filterは候補の絞り込みにのみ使われるバッファですので、このstatusline
を活用できる要素は (私には) 思いつきません。
もちろんstatusline
があるからといってdenite.nvimの機能に悪影響があるわけではありません。
ありませんが、いかんせんフィルタウィンドウはdenite.nvimを使うならパカパカと何度も開くものなので、毎回付いてくる余計な1行が次第に気にかかるようになってきました。
denite.nvim v2のときは、Pythonで実装されたlambdalisue/neovim-promptによるいい感じのプロンプトで絞り込みを行っていたので、それと比較する部分もあります。
外観というのは実際重要なファクターです。
もし最新のdenite.nvimでもオシャレなプロンプトが使えるようになれば、テンションも上がり、進捗も鰻登りに違いありません。
floating windowの可能性
ところで、 Neovimにはfloating Windowと呼ばれる、バッファの上に仮想のウィンドウを重ねて開くこと機能があります。
追従するように本家vimにも似たような性質 (同一ではない) を持つPopup windowが取り入れられたことからも、Neovimにおけるキラーフィーチャーであることが分かります。
denite.nvimでもこの新機能を活用することが可能で、deniteのバッファをfloating windowで開くことができます。
おまけに、この設定を使うと、filterのstatusline
は表示されなくなり外観がサッパリしたいい感じになります。
しかし、バッファのステータルラインも表示されなくなるので、現在開いている候補の数やパスなどが見られなくなってしまいます。(実は表示できます。詳しくは後述。)
また、deniteウィンドウ内でパスを移動するときにウィンドウがフリックするのが個人的に気になるところで、deniteバッファは安定して動作するようにしたいという思いがあり、見た目のカッコよさを惜しみつつも通常のウィンドウを使っていました。
気付き
そんな感じでdenite.nvimを使っていたところ、突然、
filterだけfloating Windowで開けばいい感じになりそうでは?
とふっと思いつきました。突然。
それでソースコードを見てみたところ、ほんの少しコードを追加するだけで簡単に実現できることがわかりました。
まあもとからあったものを組み合せただけですので……。
そうして思いつきでIssueを立て、PRを送った結果がこれ。
(figのstatusline
はlightline.vimでカスタマイズしたものです。デフォルトではただのインアクティブなstatusline
が開くので注意)
私がこのPRで行ったのは、もともと存在したfilterの開く方向を指定する-filter-split-direction
オプションに、=floating
の設定を追加したこと。
この設定を指定すると、denite-filter だけがfloating windowとしてstatusline
の一行下に開きます。
floating window機能を使っているので、Neovim限定、かつNeovimのバージョンが0.4.0+である必要があります。
ほとんど頑張っていない割には、かなりいい感じの見た目になったと自分でも思います。
もちろんvimのバッファであることには変わりないので、機能はまったく同様です。
しかし、少し問題点も。
Neovimのfloating Windowはコマンドラインを上書きできないようで、deniteのウィンドウが最下段にあるときはfilterウィンドウがstatusline
を上書きしてしまいます。
filterを抜けた後は普通にstatusline
を表示してくれますが、これでは減っていく候補数を横目に見ながら絞り込んでいく……みたいなムーブができません。
そこで、denite.nvimの-statusline
オプションが設定してあり、かつNeovimがset=title
であれば、denite.nvimがfloating Windowを使用中はtitlestring
にdeniteのステータスが表示されるようにしています。
便利ですね。
画像上部に注目。iTerm2のタイトルバーにdeniteのステータスが表示されています。
これは作者のShougo氏にIssueで言及されて初めて知りました。
便利なんですがあまり知られていない気がします (私の中で) 。
これでdenite-fileterがかっこよくなりましたね!
で終わらせてもいいのですが、denite.nvimはユーザー側でもとことんこだわって設定できるのが特徴。
せっかくなので、もうちょっと凝った設定を探してみることにしました。
winblendとlightline.vimでfilterをもっとかっこよくする
私は基本的にデフォルトの-direction=botright
でdeniteを使っているので、filterが開いている間はステータスラインが隠れてしまっています。
ここでfilterウィンドウをもう一度眺めてみると、右側に使われていない大きなスペースがありますね。
filterを開いている間もこのスペースにdeniteのステータスを表示させることができれば、もっといい感じになりそうな気がしませんか?
そこで、-direction=botright
でdeniteを開いた際、filterウィンドウがステータスラインを隠してしまうのを逆手に取ることにしました。
ところで、Neovimにはfloating windowの透過率を設定するウィンドウローカルなオプションwinblend
があります。
透過率といっても擬似的なものではあるのですが、floating windowの下にあるバッファをうっすら表示できるようにする機能です。
半透明ないい感じの見た目のfloating windowを作るためのオプションですね。
0から100の範囲で設定できるのですが、docには0から30の範囲で使うことが推奨されています。
そうです、このwinblend
を悪用活用します。
winblend
はウィンドウの背景色を透過させますが、あくまで擬似的な透過なので、文字はそのまま表示されるのです。つまり、
filterがstatusline
に重なってしまうのならば、逆にfilterを透明にしてやれば、statusline
の情報をfilterと共に表示させることができるというわけです。
denite-filterにsetlocal winblend=100
して完全に透過させたgifがこちら。
やや見辛いですが、denite-filterを開きつつも、後ろのステータスラインが見えるようになりました。
しかし、インプットした文字がファイル名にかぶってしまっていますね。
denite-filterと重なっているステータスラインは、deniteバッファを開いているウィンドウの非アクティブ時の表示です。
なのでそのウィンドウのステータスラインの表示を、左側のファイル名を消しつつ右側にdeniteの情報を表示するように設定すればよさそうです。
ここでは、そのようにステータスラインの表示をカスタマイズするためにitchyny/lightline.vimを使います。
というか私はlightlineしか使ったことがないので、vim-airlineなどの他のステータスライン改造プラグインでも実現できるのかは不明です。
デフォルトの状態から、denite.nvimのステータスを表示するまでの簡単な設定を並べていきます。
なお、lightline.vimの設定のしかたは作者のitchynyさんの解説を見るのが一番だと思います。
let g:lightline={}
let g:lightline.inactive = {
\ 'left': [ [ 'filename' ] ],
\ 'right': [ [ 'lineinfo' ],
\ [ 'percent' ] , ['denite'] ]}
let g:lightline.component_function = {}
let g:lightline.component_function['filename'] = 'LLfilename'
let g:lightline.component_function['denite'] = 'LLDenite'
lightlineは、ひとつの大きな辞書型変数g:lightline
に書き込んでいくことで設定を行います。
ここでは非アクティブ時のステータスラインを設定したいので、g:lightilne.inactive
に書き足していきましょう。
ファイル名を表示しているコンポーネントが'filename'
ですね。
この部分をfiletype
がdenite
の時にだけ表示させないようにしたいわけです。
通常filename
はg:lightline.component
で記述されています。
この下に指定されるオプションはset=statusline
でのみ機能するフラグと同様のフラグが使えますが、場合分けをする場合は評価値を返すためこのフラグが使えません (と私は理解しています) 。
lightlineでステータスラインの表示内容を設定する際は、基本的に関数を作ってそこからcomponent_function
を介して返させるのがおすすめです。
function! LLfilename() abort
if &filetype isnot# 'denite'
" g:lightline.component['filename'] = '%t' と同義
return expand('%:t')
else
return ''
endif
endfunction
右側に表示する内容はg:lightline.inactive['right']
に追加します。
deniteのステータス情報はdenite.nvimの関数denite#get_status()
を使い引っ張ってきましょう。
pathの表示が長くて表示を圧迫するので、ちょっと手を加えて短めにしています。
" deniteを開いたときだけ表示する
function! LLDenite() abort
if &filetype isnot# 'denite'
return ''
else
return s:denite_statusline()
endif
endfunction
function! s:denite_statusline() abort
let p =denite#get_status('path')
" 飾りをはずす
let p = substitute(p, '\(\[\|\]\)', '', 'g')
" パスをホームディレクトリからの相対パスに変換する
let p = fnamemodify(p,':~')
" パスが長い時は強引に切り詰める
if strlen(p) > 40
let p = '.../' . fnamemodify(p,':h:h:t'). '/'
\ . fnamemodify(p,':h:t'). '/'. fnamemodify(p, ':t')
endif
let path = '[' . p . ']'
let buf = 'buffer:' . denite#get_status('buffer_name')
let source = denite#get_status('sources')
return buf . ' ' . source . ' ' . path
endfunction
せっかくなのでアクティブなdeniteウィンドウにもステータス情報を表示させておきましょう。
filetype
、fileencoding
、fileformat
を表示している部分をひとつの関数にまとめて、&ft='denite'
のときはその関数をdeniteステータスに置換します。
let g:lightline.active = {
\ 'left': [ [ 'mode', 'paste' ],
\ [ 'readonly', 'filename', 'modified' ] ],
\ 'right': [ [ 'lineinfo' ],
\ [ 'percent' ],
\ ['filestatus', 'denite']] }
let g:lightline.component_function['filestatus'] = 'LLfilestatus'
"
function! LLfilestatus() abort
if &filetype isnot# 'denite'
let fenc = &fenc!=#""?&fenc:&enc
let ft = &ft!=#""?&ft:"no ft"
return &ff . ' | ' . ft . ' | ' . fenc
else
return ''
endif
endfunction
設定前はこんな感じ。
設定後はこんな感じ。
いい感じになりましたね。
おわりに
今回紹介したdenite.nvimとlightline.vimは、どちらも自分の気が済むまでとことん設定を書き込んでいける、とても使い甲斐のあるプラグインだと思います。
この記事に触発されて.vimrc
に5万行書き足しちゃうような人が現れてくれれば幸いです。
記事を書いててPRに結構粗があることに気付いたので、また直してもらうかもしれません。
あとカラースキームがコロコロ変わって見辛い。
追記
TL;DRを追加(19/12/16)