Skip to content

Commit 7a4ff1a

Browse files
authored
feat(#2948): add custom decorators, :help nvim-tree-decorators (#2996)
* 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
1 parent ca7c4c3 commit 7a4ff1a

25 files changed

+570
-321
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ check: luals
1313
# subtasks
1414
#
1515
luacheck:
16-
luacheck -q lua
16+
luacheck --codes --quiet lua --exclude-files "**/_meta/**"
1717

1818
# --diagnosis-as-error does not function for workspace, hence we post-process the output
1919
style-check:

doc/nvim-tree-lua.txt

Lines changed: 119 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,16 @@ CONTENTS *nvim-tree*
5252
8.2 Highlight: Overhaul |nvim-tree-highlight-overhaul|
5353
9. Events |nvim-tree-events|
5454
10. Prompts |nvim-tree-prompts|
55-
11. OS Specific Restrictions |nvim-tree-os-specific|
56-
12. Netrw |nvim-tree-netrw|
57-
13. Legacy |nvim-tree-legacy|
58-
13.1 Legacy: Opts |nvim-tree-legacy-opts|
59-
13.2 Legacy: Highlight |nvim-tree-legacy-highlight|
60-
14. Index |nvim-tree-index|
61-
14.1 Index: Opts |nvim-tree-index-opts|
62-
14.2 Index: API |nvim-tree-index-api|
55+
11. Decorators |nvim-tree-decorators|
56+
11.1 Decorator Example |nvim-tree-decorator-example|
57+
12. OS Specific Restrictions |nvim-tree-os-specific|
58+
13. Netrw |nvim-tree-netrw|
59+
14. Legacy |nvim-tree-legacy|
60+
14.1 Legacy: Opts |nvim-tree-legacy-opts|
61+
14.2 Legacy: Highlight |nvim-tree-legacy-highlight|
62+
15. Index |nvim-tree-index|
63+
15.1 Index: Opts |nvim-tree-index-opts|
64+
15.2 Index: API |nvim-tree-index-api|
6365

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

845-
Highlight precedence, additive:
846-
git < opened < modified < bookmarked < diagnostics < copied < cut
847-
848848
*nvim-tree.renderer.add_trailing*
849849
Appends a trailing slash to folder names.
850850
Type: `boolean`, Default: `false`
@@ -927,6 +927,22 @@ Show a summary of hidden files below the tree using `NvimTreeHiddenDisplay
927927
Whether to show the destination of the symlink.
928928
Type: `boolean`, Default: `true`
929929

930+
*nvim-tree.renderer.decorators*
931+
Highlighting and icons for the nodes, in increasing order of precedence.
932+
Uses strings to specify builtin decorators otherwise specify your
933+
`nvim_tree.api.decorator.UserDecorator` class.
934+
Type: `nvim_tree.api.decorator.Name[]`, Default: >lua
935+
{
936+
"Git",
937+
"Open",
938+
"Hidden",
939+
"Modified",
940+
"Bookmark",
941+
"Diagnostics",
942+
"Copied",
943+
"Cut",
944+
}
945+
<
930946
*nvim-tree.renderer.highlight_git*
931947
Enable highlight for git attributes using `NvimTreeGit*HL` highlight groups.
932948
Requires |nvim-tree.git.enable|
@@ -996,9 +1012,6 @@ Configuration options for tree indent markers.
9961012
*nvim-tree.renderer.icons*
9971013
Configuration options for icons.
9981014

999-
Icon order and sign column precedence:
1000-
git < hidden < modified < bookmarked < diagnostics
1001-
10021015
`renderer.icons.*_placement` options may be:
10031016
- `"before"` : before file/folder, after the file/folders icons
10041017
- `"after"` : after file/folder
@@ -2755,7 +2768,90 @@ configurations for different types of prompts.
27552768
send all bookmarked to trash during |nvim-tree-api.marks.bulk.trash()|
27562769

27572770
==============================================================================
2758-
11. OS SPECIFIC RESTRICTIONS *nvim-tree-os-specific*
2771+
11. DECORATORS *nvim-tree-decorators*
2772+
2773+
Highlighting and icons for nodes are provided by Decorators. You may provide
2774+
your own in addition to the builtin decorators.
2775+
2776+
Decorators may:
2777+
- Add icons
2778+
- Set highlight group for the name or icons
2779+
- Override node icon
2780+
2781+
Specify decorators and their precedence via |nvim-tree.renderer.decorators|
2782+
e.g. defaults with a user decorator class being overridden only by Cut: >lua
2783+
{
2784+
"Git",
2785+
"Open",
2786+
"Hidden",
2787+
"Modified",
2788+
"Bookmark",
2789+
"Diagnostics",
2790+
"Copied",
2791+
MyDecorator,
2792+
"Cut",
2793+
}
2794+
2795+
See `nvim-tree/_meta/api_decorator.lua` for full
2796+
`nvim_tree.api.decorator.UserDecorator` class documentation.
2797+
<
2798+
==============================================================================
2799+
11.1. DECORATOR EXAMPLE *nvim-tree-decorator-example*
2800+
>lua
2801+
---Create your decorator class
2802+
---@class (exact) MyDecorator: nvim_tree.api.decorator.UserDecorator
2803+
---@field private my_icon nvim_tree.api.HighlightedString
2804+
local MyDecorator = require("nvim-tree.api").decorator.UserDecorator:extend()
2805+
2806+
---Mandatory constructor :new() will be called once per tree render, with no arguments.
2807+
function MyDecorator:new()
2808+
self.enabled = true
2809+
self.highlight_range = "all"
2810+
self.icon_placement = "signcolumn"
2811+
2812+
-- create your icon once, for convenience
2813+
self.my_icon = { str = "I", hl = { "MyIcon" } }
2814+
2815+
-- Define the icon sign only once
2816+
-- Only needed if you are using icon_placement = "signcolumn"
2817+
self:define_sign(self.my_icon)
2818+
end
2819+
2820+
---Override node icon
2821+
---@param node nvim_tree.api.Node
2822+
---@return nvim_tree.api.HighlightedString? icon_node
2823+
function MyDecorator:icon_node(node)
2824+
if node.name == "example" then
2825+
return self.my_icon
2826+
else
2827+
return nil
2828+
end
2829+
end
2830+
2831+
---Return one icon for DecoratorIconPlacement
2832+
---@param node nvim_tree.api.Node
2833+
---@return nvim_tree.api.HighlightedString[]? icons
2834+
function MyDecorator:icons(node)
2835+
if node.name == "example" then
2836+
return { self.my_icon }
2837+
else
2838+
return nil
2839+
end
2840+
end
2841+
2842+
---Exactly one highlight group for DecoratorHighlightRange
2843+
---@param node nvim_tree.api.Node
2844+
---@return string? highlight_group
2845+
function MyDecorator:highlight_group(node)
2846+
if node.name == "example" then
2847+
return "MyHighlight"
2848+
else
2849+
return nil
2850+
end
2851+
end
2852+
<
2853+
==============================================================================
2854+
12. OS SPECIFIC RESTRICTIONS *nvim-tree-os-specific*
27592855

27602856
Windows WSL and PowerShell
27612857
- Trash is synchronized
@@ -2767,7 +2863,7 @@ Windows WSL and PowerShell
27672863
issues or disable this feature.
27682864

27692865
==============================================================================
2770-
12. NETRW *nvim-tree-netrw*
2866+
13. NETRW *nvim-tree-netrw*
27712867

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

27902886
==============================================================================
2791-
13. LEGACY *nvim-tree-legacy*
2887+
14. LEGACY *nvim-tree-legacy*
27922888

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

27972893
==============================================================================
2798-
13.1 LEGACY: OPTS *nvim-tree-legacy-opts*
2894+
14.1 LEGACY: OPTS *nvim-tree-legacy-opts*
27992895

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

28152911
==============================================================================
2816-
13.2 LEGACY: HIGHLIGHT *nvim-tree-legacy-highlight*
2912+
14.2 LEGACY: HIGHLIGHT *nvim-tree-legacy-highlight*
28172913

28182914
Legacy highlight group are still obeyed when they are defined and the current
28192915
highlight group is not, hard linking as follows: >
@@ -2862,10 +2958,10 @@ highlight group is not, hard linking as follows: >
28622958
NvimTreeLspDiagnosticsHintFolderText NvimTreeDiagnosticHintFolderHL
28632959
<
28642960
==============================================================================
2865-
14 INDEX *nvim-tree-index*
2961+
15 INDEX *nvim-tree-index*
28662962

28672963
==============================================================================
2868-
14.1 INDEX: OPTS *nvim-tree-index-opts*
2964+
15.1 INDEX: OPTS *nvim-tree-index-opts*
28692965

28702966
|nvim-tree.actions.change_dir|
28712967
|nvim-tree.actions.change_dir.enable|
@@ -2943,6 +3039,7 @@ highlight group is not, hard linking as follows: >
29433039
|nvim-tree.prefer_startup_root|
29443040
|nvim-tree.reload_on_bufenter|
29453041
|nvim-tree.renderer.add_trailing|
3042+
|nvim-tree.renderer.decorators|
29463043
|nvim-tree.renderer.full_name|
29473044
|nvim-tree.renderer.group_empty|
29483045
|nvim-tree.renderer.hidden_display|
@@ -3033,7 +3130,7 @@ highlight group is not, hard linking as follows: >
30333130
|nvim-tree.view.width.padding|
30343131

30353132
==============================================================================
3036-
14.2 INDEX: API *nvim-tree-index-api*
3133+
15.2 INDEX: API *nvim-tree-index-api*
30373134

30383135
|nvim-tree-api.commands.get()|
30393136
|nvim-tree-api.config.mappings.default_on_attach()|

lua/nvim-tree.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
284284
special_files = { "Cargo.toml", "Makefile", "README.md", "readme.md" },
285285
hidden_display = "none",
286286
symlink_destination = true,
287+
decorators = { "Git", "Open", "Hidden", "Modified", "Bookmark", "Diagnostics", "Copied", "Cut", },
287288
highlight_git = "none",
288289
highlight_diagnostics = "none",
289290
highlight_opened_files = "none",

lua/nvim-tree/_meta/api.lua

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
---@meta
2+
error("Cannot require a meta file")
3+
4+
--
5+
-- Nodes
6+
--
7+
8+
---Base Node, Abstract
9+
---@class (exact) nvim_tree.api.Node
10+
---@field type "file" | "directory" | "link" uv.fs_stat.result.type
11+
---@field absolute_path string
12+
---@field executable boolean
13+
---@field fs_stat uv.fs_stat.result?
14+
---@field git_status GitNodeStatus?
15+
---@field hidden boolean
16+
---@field name string
17+
---@field parent nvim_tree.api.DirectoryNode?
18+
---@field diag_severity lsp.DiagnosticSeverity?
19+
20+
---File
21+
---@class (exact) nvim_tree.api.FileNode: nvim_tree.api.Node
22+
---@field extension string
23+
24+
---Directory
25+
---@class (exact) nvim_tree.api.DirectoryNode: nvim_tree.api.Node
26+
---@field has_children boolean
27+
---@field nodes nvim_tree.api.Node[]
28+
---@field open boolean
29+
30+
---Root Directory
31+
---@class (exact) nvim_tree.api.RootNode: nvim_tree.api.DirectoryNode
32+
33+
---Link mixin
34+
---@class (exact) nvim_tree.api.LinkNode
35+
---@field link_to string
36+
---@field fs_stat_target uv.fs_stat.result
37+
38+
---File Link
39+
---@class (exact) nvim_tree.api.FileLinkNode: nvim_tree.api.FileNode, nvim_tree.api.LinkNode
40+
41+
---DirectoryLink
42+
---@class (exact) nvim_tree.api.DirectoryLinkNode: nvim_tree.api.DirectoryNode, nvim_tree.api.LinkNode
43+
44+
--
45+
-- Various Types
46+
--
47+
48+
---A string for rendering, with optional highlight groups to apply to it
49+
---@class (exact) nvim_tree.api.HighlightedString
50+
---@field str string
51+
---@field hl string[]

lua/nvim-tree/_meta/api_decorator.lua

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---@meta
2+
error("Cannot require a meta file")
3+
4+
local nvim_tree = { api = { decorator = {} } }
5+
6+
---Highlight group range as per nvim-tree.renderer.highlight_*
7+
---@alias nvim_tree.api.decorator.HighlightRange "none" | "icon" | "name" | "all"
8+
9+
---Icon position as per renderer.icons.*_placement
10+
---@alias nvim_tree.api.decorator.IconPlacement "none" | "before" | "after" | "signcolumn" | "right_align"
11+
12+
---Names of builtin decorators or your decorator classes. Builtins are ordered lowest to highest priority.
13+
---@alias nvim_tree.api.decorator.Name "Git" | "Opened" | "Hidden" | "Modified" | "Bookmarks" | "Diagnostics" | "Copied" | "Cut" | nvim_tree.api.decorator.UserDecorator
14+
15+
---Custom decorator, see :help nvim-tree-decorators
16+
---
17+
---@class (exact) nvim_tree.api.decorator.UserDecorator
18+
---@field protected enabled boolean
19+
---@field protected highlight_range nvim_tree.api.decorator.HighlightRange
20+
---@field protected icon_placement nvim_tree.api.decorator.IconPlacement
21+
nvim_tree.api.decorator.UserDecorator = {}
22+
23+
---Create your decorator class
24+
---
25+
function nvim_tree.api.decorator.UserDecorator:extend() end
26+
27+
---Abstract: no-args constructor must be implemented and will be called once per tree render.
28+
---Must set all fields.
29+
---
30+
function nvim_tree.api.decorator.UserDecorator:new() end
31+
32+
---Abstract: optionally implement to set the node's icon
33+
---
34+
---@param node nvim_tree.api.Node
35+
---@return nvim_tree.api.HighlightedString? icon_node
36+
function nvim_tree.api.decorator.UserDecorator:icon_node(node) end
37+
38+
---Abstract: optionally implement to provide icons and the highlight groups for your icon_placement.
39+
---
40+
---@param node nvim_tree.api.Node
41+
---@return nvim_tree.api.HighlightedString[]? icons
42+
function nvim_tree.api.decorator.UserDecorator:icons(node) end
43+
44+
---Abstract: optionally implement to provide one highlight group to apply to your highlight_range.
45+
---
46+
---@param node nvim_tree.api.Node
47+
---@return string? highlight_group
48+
function nvim_tree.api.decorator.UserDecorator:highlight_group(node) end
49+
50+
---Define a sign. This should be called in the constructor.
51+
---
52+
---@protected
53+
---@param icon nvim_tree.api.HighlightedString?
54+
function nvim_tree.api.decorator.UserDecorator:define_sign(icon) end

lua/nvim-tree/api.lua

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ local notify = require("nvim-tree.notify")
1111
local DirectoryNode = require("nvim-tree.node.directory")
1212
local FileLinkNode = require("nvim-tree.node.file-link")
1313
local RootNode = require("nvim-tree.node.root")
14+
local UserDecorator = require("nvim-tree.renderer.decorator.user")
1415

1516
local Api = {
1617
tree = {},
@@ -39,6 +40,7 @@ local Api = {
3940
},
4041
commands = {},
4142
diagnostics = {},
43+
decorator = {},
4244
}
4345

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

316+
---Create a decorator class by calling :extend()
317+
---See :help nvim-tree-decorators
318+
---@type nvim_tree.api.decorator.UserDecorator
319+
Api.decorator.UserDecorator = UserDecorator --[[@as nvim_tree.api.decorator.UserDecorator]]
320+
314321
return Api

lua/nvim-tree/explorer/init.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ function Explorer:place_cursor_on_node()
530530
end
531531

532532
---Api.tree.get_nodes
533-
---@return Node
533+
---@return nvim_tree.api.Node
534534
function Explorer:get_nodes()
535535
return self:clone()
536536
end

0 commit comments

Comments
 (0)