From 24eb27ebbaefa1d99d5863b5cc3391f3e2edaa99 Mon Sep 17 00:00:00 2001 From: Alexander Courtis Date: Fri, 22 Nov 2024 12:18:47 +1100 Subject: [PATCH] feat(#2948): add UserDecorator --- .../renderer/decorator/bookmarks.lua | 15 ++-- lua/nvim-tree/renderer/decorator/copied.lua | 15 ++-- lua/nvim-tree/renderer/decorator/cut.lua | 15 ++-- .../renderer/decorator/diagnostics.lua | 15 ++-- lua/nvim-tree/renderer/decorator/git.lua | 19 +++-- lua/nvim-tree/renderer/decorator/hidden.lua | 15 ++-- lua/nvim-tree/renderer/decorator/init.lua | 56 ++++++------- lua/nvim-tree/renderer/decorator/meta.lua | 13 +++ lua/nvim-tree/renderer/decorator/modified.lua | 15 ++-- lua/nvim-tree/renderer/decorator/opened.lua | 15 ++-- lua/nvim-tree/renderer/decorator/user.lua | 80 +++++++++++++++++-- 11 files changed, 186 insertions(+), 87 deletions(-) create mode 100644 lua/nvim-tree/renderer/decorator/meta.lua diff --git a/lua/nvim-tree/renderer/decorator/bookmarks.lua b/lua/nvim-tree/renderer/decorator/bookmarks.lua index f741dda1b15..6724f5a9bea 100644 --- a/lua/nvim-tree/renderer/decorator/bookmarks.lua +++ b/lua/nvim-tree/renderer/decorator/bookmarks.lua @@ -13,11 +13,14 @@ local DecoratorBookmarks = Decorator:extend() function DecoratorBookmarks:new(explorer) self.explorer = explorer - DecoratorBookmarks.super.new(self, { - enabled = true, - hl_pos = self.explorer.opts.renderer.highlight_bookmarks or "none", - icon_placement = self.explorer.opts.renderer.icons.bookmarks_placement or "none", - }) + ---@type DecoratorArgs + local args = { + enabled = true, + highlight_range = self.explorer.opts.renderer.highlight_bookmarks or "none", + icon_placement = self.explorer.opts.renderer.icons.bookmarks_placement or "none", + } + + DecoratorBookmarks.super.new(self, args) if self.explorer.opts.renderer.icons.show.bookmarks then self.icon = { @@ -41,7 +44,7 @@ end ---@param node Node ---@return string|nil group function DecoratorBookmarks:calculate_highlight(node) - if self.range ~= "none" and self.explorer.marks:get(node) then + if self.highlight_range ~= "none" and self.explorer.marks:get(node) then return "NvimTreeBookmarkHL" end end diff --git a/lua/nvim-tree/renderer/decorator/copied.lua b/lua/nvim-tree/renderer/decorator/copied.lua index fd9af1a2d14..16d28624917 100644 --- a/lua/nvim-tree/renderer/decorator/copied.lua +++ b/lua/nvim-tree/renderer/decorator/copied.lua @@ -12,18 +12,21 @@ local DecoratorCopied = Decorator:extend() function DecoratorCopied:new(explorer) self.explorer = explorer - DecoratorCopied.super.new(self, { - enabled = true, - hl_pos = self.explorer.opts.renderer.highlight_clipboard or "none", - icon_placement = "none", - }) + ---@type DecoratorArgs + local args = { + enabled = true, + highlight_range = self.explorer.opts.renderer.highlight_clipboard or "none", + icon_placement = "none", + } + + DecoratorCopied.super.new(self, args) end ---Copied highlight: renderer.highlight_clipboard and node is copied ---@param node Node ---@return string|nil group function DecoratorCopied:calculate_highlight(node) - if self.range ~= "none" and self.explorer.clipboard:is_copied(node) then + if self.highlight_range ~= "none" and self.explorer.clipboard:is_copied(node) then return "NvimTreeCopiedHL" end end diff --git a/lua/nvim-tree/renderer/decorator/cut.lua b/lua/nvim-tree/renderer/decorator/cut.lua index 220fa7f9d4d..7fdeaae7599 100644 --- a/lua/nvim-tree/renderer/decorator/cut.lua +++ b/lua/nvim-tree/renderer/decorator/cut.lua @@ -12,18 +12,21 @@ local DecoratorCut = Decorator:extend() function DecoratorCut:new(explorer) self.explorer = explorer - DecoratorCut.super.new(self, { - enabled = true, - hl_pos = self.explorer.opts.renderer.highlight_clipboard or "none", - icon_placement = "none", - }) + ---@type DecoratorArgs + local args = { + enabled = true, + highlight_range = self.explorer.opts.renderer.highlight_clipboard or "none", + icon_placement = "none", + } + + DecoratorCut.super.new(self, args) end ---Cut highlight: renderer.highlight_clipboard and node is cut ---@param node Node ---@return string|nil group function DecoratorCut:calculate_highlight(node) - if self.range ~= "none" and self.explorer.clipboard:is_cut(node) then + if self.highlight_range ~= "none" and self.explorer.clipboard:is_cut(node) then return "NvimTreeCutHL" end end diff --git a/lua/nvim-tree/renderer/decorator/diagnostics.lua b/lua/nvim-tree/renderer/decorator/diagnostics.lua index fb4b05e2912..055ae1de1d4 100644 --- a/lua/nvim-tree/renderer/decorator/diagnostics.lua +++ b/lua/nvim-tree/renderer/decorator/diagnostics.lua @@ -43,11 +43,14 @@ local DecoratorDiagnostics = Decorator:extend() function DecoratorDiagnostics:new(explorer) self.explorer = explorer - DecoratorDiagnostics.super.new(self, { - enabled = true, - hl_pos = self.explorer.opts.renderer.highlight_diagnostics or "none", - icon_placement = self.explorer.opts.renderer.icons.diagnostics_placement or "none", - }) + ---@type DecoratorArgs + local args = { + enabled = true, + highlight_range = self.explorer.opts.renderer.highlight_diagnostics or "none", + icon_placement = self.explorer.opts.renderer.icons.diagnostics_placement or "none", + } + + DecoratorDiagnostics.super.new(self, args) if not self.enabled then return @@ -83,7 +86,7 @@ end ---@param node Node ---@return string|nil group function DecoratorDiagnostics:calculate_highlight(node) - if not node or not self.enabled or self.range == "none" then + if not node or not self.enabled or self.highlight_range == "none" then return nil end diff --git a/lua/nvim-tree/renderer/decorator/git.lua b/lua/nvim-tree/renderer/decorator/git.lua index a52de307c46..d56ad8a9fa2 100644 --- a/lua/nvim-tree/renderer/decorator/git.lua +++ b/lua/nvim-tree/renderer/decorator/git.lua @@ -28,17 +28,20 @@ local DecoratorGit = Decorator:extend() function DecoratorGit:new(explorer) self.explorer = explorer - DecoratorGit.super.new(self, { - enabled = self.explorer.opts.git.enable, - hl_pos = self.explorer.opts.renderer.highlight_git or "none", - icon_placement = self.explorer.opts.renderer.icons.git_placement or "none", - }) + ---@type DecoratorArgs + local args = { + enabled = self.explorer.opts.git.enable, + highlight_range = self.explorer.opts.renderer.highlight_git or "none", + icon_placement = self.explorer.opts.renderer.icons.git_placement or "none", + } + + DecoratorGit.super.new(self, args) if not self.enabled then return end - if self.range ~= "none" then + if self.highlight_range ~= "none" then self:build_file_folder_hl_by_xy() end @@ -161,7 +164,7 @@ function DecoratorGit:calculate_icons(node) for _, s in pairs(git_xy) do local icons = self.icons_by_xy[s] if not icons then - if self.range == "none" then + if self.highlight_range == "none" then notify.warn(string.format("Unrecognized git state '%s'", git_xy)) end return nil @@ -207,7 +210,7 @@ end ---@param node Node ---@return string|nil group function DecoratorGit:calculate_highlight(node) - if not node or not self.enabled or self.range == "none" then + if not node or not self.enabled or self.highlight_range == "none" then return nil end diff --git a/lua/nvim-tree/renderer/decorator/hidden.lua b/lua/nvim-tree/renderer/decorator/hidden.lua index f8f06e6cbb9..3bfff8550f5 100644 --- a/lua/nvim-tree/renderer/decorator/hidden.lua +++ b/lua/nvim-tree/renderer/decorator/hidden.lua @@ -14,11 +14,14 @@ local DecoratorHidden = Decorator:extend() function DecoratorHidden:new(explorer) self.explorer = explorer - DecoratorHidden.super.new(self, { - enabled = true, - hl_pos = self.explorer.opts.renderer.highlight_hidden or "none", - icon_placement = self.explorer.opts.renderer.icons.hidden_placement or "none", - }) + ---@type DecoratorArgs + local args = { + enabled = true, + highlight_range = self.explorer.opts.renderer.highlight_hidden or "none", + icon_placement = self.explorer.opts.renderer.icons.hidden_placement or "none", + } + + DecoratorHidden.super.new(self, args) if self.explorer.opts.renderer.icons.show.hidden then self.icon = { @@ -42,7 +45,7 @@ end ---@param node Node ---@return string|nil group function DecoratorHidden:calculate_highlight(node) - if not self.enabled or self.range == "none" or not node:is_dotfile() then + if not self.enabled or self.highlight_range == "none" or not node:is_dotfile() then return nil end diff --git a/lua/nvim-tree/renderer/decorator/init.lua b/lua/nvim-tree/renderer/decorator/init.lua index 63085986f11..d6a7656f842 100644 --- a/lua/nvim-tree/renderer/decorator/init.lua +++ b/lua/nvim-tree/renderer/decorator/init.lua @@ -1,42 +1,38 @@ local Class = require("nvim-tree.classic") ----@alias DecoratorRange "none" | "icon" | "name" | "all" ----@alias DecoratorIconPlacement "none" | "before" | "after" | "signcolumn" | "right_align" - ---Abstract Decorator ---@class (exact) Decorator: Class ---@field protected enabled boolean ----@field protected range DecoratorRange +---@field protected highlight_range DecoratorHighlightRange ---@field protected icon_placement DecoratorIconPlacement local Decorator = Class:extend() ----@class (exact) DecoratorArgs ----@field enabled boolean ----@field hl_pos DecoratorRange ----@field icon_placement DecoratorIconPlacement - ---@protected ---@param args DecoratorArgs function Decorator:new(args) - self.enabled = args.enabled - self.range = args.hl_pos - self.icon_placement = args.icon_placement + if args then + self.enabled = args.enabled + self.highlight_range = args.highlight_range + self.icon_placement = args.icon_placement + else + self.enabled = false + end end ---Maybe highlight groups ---@param node Node ----@return string|nil icon highlight group ----@return string|nil name highlight group +---@return string? icon highlight group +---@return string? name highlight group function Decorator:groups_icon_name(node) local icon_hl, name_hl - if self.enabled and self.range ~= "none" then + if self.enabled and self.highlight_range ~= "none" then local hl = self:calculate_highlight(node) - if self.range == "all" or self.range == "icon" then + if self.highlight_range == "all" or self.highlight_range == "icon" then icon_hl = hl end - if self.range == "all" or self.range == "name" then + if self.highlight_range == "all" or self.highlight_range == "name" then name_hl = hl end end @@ -46,7 +42,7 @@ end ---Maybe icon sign ---@param node Node ----@return string|nil name +---@return string? name function Decorator:sign_name(node) if not self.enabled or self.icon_placement ~= "signcolumn" then return @@ -60,7 +56,7 @@ end ---Icons when "before" ---@param node Node ----@return HighlightedString[]|nil icons +---@return HighlightedString[]? icons function Decorator:icons_before(node) if not self.enabled or self.icon_placement ~= "before" then return @@ -71,7 +67,7 @@ end ---Icons when "after" ---@param node Node ----@return HighlightedString[]|nil icons +---@return HighlightedString[]? icons function Decorator:icons_after(node) if not self.enabled or self.icon_placement ~= "after" then return @@ -82,7 +78,7 @@ end ---Icons when "right_align" ---@param node Node ----@return HighlightedString[]|nil icons +---@return HighlightedString[]? icons function Decorator:icons_right_align(node) if not self.enabled or self.icon_placement ~= "right_align" then return @@ -93,23 +89,23 @@ end ---Maybe icons, optionally implemented ---@protected ----@param _ Node ----@return HighlightedString[]|nil icons -function Decorator:calculate_icons(_) - return nil +---@param node Node +---@return HighlightedString[]? icons +function Decorator:calculate_icons(node) + self:nop(node) end ---Maybe highlight group, optionally implemented ---@protected ----@param _ Node ----@return string|nil group -function Decorator:calculate_highlight(_) - return nil +---@param node Node +---@return string? group +function Decorator:calculate_highlight(node) + self:nop(node) end ---Define a sign ---@protected ----@param icon HighlightedString|nil +---@param icon HighlightedString? function Decorator:define_sign(icon) if icon and #icon.hl > 0 then local name = icon.hl[1] diff --git a/lua/nvim-tree/renderer/decorator/meta.lua b/lua/nvim-tree/renderer/decorator/meta.lua new file mode 100644 index 00000000000..6f4fead3e78 --- /dev/null +++ b/lua/nvim-tree/renderer/decorator/meta.lua @@ -0,0 +1,13 @@ +---@meta + +---Highlight group range as per nvim-tree.renderer.highlight_* +---@alias DecoratorHighlightRange "none" | "icon" | "name" | "all" + +---Icon position as per renderer.icons.*_placement +---@alias DecoratorIconPlacement "none" | "before" | "after" | "signcolumn" | "right_align" + +---Decorator Constructor Arguments +---@class (exact) DecoratorArgs +---@field enabled boolean +---@field highlight_range DecoratorHighlightRange +---@field icon_placement DecoratorIconPlacement diff --git a/lua/nvim-tree/renderer/decorator/modified.lua b/lua/nvim-tree/renderer/decorator/modified.lua index 30e044d2d66..0c4c1bdc26a 100644 --- a/lua/nvim-tree/renderer/decorator/modified.lua +++ b/lua/nvim-tree/renderer/decorator/modified.lua @@ -16,11 +16,14 @@ local DecoratorModified = Decorator:extend() function DecoratorModified:new(explorer) self.explorer = explorer - DecoratorModified.super.new(self, { - enabled = true, - hl_pos = self.explorer.opts.renderer.highlight_modified or "none", - icon_placement = self.explorer.opts.renderer.icons.modified_placement or "none", - }) + ---@type DecoratorArgs + local args = { + enabled = true, + highlight_range = self.explorer.opts.renderer.highlight_modified or "none", + icon_placement = self.explorer.opts.renderer.icons.modified_placement or "none", + } + + DecoratorModified.super.new(self, args) if not self.enabled then return @@ -48,7 +51,7 @@ end ---@param node Node ---@return string|nil group function DecoratorModified:calculate_highlight(node) - if not self.enabled or self.range == "none" or not buffers.is_modified(node) then + if not self.enabled or self.highlight_range == "none" or not buffers.is_modified(node) then return nil end diff --git a/lua/nvim-tree/renderer/decorator/opened.lua b/lua/nvim-tree/renderer/decorator/opened.lua index c58cc419984..843d456d1ff 100644 --- a/lua/nvim-tree/renderer/decorator/opened.lua +++ b/lua/nvim-tree/renderer/decorator/opened.lua @@ -15,18 +15,21 @@ local DecoratorOpened = Decorator:extend() function DecoratorOpened:new(explorer) self.explorer = explorer - DecoratorOpened.super.new(self, { - enabled = true, - hl_pos = self.explorer.opts.renderer.highlight_opened_files or "none", - icon_placement = "none", - }) + ---@type DecoratorArgs + local args = { + enabled = true, + highlight_range = self.explorer.opts.renderer.highlight_opened_files or "none", + icon_placement = "none", + } + + DecoratorOpened.super.new(self, args) end ---Opened highlight: renderer.highlight_opened_files and node has an open buffer ---@param node Node ---@return string|nil group function DecoratorOpened:calculate_highlight(node) - if self.range ~= "none" and buffers.is_opened(node) then + if self.highlight_range ~= "none" and buffers.is_opened(node) then return "NvimTreeOpenedHL" end end diff --git a/lua/nvim-tree/renderer/decorator/user.lua b/lua/nvim-tree/renderer/decorator/user.lua index 28bbf131a48..c20d180e28c 100644 --- a/lua/nvim-tree/renderer/decorator/user.lua +++ b/lua/nvim-tree/renderer/decorator/user.lua @@ -1,16 +1,82 @@ local Decorator = require("nvim-tree.renderer.decorator") ----Marker parent for user decorators +---Abstract user decorator, extend to define your own. +---Icon and highlight are optional. +---Constructor will be called once per tree render, with no arguments: +--- Must call super passing DecoratorArgs: MyDecorator.super.new(self, args) +--- Must call define_sign, when using "signcolumn" +---See example at end. + ---@class (exact) UserDecorator: Decorator local UserDecorator = Decorator:extend() ----@class UserDecorator ----@overload fun(args: DecoratorArgs): UserDecorator +---Override this method to provide icons and the highlight groups to apply to them +---@param node Node +---@return HighlightedString[]? icons +function UserDecorator:calculate_icons(node) + self:nop(node) +end ----@protected ----@param args DecoratorArgs -function UserDecorator:new(args) - UserDecorator.super.new(self, args) +---Override this method to provide one highlight group to apply to DecoratorRange +---@param node Node +---@return string? group +function UserDecorator:calculate_highlight(node) + self:nop(node) end return UserDecorator + + +--- +---Example user decorator +--[[ + +local UserDecorator = require("nvim-tree.renderer.decorator.user") + +---@class (exact) MyDecorator: UserDecorator +---@field private my_icon HighlightedString +local MyDecorator = UserDecorator:extend() + +---Constructor +function MyDecorator:new() + + ---@type DecoratorArgs + local args = { + enabled = true, + highlight_range = "all", + icon_placement = "signcolumn", + } + + MyDecorator.super.new(self, args) + + -- create your icon once, for convenience + self.my_icon = { str = "I", hl = { "MyIcon" } } + + -- Define the icon sign only once + -- Only needed if you are using icon_placement = "signcolumn" + self:define_sign(self.my_icon) +end + +---Just one icon for DecoratorIconPlacement +---@param node Node +---@return HighlightedString[]|nil icons +function MyDecorator:calculate_icons(node) + if node.name == "example" then + return { self.my_icon } + else + return nil + end +end + +---Exactly one highlight group for DecoratorHighlightRange +---@param node Node +---@return string|nil group +function MyDecorator:calculate_highlight(node) + if node.name == "example" then + return "ExampleHighlight" + else + return nil + end +end + +--]]