nvim-tree / nvim-tree.lua

A file explorer tree for neovim written in lua
Other
7.16k stars 610 forks source link

[Feature] Custom Decorators Support #2948

Open b0o opened 1 week ago

b0o commented 1 week ago

I would love to have an API for defining custom decorators. My use case is to add an icon/highlight for nodes which are in the quickfix list. I've come up with a hacky way to accomplish this, by overriding the bookmarks decorator:

-- Important: Put this in your config directory at lua/nvim-tree/renderer/decorator/bookmarks.lua
--
-- HACK: Although this file is named bookmarks.lua, it is actually a decorator
-- for the quickfix list. Because nvim-tree does not provide a way to add a custom decorator,
-- we are overriding the built-in bookmarks decorator with our own quickfix list decorator.

local HL_POSITION = require('nvim-tree.enum').HL_POSITION
local ICON_PLACEMENT = require('nvim-tree.enum').ICON_PLACEMENT

local Decorator = require 'nvim-tree.renderer.decorator'

---@class (exact) DecoratorQuickfix: Decorator
---@field icon HighlightedString|nil
local DecoratorQuickfix = Decorator:new()

local augroup = vim.api.nvim_create_augroup('nvim-tree-decorator-quickfix', { clear = true })

---@return DecoratorQuickfix
function DecoratorQuickfix:new(opts, explorer)
  local o = Decorator.new(self, {
    explorer = explorer,
    enabled = true,
    hl_pos = HL_POSITION[opts.renderer.highlight_bookmarks] or HL_POSITION.none,
    icon_placement = ICON_PLACEMENT[opts.renderer.icons.bookmarks_placement] or ICON_PLACEMENT.none,
  })
  ---@cast o DecoratorQuickfix
  if not o.enabled then
    return o
  end
  o.icon = {
    str = '',
    hl = { 'QuickFixLine' },
  }
  o:define_sign(o.icon)
  vim.api.nvim_create_autocmd('QuickfixCmdPost', {
    group = augroup,
    callback = function()
      explorer.renderer:draw()
    end,
  })
  return o
end

---@param node Node
local function is_qf_item(node)
  if node.name == '..' or node.type == 'directory' then
    return false
  end
  local bufnr = vim.fn.bufnr(node.absolute_path)
  return bufnr ~= -1 and vim.iter(vim.fn.getqflist()):any(function(qf)
    return qf.bufnr == bufnr
  end)
end

---@param node Node
---@return HighlightedString[]|nil icons
function DecoratorQuickfix:calculate_icons(node)
  if is_qf_item(node) then
    return { self.icon }
  end
end

---@param node Node
---@return string|nil group
function DecoratorQuickfix:calculate_highlight(node)
  if is_qf_item(node) then
    return 'QuickFixLine'
  end
end

It would be nice if there was an official way to do this.

alex-courtis commented 1 week ago

That's a great idea! There's nothing blocking us from doing this:

Add a renderer.decorators option, which would take a table of the user's decorator class.

Instantiate them with highest priority.

Make the Decorator abstract class user friendly, with documentation on what must be implemented etc.

Add help entries that just direct to the Decorator class documentation which is the one and only source of truth.

Add DecoratorQuickfix recipe.

alex-courtis commented 1 week ago
alex-courtis commented 1 week ago

Safer approach:

Create a UserDecorator class, exposed and documented via a new API function, say, api.appearance.decorator.add.

This class will be similar to Decorator however: