feat(#2415): colour and highlight overhaul, see :help nvim-tree-highlight-overhaul (#2455)

* feat(#2415): granular highlight_diagnostics, normalise groups (#2454)

* chore: normalise colours and enable cterm (#2471)

* feat(#2415): granular highlight_git, normalise git groups (#2487)

* docs: update CONTRIBUTING.md (#2485)

* feat(#2415): granular highlight_git, normalise git groups

* feat(#2415): normalise and add modified groups

* feat(#2415): create Decorator class for modified and bookmarks

* feat(#2415): create DecoratorDiagnostics

* feat(#2415): create DecoratorGit

* feat(#2415): create DecoratorGit

* add DecoratorCopied DecoratorCut

* add DecoratorOpened

* remove unloaded_bufnr checks as the view debouncer takes care of it

* Add `renderer.highlight_git` to accepted strings

* fix(#2415): builder refactor (#2538)

* simplify builder signs

* decorators take care of themselves and are priority ordered

* simplify builder hl groups

* refactor builder for icon arrays

* builder use decorators generically

* fix(#2415): harden sign creation (#2539)

* fix(#2415): harden unicode signs

* Decorator tidy

* normalise git sign creation and tidy

* tidy builder

* NvimTreeBookmarkIcon

* tidy HL doc

* tidy HL doc

* tidy HL doc

* tidy builder doc

* standardise on '---@param'

* DiagnosticWarning -> DiagnosticWarn

* annotate decorators

* limit to two highlight groups for line rendering

* style

* apply #2519

* feat(#2415): combined hl groups (#2601)

* feat(#2415): create combined highlight groups

* feat(#2415): create combined highlight groups

* feat(#2415): create combined highlight groups

* ci: allow workflow_dispatch (#2620)

* one and only one hl namespace, required winhl removal

* small tidies

* colors.lua -> appearance.lua

* full-name uses one and only namespace

* don't highlight fast, just apply to namespace, safer win_set_hl

* gut builder (#2622)

collapse Builder

* fix group_empty function check

* feat(#2415): highlight-overhaul release date

---------

Co-authored-by: Akmadan23 <azadahmadi@mailo.com>
This commit is contained in:
Alexander Courtis 2024-01-20 16:12:13 +11:00 committed by GitHub
parent f24afa2cef
commit e9c5abe073
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 1468 additions and 1004 deletions

View File

@ -53,7 +53,7 @@ Setup the plugin in your `init.lua`
vim.g.loaded_netrw = 1
vim.g.loaded_netrwPlugin = 1
-- set termguicolors to enable highlight groups
-- optionally enable 24-bit colour
vim.opt.termguicolors = true
-- empty setup using defaults

View File

@ -46,6 +46,7 @@ CONTENTS *nvim-tree*
7. Mappings |nvim-tree-mappings|
7.1 Mappings: Default |nvim-tree-mappings-default|
8. Highlight |nvim-tree-highlight|
8.1 Highlight Overhaul |nvim-tree-highlight-overhaul|
9. Events |nvim-tree-events|
10. Prompts |nvim-tree-prompts|
11. OS Specific Restrictions |nvim-tree-os-specific|
@ -113,7 +114,7 @@ Setup the plugin in your `init.lua` >
vim.g.loaded_netrw = 1
vim.g.loaded_netrwPlugin = 1
-- set termguicolors to enable highlight groups
-- optionally enable 24-bit colour
vim.opt.termguicolors = true
-- empty setup using defaults
@ -387,8 +388,8 @@ Following is the default configuration. See |nvim-tree-opts| for details.
indent_width = 2,
special_files = { "Cargo.toml", "Makefile", "README.md", "readme.md" },
symlink_destination = true,
highlight_git = false,
highlight_diagnostics = false,
highlight_git = "none",
highlight_diagnostics = "none",
highlight_opened_files = "none",
highlight_modified = "none",
highlight_bookmarks = "none",
@ -789,8 +790,8 @@ Use nvim-tree in a floating window.
==============================================================================
5.3 OPTS: RENDERER *nvim-tree-opts-renderer*
Highlight precedence:
clipboard > diagnostics > bookmarked > modified > opened > git
Highlight precedence, additive:
git < opened < modified < bookmarked < diagnostics < copied < cut
*nvim-tree.renderer.add_trailing*
Appends a trailing slash to folder names.
@ -832,14 +833,16 @@ Whether to show the destination of the symlink.
Type: `boolean`, Default: `true`
*nvim-tree.renderer.highlight_git*
Enable highlight for git attributes using `NvimTreeGit*` highlight groups.
Enable highlight for git attributes using `NvimTreeGit*HL` highlight groups.
Requires |nvim-tree.git.enable|
Type: `boolean`, Default: `false`
Value can be `"none"`, `"icon"`, `"name"` or `"all"`.
Type: `string`, Default: `"none"`
*nvim-tree.renderer.highlight_diagnostics*
Enable highlight for diagnostics using `LspDiagnosticsError*Text` highlight groups.
Enable highlight for diagnostics using `NvimTreeDiagnostic*HL` highlight groups.
Requires |nvim-tree.diagnostics.enable|
Type: `boolean`, Default: `false`
Value can be `"none"`, `"icon"`, `"name"` or `"all"`.
Type: `string`, Default: `"none"`
*nvim-tree.renderer.highlight_opened_files*
Highlight icons and/or names for |bufloaded()| files using the
@ -892,8 +895,8 @@ Configuration options for tree indent markers.
*nvim-tree.renderer.icons*
Configuration options for icons.
Icon sign column precedence:
diagnostics > modified > git > bookmarked
Icon order and sign column precedence:
git < modified < bookmarked < diagnostics
*nvim-tree.renderer.icons.web_devicons*
Configure optional plugin `"nvim-tree/nvim-web-devicons"`
@ -907,7 +910,7 @@ Icon sign column precedence:
Type: `boolean`, Default: `true`
*nvim-tree.renderer.icons.web_devicons.file.color*
Use icon colors for files.
Use icon colors for files. Overrides highlight groups.
Type: `boolean`, Default: `true`
*nvim-tree.renderer.icons.web_devicons.folder*
@ -919,7 +922,7 @@ Icon sign column precedence:
Type: `boolean`, Default: `false`
*nvim-tree.renderer.icons.web_devicons.folder.color*
Use icon colors for folders.
Use icon colors for folders. Overrides highlight groups.
Type: `boolean`, Default: `true`
*nvim-tree.renderer.icons.git_placement*
@ -2237,42 +2240,13 @@ groups.
Example |:highlight| >
:hi NvimTreeSymlink guifg=blue gui=bold,underline
<
You should have 'termguicolors' enabled, otherwise, colors will not be
applied.
It is recommended to enable 'termguicolors' for the more pleasant 24-bit colours.
To view the active highlight groups run `:so $VIMRUNTIME/syntax/hitest.vim`
as per |:highlight|
Default linked group follows name.
Default linked group or definition follows name.
File Text: >
NvimTreeSymlink
NvimTreeExecFile
NvimTreeOpenedFile
NvimTreeModifiedFile
NvimTreeSpecialFile
NvimTreeImageFile
<
Folder Text: >
NvimTreeFolderName Directory
NvimTreeEmptyFolderName Directory
NvimTreeOpenedFolderName Directory
NvimTreeSymlinkFolderName Directory
NvimTreeRootFolder
<
Icon: >
NvimTreeFileIcon
NvimTreeOpenedFileIcon NvimTreeOpenedFile
NvimTreeSymlinkIcon
NvimTreeFolderIcon
NvimTreeOpenedFolderIcon NvimTreeFolderIcon
NvimTreeClosedFolderIcon NvimTreeFolderIcon
NvimTreeFolderArrowClosed NvimTreeIndentMarker
NvimTreeFolderArrowOpen NvimTreeIndentMarker
<
Indent: >
NvimTreeIndentMarker
<
Standard: >
NvimTreeNormal Normal
NvimTreeNormalFloat NormalFloat
@ -2291,67 +2265,163 @@ Standard: >
NvimTreeStatusLine StatusLine
NvimTreeStatusLineNC StatusLineNC
<
File Text: >
NvimTreeExecFile Constant
NvimTreeImageFile PreProc
NvimTreeOpenedFile Constant
NvimTreeSpecialFile PreProc
NvimTreeSymlink Statement
<
Folder Text: >
NvimTreeRootFolder PreProc
NvimTreeFolderName Directory
NvimTreeEmptyFolderName Directory
NvimTreeOpenedFolderName Directory
NvimTreeSymlinkFolderName Directory
<
File Icons: >
NvimTreeFileIcon NvimTreeNormal
NvimTreeSymlinkIcon NvimTreeNormal
NvimTreeOpenedFileIcon NvimTreeOpenedFile
<
Folder Icons: >
NvimTreeFolderIcon guifg=#8094b4 ctermfg=Blue
NvimTreeOpenedFolderIcon NvimTreeFolderIcon
NvimTreeClosedFolderIcon NvimTreeFolderIcon
NvimTreeFolderArrowClosed NvimTreeIndentMarker
NvimTreeFolderArrowOpen NvimTreeIndentMarker
<
Indent: >
NvimTreeIndentMarker NvimTreeFileIcon
<
Picker: >
NvimTreeWindowPicker guifg=#ededed guibg=#4493c8 gui=bold ctermfg=White ctermbg=Cyan
<
Live Filter: >
NvimTreeLiveFilterPrefix PreProc
NvimTreeLiveFilterValue ModeMsg
<
Clipboard: >
NvimTreeCopiedHL SpellRare
NvimTreeCutHL SpellBad
<
Bookmark Icon: >
NvimTreeBookmark
<
Bookmark Highlight: >
Bookmarks: >
NvimTreeBookmarkIcon Constant
NvimTreeBookmarkHL SpellLocal
<
Picker: >
NvimTreeWindowPicker
Modified: >
NvimTreeModifiedIcon Constant
NvimTreeModifiedFileHL NvimTreeModifiedIcon
NvimTreeModifiedFolderHL NvimTreeModifiedIcon
<
Live Filter: >
NvimTreeLiveFilterPrefix
NvimTreeLiveFilterValue
Opened: >
NvimTreeOpenedHL Constant
<
Git Icon: >
NvimTreeGitDirty
NvimTreeGitStaged
NvimTreeGitMerge
NvimTreeGitRenamed
NvimTreeGitNew
NvimTreeGitDeleted
NvimTreeGitIgnored Comment
NvimTreeGitDeletedIcon Statement
NvimTreeGitDirtyIcon Statement
NvimTreeGitIgnoredIcon Comment
NvimTreeGitMergeIcon Constant
NvimTreeGitNewIcon PreProc
NvimTreeGitRenamedIcon PreProc
NvimTreeGitStagedIcon Constant
<
Git File Text: >
NvimTreeFileDirty NvimTreeGitDirty
NvimTreeFileStaged NvimTreeGitStaged
NvimTreeFileMerge NvimTreeGitMerge
NvimTreeFileRenamed NvimTreeGitRenamed
NvimTreeFileNew NvimTreeGitNew
NvimTreeFileDeleted NvimTreeGitDeleted
NvimTreeFileIgnored NvimTreeGitIgnored
Git File File Highlight: >
NvimTreeGitFileDeletedHL NvimTreeGitDeletedIcon
NvimTreeGitFileDirtyHL NvimTreeGitDirtyIcon
NvimTreeGitFileIgnoredHL NvimTreeGitIgnoredIcon
NvimTreeGitFileMergeHL NvimTreeGitMergeIcon
NvimTreeGitFileNewHL NvimTreeGitNewIcon
NvimTreeGitFileRenamedHL NvimTreeGitRenamedIcon
NvimTreeGitFileStagedHL NvimTreeGitStagedIcon
<
Git Folder Text: >
NvimTreeFolderDirty NvimTreeFileDirty
NvimTreeFolderStaged NvimTreeFileStaged
NvimTreeFolderMerge NvimTreeFileMerge
NvimTreeFolderRenamed NvimTreeFileRenamed
NvimTreeFolderNew NvimTreeFileNew
NvimTreeFolderDeleted NvimTreeFileDeleted
NvimTreeFolderIgnored NvimTreeFileIgnored
Git Folder Folder Highlight: >
NvimTreeGitFolderDeletedHL NvimTreeGitFileDeletedHL
NvimTreeGitFolderDirtyHL NvimTreeGitFileDirtyHL
NvimTreeGitFolderIgnoredHL NvimTreeGitFileIgnoredHL
NvimTreeGitFolderMergeHL NvimTreeGitFileMergeHL
NvimTreeGitFolderNewHL NvimTreeGitFileNewHL
NvimTreeGitFolderRenamedHL NvimTreeGitFileRenamedHL
NvimTreeGitFolderStagedHL NvimTreeGitFileStagedHL
<
Diagnostics Icon: >
NvimTreeLspDiagnosticsError DiagnosticError
NvimTreeLspDiagnosticsWarning DiagnosticWarn
NvimTreeLspDiagnosticsInformation DiagnosticInfo
NvimTreeLspDiagnosticsHint DiagnosticHint
NvimTreeDiagnosticErrorIcon DiagnosticError
NvimTreeDiagnosticWarnIcon DiagnosticWarn
NvimTreeDiagnosticInfoIcon DiagnosticInfo
NvimTreeDiagnosticHintIcon DiagnosticHint
<
Diagnostics File Text: >
NvimTreeLspDiagnosticsErrorText NvimTreeLspDiagnosticsError
NvimTreeLspDiagnosticsWarningText NvimTreeLspDiagnosticsWarning
NvimTreeLspDiagnosticsInfoText NvimTreeLspDiagnosticsInformation
NvimTreeLspDiagnosticsHintText NvimTreeLspDiagnosticsHint
Diagnostics File Highlight: >
NvimTreeDiagnosticErrorFileHL DiagnosticUnderlineError
NvimTreeDiagnosticWarnFileHL DiagnosticUnderlineWarn
NvimTreeDiagnosticInfoFileHL DiagnosticUnderlineInfo
NvimTreeDiagnosticHintFileHL DiagnosticUnderlineHint
<
Diagnostics Folder Text: >
NvimTreeLspDiagnosticsErrorFolderText NvimTreeLspDiagnosticsErrorText
NvimTreeLspDiagnosticsWarningFolderText NvimTreeLspDiagnosticsWarningText
NvimTreeLspDiagnosticsInfoFolderText NvimTreeLspDiagnosticsInfoText
NvimTreeLspDiagnosticsHintFolderText NvimTreeLspDiagnosticsHintText
Diagnostics Folder Highlight: >
NvimTreeDiagnosticErrorFolderHL NvimTreeDiagnosticErrorFileHL
NvimTreeDiagnosticWarnFolderHL NvimTreeDiagnosticWarnFileHL
NvimTreeDiagnosticInfoFolderHL NvimTreeDiagnosticInfoFileHL
NvimTreeDiagnosticHintFolderHL NvimTreeDiagnosticHintFileHL
<
==============================================================================
8.1 HIGHLIGHT OVERHAUL *nvim-tree-highlight-overhaul*
2024-01-20: significant highlighting changes, some breaking:
- Full cterm support.
- Standard vim highlight groups such |DiagnosticUnderlineError| are now the
defaults.
- Highlight groups named consistently.
- All `highlight_xxx` e.g. |nvim-tree.renderer.highlight_git| are granular,
allowing `"none"`, `"icon"`, `"name"` or `"all"`
- `highlight_xxx` has highlight groups for both File and Folder
- `highlight_xxx` is additive instead of overwriting. See
|nvim-tree-opts-renderer| for precedence.
Legacy highlight group are still obeyed when they are defined and the current
highlight group is not, hard linking as follows: >
NvimTreeModifiedIcon NvimTreeModifiedFile
NvimTreeOpenedHL NvimTreeOpenedFile
NvimTreeBookmarkIcon NvimTreeBookmark
NvimTreeGitDeletedIcon NvimTreeGitDeleted
NvimTreeGitDirtyIcon NvimTreeGitDirty
NvimTreeGitIgnoredIcon NvimTreeGitIgnored
NvimTreeGitMergeIcon NvimTreeGitMerge
NvimTreeGitNewIcon NvimTreeGitNew
NvimTreeGitRenamedIcon NvimTreeGitRenamed
NvimTreeGitStagedIcon NvimTreeGitStaged
NvimTreeGitFileDeletedHL NvimTreeFileDeleted
NvimTreeGitFileDirtyHL NvimTreeFileDirty
NvimTreeGitFileIgnoredHL NvimTreeFileIgnored
NvimTreeGitFileMergeHL NvimTreeFileMerge
NvimTreeGitFileNewHL NvimTreeFileNew
NvimTreeGitFileRenamedHL NvimTreeFileRenamed
NvimTreeGitFileStagedHL NvimTreeFileStaged
NvimTreeGitFolderDeletedHL NvimTreeFolderDeleted
NvimTreeGitFolderDirtyHL NvimTreeFolderDirty
NvimTreeGitFolderIgnoredHL NvimTreeFolderIgnored
NvimTreeGitFolderMergeHL NvimTreeFolderMerge
NvimTreeGitFolderNewHL NvimTreeFolderNew
NvimTreeGitFolderRenamedHL NvimTreeFolderRenamed
NvimTreeGitFolderStagedHL NvimTreeFolderStaged
NvimTreeLspDiagnosticsError NvimTreeDiagnosticErrorIcon
NvimTreeLspDiagnosticsWarning NvimTreeDiagnosticWarnIcon
NvimTreeLspDiagnosticsInformation NvimTreeDiagnosticInfoIcon
NvimTreeLspDiagnosticsHint NvimTreeDiagnosticHintIcon
NvimTreeLspDiagnosticsErrorText NvimTreeDiagnosticErrorFileHL
NvimTreeLspDiagnosticsWarningText NvimTreeDiagnosticWarnFileHL
NvimTreeLspDiagnosticsInformationText NvimTreeDiagnosticInfoFileHL
NvimTreeLspDiagnosticsHintText NvimTreeDiagnosticHintFileHL
NvimTreeLspDiagnosticsErrorFolderText NvimTreeDiagnosticErrorFolderHL
NvimTreeLspDiagnosticsWarningFolderText NvimTreeDiagnosticWarnFolderHL
NvimTreeLspDiagnosticsInformationFolderText NvimTreeDiagnosticInfoFolderHL
NvimTreeLspDiagnosticsHintFolderText NvimTreeDiagnosticHintFolderHL
<
==============================================================================
9. EVENTS *nvim-tree-events*

View File

@ -1,6 +1,6 @@
local lib = require "nvim-tree.lib"
local log = require "nvim-tree.log"
local colors = require "nvim-tree.colors"
local appearance = require "nvim-tree.appearance"
local renderer = require "nvim-tree.renderer"
local view = require "nvim-tree.view"
local commands = require "nvim-tree.commands"
@ -104,12 +104,6 @@ function M.open_on_directory()
actions.root.change_dir.force_dirchange(bufname, true)
end
function M.reset_highlight()
colors.setup()
view.reset_winhl()
renderer.render_hl(view.get_bufnr())
end
function M.place_cursor_on_node()
local search = vim.fn.searchcount()
if search and search.exact_match == 1 then
@ -168,8 +162,13 @@ local function setup_autocommands(opts)
vim.api.nvim_create_autocmd(name, vim.tbl_extend("force", default_opts, custom_opts))
end
-- reset highlights when colorscheme is changed
create_nvim_tree_autocmd("ColorScheme", { callback = M.reset_highlight })
-- reset and draw highlights when colorscheme is changed
create_nvim_tree_autocmd("ColorScheme", {
callback = function()
appearance.setup()
renderer.render_hl(view.get_bufnr())
end,
})
-- prevent new opened file from opening in the same window as nvim-tree
create_nvim_tree_autocmd("BufWipeout", {
@ -210,7 +209,7 @@ local function setup_autocommands(opts)
-- update opened file buffers
if (filters.config.filter_no_buffer or renderer.config.highlight_opened_files ~= "none") and vim.bo[data.buf].buftype == "" then
utils.debounce("Buf:filter_buffer", opts.view.debounce_delay, function()
actions.reloaders.reload_explorer(nil, data.buf)
actions.reloaders.reload_explorer()
end)
end
end,
@ -386,8 +385,8 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
indent_width = 2,
special_files = { "Cargo.toml", "Makefile", "README.md", "readme.md" },
symlink_destination = true,
highlight_git = false,
highlight_diagnostics = false,
highlight_git = "none",
highlight_diagnostics = "none",
highlight_opened_files = "none",
highlight_modified = "none",
highlight_bookmarks = "none",
@ -641,9 +640,11 @@ local ACCEPTED_STRINGS = {
signcolumn = { "yes", "no", "auto" },
},
renderer = {
highlight_git = { "none", "icon", "name", "all" },
highlight_opened_files = { "none", "icon", "name", "all" },
highlight_modified = { "none", "icon", "name", "all" },
highlight_bookmarks = { "none", "icon", "name", "all" },
highlight_diagnostics = { "none", "icon", "name", "all" },
highlight_clipboard = { "none", "icon", "name", "all" },
icons = {
git_placement = { "before", "after", "signcolumn" },
@ -786,7 +787,7 @@ function M.setup(conf)
require("nvim-tree.actions").setup(opts)
require("nvim-tree.keymap").setup(opts)
require("nvim-tree.colors").setup()
require("nvim-tree.appearance").setup()
require("nvim-tree.diagnostics").setup(opts)
require("nvim-tree.explorer").setup(opts)
require("nvim-tree.git").setup(opts)
@ -796,7 +797,7 @@ function M.setup(conf)
require("nvim-tree.renderer").setup(opts)
require("nvim-tree.live-filter").setup(opts)
require("nvim-tree.marks").setup(opts)
require("nvim-tree.modified").setup(opts)
require("nvim-tree.buffers").setup(opts)
require("nvim-tree.help").setup(opts)
require("nvim-tree.watcher").setup(opts)
if M.config.renderer.icons.show.file and pcall(require, "nvim-web-devicons") then

View File

@ -7,8 +7,6 @@ local notify = require "nvim-tree.notify"
local renderer = require "nvim-tree.renderer"
local reloaders = require "nvim-tree.actions.reloaders"
local HL_POSITION = require("nvim-tree.enum").HL_POSITION
local find_file = require("nvim-tree.actions.finders.find-file").fn
local M = {
@ -317,34 +315,23 @@ function M.copy_absolute_path(node)
copy_to_clipboard(content)
end
--- Clipboard text highlight group and position when highlight_clipboard.
---Node is cut. Will not be copied.
---@param node Node
---@return HL_POSITION position none when clipboard empty
---@return string|nil group only when node present in clipboard
function M.get_highlight(node)
if M.hl_pos == HL_POSITION.none then
return HL_POSITION.none, nil
---@return boolean
function M.is_cut(node)
return vim.tbl_contains(clipboard.cut, node)
end
for _, n in ipairs(clipboard.cut) do
if node == n then
return M.hl_pos, "NvimTreeCutHL"
end
end
for _, n in ipairs(clipboard.copy) do
if node == n then
return M.hl_pos, "NvimTreeCopiedHL"
end
end
return HL_POSITION.none, nil
---Node is copied. Will not be cut.
---@param node Node
---@return boolean
function M.is_copied(node)
return vim.tbl_contains(clipboard.copy, node)
end
function M.setup(opts)
M.config.filesystem_watchers = opts.filesystem_watchers
M.config.actions = opts.actions
M.hl_pos = HL_POSITION[opts.renderer.highlight_clipboard] or HL_POSITION.none
end
return M

View File

@ -10,13 +10,12 @@ local M = {}
---@param node Explorer|nil
---@param projects table
---@param unloaded_bufnr number|nil
local function refresh_nodes(node, projects, unloaded_bufnr)
local function refresh_nodes(node, projects)
Iterator.builder({ node })
:applier(function(n)
if n.nodes then
local toplevel = git.get_toplevel(n.cwd or n.link_to or n.absolute_path)
explorer_module.reload(n, projects[toplevel] or {}, unloaded_bufnr)
explorer_module.reload(n, projects[toplevel] or {})
end
end)
:recursor(function(n)
@ -43,18 +42,16 @@ function M.reload_node_status(parent_node, projects)
end
local event_running = false
---@param _ table|nil unused node passed by action
---@param unloaded_bufnr number|nil optional bufnr recently unloaded via BufUnload event
function M.reload_explorer(_, unloaded_bufnr)
function M.reload_explorer()
if event_running or not core.get_explorer() or vim.v.exiting ~= vim.NIL then
return
end
event_running = true
local projects = git.reload()
refresh_nodes(core.get_explorer(), projects, unloaded_bufnr)
refresh_nodes(core.get_explorer(), projects)
if view.is_visible() then
renderer.draw(unloaded_bufnr)
renderer.draw()
end
event_running = false
end

View File

@ -0,0 +1,217 @@
local M = {
-- namespace for all tree window highlights
NS_ID = vim.api.nvim_create_namespace "nvim_tree",
}
-- directly defined groups, please keep these to an absolute minimum
local DEFAULT_DEFS = {
NvimTreeFolderIcon = "guifg=#8094b4 ctermfg=Blue",
NvimTreeWindowPicker = "guifg=#ededed guibg=#4493c8 gui=bold ctermfg=White ctermbg=Cyan",
}
-- nvim-tree default highlight group links, please attempt to keep in order with help
local DEFAULT_LINKS = {
-- Standard
NvimTreeNormal = "Normal",
NvimTreeNormalFloat = "NormalFloat",
NvimTreeNormalNC = "NvimTreeNormal",
NvimTreeLineNr = "LineNr",
NvimTreeWinSeparator = "WinSeparator",
NvimTreeEndOfBuffer = "EndOfBuffer",
NvimTreePopup = "Normal",
NvimTreeSignColumn = "NvimTreeNormal",
NvimTreeCursorColumn = "CursorColumn",
NvimTreeCursorLine = "CursorLine",
NvimTreeCursorLineNr = "CursorLineNr",
NvimTreeStatusLine = "StatusLine",
NvimTreeStatusLineNC = "StatusLineNC",
-- File Text
NvimTreeExecFile = "Constant",
NvimTreeImageFile = "PreProc",
NvimTreeOpenedFile = "Constant",
NvimTreeSpecialFile = "PreProc",
NvimTreeSymlink = "Statement",
-- Folder Text
NvimTreeRootFolder = "PreProc",
NvimTreeFolderName = "Directory",
NvimTreeEmptyFolderName = "Directory",
NvimTreeOpenedFolderName = "Directory",
NvimTreeSymlinkFolderName = "Directory",
-- File Icons
NvimTreeFileIcon = "NvimTreeNormal",
NvimTreeSymlinkIcon = "NvimTreeNormal",
NvimTreeOpenedFileIcon = "NvimTreeOpenedFile",
-- Folder Icons
NvimTreeOpenedFolderIcon = "NvimTreeFolderIcon",
NvimTreeClosedFolderIcon = "NvimTreeFolderIcon",
NvimTreeFolderArrowClosed = "NvimTreeIndentMarker",
NvimTreeFolderArrowOpen = "NvimTreeIndentMarker",
-- Indent
NvimTreeIndentMarker = "NvimTreeFileIcon",
-- LiveFilter
NvimTreeLiveFilterPrefix = "PreProc",
NvimTreeLiveFilterValue = "ModeMsg",
-- Clipboard
NvimTreeCutHL = "SpellBad",
NvimTreeCopiedHL = "SpellRare",
-- Bookmark
NvimTreeBookmarkIcon = "Constant",
NvimTreeBookmarkHL = "SpellLocal",
-- Modified
NvimTreeModifiedIcon = "Constant",
NvimTreeModifiedFileHL = "NvimTreeModifiedIcon",
NvimTreeModifiedFolderHL = "NvimTreeModifiedFileHL",
-- Opened
NvimTreeOpenedHL = "Constant",
-- Git Icon
NvimTreeGitDeletedIcon = "Statement",
NvimTreeGitDirtyIcon = "Statement",
NvimTreeGitIgnoredIcon = "Comment",
NvimTreeGitMergeIcon = "Constant",
NvimTreeGitNewIcon = "PreProc",
NvimTreeGitRenamedIcon = "PreProc",
NvimTreeGitStagedIcon = "Constant",
-- Git File Highlight
NvimTreeGitFileDeletedHL = "NvimTreeGitDeletedIcon",
NvimTreeGitFileDirtyHL = "NvimTreeGitDirtyIcon",
NvimTreeGitFileIgnoredHL = "NvimTreeGitIgnoredIcon",
NvimTreeGitFileMergeHL = "NvimTreeGitMergeIcon",
NvimTreeGitFileNewHL = "NvimTreeGitNewIcon",
NvimTreeGitFileRenamedHL = "NvimTreeGitRenamedIcon",
NvimTreeGitFileStagedHL = "NvimTreeGitStagedIcon",
-- Git Folder Highlight
NvimTreeGitFolderDeletedHL = "NvimTreeGitFileDeletedHL",
NvimTreeGitFolderDirtyHL = "NvimTreeGitFileDirtyHL",
NvimTreeGitFolderIgnoredHL = "NvimTreeGitFileIgnoredHL",
NvimTreeGitFolderMergeHL = "NvimTreeGitFileMergeHL",
NvimTreeGitFolderNewHL = "NvimTreeGitFileNewHL",
NvimTreeGitFolderRenamedHL = "NvimTreeGitFileRenamedHL",
NvimTreeGitFolderStagedHL = "NvimTreeGitFileStagedHL",
-- Diagnostics Icon
NvimTreeDiagnosticErrorIcon = "DiagnosticError",
NvimTreeDiagnosticWarnIcon = "DiagnosticWarn",
NvimTreeDiagnosticInfoIcon = "DiagnosticInfo",
NvimTreeDiagnosticHintIcon = "DiagnosticHint",
-- Diagnostics File Highlight
NvimTreeDiagnosticErrorFileHL = "DiagnosticUnderlineError",
NvimTreeDiagnosticWarnFileHL = "DiagnosticUnderlineWarn",
NvimTreeDiagnosticInfoFileHL = "DiagnosticUnderlineInfo",
NvimTreeDiagnosticHintFileHL = "DiagnosticUnderlineHint",
-- Diagnostics Folder Highlight
NvimTreeDiagnosticErrorFolderHL = "NvimTreeDiagnosticErrorFileHL",
NvimTreeDiagnosticWarnFolderHL = "NvimTreeDiagnosticWarnFileHL",
NvimTreeDiagnosticInfoFolderHL = "NvimTreeDiagnosticInfoFileHL",
NvimTreeDiagnosticHintFolderHL = "NvimTreeDiagnosticHintFileHL",
}
-- namespace standard links
local NS_LINKS = {
EndOfBuffer = "NvimTreeEndOfBuffer",
CursorLine = "NvimTreeCursorLine",
CursorLineNr = "NvimTreeCursorLineNr",
LineNr = "NvimTreeLineNr",
WinSeparator = "NvimTreeWinSeparator",
StatusLine = "NvimTreeStatusLine",
StatusLineNC = "NvimTreeStatuslineNC",
SignColumn = "NvimTreeSignColumn",
Normal = "NvimTreeNormal",
NormalNC = "NvimTreeNormalNC",
NormalFloat = "NvimTreeNormalFloat",
}
-- nvim-tree highlight groups to legacy
local LEGACY_LINKS = {
NvimTreeModifiedIcon = "NvimTreeModifiedFile",
NvimTreeOpenedHL = "NvimTreeOpenedFile",
NvimTreeBookmarkIcon = "NvimTreeBookmark",
NvimTreeGitDeletedIcon = "NvimTreeGitDeleted",
NvimTreeGitDirtyIcon = "NvimTreeGitDirty",
NvimTreeGitIgnoredIcon = "NvimTreeGitIgnored",
NvimTreeGitMergeIcon = "NvimTreeGitMerge",
NvimTreeGitNewIcon = "NvimTreeGitNew",
NvimTreeGitRenamedIcon = "NvimTreeGitRenamed",
NvimTreeGitStagedIcon = "NvimTreeGitStaged",
NvimTreeGitFileDeletedHL = "NvimTreeFileDeleted",
NvimTreeGitFileDirtyHL = "NvimTreeFileDirty",
NvimTreeGitFileIgnoredHL = "NvimTreeFileIgnored",
NvimTreeGitFileMergeHL = "NvimTreeFileMerge",
NvimTreeGitFileNewHL = "NvimTreeFileNew",
NvimTreeGitFileRenamedHL = "NvimTreeFileRenamed",
NvimTreeGitFileStagedHL = "NvimTreeFileStaged",
NvimTreeGitFolderDeletedHL = "NvimTreeFolderDeleted",
NvimTreeGitFolderDirtyHL = "NvimTreeFolderDirty",
NvimTreeGitFolderIgnoredHL = "NvimTreeFolderIgnored",
NvimTreeGitFolderMergeHL = "NvimTreeFolderMerge",
NvimTreeGitFolderNewHL = "NvimTreeFolderNew",
NvimTreeGitFolderRenamedHL = "NvimTreeFolderRenamed",
NvimTreeGitFolderStagedHL = "NvimTreeFolderStaged",
NvimTreeDiagnosticErrorIcon = "NvimTreeLspDiagnosticsError",
NvimTreeDiagnosticWarnIcon = "NvimTreeLspDiagnosticsWarning",
NvimTreeDiagnosticInfoIcon = "NvimTreeLspDiagnosticsInformation",
NvimTreeDiagnosticHintIcon = "NvimTreeLspDiagnosticsHint",
NvimTreeDiagnosticErrorFileHL = "NvimTreeLspDiagnosticsErrorText",
NvimTreeDiagnosticWarnFileHL = "NvimTreeLspDiagnosticsWarningText",
NvimTreeDiagnosticInfoFileHL = "NvimTreeLspDiagnosticsInformationText",
NvimTreeDiagnosticHintFileHL = "NvimTreeLspDiagnosticsHintText",
NvimTreeDiagnosticErrorFolderHL = "NvimTreeLspDiagnosticsErrorFolderText",
NvimTreeDiagnosticWarnFolderHL = "NvimTreeLspDiagnosticsWarningFolderText",
NvimTreeDiagnosticInfoFolderHL = "NvimTreeLspDiagnosticsInformationFolderText",
NvimTreeDiagnosticHintFolderHL = "NvimTreeLspDiagnosticsHintFolderText",
}
function M.setup()
-- non-linked
for k, d in pairs(DEFAULT_DEFS) do
vim.api.nvim_command("hi " .. k .. " " .. d)
end
-- hard link override when legacy only is present
for from, to in pairs(LEGACY_LINKS) do
local hl_from = vim.api.nvim_get_hl(0, { name = from })
local hl_to = vim.api.nvim_get_hl(0, { name = to })
if vim.tbl_isempty(hl_from) and not vim.tbl_isempty(hl_to) then
vim.api.nvim_command("hi link " .. from .. " " .. to)
end
end
-- default links
for from, to in pairs(DEFAULT_LINKS) do
vim.api.nvim_command("hi def link " .. from .. " " .. to)
end
-- window standard; this doesn't appear to clear on ColorScheme however we err on the side of caution
for from, to in pairs(NS_LINKS) do
vim.api.nvim_set_hl(M.NS_ID, from, { link = to })
end
end
return M

50
lua/nvim-tree/buffers.lua Normal file
View File

@ -0,0 +1,50 @@
local M = {}
---@type table<string, boolean> record of which file is modified
M._modified = {}
---refresh M._modified
function M.reload_modified()
M._modified = {}
local bufs = vim.fn.getbufinfo { bufmodified = true, buflisted = true }
for _, buf in pairs(bufs) do
local path = buf.name
if path ~= "" then -- not a [No Name] buffer
-- mark all the parent as modified as well
while
M._modified[path] ~= true
-- no need to keep going if already recorded
-- This also prevents an infinite loop
do
M._modified[path] = true
path = vim.fn.fnamemodify(path, ":h")
end
end
end
end
---@param node table
---@return boolean
function M.is_modified(node)
return node
and M.config.modified.enable
and M._modified[node.absolute_path]
and (not node.nodes or M.config.modified.show_on_dirs)
and (not node.open or M.config.modified.show_on_open_dirs)
end
---A buffer exists for the node's absolute path
---@param node table
---@return boolean
function M.is_opened(node)
return node and vim.fn.bufloaded(node.absolute_path) > 0
end
---@param opts table
function M.setup(opts)
M.config = {
modified = opts.modified,
}
end
return M

View File

@ -1,130 +0,0 @@
local M = {}
local function get_color_from_hl(hl_name, fallback)
local id = vim.api.nvim_get_hl_id_by_name(hl_name)
if not id then
return fallback
end
local foreground = vim.fn.synIDattr(vim.fn.synIDtrans(id), "fg")
if not foreground or foreground == "" then
return fallback
end
return foreground
end
local function get_colors()
return {
red = vim.g.terminal_color_1 or get_color_from_hl("Keyword", "Red"),
green = vim.g.terminal_color_2 or get_color_from_hl("Character", "Green"),
yellow = vim.g.terminal_color_3 or get_color_from_hl("PreProc", "Yellow"),
blue = vim.g.terminal_color_4 or get_color_from_hl("Include", "Blue"),
purple = vim.g.terminal_color_5 or get_color_from_hl("Define", "Purple"),
cyan = vim.g.terminal_color_6 or get_color_from_hl("Conditional", "Cyan"),
dark_red = vim.g.terminal_color_9 or get_color_from_hl("Keyword", "DarkRed"),
orange = vim.g.terminal_color_11 or get_color_from_hl("Number", "Orange"),
}
end
local function get_hl_groups()
local colors = get_colors()
return {
IndentMarker = { fg = "#8094b4" },
Symlink = { gui = "bold", fg = colors.cyan },
FolderIcon = { fg = "#8094b4" },
RootFolder = { fg = colors.purple },
ExecFile = { gui = "bold", fg = colors.green },
SpecialFile = { gui = "bold,underline", fg = colors.yellow },
ImageFile = { gui = "bold", fg = colors.purple },
OpenedFile = { gui = "bold", fg = colors.green },
ModifiedFile = { fg = colors.green },
GitDirty = { fg = colors.dark_red },
GitDeleted = { fg = colors.dark_red },
GitStaged = { fg = colors.green },
GitMerge = { fg = colors.orange },
GitRenamed = { fg = colors.purple },
GitNew = { fg = colors.yellow },
WindowPicker = { gui = "bold", fg = "#ededed", bg = "#4493c8" },
LiveFilterPrefix = { gui = "bold", fg = colors.purple },
LiveFilterValue = { gui = "bold", fg = "#fff" },
Bookmark = { fg = colors.green },
}
end
local function get_links()
return {
FolderName = "Directory",
EmptyFolderName = "Directory",
OpenedFolderName = "Directory",
SymlinkFolderName = "Directory",
OpenedFolderIcon = "NvimTreeFolderIcon",
ClosedFolderIcon = "NvimTreeFolderIcon",
OpenedFileIcon = "NvimTreeOpenedFile",
Normal = "Normal",
NormalFloat = "NormalFloat",
NormalNC = "NvimTreeNormal",
EndOfBuffer = "EndOfBuffer",
CursorLineNr = "CursorLineNr",
LineNr = "LineNr",
CursorLine = "CursorLine",
WinSeparator = "WinSeparator",
CursorColumn = "CursorColumn",
FileDirty = "NvimTreeGitDirty",
FileNew = "NvimTreeGitNew",
FileRenamed = "NvimTreeGitRenamed",
FileMerge = "NvimTreeGitMerge",
FileStaged = "NvimTreeGitStaged",
FileDeleted = "NvimTreeGitDeleted",
FileIgnored = "NvimTreeGitIgnored",
FolderDirty = "NvimTreeFileDirty",
FolderNew = "NvimTreeFileNew",
FolderRenamed = "NvimTreeFileRenamed",
FolderMerge = "NvimTreeFileMerge",
FolderStaged = "NvimTreeFileStaged",
FolderDeleted = "NvimTreeFileDeleted",
FolderIgnored = "NvimTreeFileIgnored",
LspDiagnosticsError = "DiagnosticError",
LspDiagnosticsWarning = "DiagnosticWarn",
LspDiagnosticsInformation = "DiagnosticInfo",
LspDiagnosticsHint = "DiagnosticHint",
LspDiagnosticsErrorText = "NvimTreeLspDiagnosticsError",
LspDiagnosticsWarningText = "NvimTreeLspDiagnosticsWarning",
LspDiagnosticsInformationText = "NvimTreeLspDiagnosticsInformation",
LspDiagnosticsHintText = "NvimTreeLspDiagnosticsHintFile",
LspDiagnosticsErrorFolderText = "NvimTreeLspDiagnosticsErrorText",
LspDiagnosticsWarningFolderText = "NvimTreeLspDiagnosticsWarningText",
LspDiagnosticsInformationFolderText = "NvimTreeLspDiagnosticsInformationText",
LspDiagnosticsHintFolderText = "NvimTreeLspDiagnosticsHintFileText",
Popup = "Normal",
GitIgnored = "Comment",
StatusLine = "StatusLine",
StatusLineNC = "StatusLineNC",
SignColumn = "NvimTreeNormal",
CutHL = "SpellBad",
CopiedHL = "SpellRare",
BookmarkHL = "SpellLocal",
}
end
function M.setup()
local highlight_groups = get_hl_groups()
for k, d in pairs(highlight_groups) do
local gui = d.gui and " gui=" .. d.gui or ""
local fg = d.fg and " guifg=" .. d.fg or ""
local bg = d.bg and " guibg=" .. d.bg or ""
vim.api.nvim_command("hi def NvimTree" .. k .. gui .. fg .. bg)
end
local links = get_links()
for k, d in pairs(links) do
vim.api.nvim_command("hi def link NvimTree" .. k .. " " .. d)
end
end
return M

View File

@ -12,9 +12,10 @@ M.HL_POSITION = {
---Setup options for "*_placement"
---@enum ICON_PLACEMENT
M.ICON_PLACEMENT = {
signcolumn = 0,
before = 1,
after = 2,
none = 0,
signcolumn = 1,
before = 2,
after = 3,
}
return M

View File

@ -47,16 +47,15 @@ end
---Check if the given path has no listed buffer
---@param path string Absolute path
---@param bufinfo table vim.fn.getbufinfo { buflisted = 1 }
---@param unloaded_bufnr number optional bufnr recently unloaded via BufUnload event
---@return boolean
local function buf(path, bufinfo, unloaded_bufnr)
local function buf(path, bufinfo)
if not M.config.filter_no_buffer or type(bufinfo) ~= "table" then
return false
end
-- filter files with no open buffer and directories containing no open buffers
for _, b in ipairs(bufinfo) do
if b.name == path or b.name:find(path .. "/", 1, true) and b.bufnr ~= unloaded_bufnr then
if b.name == path or b.name:find(path .. "/", 1, true) then
return false
end
end
@ -105,16 +104,13 @@ end
---Prepare arguments for should_filter. This is done prior to should_filter for efficiency reasons.
---@param git_status table|nil optional results of git.load_project_status(...)
---@param unloaded_bufnr number|nil optional bufnr recently unloaded via BufUnload event
---@return table
--- git_status: reference
--- unloaded_bufnr: copy
--- bufinfo: empty unless no_buffer set: vim.fn.getbufinfo { buflisted = 1 }
--- bookmarks: absolute paths to boolean
function M.prepare(git_status, unloaded_bufnr)
function M.prepare(git_status)
local status = {
git_status = git_status or {},
unloaded_bufnr = unloaded_bufnr,
bufinfo = {},
bookmarks = {},
}
@ -140,11 +136,7 @@ function M.should_filter(path, status)
return false
end
return git(path, status.git_status)
or buf(path, status.bufinfo, status.unloaded_bufnr)
or dotfile(path)
or custom(path)
or bookmark(path, status.bookmarks)
return git(path, status.git_status) or buf(path, status.bufinfo) or dotfile(path) or custom(path) or bookmark(path, status.bookmarks)
end
function M.setup(opts)

View File

@ -69,8 +69,7 @@ end
---@param node Node
---@param git_status table
---@param unloaded_bufnr number|nil
function M.reload(node, git_status, unloaded_bufnr)
function M.reload(node, git_status)
local cwd = node.link_to or node.absolute_path
local handle = vim.loop.fs_scandir(cwd)
if not handle then
@ -79,7 +78,7 @@ function M.reload(node, git_status, unloaded_bufnr)
local profile = log.profile_start("reload %s", node.absolute_path)
local filter_status = filters.prepare(git_status, unloaded_bufnr)
local filter_status = filters.prepare(git_status)
if node.group_next then
node.nodes = { node.group_next }

View File

@ -41,6 +41,16 @@ local function refactored(opts)
-- 2023/08/26
utils.move_missing_val(opts, "renderer.icons", "webdev_colors", opts, "renderer.icons.web_devicons.file", "color", true)
-- 2023/10/08
if type(opts.renderer) == "table" and type(opts.renderer.highlight_diagnostics) == "boolean" then
opts.renderer.highlight_diagnostics = opts.renderer.highlight_diagnostics and "name" or "none"
end
-- 2023/10/21
if type(opts.renderer) == "table" and type(opts.renderer.highlight_git) == "boolean" then
opts.renderer.highlight_git = opts.renderer.highlight_git and "name" or "none"
end
end
local function deprecated(opts)

View File

@ -45,7 +45,7 @@ end
---@param node Node|MinimalNode
---@return table|nil
function M.get_mark(node)
return NvimTreeMarks[node.absolute_path]
return node and NvimTreeMarks[node.absolute_path]
end
---@return table

View File

@ -26,7 +26,8 @@ end
---@param node Node
---@return boolean
function M.is_modified(node)
return M.config.enable
return node
and M.config.enable
and M._record[node.absolute_path]
and (not node.nodes or M.config.show_on_dirs)
and (not node.open or M.config.show_on_open_dirs)

View File

@ -1,136 +1,94 @@
local utils = require "nvim-tree.utils"
local appearance = require "nvim-tree.appearance"
local core = require "nvim-tree.core"
local live_filter = require "nvim-tree.live-filter"
local notify = require "nvim-tree.notify"
local utils = require "nvim-tree.utils"
local view = require "nvim-tree.view"
local log = require "nvim-tree.log"
local DecoratorBookmarks = require "nvim-tree.renderer.decorator.bookmarks"
local DecoratorCopied = require "nvim-tree.renderer.decorator.copied"
local DecoratorCut = require "nvim-tree.renderer.decorator.cut"
local DecoratorDiagnostics = require "nvim-tree.renderer.decorator.diagnostics"
local DecoratorGit = require "nvim-tree.renderer.decorator.git"
local DecoratorModified = require "nvim-tree.renderer.decorator.modified"
local DecoratorOpened = require "nvim-tree.renderer.decorator.opened"
local git = require "nvim-tree.renderer.components.git"
local pad = require "nvim-tree.renderer.components.padding"
local icons = require "nvim-tree.renderer.components.icons"
local modified = require "nvim-tree.renderer.components.modified"
local diagnostics = require "nvim-tree.renderer.components.diagnostics"
local bookmarks = require "nvim-tree.renderer.components.bookmarks"
local HL_POSITION = require("nvim-tree.enum").HL_POSITION
local M = {
opts = {},
decorators = {},
picture_map = {
jpg = true,
jpeg = true,
png = true,
gif = true,
webp = true,
jxl = true,
},
}
---@class HighlightedString
---@field str string
---@field hl string[]
---@class AddHighlightArgs
---@field group string[]
---@field line number
---@field col_start number
---@field col_end number
---@class Builder
---@field lines string[] includes icons etc.
---@field hl_args AddHighlightArgs[] line highlights
---@field signs string[] line signs
---@field private root_cwd string absolute path
---@field private index number
---@field private depth number
---@field private combined_groups string[] combined group names
---@field private markers boolean[] indent markers
local Builder = {}
Builder.__index = Builder
local DEFAULT_ROOT_FOLDER_LABEL = ":~:s?$?/..?"
function Builder.new(root_cwd)
return setmetatable({
---@return Builder
function Builder:new()
local o = {
root_cwd = core.get_cwd(),
index = 0,
depth = 0,
highlights = {},
hl_args = {},
combined_groups = {},
lines = {},
markers = {},
signs = {},
root_cwd = root_cwd,
}, Builder)
}
setmetatable(o, self)
self.__index = self
return o
end
function Builder:configure_root_label(root_folder_label)
self.root_folder_label = root_folder_label or DEFAULT_ROOT_FOLDER_LABEL
return self
---Insert ranged highlight groups into self.highlights
---@private
---@param groups string[]
---@param start number
---@param end_ number|nil
function Builder:insert_highlight(groups, start, end_)
table.insert(self.hl_args, { groups, self.index, start, end_ or -1 })
end
function Builder:configure_trailing_slash(with_trailing)
self.trailing_slash = with_trailing and "/" or ""
return self
end
function Builder:configure_special_files(special_files)
self.special_files = special_files
return self
end
function Builder:configure_picture_map(picture_map)
self.picture_map = picture_map
return self
end
function Builder:configure_filter(filter, prefix)
self.filter_prefix = prefix
self.filter = filter
return self
end
function Builder:configure_opened_file_highlighting(highlight_opened_files)
self.highlight_opened_files = highlight_opened_files
return self
end
function Builder:configure_modified_highlighting(highlight_modified)
self.highlight_modified = highlight_modified
return self
end
function Builder:configure_icon_padding(padding)
self.icon_padding = padding or " "
return self
end
function Builder:configure_git_icons_placement(where)
if where ~= "after" and where ~= "before" and where ~= "signcolumn" then
where = "before" -- default before
end
self.git_placement = where
return self
end
function Builder:configure_diagnostics_icon_placement(where)
if where ~= "after" and where ~= "before" and where ~= "signcolumn" then
where = "before" -- default before
end
self.diagnostics_placement = where
return self
end
function Builder:configure_bookmark_icon_placement(where)
if where ~= "after" and where ~= "before" and where ~= "signcolumn" then
where = "before" -- default before
end
self.bookmarks_placement = where
return self
end
function Builder:configure_modified_placement(where)
if where ~= "after" and where ~= "before" and where ~= "signcolumn" then
where = "after" -- default after
end
self.modified_placement = where
return self
end
function Builder:configure_symlink_destination(show)
self.symlink_destination = show
return self
end
function Builder:configure_group_name_modifier(group_name_modifier)
if type(group_name_modifier) == "function" then
self.group_name_modifier = group_name_modifier
end
return self
end
function Builder:_insert_highlight(group, start, end_)
table.insert(self.highlights, { group, self.index, start, end_ or -1 })
end
function Builder:_insert_line(line)
table.insert(self.lines, line)
end
function Builder:_get_folder_name(node)
---@private
function Builder:get_folder_name(node)
local name = node.name
local next = node.group_next
while next do
name = name .. "/" .. next.name
name = string.format("%s/%s", name, next.name)
next = next.group_next
end
if node.group_next and self.group_name_modifier then
local new_name = self.group_name_modifier(name)
if node.group_next and type(M.opts.renderer.group_empty) == "function" then
local new_name = M.opts.renderer.group_empty(name)
if type(new_name) == "string" then
name = new_name
else
@ -138,16 +96,13 @@ function Builder:_get_folder_name(node)
end
end
return name .. self.trailing_slash
return string.format("%s%s", name, M.opts.renderer.add_trailing and "/" or "")
end
---@class HighlightedString
---@field str string
---@field hl string[]
---@private
---@param highlighted_strings HighlightedString[]
---@return string
function Builder:_unwrap_highlighted_strings(highlighted_strings)
function Builder:unwrap_highlighted_strings(highlighted_strings)
if not highlighted_strings then
return ""
end
@ -156,21 +111,22 @@ function Builder:_unwrap_highlighted_strings(highlighted_strings)
for _, v in ipairs(highlighted_strings) do
if #v.str > 0 then
if v.hl and type(v.hl) == "table" then
self:_insert_highlight(v.hl, #string, #string + #v.str)
self:insert_highlight(v.hl, #string, #string + #v.str)
end
string = string .. v.str
string = string.format("%s%s", string, v.str)
end
end
return string
end
---@private
---@param node table
---@return HighlightedString icon
---@return HighlightedString name
function Builder:_build_folder(node)
function Builder:build_folder(node)
local has_children = #node.nodes ~= 0 or node.has_children
local icon, icon_hl = icons.get_folder_icon(node, has_children)
local foldername = self:_get_folder_name(node)
local foldername = self:get_folder_name(node)
if #icon > 0 and icon_hl == nil then
if node.open then
@ -181,12 +137,14 @@ function Builder:_build_folder(node)
end
local foldername_hl = "NvimTreeFolderName"
if node.link_to and self.symlink_destination then
if node.link_to and M.opts.renderer.symlink_destination then
local arrow = icons.i.symlink_arrow
local link_to = utils.path_relative(node.link_to, core.get_cwd())
foldername = foldername .. arrow .. link_to
local link_to = utils.path_relative(node.link_to, self.root_cwd)
foldername = string.format("%s%s%s", foldername, arrow, link_to)
foldername_hl = "NvimTreeSymlinkFolderName"
elseif vim.tbl_contains(self.special_files, node.absolute_path) or vim.tbl_contains(self.special_files, node.name) then
elseif
vim.tbl_contains(M.opts.renderer.special_files, node.absolute_path) or vim.tbl_contains(M.opts.renderer.special_files, node.name)
then
foldername_hl = "NvimTreeSpecialFolderName"
elseif node.open then
foldername_hl = "NvimTreeOpenedFolderName"
@ -197,174 +155,56 @@ function Builder:_build_folder(node)
return { str = icon, hl = { icon_hl } }, { str = foldername, hl = { foldername_hl } }
end
---@private
---@param node table
---@return HighlightedString icon
---@return HighlightedString name
function Builder:_build_symlink(node)
function Builder:build_symlink(node)
local icon = icons.i.symlink
local arrow = icons.i.symlink_arrow
local symlink_formatted = node.name
if self.symlink_destination then
local link_to = utils.path_relative(node.link_to, core.get_cwd())
symlink_formatted = symlink_formatted .. arrow .. link_to
if M.opts.renderer.symlink_destination then
local link_to = utils.path_relative(node.link_to, self.root_cwd)
symlink_formatted = string.format("%s%s%s", symlink_formatted, arrow, link_to)
end
return { str = icon, hl = { "NvimTreeSymlinkIcon" } }, { str = symlink_formatted, hl = { "NvimTreeSymlink" } }
end
---@param node table
---@return HighlightedString icon
function Builder:_build_file_icon(node)
local icon, hl_group = icons.get_file_icon(node.name, node.extension)
return { str = icon, hl = { hl_group } }
end
---@private
---@param node table
---@return HighlightedString icon
---@return HighlightedString name
function Builder:_build_file(node)
local icon = self:_build_file_icon(node)
function Builder:build_file(node)
local hl
if vim.tbl_contains(self.special_files, node.absolute_path) or vim.tbl_contains(self.special_files, node.name) then
if vim.tbl_contains(M.opts.renderer.special_files, node.absolute_path) or vim.tbl_contains(M.opts.renderer.special_files, node.name) then
hl = "NvimTreeSpecialFile"
elseif node.executable then
hl = "NvimTreeExecFile"
elseif self.picture_map[node.extension] then
elseif M.picture_map[node.extension] then
hl = "NvimTreeImageFile"
end
return icon, { str = node.name, hl = { hl } }
end
---@param node table
---@return HighlightedString[]|nil icon
function Builder:_get_git_icons(node)
local git_icons = git.get_icons(node)
if git_icons and #git_icons > 0 and self.git_placement == "signcolumn" then
table.insert(self.signs, {
sign = git_icons[1].hl[1],
lnum = self.index + 1,
priority = 1,
})
git_icons = nil
end
return git_icons
end
---@param node table
---@return HighlightedString[]|nil icon
function Builder:_get_diagnostics_icon(node)
local diagnostics_icon = diagnostics.get_icon(node)
if diagnostics_icon and self.diagnostics_placement == "signcolumn" then
table.insert(self.signs, {
sign = diagnostics_icon.hl[1],
lnum = self.index + 1,
priority = 2,
})
diagnostics_icon = nil
end
return diagnostics_icon
end
---@param node table
---@return HighlightedString|nil icon
function Builder:_get_modified_icon(node)
local modified_icon = modified.get_icon(node)
if modified_icon and self.modified_placement == "signcolumn" then
table.insert(self.signs, {
sign = modified_icon.hl[1],
lnum = self.index + 1,
priority = 3,
})
modified_icon = nil
end
return modified_icon
end
---@param node table
---@return HighlightedString[]|nil icon
function Builder:_get_bookmark_icon(node)
local bookmark_icon = bookmarks.get_icon(node)
if bookmark_icon and self.bookmarks_placement == "signcolumn" then
table.insert(self.signs, {
sign = bookmark_icon.hl[1],
lnum = self.index + 1,
priority = 4,
})
bookmark_icon = nil
end
return bookmark_icon
end
---@param node table
---@return string|nil icon_hl
---@return string|nil name_hl
function Builder:_get_highlight_override(node, unloaded_bufnr)
local name_hl, icon_hl
-- git
local git_highlight = git.get_highlight(node)
if git_highlight then
name_hl = git_highlight
end
-- opened file
if self.highlight_opened_files and vim.fn.bufloaded(node.absolute_path) > 0 and vim.fn.bufnr(node.absolute_path) ~= unloaded_bufnr then
if self.highlight_opened_files == "all" or self.highlight_opened_files == "name" then
name_hl = "NvimTreeOpenedFile"
end
if self.highlight_opened_files == "all" or self.highlight_opened_files == "icon" then
icon_hl = "NvimTreeOpenedFileIcon"
end
end
-- modified file
local modified_highlight = modified.get_highlight(node)
if modified_highlight then
if self.highlight_modified == "all" or self.highlight_modified == "name" then
name_hl = modified_highlight
end
if self.highlight_modified == "all" or self.highlight_modified == "icon" then
icon_hl = modified_highlight
end
end
return icon_hl, name_hl
end
---Append optional highlighting to icon or name.
---@param node table
---@param get_hl fun(node: table): HL_POSITION, string
---@param icon_hl string[] icons to append to
---@param name_hl string[] names to append to
function Builder:_append_highlight(node, get_hl, icon_hl, name_hl)
local pos, hl = get_hl(node)
if pos ~= HL_POSITION.none and hl then
if pos == HL_POSITION.all or pos == HL_POSITION.icon then
table.insert(icon_hl, hl)
end
if pos == HL_POSITION.all or pos == HL_POSITION.name then
table.insert(name_hl, hl)
end
end
local icon, hl_group = icons.get_file_icon(node.name, node.extension)
return { str = icon, hl = { hl_group } }, { str = node.name, hl = { hl } }
end
---@private
---@param indent_markers HighlightedString[]
---@param arrows HighlightedString[]|nil
---@param icon HighlightedString
---@param name HighlightedString
---@param git_icons HighlightedString[]|nil
---@param diagnostics_icon HighlightedString|nil
---@param modified_icon HighlightedString|nil
---@param bookmark_icon HighlightedString|nil
---@param node table
---@return HighlightedString[]
function Builder:_format_line(indent_markers, arrows, icon, name, git_icons, diagnostics_icon, modified_icon, bookmark_icon)
function Builder:format_line(indent_markers, arrows, icon, name, node)
local added_len = 0
local function add_to_end(t1, t2)
if not t2 then
return
end
for _, v in ipairs(t2) do
if added_len > 0 then
table.insert(t1, { str = self.icon_padding })
table.insert(t1, { str = M.opts.renderer.icons.padding })
end
table.insert(t1, v)
end
@ -379,78 +219,132 @@ function Builder:_format_line(indent_markers, arrows, icon, name, git_icons, dia
local line = { indent_markers, arrows }
add_to_end(line, { icon })
if git_icons and self.git_placement == "before" then
add_to_end(line, git_icons)
end
if modified_icon and self.modified_placement == "before" then
add_to_end(line, { modified_icon })
end
if diagnostics_icon and self.diagnostics_placement == "before" then
add_to_end(line, { diagnostics_icon })
end
if bookmark_icon and self.bookmarks_placement == "before" then
add_to_end(line, { bookmark_icon })
for i = #M.decorators, 1, -1 do
add_to_end(line, M.decorators[i]:icons_before(node))
end
add_to_end(line, { name })
if git_icons and self.git_placement == "after" then
add_to_end(line, git_icons)
end
if modified_icon and self.modified_placement == "after" then
add_to_end(line, { modified_icon })
end
if diagnostics_icon and self.diagnostics_placement == "after" then
add_to_end(line, { diagnostics_icon })
end
if bookmark_icon and self.bookmarks_placement == "after" then
add_to_end(line, { bookmark_icon })
for i = #M.decorators, 1, -1 do
add_to_end(line, M.decorators[i]:icons_after(node))
end
log.line("dev", "line = %s", vim.inspect(line))
return line
end
function Builder:_build_line(node, idx, num_children, unloaded_bufnr)
local copy_paste = require "nvim-tree.actions.fs.copy-paste"
---@private
---@param node Node
function Builder:build_signs(node)
-- first in priority order
local sign_name
for _, d in ipairs(M.decorators) do
sign_name = d:sign_name(node)
if sign_name then
self.signs[self.index] = sign_name
break
end
end
end
---Combined group name less than the 200 byte limit of highlight group names
---@private
---@param groups string[] highlight group names
---@return string name "NvimTreeCombinedHL" .. sha256
function Builder:combined_group_name(groups)
return string.format("NvimTreeCombinedHL%s", vim.fn.sha256(table.concat(groups)))
end
---Create a highlight group for groups with later groups overriding previous.
---@private
---@param groups string[] highlight group names
function Builder:create_combined_group(groups)
local combined_name = self:combined_group_name(groups)
-- only create if necessary
if not vim.tbl_contains(self.combined_groups, combined_name) then
local combined_hl = {}
-- build the highlight, overriding values
for _, group in ipairs(groups) do
local hl = vim.api.nvim_get_hl(0, { name = group, link = false })
combined_hl = vim.tbl_extend("force", combined_hl, hl)
end
-- highlight directly in the namespace
vim.api.nvim_set_hl(appearance.NS_ID, combined_name, combined_hl)
table.insert(self.combined_groups, combined_name)
end
end
---Calculate highlight group for icon and name. A combined highlight group will be created
---when there is more than one highlight.
---A highlight group is always calculated and upserted for the case of highlights changing.
---@private
---@param node Node
---@return string|nil icon_hl_group
---@return string|nil name_hl_group
function Builder:add_highlights(node)
-- result
local icon_hl_group, name_hl_group
-- calculate all groups
local icon_groups = {}
local name_groups = {}
local d, icon, name
for i = #M.decorators, 1, -1 do
d = M.decorators[i]
icon, name = d:groups_icon_name(node)
table.insert(icon_groups, icon)
table.insert(name_groups, name)
end
-- one or many icon groups
if #icon_groups > 1 then
icon_hl_group = self:combined_group_name(icon_groups)
self:create_combined_group(icon_groups)
else
icon_hl_group = icon_groups[1]
end
-- one or many name groups
if #name_groups > 1 then
name_hl_group = self:combined_group_name(name_groups)
self:create_combined_group(name_groups)
else
name_hl_group = name_groups[1]
end
return icon_hl_group, name_hl_group
end
---@private
function Builder:build_line(node, idx, num_children)
-- various components
local indent_markers = pad.get_indent_markers(self.depth, idx, num_children, node, self.markers)
local arrows = pad.get_arrows(node)
-- adds icons to signcolumn
local bookmark_icon = self:_get_bookmark_icon(node)
local git_icons = self:_get_git_icons(node)
local modified_icon = self:_get_modified_icon(node)
local diagnostics_icon = self:_get_diagnostics_icon(node)
-- main components
local is_folder = node.nodes ~= nil
local is_symlink = node.link_to ~= nil
local icon, name
if is_folder then
icon, name = self:_build_folder(node)
icon, name = self:build_folder(node)
elseif is_symlink then
icon, name = self:_build_symlink(node)
icon, name = self:build_symlink(node)
else
icon, name = self:_build_file(node)
icon, name = self:build_file(node)
end
-- highlight override
local icon_hl_override, name_hl_override = self:_get_highlight_override(node, unloaded_bufnr)
if icon_hl_override then
icon.hl = { icon_hl_override }
end
if name_hl_override then
name.hl = { name_hl_override }
end
-- highighting
local icon_hl_group, name_hl_group = self:add_highlights(node)
table.insert(icon.hl, icon_hl_group)
table.insert(name.hl, name_hl_group)
-- extra highighting
self:_append_highlight(node, bookmarks.get_highlight, icon.hl, name.hl)
self:_append_highlight(node, diagnostics.get_highlight, icon.hl, name.hl)
self:_append_highlight(node, copy_paste.get_highlight, icon.hl, name.hl)
local line = self:_format_line(indent_markers, arrows, icon, name, git_icons, diagnostics_icon, modified_icon, bookmark_icon)
self:_insert_line(self:_unwrap_highlighted_strings(line))
local line = self:format_line(indent_markers, arrows, icon, name, node)
table.insert(self.lines, self:unwrap_highlighted_strings(line))
self.index = self.index + 1
@ -458,13 +352,14 @@ function Builder:_build_line(node, idx, num_children, unloaded_bufnr)
if node.open then
self.depth = self.depth + 1
self:build(node, unloaded_bufnr)
self:build_lines(node)
self.depth = self.depth - 1
end
end
function Builder:_get_nodes_number(nodes)
if not self.filter then
---@private
function Builder:get_nodes_number(nodes)
if not live_filter.filter then
return #nodes
end
@ -477,53 +372,77 @@ function Builder:_get_nodes_number(nodes)
return i
end
function Builder:build(tree, unloaded_bufnr)
local num_children = self:_get_nodes_number(tree.nodes)
---@private
function Builder:build_lines(node)
if not node then
node = core.get_explorer()
end
local num_children = self:get_nodes_number(node.nodes)
local idx = 1
for _, node in ipairs(tree.nodes) do
if not node.hidden then
self:_build_line(node, idx, num_children, unloaded_bufnr)
for _, n in ipairs(node.nodes) do
if not n.hidden then
self:build_signs(n)
self:build_line(n, idx, num_children)
idx = idx + 1
end
end
return self
end
local function format_root_name(root_cwd, root_label)
---@private
---@param root_label function|string
---@return string
function Builder:format_root_name(root_label)
if type(root_label) == "function" then
local label = root_label(root_cwd)
local label = root_label(self.root_cwd)
if type(label) == "string" then
return label
else
root_label = DEFAULT_ROOT_FOLDER_LABEL
return "???"
end
end
return utils.path_remove_trailing(vim.fn.fnamemodify(root_cwd, root_label))
return utils.path_remove_trailing(vim.fn.fnamemodify(self.root_cwd, root_label))
end
function Builder:build_header(show_header)
if show_header then
local root_name = format_root_name(self.root_cwd, self.root_folder_label)
self:_insert_line(root_name)
self:_insert_highlight({ "NvimTreeRootFolder" }, 0, string.len(root_name))
---@private
function Builder:build_header()
if view.is_root_folder_visible(core.get_cwd()) then
local root_name = self:format_root_name(M.opts.renderer.root_folder_label)
table.insert(self.lines, root_name)
self:insert_highlight({ "NvimTreeRootFolder" }, 0, string.len(root_name))
self.index = 1
end
if self.filter then
local filter_line = self.filter_prefix .. "/" .. self.filter .. "/"
self:_insert_line(filter_line)
local prefix_length = string.len(self.filter_prefix)
self:_insert_highlight({ "NvimTreeLiveFilterPrefix" }, 0, prefix_length)
self:_insert_highlight({ "NvimTreeLiveFilterValue" }, prefix_length, string.len(filter_line))
if live_filter.filter then
local filter_line = string.format("%s/%s/", M.opts.live_filter.prefix, live_filter.filter)
table.insert(self.lines, filter_line)
local prefix_length = string.len(M.opts.live_filter.prefix)
self:insert_highlight({ "NvimTreeLiveFilterPrefix" }, 0, prefix_length)
self:insert_highlight({ "NvimTreeLiveFilterValue" }, prefix_length, string.len(filter_line))
self.index = self.index + 1
end
end
---Build all lines with highlights and signs
---@return Builder
function Builder:build()
self:build_header()
self:build_lines()
return self
end
function Builder:unwrap()
return self.lines, self.highlights, self.signs
function Builder.setup(opts)
M.opts = opts
-- priority order
M.decorators = {
DecoratorCut:new(opts),
DecoratorCopied:new(opts),
DecoratorDiagnostics:new(opts),
DecoratorBookmarks:new(opts),
DecoratorModified:new(opts),
DecoratorOpened:new(opts),
DecoratorGit:new(opts),
}
end
return Builder

View File

@ -1,51 +0,0 @@
local marks = require "nvim-tree.marks"
local HL_POSITION = require("nvim-tree.enum").HL_POSITION
local M = {
ICON = {},
hl_pos = HL_POSITION.none,
}
---Bookmark highlight group and position when highlight_bookmark.
---@param node table
---@return HL_POSITION position none when clipboard empty
---@return string|nil group only when node present in clipboard
function M.get_highlight(node)
if M.hl_pos == HL_POSITION.none then
return HL_POSITION.none, nil
end
local mark = marks.get_mark(node)
if mark then
return M.hl_pos, "NvimTreeBookmarkHL"
else
return HL_POSITION.none, nil
end
end
---bookmark icon if marked
---@param node table
---@return HighlightedString|nil bookmark icon
function M.get_icon(node)
if M.config.renderer.icons.show.bookmarks and marks.get_mark(node) then
return M.ICON
end
end
function M.setup(opts)
M.config = {
renderer = opts.renderer,
}
M.hl_pos = HL_POSITION[opts.renderer.highlight_bookmarks] or HL_POSITION.none
M.ICON = {
str = opts.renderer.icons.glyphs.bookmark,
hl = { "NvimTreeBookmark" },
}
vim.fn.sign_define(M.ICON.hl[1], { text = M.ICON.str, texthl = M.ICON.hl[1] })
end
return M

View File

@ -2,18 +2,23 @@ local HL_POSITION = require("nvim-tree.enum").HL_POSITION
local diagnostics = require "nvim-tree.diagnostics"
local M = {
HS_FILE = {},
HS_FOLDER = {},
ICON = {},
hl_pos = HL_POSITION.none,
-- highlight strings for the icons
HS_ICON = {},
-- highlight groups for HL
HG_FILE = {},
HG_FOLDER = {},
-- position for HL
HL_POS = HL_POSITION.none,
}
---Diagnostics text highlight group when highlight_diagnostics.
---Diagnostics highlight group and position when highlight_diagnostics.
---@param node table
---@return HL_POSITION position none when no status
---@return string|nil group only when status
function M.get_highlight(node)
if not node or M.hl_pos == HL_POSITION.none then
if not node or M.HL_POS == HL_POSITION.none then
return HL_POSITION.none, nil
end
@ -26,7 +31,7 @@ function M.get_highlight(node)
end
if group then
return M.hl_pos, group
return M.HL_POS, group
else
return HL_POSITION.none, nil
end
@ -49,40 +54,38 @@ function M.setup(opts)
}
if opts.diagnostics.enable and opts.renderer.highlight_diagnostics then
-- TODO add a HL_POSITION
-- M.hl_pos = HL_POSITION[opts.renderer.highlight_diagnostics]
M.hl_pos = HL_POSITION.name
M.HL_POS = HL_POSITION[opts.renderer.highlight_diagnostics]
end
M.HS_FILE[vim.diagnostic.severity.ERROR] = "NvimTreeLspDiagnosticsErrorText"
M.HS_FILE[vim.diagnostic.severity.WARN] = "NvimTreeLspDiagnosticsWarningText"
M.HS_FILE[vim.diagnostic.severity.INFO] = "NvimTreeLspDiagnosticsInfoText"
M.HS_FILE[vim.diagnostic.severity.HINT] = "NvimTreeLspDiagnosticsHintText"
M.HG_FILE[vim.diagnostic.severity.ERROR] = "NvimTreeDiagnosticErrorFileHL"
M.HG_FILE[vim.diagnostic.severity.WARN] = "NvimTreeDiagnosticWarningFileHL"
M.HG_FILE[vim.diagnostic.severity.INFO] = "NvimTreeDiagnosticInfoFileHL"
M.HG_FILE[vim.diagnostic.severity.HINT] = "NvimTreeDiagnosticHintFileHL"
M.HS_FOLDER[vim.diagnostic.severity.ERROR] = "NvimTreeLspDiagnosticsErrorFolderText"
M.HS_FOLDER[vim.diagnostic.severity.WARN] = "NvimTreeLspDiagnosticsWarningFolderText"
M.HS_FOLDER[vim.diagnostic.severity.INFO] = "NvimTreeLspDiagnosticsInfoFolderText"
M.HS_FOLDER[vim.diagnostic.severity.HINT] = "NvimTreeLspDiagnosticsHintFolderText"
M.HG_FOLDER[vim.diagnostic.severity.ERROR] = "NvimTreeDiagnosticErrorFolderHL"
M.HG_FOLDER[vim.diagnostic.severity.WARN] = "NvimTreeDiagnosticWarningFolderHL"
M.HG_FOLDER[vim.diagnostic.severity.INFO] = "NvimTreeDiagnosticInfoFolderHL"
M.HG_FOLDER[vim.diagnostic.severity.HINT] = "NvimTreeDiagnosticHintFolderHL"
M.ICON[vim.diagnostic.severity.ERROR] = {
M.HS_ICON[vim.diagnostic.severity.ERROR] = {
str = M.config.diagnostics.icons.error,
hl = { "NvimTreeLspDiagnosticsError" },
hl = { "NvimTreeDiagnosticErrorIcon" },
}
M.ICON[vim.diagnostic.severity.WARN] = {
M.HS_ICON[vim.diagnostic.severity.WARN] = {
str = M.config.diagnostics.icons.warning,
hl = { "NvimTreeLspDiagnosticsWarning" },
hl = { "NvimTreeDiagnosticWarningIcon" },
}
M.ICON[vim.diagnostic.severity.INFO] = {
M.HS_ICON[vim.diagnostic.severity.INFO] = {
str = M.config.diagnostics.icons.info,
hl = { "NvimTreeLspDiagnosticsInformation" },
hl = { "NvimTreeDiagnosticInfoIcon" },
}
M.ICON[vim.diagnostic.severity.HINT] = {
M.HS_ICON[vim.diagnostic.severity.HINT] = {
str = M.config.diagnostics.icons.hint,
hl = { "NvimTreeLspDiagnosticsHint" },
hl = { "NvimTreeDiagnosticHintIcon" },
}
for _, i in ipairs(M.ICON) do
for _, i in ipairs(M.HS_ICON) do
vim.fn.sign_define(i.hl[1], { text = i.str, texthl = i.hl[1] })
end
end

View File

@ -1,3 +1,5 @@
local appearance = require "nvim-tree.appearance"
local M = {}
local utils = require "nvim-tree.utils"
@ -66,13 +68,12 @@ local function show()
style = "minimal",
})
local ns_id = vim.api.nvim_get_namespaces()["NvimTreeHighlights"]
local extmarks = vim.api.nvim_buf_get_extmarks(0, ns_id, { line_nr - 1, 0 }, { line_nr - 1, -1 }, { details = 1 })
local extmarks = vim.api.nvim_buf_get_extmarks(0, appearance.NS_ID, { line_nr - 1, 0 }, { line_nr - 1, -1 }, { details = 1 })
vim.api.nvim_win_call(M.popup_win, function()
vim.api.nvim_buf_set_lines(0, 0, -1, true, { line })
for _, extmark in ipairs(extmarks) do
local hl = extmark[4]
vim.api.nvim_buf_add_highlight(0, ns_id, hl.hl_group, 0, extmark[3], hl.end_col)
vim.api.nvim_buf_add_highlight(0, appearance.NS_ID, hl.hl_group, 0, extmark[3], hl.end_col)
end
vim.cmd [[ setlocal nowrap cursorline noswapfile nobuflisted buftype=nofile bufhidden=hide ]]
end)

View File

@ -1,191 +0,0 @@
local notify = require "nvim-tree.notify"
local explorer_node = require "nvim-tree.explorer.node"
local M = {}
local function build_icons_table(i)
local icons = {
staged = { str = i.staged, hl = { "NvimTreeGitStaged" }, ord = 1 },
unstaged = { str = i.unstaged, hl = { "NvimTreeGitDirty" }, ord = 2 },
renamed = { str = i.renamed, hl = { "NvimTreeGitRenamed" }, ord = 3 },
deleted = { str = i.deleted, hl = { "NvimTreeGitDeleted" }, ord = 4 },
unmerged = { str = i.unmerged, hl = { "NvimTreeGitMerge" }, ord = 5 },
untracked = { str = i.untracked, hl = { "NvimTreeGitNew" }, ord = 6 },
ignored = { str = i.ignored, hl = { "NvimTreeGitIgnored" }, ord = 7 },
}
return {
["M "] = { icons.staged },
[" M"] = { icons.unstaged },
["C "] = { icons.staged },
[" C"] = { icons.unstaged },
["CM"] = { icons.unstaged },
[" T"] = { icons.unstaged },
["T "] = { icons.staged },
["TM"] = { icons.staged, icons.unstaged },
["MM"] = { icons.staged, icons.unstaged },
["MD"] = { icons.staged },
["A "] = { icons.staged },
["AD"] = { icons.staged },
[" A"] = { icons.untracked },
-- not sure about this one
["AA"] = { icons.unmerged, icons.untracked },
["AU"] = { icons.unmerged, icons.untracked },
["AM"] = { icons.staged, icons.unstaged },
["??"] = { icons.untracked },
["R "] = { icons.renamed },
[" R"] = { icons.renamed },
["RM"] = { icons.unstaged, icons.renamed },
["UU"] = { icons.unmerged },
["UD"] = { icons.unmerged },
["UA"] = { icons.unmerged },
[" D"] = { icons.deleted },
["D "] = { icons.deleted },
["DA"] = { icons.unstaged },
["RD"] = { icons.deleted },
["DD"] = { icons.deleted },
["DU"] = { icons.deleted, icons.unmerged },
["!!"] = { icons.ignored },
dirty = { icons.unstaged },
}
end
local function build_hl_table()
local file = {
["M "] = "NvimTreeFileStaged",
["C "] = "NvimTreeFileStaged",
["AA"] = "NvimTreeFileStaged",
["AD"] = "NvimTreeFileStaged",
["MD"] = "NvimTreeFileStaged",
["T "] = "NvimTreeFileStaged",
["TT"] = "NvimTreeFileStaged",
[" M"] = "NvimTreeFileDirty",
["CM"] = "NvimTreeFileDirty",
[" C"] = "NvimTreeFileDirty",
[" T"] = "NvimTreeFileDirty",
["MM"] = "NvimTreeFileDirty",
["AM"] = "NvimTreeFileDirty",
dirty = "NvimTreeFileDirty",
["A "] = "NvimTreeFileStaged",
["??"] = "NvimTreeFileNew",
["AU"] = "NvimTreeFileMerge",
["UU"] = "NvimTreeFileMerge",
["UD"] = "NvimTreeFileMerge",
["DU"] = "NvimTreeFileMerge",
["UA"] = "NvimTreeFileMerge",
[" D"] = "NvimTreeFileDeleted",
["DD"] = "NvimTreeFileDeleted",
["RD"] = "NvimTreeFileDeleted",
["D "] = "NvimTreeFileDeleted",
["R "] = "NvimTreeFileRenamed",
["RM"] = "NvimTreeFileRenamed",
[" R"] = "NvimTreeFileRenamed",
["!!"] = "NvimTreeFileIgnored",
[" A"] = "none",
}
local folder = {}
for k, v in pairs(file) do
folder[k] = v:gsub("File", "Folder")
end
return file, folder
end
local function nil_() end
local function warn_status(git_status)
notify.warn(string.format("Unrecognized git state '%s'", git_status))
end
---@param node table
---@return HighlightedString[]|nil
local function get_icons_(node)
local git_status = explorer_node.get_git_status(node)
if git_status == nil then
return nil
end
local inserted = {}
local iconss = {}
for _, s in pairs(git_status) do
local icons = M.git_icons[s]
if not icons then
if not M.config.highlight_git then
warn_status(s)
end
return nil
end
for _, icon in pairs(icons) do
if #icon.str > 0 then
if not inserted[icon] then
table.insert(iconss, icon)
inserted[icon] = true
end
end
end
end
if #iconss == 0 then
return nil
end
-- sort icons so it looks slightly better
table.sort(iconss, function(a, b)
return a.ord < b.ord
end)
return iconss
end
function M.setup_signs(i)
vim.fn.sign_define("NvimTreeGitDirty", { text = i.unstaged, texthl = "NvimTreeGitDirty" })
vim.fn.sign_define("NvimTreeGitStaged", { text = i.staged, texthl = "NvimTreeGitStaged" })
vim.fn.sign_define("NvimTreeGitMerge", { text = i.unmerged, texthl = "NvimTreeGitMerge" })
vim.fn.sign_define("NvimTreeGitRenamed", { text = i.renamed, texthl = "NvimTreeGitRenamed" })
vim.fn.sign_define("NvimTreeGitNew", { text = i.untracked, texthl = "NvimTreeGitNew" })
vim.fn.sign_define("NvimTreeGitDeleted", { text = i.deleted, texthl = "NvimTreeGitDeleted" })
vim.fn.sign_define("NvimTreeGitIgnored", { text = i.ignored, texthl = "NvimTreeGitIgnored" })
end
local function get_highlight_(node)
local git_status = explorer_node.get_git_status(node)
if git_status == nil then
return
end
if node.nodes then
return M.folder_hl[git_status[1]]
else
return M.file_hl[git_status[1]]
end
end
function M.setup(opts)
M.config = opts.renderer
M.git_icons = build_icons_table(opts.renderer.icons.glyphs.git)
M.file_hl, M.folder_hl = build_hl_table()
if opts.renderer.icons.git_placement == "signcolumn" then
M.setup_signs(opts.renderer.icons.glyphs.git)
end
if opts.renderer.icons.show.git then
M.get_icons = get_icons_
else
M.get_icons = nil_
end
if opts.renderer.highlight_git then
M.get_highlight = get_highlight_
else
M.get_highlight = nil_
end
M.git_show_on_open_dirs = opts.git.show_on_open_dirs
end
return M

View File

@ -1,41 +0,0 @@
local modified = require "nvim-tree.modified"
local M = {}
local HIGHLIGHT = "NvimTreeModifiedFile"
---return modified icon if node is modified, otherwise return empty string
---@param node table
---@return HighlightedString|nil modified icon
function M.get_icon(node)
if not modified.is_modified(node) or not M.show_icon then
return nil
end
return { str = M.icon, hl = { HIGHLIGHT } }
end
function M.setup_signs()
vim.fn.sign_define(HIGHLIGHT, { text = M.icon, texthl = HIGHLIGHT })
end
---@param node table
---@return string|nil
function M.get_highlight(node)
if not modified.is_modified(node) then
return nil
end
return HIGHLIGHT
end
function M.setup(opts)
M.icon = opts.renderer.icons.glyphs.modified
M.show_icon = opts.renderer.icons.show.modified
if opts.renderer.icons.modified_placement == "signcolumn" then
M.setup_signs()
end
end
return M

View File

@ -0,0 +1,51 @@
local marks = require "nvim-tree.marks"
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 DecoratorBookmarks: Decorator
---@field icon HighlightedString
local DecoratorBookmarks = Decorator:new()
---@param opts table
---@return DecoratorBookmarks
function DecoratorBookmarks:new(opts)
local o = Decorator.new(self, {
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 DecoratorBookmarks
if opts.renderer.icons.show.bookmarks then
o.icon = {
str = opts.renderer.icons.glyphs.bookmark,
hl = { "NvimTreeBookmarkIcon" },
}
o:define_sign(o.icon)
end
return o
end
---Bookmark icon: renderer.icons.show.bookmarks and node is marked
---@param node Node
---@return HighlightedString[]|nil icons
function DecoratorBookmarks:calculate_icons(node)
if marks.get_mark(node) then
return { self.icon }
end
end
---Bookmark highlight: renderer.highlight_bookmarks and node is marked
---@param node Node
---@return string|nil group
function DecoratorBookmarks:calculate_highlight(node)
if self.hl_pos ~= HL_POSITION.none and marks.get_mark(node) then
return "NvimTreeBookmarkHL"
end
end
return DecoratorBookmarks

View File

@ -0,0 +1,38 @@
local copy_paste
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 DecoratorCopied: Decorator
---@field enabled boolean
---@field icon HighlightedString|nil
local DecoratorCopied = Decorator:new()
---@param opts table
---@return DecoratorCopied
function DecoratorCopied:new(opts)
local o = Decorator.new(self, {
enabled = true,
hl_pos = HL_POSITION[opts.renderer.highlight_clipboard] or HL_POSITION.none,
icon_placement = ICON_PLACEMENT.none,
})
---@cast o DecoratorCopied
-- cyclic
copy_paste = copy_paste or require "nvim-tree.actions.fs.copy-paste"
return o
end
---Copied highlight: renderer.highlight_clipboard and node is copied
---@param node Node
---@return string|nil group
function DecoratorCopied:calculate_highlight(node)
if self.hl_pos ~= HL_POSITION.none and copy_paste.is_copied(node) then
return "NvimTreeCopiedHL"
end
end
return DecoratorCopied

View File

@ -0,0 +1,38 @@
local copy_paste
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 DecoratorCut: Decorator
---@field enabled boolean
---@field icon HighlightedString|nil
local DecoratorCut = Decorator:new()
---@param opts table
---@return DecoratorCut
function DecoratorCut:new(opts)
local o = Decorator.new(self, {
enabled = true,
hl_pos = HL_POSITION[opts.renderer.highlight_clipboard] or HL_POSITION.none,
icon_placement = ICON_PLACEMENT.none,
})
---@cast o DecoratorCut
-- cyclic
copy_paste = copy_paste or require "nvim-tree.actions.fs.copy-paste"
return o
end
---Cut highlight: renderer.highlight_clipboard and node is cut
---@param node Node
---@return string|nil group
function DecoratorCut:calculate_highlight(node)
if self.hl_pos ~= HL_POSITION.none and copy_paste.is_cut(node) then
return "NvimTreeCutHL"
end
end
return DecoratorCut

View File

@ -0,0 +1,110 @@
local diagnostics = require "nvim-tree.diagnostics"
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"
-- highlight groups by severity
local HG_ICON = {
[vim.diagnostic.severity.ERROR] = "NvimTreeDiagnosticErrorIcon",
[vim.diagnostic.severity.WARN] = "NvimTreeDiagnosticWarnIcon",
[vim.diagnostic.severity.INFO] = "NvimTreeDiagnosticInfoIcon",
[vim.diagnostic.severity.HINT] = "NvimTreeDiagnosticHintIcon",
}
local HG_FILE = {
[vim.diagnostic.severity.ERROR] = "NvimTreeDiagnosticErrorFileHL",
[vim.diagnostic.severity.WARN] = "NvimTreeDiagnosticWarnFileHL",
[vim.diagnostic.severity.INFO] = "NvimTreeDiagnosticInfoFileHL",
[vim.diagnostic.severity.HINT] = "NvimTreeDiagnosticHintFileHL",
}
local HG_FOLDER = {
[vim.diagnostic.severity.ERROR] = "NvimTreeDiagnosticErrorFolderHL",
[vim.diagnostic.severity.WARN] = "NvimTreeDiagnosticWarnFolderHL",
[vim.diagnostic.severity.INFO] = "NvimTreeDiagnosticInfoFolderHL",
[vim.diagnostic.severity.HINT] = "NvimTreeDiagnosticHintFolderHL",
}
-- opts.diagnostics.icons.
local ICON_KEYS = {
["error"] = vim.diagnostic.severity.ERROR,
["warning"] = vim.diagnostic.severity.WARN,
["info"] = vim.diagnostic.severity.INFO,
["hint"] = vim.diagnostic.severity.HINT,
}
---@class DecoratorDiagnostics: Decorator
---@field icons HighlightedString[]
local DecoratorDiagnostics = Decorator:new()
---@param opts table
---@return DecoratorDiagnostics
function DecoratorDiagnostics:new(opts)
local o = Decorator.new(self, {
enabled = opts.diagnostics.enable,
hl_pos = HL_POSITION[opts.renderer.highlight_diagnostics] or HL_POSITION.none,
icon_placement = ICON_PLACEMENT[opts.renderer.icons.diagnostics_placement] or ICON_PLACEMENT.none,
})
---@cast o DecoratorDiagnostics
if not o.enabled then
return o
end
if opts.renderer.icons.show.diagnostics then
o.icons = {}
for name, sev in pairs(ICON_KEYS) do
o.icons[sev] = {
str = opts.diagnostics.icons[name],
hl = { HG_ICON[sev] },
}
o:define_sign(o.icons[sev])
end
end
return o
end
---Diagnostic icon: diagnostics.enable, renderer.icons.show.diagnostics and node has status
---@param node Node
---@return HighlightedString[]|nil icons
function DecoratorDiagnostics:calculate_icons(node)
if node and self.enabled and self.icons then
local diag_status = diagnostics.get_diag_status(node)
local diag_value = diag_status and diag_status.value
if diag_value then
return { self.icons[diag_value] }
end
end
end
---Diagnostic highlight: diagnostics.enable, renderer.highlight_diagnostics and node has status
---@param node Node
---@return string|nil group
function DecoratorDiagnostics:calculate_highlight(node)
if not node or not self.enabled or self.hl_pos == HL_POSITION.none then
return nil
end
local diag_status = diagnostics.get_diag_status(node)
local diag_value = diag_status and diag_status.value
if not diag_value then
return nil
end
local group
if node.nodes then
group = HG_FOLDER[diag_value]
else
group = HG_FILE[diag_value]
end
if group then
return group
else
return nil
end
end
return DecoratorDiagnostics

View File

@ -0,0 +1,221 @@
local notify = require "nvim-tree.notify"
local explorer_node = require "nvim-tree.explorer.node"
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 HighlightedStringGit: HighlightedString
---@field ord number decreasing priority
---@class DecoratorGit: Decorator
---@field file_hl table<string, string> by porcelain status e.g. "AM"
---@field folder_hl table<string, string> by porcelain status
---@field icons_by_status HighlightedStringGit[] by human status
---@field icons_by_xy table<string, HighlightedStringGit[]> by porcelain status
local DecoratorGit = Decorator:new()
---@param opts table
---@return DecoratorGit
function DecoratorGit:new(opts)
local o = Decorator.new(self, {
enabled = opts.git.enable,
hl_pos = HL_POSITION[opts.renderer.highlight_git] or HL_POSITION.none,
icon_placement = ICON_PLACEMENT[opts.renderer.icons.git_placement] or ICON_PLACEMENT.none,
})
---@cast o DecoratorGit
if not o.enabled then
return o
end
if o.hl_pos ~= HL_POSITION.none then
o:build_hl_table()
end
if opts.renderer.icons.show.git then
o:build_icons_by_status(opts.renderer.icons.glyphs.git)
o:build_icons_by_xy(o.icons_by_status)
for _, icon in pairs(o.icons_by_status) do
self:define_sign(icon)
end
end
return o
end
---@param glyphs table<string, string> user glyps
function DecoratorGit:build_icons_by_status(glyphs)
self.icons_by_status = {
staged = { str = glyphs.staged, hl = { "NvimTreeGitStagedIcon" }, ord = 1 },
unstaged = { str = glyphs.unstaged, hl = { "NvimTreeGitDirtyIcon" }, ord = 2 },
renamed = { str = glyphs.renamed, hl = { "NvimTreeGitRenamedIcon" }, ord = 3 },
deleted = { str = glyphs.deleted, hl = { "NvimTreeGitDeletedIcon" }, ord = 4 },
unmerged = { str = glyphs.unmerged, hl = { "NvimTreeGitMergeIcon" }, ord = 5 },
untracked = { str = glyphs.untracked, hl = { "NvimTreeGitNewIcon" }, ord = 6 },
ignored = { str = glyphs.ignored, hl = { "NvimTreeGitIgnoredIcon" }, ord = 7 },
}
end
---@param icons HighlightedStringGit[]
function DecoratorGit:build_icons_by_xy(icons)
self.icons_by_xy = {
["M "] = { icons.staged },
[" M"] = { icons.unstaged },
["C "] = { icons.staged },
[" C"] = { icons.unstaged },
["CM"] = { icons.unstaged },
[" T"] = { icons.unstaged },
["T "] = { icons.staged },
["TM"] = { icons.staged, icons.unstaged },
["MM"] = { icons.staged, icons.unstaged },
["MD"] = { icons.staged },
["A "] = { icons.staged },
["AD"] = { icons.staged },
[" A"] = { icons.untracked },
-- not sure about this one
["AA"] = { icons.unmerged, icons.untracked },
["AU"] = { icons.unmerged, icons.untracked },
["AM"] = { icons.staged, icons.unstaged },
["??"] = { icons.untracked },
["R "] = { icons.renamed },
[" R"] = { icons.renamed },
["RM"] = { icons.unstaged, icons.renamed },
["UU"] = { icons.unmerged },
["UD"] = { icons.unmerged },
["UA"] = { icons.unmerged },
[" D"] = { icons.deleted },
["D "] = { icons.deleted },
["DA"] = { icons.unstaged },
["RD"] = { icons.deleted },
["DD"] = { icons.deleted },
["DU"] = { icons.deleted, icons.unmerged },
["!!"] = { icons.ignored },
dirty = { icons.unstaged },
}
end
function DecoratorGit:build_hl_table()
self.file_hl = {
["M "] = "NvimTreeGitFileStagedHL",
["C "] = "NvimTreeGitFileStagedHL",
["AA"] = "NvimTreeGitFileStagedHL",
["AD"] = "NvimTreeGitFileStagedHL",
["MD"] = "NvimTreeGitFileStagedHL",
["T "] = "NvimTreeGitFileStagedHL",
["TT"] = "NvimTreeGitFileStagedHL",
[" M"] = "NvimTreeGitFileDirtyHL",
["CM"] = "NvimTreeGitFileDirtyHL",
[" C"] = "NvimTreeGitFileDirtyHL",
[" T"] = "NvimTreeGitFileDirtyHL",
["MM"] = "NvimTreeGitFileDirtyHL",
["AM"] = "NvimTreeGitFileDirtyHL",
dirty = "NvimTreeGitFileDirtyHL",
["A "] = "NvimTreeGitFileStagedHL",
["??"] = "NvimTreeGitFileNewHL",
["AU"] = "NvimTreeGitFileMergeHL",
["UU"] = "NvimTreeGitFileMergeHL",
["UD"] = "NvimTreeGitFileMergeHL",
["DU"] = "NvimTreeGitFileMergeHL",
["UA"] = "NvimTreeGitFileMergeHL",
[" D"] = "NvimTreeGitFileDeletedHL",
["DD"] = "NvimTreeGitFileDeletedHL",
["RD"] = "NvimTreeGitFileDeletedHL",
["D "] = "NvimTreeGitFileDeletedHL",
["R "] = "NvimTreeGitFileRenamedHL",
["RM"] = "NvimTreeGitFileRenamedHL",
[" R"] = "NvimTreeGitFileRenamedHL",
["!!"] = "NvimTreeGitFileIgnoredHL",
[" A"] = "none",
}
self.folder_hl = {}
for k, v in pairs(self.file_hl) do
self.folder_hl[k] = v:gsub("File", "Folder")
end
end
---Git icons: git.enable, renderer.icons.show.git and node has status
---@param node Node
---@return HighlightedString[]|nil modified icon
function DecoratorGit:calculate_icons(node)
if not node or not self.enabled or not self.icons_by_xy then
return nil
end
local git_status = explorer_node.get_git_status(node)
if git_status == nil then
return nil
end
local inserted = {}
local iconss = {}
for _, s in pairs(git_status) do
local icons = self.icons_by_xy[s]
if not icons then
if self.hl_pos == HL_POSITION.none then
notify.warn(string.format("Unrecognized git state '%s'", git_status))
end
return nil
end
for _, icon in pairs(icons) do
if #icon.str > 0 then
if not inserted[icon] then
table.insert(iconss, icon)
inserted[icon] = true
end
end
end
end
if #iconss == 0 then
return nil
end
-- sort icons so it looks slightly better
table.sort(iconss, function(a, b)
return a.ord < b.ord
end)
return iconss
end
---Get the first icon as the sign if appropriate
---@param node Node
---@return string|nil name
function DecoratorGit:sign_name(node)
if self.icon_placement ~= ICON_PLACEMENT.signcolumn then
return
end
local icons = self:calculate_icons(node)
if icons and #icons > 0 then
return icons[1].hl[1]
end
end
---Git highlight: git.enable, renderer.highlight_git and node has status
---@param node Node
---@return string|nil group
function DecoratorGit:calculate_highlight(node)
if not node or not self.enabled or self.hl_pos == HL_POSITION.none then
return nil
end
local git_status = explorer_node.get_git_status(node)
if not git_status then
return nil
end
if node.nodes then
return self.folder_hl[git_status[1]]
else
return self.file_hl[git_status[1]]
end
end
return DecoratorGit

View File

@ -0,0 +1,122 @@
local HL_POSITION = require("nvim-tree.enum").HL_POSITION
local ICON_PLACEMENT = require("nvim-tree.enum").ICON_PLACEMENT
---@class Decorator
---@field protected enabled boolean
---@field protected hl_pos HL_POSITION
---@field protected icon_placement ICON_PLACEMENT
local Decorator = {}
---@param o Decorator|nil
---@return Decorator
function Decorator:new(o)
o = o or {}
setmetatable(o, self)
self.__index = self
return o
end
---Maybe highlight groups
---@param node Node
---@return string|nil icon highlight group
---@return string|nil name highlight group
function Decorator:groups_icon_name(node)
local icon_hl, name_hl
if self.enabled and self.hl_pos ~= HL_POSITION.none then
local hl = self:calculate_highlight(node)
if self.hl_pos == HL_POSITION.all or self.hl_pos == HL_POSITION.icon then
icon_hl = hl
end
if self.hl_pos == HL_POSITION.all or self.hl_pos == HL_POSITION.name then
name_hl = hl
end
end
return icon_hl, name_hl
end
---Maybe icon sign
---@param node Node
---@return string|nil name
function Decorator:sign_name(node)
if not self.enabled or self.icon_placement ~= ICON_PLACEMENT.signcolumn then
return
end
local icons = self:calculate_icons(node)
if icons and #icons > 0 then
return icons[1].hl[1]
end
end
---Icons when ICON_PLACEMENT.before
---@param node Node
---@return HighlightedString[]|nil icons
function Decorator:icons_before(node)
if not self.enabled or self.icon_placement ~= ICON_PLACEMENT.before then
return
end
return self:calculate_icons(node)
end
---Icons when ICON_PLACEMENT.after
---@param node Node
---@return HighlightedString[]|nil icons
function Decorator:icons_after(node)
if not self.enabled or self.icon_placement ~= ICON_PLACEMENT.after then
return
end
return self:calculate_icons(node)
end
---Maybe icons, optionally implemented
---@protected
---@param _ Node
---@return HighlightedString[]|nil icons
function Decorator:calculate_icons(_)
return nil
end
---Maybe highlight group, optionally implemented
---@protected
---@param _ Node
---@return string|nil group
function Decorator:calculate_highlight(_)
return nil
end
---Define a sign
---@protected
---@param icon HighlightedString|nil
function Decorator:define_sign(icon)
if icon and #icon.hl > 0 then
local name = icon.hl[1]
if not vim.tbl_isempty(vim.fn.sign_getdefined(name)) then
vim.fn.sign_undefine(name)
end
-- don't use sign if not defined
if #icon.str < 1 then
self.icon_placement = ICON_PLACEMENT.none
return
end
-- byte index of the next character, allowing for wide
local bi = vim.fn.byteidx(icon.str, 1)
-- first (wide) character, falls back to empty string
local text = string.sub(icon.str, 1, bi)
vim.fn.sign_define(name, {
text = text,
texthl = name,
})
end
end
return Decorator

View File

@ -0,0 +1,61 @@
local buffers = require "nvim-tree.buffers"
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 DecoratorModified: Decorator
---@field icon HighlightedString|nil
local DecoratorModified = Decorator:new()
---@param opts table
---@return DecoratorModified
function DecoratorModified:new(opts)
local o = Decorator.new(self, {
enabled = opts.modified.enable,
hl_pos = HL_POSITION[opts.renderer.highlight_modified] or HL_POSITION.none,
icon_placement = ICON_PLACEMENT[opts.renderer.icons.modified_placement] or ICON_PLACEMENT.none,
})
---@cast o DecoratorModified
if not o.enabled then
return o
end
if opts.renderer.icons.show.modified then
o.icon = {
str = opts.renderer.icons.glyphs.modified,
hl = { "NvimTreeModifiedIcon" },
}
o:define_sign(o.icon)
end
return o
end
---Modified icon: modified.enable, renderer.icons.show.modified and node is modified
---@param node Node
---@return HighlightedString[]|nil icons
function DecoratorModified:calculate_icons(node)
if self.enabled and buffers.is_modified(node) then
return { self.icon }
end
end
---Modified highlight: modified.enable, renderer.highlight_modified and node is modified
---@param node Node
---@return string|nil group
function DecoratorModified:calculate_highlight(node)
if not self.enabled or self.hl_pos == HL_POSITION.none or not buffers.is_modified(node) then
return nil
end
if node.nodes then
return "NvimTreeModifiedFolderHL"
else
return "NvimTreeModifiedFileHL"
end
end
return DecoratorModified

View File

@ -0,0 +1,35 @@
local buffers = require "nvim-tree.buffers"
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 DecoratorOpened: Decorator
---@field enabled boolean
---@field icon HighlightedString|nil
local DecoratorOpened = Decorator:new()
---@param opts table
---@return DecoratorOpened
function DecoratorOpened:new(opts)
local o = Decorator.new(self, {
enabled = true,
hl_pos = HL_POSITION[opts.renderer.highlight_opened_files] or HL_POSITION.none,
icon_placement = ICON_PLACEMENT.none,
})
---@cast o DecoratorOpened
return o
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.hl_pos ~= HL_POSITION.none and buffers.is_opened(node) then
return "NvimTreeOpenedHL"
end
end
return DecoratorOpened

View File

@ -1,34 +1,32 @@
local appearance = require "nvim-tree.appearance"
local core = require "nvim-tree.core"
local log = require "nvim-tree.log"
local view = require "nvim-tree.view"
local events = require "nvim-tree.events"
local modified = require "nvim-tree.renderer.components.modified"
local _padding = require "nvim-tree.renderer.components.padding"
local icon_component = require "nvim-tree.renderer.components.icons"
local full_name = require "nvim-tree.renderer.components.full-name"
local git = require "nvim-tree.renderer.components.git"
local diagnostics = require "nvim-tree.renderer.components.diagnostics"
local Builder = require "nvim-tree.renderer.builder"
local live_filter = require "nvim-tree.live-filter"
local bookmarks = require "nvim-tree.renderer.components.bookmarks"
local M = {
last_highlights = {},
last_hl_args = {},
}
local SIGN_GROUP = "NvimTreeRendererSigns"
local namespace_id = vim.api.nvim_create_namespace "NvimTreeHighlights"
local function _draw(bufnr, lines, hl, signs)
---@param bufnr number
---@param lines string[]
---@param hl_args AddHighlightArgs[]
---@param signs string[]
local function _draw(bufnr, lines, hl_args, signs)
vim.api.nvim_buf_set_option(bufnr, "modifiable", true)
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
M.render_hl(bufnr, hl)
M.render_hl(bufnr, hl_args)
vim.api.nvim_buf_set_option(bufnr, "modifiable", false)
vim.fn.sign_unplace(SIGN_GROUP)
for _, sign in pairs(signs) do
vim.fn.sign_place(0, SIGN_GROUP, sign.sign, bufnr, { lnum = sign.lnum, priority = sign.priority })
for i, sign_name in pairs(signs) do
vim.fn.sign_place(0, SIGN_GROUP, sign_name, bufnr, { lnum = i + 1 })
end
end
@ -36,26 +34,17 @@ function M.render_hl(bufnr, hl)
if not bufnr or not vim.api.nvim_buf_is_loaded(bufnr) then
return
end
vim.api.nvim_buf_clear_namespace(bufnr, namespace_id, 0, -1)
for _, data in ipairs(hl or M.last_highlights) do
vim.api.nvim_buf_clear_namespace(bufnr, appearance.NS_ID, 0, -1)
for _, data in ipairs(hl or M.last_hl_args) do
if type(data[1]) == "table" then
for _, group in ipairs(data[1]) do
vim.api.nvim_buf_add_highlight(bufnr, namespace_id, group, data[2], data[3], data[4])
vim.api.nvim_buf_add_highlight(bufnr, appearance.NS_ID, group, data[2], data[3], data[4])
end
end
end
end
local picture_map = {
jpg = true,
jpeg = true,
png = true,
gif = true,
webp = true,
jxl = true,
}
function M.draw(unloaded_bufnr)
function M.draw()
local bufnr = view.get_bufnr()
if not core.get_explorer() or not bufnr or not vim.api.nvim_buf_is_loaded(bufnr) then
return
@ -66,30 +55,13 @@ function M.draw(unloaded_bufnr)
local cursor = vim.api.nvim_win_get_cursor(view.get_winnr())
icon_component.reset_config()
local lines, hl, signs = Builder.new(core.get_cwd())
:configure_root_label(M.config.root_folder_label)
:configure_trailing_slash(M.config.add_trailing)
:configure_special_files(M.config.special_files)
:configure_picture_map(picture_map)
:configure_opened_file_highlighting(M.config.highlight_opened_files)
:configure_modified_highlighting(M.config.highlight_modified)
:configure_icon_padding(M.config.icons.padding)
:configure_git_icons_placement(M.config.icons.git_placement)
:configure_diagnostics_icon_placement(M.config.icons.diagnostics_placement)
:configure_bookmark_icon_placement(M.config.icons.bookmarks_placement)
:configure_modified_placement(M.config.icons.modified_placement)
:configure_symlink_destination(M.config.symlink_destination)
:configure_filter(live_filter.filter, live_filter.prefix)
:configure_group_name_modifier(M.config.group_empty)
:build_header(view.is_root_folder_visible(core.get_cwd()))
:build(core.get_explorer(), unloaded_bufnr)
:unwrap()
local builder = Builder:new():build()
_draw(bufnr, lines, hl, signs)
_draw(bufnr, builder.lines, builder.hl_args, builder.signs)
M.last_highlights = hl
M.last_hl_args = builder.hl_args
if cursor and #lines >= cursor[1] then
if cursor and #builder.lines >= cursor[1] then
vim.api.nvim_win_set_cursor(view.get_winnr(), cursor)
end
@ -102,15 +74,12 @@ end
function M.setup(opts)
M.config = opts.renderer
M.config.modified = opts.modified
_padding.setup(opts)
full_name.setup(opts)
git.setup(opts)
modified.setup(opts)
diagnostics.setup(opts)
bookmarks.setup(opts)
icon_component.setup(opts)
Builder.setup(opts)
end
return M

View File

@ -1,3 +1,4 @@
local appearance = require "nvim-tree.appearance"
local events = require "nvim-tree.events"
local utils = require "nvim-tree.utils"
local log = require "nvim-tree.log"
@ -38,19 +39,6 @@ M.View = {
cursorlineopt = "both",
colorcolumn = "0",
wrap = false,
winhl = table.concat({
"EndOfBuffer:NvimTreeEndOfBuffer",
"CursorLine:NvimTreeCursorLine",
"CursorLineNr:NvimTreeCursorLineNr",
"LineNr:NvimTreeLineNr",
"WinSeparator:NvimTreeWinSeparator",
"StatusLine:NvimTreeStatusLine",
"StatusLineNC:NvimTreeStatuslineNC",
"SignColumn:NvimTreeSignColumn",
"Normal:NvimTreeNormal",
"NormalNC:NvimTreeNormalNC",
"NormalFloat:NvimTreeNormalFloat",
}, ","),
},
}
@ -147,6 +135,9 @@ local function set_window_options_and_buffer()
vim.opt_local[k] = v
end
vim.opt.eventignore = eventignore
-- use highlights from the nvim_tree namespace
vim.api.nvim_win_set_hl_ns(M.get_winnr(), appearance.NS_ID)
end
---@return table
@ -539,13 +530,6 @@ function M.is_root_folder_visible(cwd)
return cwd ~= "/" and not M.View.hide_root_folder
end
-- used on ColorScheme event
function M.reset_winhl()
if M.get_winnr() and vim.api.nvim_win_is_valid(M.get_winnr()) then
vim.wo[M.get_winnr()].winhl = M.View.winopts.winhl
end
end
function M.setup(opts)
local options = opts.view or {}
M.View.centralize_selection = options.centralize_selection