テキストの折り畳みを彩る vim.treesitter.foldtext() を使ってみる

by
カテゴリ:

2023/10/27のVim駅伝記事です。前回はKyoh86さんによる「忘れられないQuickfix」でした。

NeovimはTree-sitterを組込むことで、コードをパースし、文法に基づいたシンタックスハイライトや文字列選択を可能にしています。

最近、Neovimにvim.treesitter.foldtext関数が追加され、折り畳んだコードのハイライトが可能になりました1。 Neovim 0.10で利用可能になる見込みです(2023/10/17時点の最新版は0.9.4)。

set foldtext=v:lua.vim.treesitter.foldtext()すると有効化でき、以下のように折り畳んだ部分が色付きます。ステキ!関連するところで、vim.treesitter.foldexpr()を使うと折り畳み範囲の決定もTreesitter任せにできます。これらの機能は独立しているので、折り畳み範囲は手動で決めつつ (set foldmethod=manual)、色付けはTreesitterに任せるのもアリです。

対するデフォルト(set foldtext=foldtext())は以下の通り、色付いていません。個人的には折り畳んだ頭の部分に付加情報の+-- 3 lines:がつくのも苦手でした。

ちなみに、可読性の高いfoldtextを実現するプラグインにはreadablefold.vimpretty-fold.nvimnvim-ufoなどがあります。 nvim-ufoはTreesitterによる色付けにも対応しているようです。

もひとつちなんでおくとこの機能の実現の立役者は、foldtextのhighlightをvirtual textの書式で定義できるようになった下記のPRです。

feat(folds): support virtual text format for ‘foldtext’ https://github.com/neovim/neovim/pull/25209

さて、これだけだとただの使ってみた系の記事なのですが、少し工夫してあげると、JSONの折り畳みなんかもキレイに表示できるようになります。通常、オブジェクトを折り畳んでしまうと{・・・・・・・・・・・・・・・みたいな表示になってしまって中身に皆目検討つかなくなります。 vim.treesitter.foldtext関数をうまく使ってあげると、2行目の内容も色付けた上で1行目にマージしたfoldtextを生成できます。

function Foldtext()
  local res = vim.treesitter.foldtext()

  if type(res) == "string" then
    return res
  end

  if (#res == 1 and res[1][1] == "{") or (#res == 2 and res[1][1]:match("^%s+$") and res[2][1] == "{") then
    local foldstart = vim.v.foldstart
    vim.v.foldstart = foldstart + 1
    local text = vim.treesitter.foldtext()
    if type(text) == "table" then
      for i, v in pairs(text) do
        if i == 1 and v[1]:match("^%s+$") then
          v[1] = " "
        end
        table.insert(res, v)
      end
    end
    vim.v.foldstart = foldstart
  end
  return res
end

vim.opt.foldtext = [[v:lua.Foldtext()]]

Vim駅伝、次回は10/30でmityuさんによる「:terminal から親の Vim でファイルを開く(cmd.exe 編)」です。

ENJOY!


  1. feat(treesitter): add foldtext with treesitter highlighting https://github.com/neovim/neovim/pull/25391↩︎