Skip to content

Commit

Permalink
feat(#2415): combined hl groups (#2601)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
alex-courtis authored Jan 7, 2024
1 parent 11c0720 commit 145d09c
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 47 deletions.
3 changes: 0 additions & 3 deletions doc/nvim-tree-lua.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2247,9 +2247,6 @@ as per |:highlight|

Default linked group or definition follows name.

neovim 0.9 has a limit of two highlight groups per range. The two highest
priority groups as per |nvim-tree-opts-renderer| will be used.

Standard: >
NvimTreeNormal Normal
NvimTreeNormalFloat NormalFloat
Expand Down
15 changes: 7 additions & 8 deletions lua/nvim-tree.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
colors.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", {
Expand Down
25 changes: 24 additions & 1 deletion lua/nvim-tree/colors.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
local M = {}
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 = {
Expand Down Expand Up @@ -122,6 +125,21 @@ local DEFAULT_LINKS = {
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",
Expand Down Expand Up @@ -189,6 +207,11 @@ function M.setup()
for from, to in pairs(DEFAULT_LINKS) do
vim.api.nvim_command("hi def link " .. from .. " " .. to)
end

-- window namespace; these don't appear to be cleared on colorscheme however 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
88 changes: 77 additions & 11 deletions lua/nvim-tree/renderer/builder.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
local colors = require "nvim-tree.colors"
local utils = require "nvim-tree.utils"
local notify = require "nvim-tree.notify"

Expand All @@ -8,6 +9,7 @@ local icons = require "nvim-tree.renderer.components.icons"
---@field private index number
---@field private depth number
---@field private highlights table[] hl_group, line, col_start, col_end arguments for vim.api.nvim_buf_add_highlight
---@field private combined_groups boolean[] combined group names
---@field private lines string[] includes icons etc.
---@field private markers boolean[] indent markers
---@field private sign_names string[] line signs
Expand All @@ -23,6 +25,7 @@ function Builder.new(root_cwd, decorators)
index = 0,
depth = 0,
highlights = {},
combined_groups = {},
lines = {},
markers = {},
sign_names = {},
Expand Down Expand Up @@ -75,15 +78,11 @@ function Builder:configure_group_name_modifier(group_name_modifier)
end

---Insert ranged highlight groups into self.highlights
---neovim 0.9 is limited to two highlight groups for a range so choose the highest two
---@param groups string[]
---@param start number
---@param end_ number|nil
function Builder:_insert_highlight(groups, start, end_)
local top_two_groups = {}
table.insert(top_two_groups, groups[#groups - 1])
table.insert(top_two_groups, groups[#groups])
table.insert(self.highlights, { top_two_groups, self.index, start, end_ or -1 })
table.insert(self.highlights, { groups, self.index, start, end_ or -1 })
end

function Builder:_insert_line(line)
Expand Down Expand Up @@ -261,6 +260,76 @@ function Builder:_build_signs(node)
end
end

---Combined group name less than the 200 byte limit of highlight group names
---@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.
---@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 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_ns_fast(colors.NS_ID)
vim.api.nvim_set_hl(colors.NS_ID, combined_name, combined_hl)

self.combined_groups[combined_name] = true
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.
---@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 = #self.decorators, 1, -1 do
d = self.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

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)
Expand All @@ -279,12 +348,9 @@ function Builder:_build_line(node, idx, num_children)
end

-- highighting
for i = #self.decorators, 1, -1 do
local d = self.decorators[i]
local icon_group, name_group = d:groups_icon_name(node)
table.insert(icon.hl, icon_group)
table.insert(name.hl, name_group)
end
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)

local line = self:_format_line(indent_markers, arrows, icon, name, node)
self:_insert_line(self:_unwrap_highlighted_strings(line))
Expand Down
7 changes: 3 additions & 4 deletions lua/nvim-tree/renderer/init.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
local colors = require "nvim-tree.colors"
local core = require "nvim-tree.core"
local log = require "nvim-tree.log"
local view = require "nvim-tree.view"
Expand All @@ -24,8 +25,6 @@ local M = {

local SIGN_GROUP = "NvimTreeRendererSigns"

local namespace_id = vim.api.nvim_create_namespace "NvimTreeHighlights"

local function _draw(bufnr, lines, hl, sign_names)
vim.api.nvim_buf_set_option(bufnr, "modifiable", true)
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
Expand All @@ -41,11 +40,11 @@ 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)
vim.api.nvim_buf_clear_namespace(bufnr, colors.NS_ID, 0, -1)
for _, data in ipairs(hl or M.last_highlights) 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, colors.NS_ID, group, data[2], data[3], data[4])
end
end
end
Expand Down
22 changes: 2 additions & 20 deletions lua/nvim-tree/view.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
local colors = require "nvim-tree.colors"
local events = require "nvim-tree.events"
local utils = require "nvim-tree.utils"
local log = require "nvim-tree.log"
Expand Down Expand Up @@ -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",
}, ","),
},
}

Expand Down Expand Up @@ -147,6 +135,7 @@ local function set_window_options_and_buffer()
vim.opt_local[k] = v
end
vim.opt.eventignore = eventignore
vim.api.nvim_win_set_hl_ns(0, colors.NS_ID)
end

---@return table
Expand Down Expand Up @@ -539,13 +528,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
Expand Down

0 comments on commit 145d09c

Please sign in to comment.