Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(#2415): combined hl groups #2601

Merged
merged 5 commits into from
Jan 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading