Folding dart/flutter code with treesitter in neovim

1726148391.png

Using :InspectTree I see that the nodes I want folded are selector, named_argument and list_literal.

In the code below, I bind the function that folds to <C-f> or Ctrl + f.

local fold_named_arg_func = function()
  local allowed_types = {
    ["selector"] = true,
    ["named_argument"] = true,
    ["list_literal"] = true
  }

  local ts_utils = require('nvim-treesitter.ts_utils')
  local winid = vim.fn.bufwinid(1)
  local nuc = ts_utils.get_node_at_cursor(winid)
  local cn = nuc
  while cn ~= nil and allowed_types[cn:type()] == nil
  do
      cn = cn:parent()
  end
  local start_row
  local end_row
  if cn ~=nil then
    start_row, _, end_row, _ = cn:range()
    vim.cmd(start_row + 1 .. "," .. end_row + 1 .. "fold")
  end
end

vim.keymap.set('n', '<C-f>', function()
  local fold_level = vim.fn.foldlevel('.')
  local fold_closed = vim.fn.foldclosed('.')

  if fold_level > 0 then
    if fold_closed == -1 then
      fold_named_arg_func()
    else
      vim.cmd("foldopen")
    end
  else
    fold_named_arg_func()
  end
end)

In conjunction to the above, I also modded how folds appear in neovim because the default implementation didn't preserve code indendation.

vim.opt.fillchars = { eob = "-", fold = " " }
vim.opt.foldtext = 'v:lua.MyFoldText()'
function MyFoldText()
  local start_line = vim.fn.getline(vim.v.foldstart)
  local end_line = vim.fn.getline(vim.v.foldend)
  end_line = end_line:gsub("^%s+", "")
  return start_line .. " ... " .. end_line
end

vim.keymap.set("v", "<C-f>", ":fold<CR>", { desc = "fold" })

Alternatively (what I do now), is to fold all nodes. If the node isn't multiline, fold its parent instead.

local fold_node = function ()
  local ts_utils = require('nvim-treesitter.ts_utils')
  local cn = ts_utils.get_node_at_cursor(0)
  while cn ~= nil
  do
    local node_start_row, _, node_end_row, _ = cn:range()
    if node_end_row == node_start_row then
      cn = cn:parent()
    else
      vim.cmd(node_start_row + 1 .. "," .. node_end_row + 1 .. "fold")
      break
    end
  end
end

vim.keymap.set('n', '<C-f>', function()
  local fold_level = vim.fn.foldlevel('.')
  local fold_closed = vim.fn.foldclosed('.')

  if fold_level > 0 then
    if fold_closed == -1 then
      -- fold_named_arg_func()
      fold_node()
    else
      vim.cmd("foldopen")
    end
  else
    -- fold_named_arg_func()
    fold_node()
  end
end)

Comments