📑

Vimに新しく追加されたtabpanelに非表示のバッファのリストを表示する

に公開

はじめに

先日ついに追加されたtabpanelでなにかできないかなぁと考え
どのタブのどのウィンドウにも表示されていないバッファも表示したら便利かも??
と思って実装してみました!
以下画像のHIDDENのリストがそれです

実装

ちょっと長いです
このあと解説しますから逃げないでください

vim9script

def BufLabel(b: dict<any>): string
  const current = b.bufnr ==# bufnr('%') ? '>' : ' '
  const mod = !b.changed ? '' : '+'
  const nr = !b.hidden ? '' : $'{b.bufnr}:'
  const name = b.name->fnamemodify(':t') ?? '[No Name]'
  const width = &tabpanelopt
    ->matchstr('\(columns:\)\@<=\d\+') ?? '20'
  return $' {current}{mod}{nr}{name}'
    ->substitute($'\%{width}v.*', '>', '')
enddef

export def TabPanel(): string
  var label = [$'{g:actual_curtabpage}']
  for b in tabpagebuflist(g:actual_curtabpage)
    label->add(b->getbufinfo()[0]->BufLabel())
  endfor

  # Show Hiddens
  if g:actual_curtabpage ==# tabpagenr('$')
    const hiddens = getbufinfo({ buflisted: 1 })
      ->filter((_, v) => v.hidden)
    if !!hiddens
      label->add('%#TabPanel#Hidden')
      for h in hiddens
        label->add($'%#TabPanel#{h->BufLabel()}')
      endfor
    endif
  endif

  return label->join("\n")
enddef

set tabpanel=%!vimrc#tabpanel#TabPanel()

augroup showbuffers_in_tabpanel
  autocmd!
  autocmd BufDelete * autocmd SafeState * ++once redrawtabp
augroup END

解説

今回のメインは非表示のバッファのリストを表示するところなので
他の箇所は簡単に流しますね

BufLabel(b: dict<any>): string

getbufinfo()の結果を元に表示する文字列を返します
ポイントとしては、以下のように
長い文字列の場合に先頭ではなく末尾を省略するようにしているところ
ですかね

const width = &tabpanelopt
  ->matchstr('\(columns:\)\@<=\d\+') ?? '20'
return $' {current}{mod}{nr}{name}'
  ->substitute($'\%{width}v.*', '>', '')

tabpanelの幅は&tabpaneloptcolumnsで指定されています
指定がない場合は20です(h: tabpanelopt)
全角文字などを考慮し指定のVirtual column幅以降を>へ置換しています (h: regex)

また、非表示のバッファは:bでアクセスし易いようにbufnrを表示するようにしてみました

TabPanel(): string

タブパネル全体の内容を返します
前4行は普通にタブの情報を表示しています
5行目以降でバッファのリストを表示しています
タブのリストの下に表示したいので、最後のタブの情報に追加する形です
最後のタブが選択中の場合に追加したバッファのリストもハイライトされてしまうのを%#TabPanel#によって防いでいます

tabpanelへの設定

私はこの関数を~/.vim/autoload/vimrc/tabpanel.vimに定義しているので
以下のように設定しています

set tabpanel=%!vimrc#tabpanel#TabPanel()

個人々々の環境にあわせて設定してください
(g:MyTabPanel()とかにしちゃっても良い気もする…)

BufDeleteへの対応

非表示のバッファを:bdなどでこっそり閉じた場合に即座にtabpanelを更新させるため &showtabpanelを再セットしています redrawtabpで再描画しています
SafeStateを挟んでいるのはBufDeleteのタイミングではまだバッファが削除されていない(そして<abuf>を見るのが面倒くさい)からです
(redrawtabpの情報はthincaさんから🙇)

augroup show_hiddens_in_tabpanel
  autocmd!
  autocmd BufDelete * autocmd SafeState * ++once redrawtabp
augroup END

(余談)SafeStateredrawtabpするのにモヤる場合はこう書いたら納得するかな…

augroup show_hiddens_in_tabpanel
  autocmd!
  autocmd User BufDeletePost redrawtabp
  autocmd BufDelete * autocmd SafeState * ++once doautocmd User BufDeletePost
augroup END

おわりに

いかがだったでしょうか
今回の実装は簡単なものです
色をかえたり、アイコンを表示したりと色々カスタマイズしてもいいかもしれません


GitHubで編集を提案

Discussion