Skip to content

Commit

Permalink
feat(#2948): add custom decorators, :help nvim-tree-decorators (#2996)
Browse files Browse the repository at this point in the history
* feat(#2948): add UserDecorator, proof of concept

* feat(#2948): add UserDecorator, proof of concept

* feat(#2948): add UserDecorator, proof of concept

* feat(#2948): add UserDecorator

* feat(#2948): add UserDecorator

* feat(#2948): add UserDecorator

* feat(#2948): add Decorator node icon override

* feat(#2948): add nvim_tree.api.* node classes

* feat(#2948): extract _meta following nvim pattern

* feat(#2948): extract _meta following nvim pattern

* feat(#2948): add decorator registry and order

* feat(#2948): add decorator registry and order

* feat(#2948): tidy

* feat(#2948): document API

* feat(#2948): document API

* feat(#2948): document API

* feat(#2948): pass api nodes to user decorators

* feat(#2948): document API

* feat(#2948): use renderer.decorators to define order and register

* feat(#2948): tidy decorator args and complete documentation

* feat(#2948): decorator classes specified by prefix rather than suffix

* feat(#2948): improve doc

* feat(#2948): improve doc

* feat(#2948): improve doc

* feat(#2948): additional user decorator safety

* feat(#2948): create nvim_tree.api.decorator.UserDecorator class in API, add :extend

* feat(#2948): improve doc
  • Loading branch information
alex-courtis authored Dec 7, 2024
1 parent ca7c4c3 commit 7a4ff1a
Show file tree
Hide file tree
Showing 25 changed files with 570 additions and 321 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ check: luals
# subtasks
#
luacheck:
luacheck -q lua
luacheck --codes --quiet lua --exclude-files "**/_meta/**"

# --diagnosis-as-error does not function for workspace, hence we post-process the output
style-check:
Expand Down
141 changes: 119 additions & 22 deletions doc/nvim-tree-lua.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,16 @@ CONTENTS *nvim-tree*
8.2 Highlight: Overhaul |nvim-tree-highlight-overhaul|
9. Events |nvim-tree-events|
10. Prompts |nvim-tree-prompts|
11. OS Specific Restrictions |nvim-tree-os-specific|
12. Netrw |nvim-tree-netrw|
13. Legacy |nvim-tree-legacy|
13.1 Legacy: Opts |nvim-tree-legacy-opts|
13.2 Legacy: Highlight |nvim-tree-legacy-highlight|
14. Index |nvim-tree-index|
14.1 Index: Opts |nvim-tree-index-opts|
14.2 Index: API |nvim-tree-index-api|
11. Decorators |nvim-tree-decorators|
11.1 Decorator Example |nvim-tree-decorator-example|
12. OS Specific Restrictions |nvim-tree-os-specific|
13. Netrw |nvim-tree-netrw|
14. Legacy |nvim-tree-legacy|
14.1 Legacy: Opts |nvim-tree-legacy-opts|
14.2 Legacy: Highlight |nvim-tree-legacy-highlight|
15. Index |nvim-tree-index|
15.1 Index: Opts |nvim-tree-index-opts|
15.2 Index: API |nvim-tree-index-api|

==============================================================================
1. INTRODUCTION *nvim-tree-introduction*
Expand Down Expand Up @@ -425,6 +427,7 @@ Following is the default configuration. See |nvim-tree-opts| for details. >lua
special_files = { "Cargo.toml", "Makefile", "README.md", "readme.md" },
hidden_display = "none",
symlink_destination = true,
decorators = { "Git", "Open", "Hidden", "Modified", "Bookmark", "Diagnostics", "Copied", "Cut", },
highlight_git = "none",
highlight_diagnostics = "none",
highlight_opened_files = "none",
Expand Down Expand Up @@ -842,9 +845,6 @@ Use nvim-tree in a floating window.
==============================================================================
5.3 OPTS: RENDERER *nvim-tree-opts-renderer*

Highlight precedence, additive:
git < opened < modified < bookmarked < diagnostics < copied < cut

*nvim-tree.renderer.add_trailing*
Appends a trailing slash to folder names.
Type: `boolean`, Default: `false`
Expand Down Expand Up @@ -927,6 +927,22 @@ Show a summary of hidden files below the tree using `NvimTreeHiddenDisplay
Whether to show the destination of the symlink.
Type: `boolean`, Default: `true`

*nvim-tree.renderer.decorators*
Highlighting and icons for the nodes, in increasing order of precedence.
Uses strings to specify builtin decorators otherwise specify your
`nvim_tree.api.decorator.UserDecorator` class.
Type: `nvim_tree.api.decorator.Name[]`, Default: >lua
{
"Git",
"Open",
"Hidden",
"Modified",
"Bookmark",
"Diagnostics",
"Copied",
"Cut",
}
<
*nvim-tree.renderer.highlight_git*
Enable highlight for git attributes using `NvimTreeGit*HL` highlight groups.
Requires |nvim-tree.git.enable|
Expand Down Expand Up @@ -996,9 +1012,6 @@ Configuration options for tree indent markers.
*nvim-tree.renderer.icons*
Configuration options for icons.

Icon order and sign column precedence:
git < hidden < modified < bookmarked < diagnostics

`renderer.icons.*_placement` options may be:
- `"before"` : before file/folder, after the file/folders icons
- `"after"` : after file/folder
Expand Down Expand Up @@ -2755,7 +2768,90 @@ configurations for different types of prompts.
send all bookmarked to trash during |nvim-tree-api.marks.bulk.trash()|

==============================================================================
11. OS SPECIFIC RESTRICTIONS *nvim-tree-os-specific*
11. DECORATORS *nvim-tree-decorators*

Highlighting and icons for nodes are provided by Decorators. You may provide
your own in addition to the builtin decorators.

Decorators may:
- Add icons
- Set highlight group for the name or icons
- Override node icon

Specify decorators and their precedence via |nvim-tree.renderer.decorators|
e.g. defaults with a user decorator class being overridden only by Cut: >lua
{
"Git",
"Open",
"Hidden",
"Modified",
"Bookmark",
"Diagnostics",
"Copied",
MyDecorator,
"Cut",
}

See `nvim-tree/_meta/api_decorator.lua` for full
`nvim_tree.api.decorator.UserDecorator` class documentation.
<
==============================================================================
11.1. DECORATOR EXAMPLE *nvim-tree-decorator-example*
>lua
---Create your decorator class
---@class (exact) MyDecorator: nvim_tree.api.decorator.UserDecorator
---@field private my_icon nvim_tree.api.HighlightedString
local MyDecorator = require("nvim-tree.api").decorator.UserDecorator:extend()

---Mandatory constructor :new() will be called once per tree render, with no arguments.
function MyDecorator:new()
self.enabled = true
self.highlight_range = "all"
self.icon_placement = "signcolumn"

-- 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

---Override node icon
---@param node nvim_tree.api.Node
---@return nvim_tree.api.HighlightedString? icon_node
function MyDecorator:icon_node(node)
if node.name == "example" then
return self.my_icon
else
return nil
end
end

---Return one icon for DecoratorIconPlacement
---@param node nvim_tree.api.Node
---@return nvim_tree.api.HighlightedString[]? icons
function MyDecorator:icons(node)
if node.name == "example" then
return { self.my_icon }
else
return nil
end
end

---Exactly one highlight group for DecoratorHighlightRange
---@param node nvim_tree.api.Node
---@return string? highlight_group
function MyDecorator:highlight_group(node)
if node.name == "example" then
return "MyHighlight"
else
return nil
end
end
<
==============================================================================
12. OS SPECIFIC RESTRICTIONS *nvim-tree-os-specific*

Windows WSL and PowerShell
- Trash is synchronized
Expand All @@ -2767,7 +2863,7 @@ Windows WSL and PowerShell
issues or disable this feature.

==============================================================================
12. NETRW *nvim-tree-netrw*
13. NETRW *nvim-tree-netrw*

|netrw| is a standard neovim plugin that is enabled by default. It provides,
amongst other functionality, a file/directory browser.
Expand All @@ -2788,14 +2884,14 @@ keep using |netrw| without its browser features please ensure:
|nvim-tree.hijack_netrw| ` = true`

==============================================================================
13. LEGACY *nvim-tree-legacy*
14. LEGACY *nvim-tree-legacy*

Breaking refactors have been made however the legacy versions will be silently
migrated and used.
There are no plans to remove this migration.

==============================================================================
13.1 LEGACY: OPTS *nvim-tree-legacy-opts*
14.1 LEGACY: OPTS *nvim-tree-legacy-opts*

Legacy options are translated to the current, making type and value changes as
needed.
Expand All @@ -2813,7 +2909,7 @@ needed.
`renderer.icons.webdev_colors` |nvim-tree.renderer.icons.web_devicons.file.color|

==============================================================================
13.2 LEGACY: HIGHLIGHT *nvim-tree-legacy-highlight*
14.2 LEGACY: HIGHLIGHT *nvim-tree-legacy-highlight*

Legacy highlight group are still obeyed when they are defined and the current
highlight group is not, hard linking as follows: >
Expand Down Expand Up @@ -2862,10 +2958,10 @@ highlight group is not, hard linking as follows: >
NvimTreeLspDiagnosticsHintFolderText NvimTreeDiagnosticHintFolderHL
<
==============================================================================
14 INDEX *nvim-tree-index*
15 INDEX *nvim-tree-index*

==============================================================================
14.1 INDEX: OPTS *nvim-tree-index-opts*
15.1 INDEX: OPTS *nvim-tree-index-opts*

|nvim-tree.actions.change_dir|
|nvim-tree.actions.change_dir.enable|
Expand Down Expand Up @@ -2943,6 +3039,7 @@ highlight group is not, hard linking as follows: >
|nvim-tree.prefer_startup_root|
|nvim-tree.reload_on_bufenter|
|nvim-tree.renderer.add_trailing|
|nvim-tree.renderer.decorators|
|nvim-tree.renderer.full_name|
|nvim-tree.renderer.group_empty|
|nvim-tree.renderer.hidden_display|
Expand Down Expand Up @@ -3033,7 +3130,7 @@ highlight group is not, hard linking as follows: >
|nvim-tree.view.width.padding|

==============================================================================
14.2 INDEX: API *nvim-tree-index-api*
15.2 INDEX: API *nvim-tree-index-api*

|nvim-tree-api.commands.get()|
|nvim-tree-api.config.mappings.default_on_attach()|
Expand Down
1 change: 1 addition & 0 deletions lua/nvim-tree.lua
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
special_files = { "Cargo.toml", "Makefile", "README.md", "readme.md" },
hidden_display = "none",
symlink_destination = true,
decorators = { "Git", "Open", "Hidden", "Modified", "Bookmark", "Diagnostics", "Copied", "Cut", },
highlight_git = "none",
highlight_diagnostics = "none",
highlight_opened_files = "none",
Expand Down
51 changes: 51 additions & 0 deletions lua/nvim-tree/_meta/api.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---@meta
error("Cannot require a meta file")

--
-- Nodes
--

---Base Node, Abstract
---@class (exact) nvim_tree.api.Node
---@field type "file" | "directory" | "link" uv.fs_stat.result.type
---@field absolute_path string
---@field executable boolean
---@field fs_stat uv.fs_stat.result?
---@field git_status GitNodeStatus?
---@field hidden boolean
---@field name string
---@field parent nvim_tree.api.DirectoryNode?
---@field diag_severity lsp.DiagnosticSeverity?

---File
---@class (exact) nvim_tree.api.FileNode: nvim_tree.api.Node
---@field extension string

---Directory
---@class (exact) nvim_tree.api.DirectoryNode: nvim_tree.api.Node
---@field has_children boolean
---@field nodes nvim_tree.api.Node[]
---@field open boolean

---Root Directory
---@class (exact) nvim_tree.api.RootNode: nvim_tree.api.DirectoryNode

---Link mixin
---@class (exact) nvim_tree.api.LinkNode
---@field link_to string
---@field fs_stat_target uv.fs_stat.result

---File Link
---@class (exact) nvim_tree.api.FileLinkNode: nvim_tree.api.FileNode, nvim_tree.api.LinkNode

---DirectoryLink
---@class (exact) nvim_tree.api.DirectoryLinkNode: nvim_tree.api.DirectoryNode, nvim_tree.api.LinkNode

--
-- Various Types
--

---A string for rendering, with optional highlight groups to apply to it
---@class (exact) nvim_tree.api.HighlightedString
---@field str string
---@field hl string[]
54 changes: 54 additions & 0 deletions lua/nvim-tree/_meta/api_decorator.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---@meta
error("Cannot require a meta file")

local nvim_tree = { api = { decorator = {} } }

---Highlight group range as per nvim-tree.renderer.highlight_*
---@alias nvim_tree.api.decorator.HighlightRange "none" | "icon" | "name" | "all"

---Icon position as per renderer.icons.*_placement
---@alias nvim_tree.api.decorator.IconPlacement "none" | "before" | "after" | "signcolumn" | "right_align"

---Names of builtin decorators or your decorator classes. Builtins are ordered lowest to highest priority.
---@alias nvim_tree.api.decorator.Name "Git" | "Opened" | "Hidden" | "Modified" | "Bookmarks" | "Diagnostics" | "Copied" | "Cut" | nvim_tree.api.decorator.UserDecorator

---Custom decorator, see :help nvim-tree-decorators
---
---@class (exact) nvim_tree.api.decorator.UserDecorator
---@field protected enabled boolean
---@field protected highlight_range nvim_tree.api.decorator.HighlightRange
---@field protected icon_placement nvim_tree.api.decorator.IconPlacement
nvim_tree.api.decorator.UserDecorator = {}

---Create your decorator class
---
function nvim_tree.api.decorator.UserDecorator:extend() end

---Abstract: no-args constructor must be implemented and will be called once per tree render.
---Must set all fields.
---
function nvim_tree.api.decorator.UserDecorator:new() end

---Abstract: optionally implement to set the node's icon
---
---@param node nvim_tree.api.Node
---@return nvim_tree.api.HighlightedString? icon_node
function nvim_tree.api.decorator.UserDecorator:icon_node(node) end

---Abstract: optionally implement to provide icons and the highlight groups for your icon_placement.
---
---@param node nvim_tree.api.Node
---@return nvim_tree.api.HighlightedString[]? icons
function nvim_tree.api.decorator.UserDecorator:icons(node) end

---Abstract: optionally implement to provide one highlight group to apply to your highlight_range.
---
---@param node nvim_tree.api.Node
---@return string? highlight_group
function nvim_tree.api.decorator.UserDecorator:highlight_group(node) end

---Define a sign. This should be called in the constructor.
---
---@protected
---@param icon nvim_tree.api.HighlightedString?
function nvim_tree.api.decorator.UserDecorator:define_sign(icon) end
7 changes: 7 additions & 0 deletions lua/nvim-tree/api.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ local notify = require("nvim-tree.notify")
local DirectoryNode = require("nvim-tree.node.directory")
local FileLinkNode = require("nvim-tree.node.file-link")
local RootNode = require("nvim-tree.node.root")
local UserDecorator = require("nvim-tree.renderer.decorator.user")

local Api = {
tree = {},
Expand Down Expand Up @@ -39,6 +40,7 @@ local Api = {
},
commands = {},
diagnostics = {},
decorator = {},
}

---Print error when setup not called.
Expand Down Expand Up @@ -311,4 +313,9 @@ Api.commands.get = wrap(function()
return require("nvim-tree.commands").get()
end)

---Create a decorator class by calling :extend()
---See :help nvim-tree-decorators
---@type nvim_tree.api.decorator.UserDecorator
Api.decorator.UserDecorator = UserDecorator --[[@as nvim_tree.api.decorator.UserDecorator]]

return Api
2 changes: 1 addition & 1 deletion lua/nvim-tree/explorer/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,7 @@ function Explorer:place_cursor_on_node()
end

---Api.tree.get_nodes
---@return Node
---@return nvim_tree.api.Node
function Explorer:get_nodes()
return self:clone()
end
Expand Down
Loading

0 comments on commit 7a4ff1a

Please sign in to comment.