nvim-tree.lua/lua/nvim-tree/renderer/init.lua

448 lines
15 KiB
Lua

local utils = require "nvim-tree.utils"
local view = require "nvim-tree.view"
local _padding = require "nvim-tree.renderer.padding"
local _help = require "nvim-tree.renderer.help"
local _icons = require "nvim-tree.renderer.icons"
local api = vim.api
local lines = {}
local hl = {}
local index = 0
local namespace_id = api.nvim_create_namespace "NvimTreeHighlights"
local icon_state = _icons.get_config()
local should_hl_opened_files = (vim.g.nvim_tree_highlight_opened_files or 0) ~= 0
local get_folder_icon = function()
return ""
end
local function get_trailing_length()
return vim.g.nvim_tree_add_trailing and 1 or 0
end
local set_folder_hl = function(line, depth, git_icon_len, _, hl_group)
table.insert(hl, { hl_group, line, depth + git_icon_len, -1 })
end
local icon_padding = vim.g.nvim_tree_icon_padding or " "
if icon_state.show_folder_icon then
get_folder_icon = function(open, is_symlink, has_children)
local n
if is_symlink and open then
n = icon_state.icons.folder_icons.symlink_open
elseif is_symlink then
n = icon_state.icons.folder_icons.symlink
elseif open then
if has_children then
n = icon_state.icons.folder_icons.open
else
n = icon_state.icons.folder_icons.empty_open
end
else
if has_children then
n = icon_state.icons.folder_icons.default
else
n = icon_state.icons.folder_icons.empty
end
end
return n .. icon_padding
end
set_folder_hl = function(line, depth, icon_len, name_len, hl_group)
table.insert(hl, { hl_group, line, depth + icon_len, depth + icon_len + name_len + get_trailing_length() })
local hl_icon = (vim.g.nvim_tree_highlight_opened_files or 0) ~= 0 and hl_group or "NvimTreeFolderIcon"
table.insert(hl, { hl_icon, line, depth, depth + icon_len })
end
end
local get_file_icon = function()
return ""
end
if icon_state.show_file_icon then
if icon_state.has_devicons then
local web_devicons = require "nvim-web-devicons"
get_file_icon = function(fname, extension, line, depth)
local icon, hl_group = web_devicons.get_icon(fname, extension)
if icon and hl_group ~= "DevIconDefault" then
if hl_group then
table.insert(hl, { hl_group, line, depth, depth + #icon + 1 })
end
return icon .. icon_padding
elseif string.match(extension, "%.(.*)") then
-- If there are more extensions to the file, try to grab the icon for them recursively
return get_file_icon(fname, string.match(extension, "%.(.*)"), line, depth)
else
return #icon_state.icons.default > 0 and icon_state.icons.default .. icon_padding or ""
end
end
else
get_file_icon = function()
return #icon_state.icons.default > 0 and icon_state.icons.default .. icon_padding or ""
end
end
end
local get_symlink_icon = function()
return icon_state.icons.symlink
end
if icon_state.show_file_icon then
get_symlink_icon = function()
return #icon_state.icons.symlink > 0 and icon_state.icons.symlink .. icon_padding or ""
end
end
local get_special_icon = function()
return ""
end
if icon_state.show_file_icon then
get_special_icon = function()
return #icon_state.icons.default > 0 and icon_state.icons.default .. icon_padding or ""
end
end
local get_git_icons = function()
return ""
end
local get_git_hl = function() end
if vim.g.nvim_tree_git_hl == 1 then
local git_hl = {
["M "] = { { hl = "NvimTreeFileStaged" } },
[" M"] = { { hl = "NvimTreeFileDirty" } },
["CM"] = { { hl = "NvimTreeFileDirty" } },
["C "] = { { hl = "NvimTreeFileStaged" } },
[" C"] = { { hl = "NvimTreeFileDirty" } },
[" T"] = { { hl = "NvimTreeFileDirty" } },
["MM"] = {
{ hl = "NvimTreeFileStaged" },
{ hl = "NvimTreeFileDirty" },
},
["A "] = {
{ hl = "NvimTreeFileStaged" },
{ hl = "NvimTreeFileNew" },
},
["AU"] = {
{ hl = "NvimTreeFileMerge" },
{ hl = "NvimTreeFileStaged" },
},
-- not sure about this one
["AA"] = {
{ hl = "NvimTreeFileMerge" },
{ hl = "NvimTreeFileStaged" },
},
["AD"] = {
{ hl = "NvimTreeFileStaged" },
},
["MD"] = {
{ hl = "NvimTreeFileStaged" },
},
["AM"] = {
{ hl = "NvimTreeFileStaged" },
{ hl = "NvimTreeFileNew" },
{ hl = "NvimTreeFileDirty" },
},
["??"] = { { hl = "NvimTreeFileNew" } },
["R "] = { { hl = "NvimTreeFileRenamed" } },
["UU"] = { { hl = "NvimTreeFileMerge" } },
["UD"] = { { hl = "NvimTreeFileMerge" } },
["UA"] = { { hl = "NvimTreeFileMerge" } },
[" D"] = { { hl = "NvimTreeFileDeleted" } },
["DD"] = { { hl = "NvimTreeFileDeleted" } },
["RD"] = { { hl = "NvimTreeFileDeleted" } },
["D "] = {
{ hl = "NvimTreeFileDeleted" },
{ hl = "NvimTreeFileStaged" },
},
["DU"] = {
{ hl = "NvimTreeFileDeleted" },
{ hl = "NvimTreeFileMerge" },
},
[" A"] = { { hl = "none" } },
["RM"] = { { hl = "NvimTreeFileRenamed" } },
[" R"] = { { hl = "NvimTreeFileRenamed" } },
["!!"] = { { hl = "NvimTreeGitIgnored" } },
dirty = { { hl = "NvimTreeFileDirty" } },
}
get_git_hl = function(node)
local git_status = node.git_status
if not git_status then
return
end
local icons = git_hl[git_status]
if icons == nil then
utils.warn(
'Unrecognized git state "'
.. git_status
.. '". Please open up an issue on https://github.com/kyazdani42/nvim-tree.lua/issues with this message.'
)
icons = git_hl.dirty
end
-- TODO: how would we determine hl color when multiple git status are active ?
return icons[1].hl
-- return icons[#icons].hl
end
end
if icon_state.show_git_icon then
local git_icon_state = {
["M "] = { { icon = icon_state.icons.git_icons.staged, hl = "NvimTreeGitStaged" } },
[" M"] = { { icon = icon_state.icons.git_icons.unstaged, hl = "NvimTreeGitDirty" } },
["C "] = { { icon = icon_state.icons.git_icons.staged, hl = "NvimTreeGitStaged" } },
[" C"] = { { icon = icon_state.icons.git_icons.unstaged, hl = "NvimTreeGitDirty" } },
["CM"] = { { icon = icon_state.icons.git_icons.unstaged, hl = "NvimTreeGitDirty" } },
[" T"] = { { icon = icon_state.icons.git_icons.unstaged, hl = "NvimTreeGitDirty" } },
["MM"] = {
{ icon = icon_state.icons.git_icons.staged, hl = "NvimTreeGitStaged" },
{ icon = icon_state.icons.git_icons.unstaged, hl = "NvimTreeGitDirty" },
},
["MD"] = {
{ icon = icon_state.icons.git_icons.staged, hl = "NvimTreeGitStaged" },
},
["A "] = {
{ icon = icon_state.icons.git_icons.staged, hl = "NvimTreeGitStaged" },
},
["AD"] = {
{ icon = icon_state.icons.git_icons.staged, hl = "NvimTreeGitStaged" },
},
[" A"] = {
{ icon = icon_state.icons.git_icons.untracked, hl = "NvimTreeGitNew" },
},
-- not sure about this one
["AA"] = {
{ icon = icon_state.icons.git_icons.unmerged, hl = "NvimTreeGitMerge" },
{ icon = icon_state.icons.git_icons.untracked, hl = "NvimTreeGitNew" },
},
["AU"] = {
{ icon = icon_state.icons.git_icons.unmerged, hl = "NvimTreeGitMerge" },
{ icon = icon_state.icons.git_icons.untracked, hl = "NvimTreeGitNew" },
},
["AM"] = {
{ icon = icon_state.icons.git_icons.staged, hl = "NvimTreeGitStaged" },
{ icon = icon_state.icons.git_icons.unstaged, hl = "NvimTreeGitDirty" },
},
["??"] = { { icon = icon_state.icons.git_icons.untracked, hl = "NvimTreeGitNew" } },
["R "] = { { icon = icon_state.icons.git_icons.renamed, hl = "NvimTreeGitRenamed" } },
[" R"] = { { icon = icon_state.icons.git_icons.renamed, hl = "NvimTreeGitRenamed" } },
["RM"] = {
{ icon = icon_state.icons.git_icons.unstaged, hl = "NvimTreeGitDirty" },
{ icon = icon_state.icons.git_icons.renamed, hl = "NvimTreeGitRenamed" },
},
["UU"] = { { icon = icon_state.icons.git_icons.unmerged, hl = "NvimTreeGitMerge" } },
["UD"] = { { icon = icon_state.icons.git_icons.unmerged, hl = "NvimTreeGitMerge" } },
["UA"] = { { icon = icon_state.icons.git_icons.unmerged, hl = "NvimTreeGitMerge" } },
[" D"] = { { icon = icon_state.icons.git_icons.deleted, hl = "NvimTreeGitDeleted" } },
["D "] = { { icon = icon_state.icons.git_icons.deleted, hl = "NvimTreeGitDeleted" } },
["RD"] = { { icon = icon_state.icons.git_icons.deleted, hl = "NvimTreeGitDeleted" } },
["DD"] = { { icon = icon_state.icons.git_icons.deleted, hl = "NvimTreeGitDeleted" } },
["DU"] = {
{ icon = icon_state.icons.git_icons.deleted, hl = "NvimTreeGitDeleted" },
{ icon = icon_state.icons.git_icons.unmerged, hl = "NvimTreeGitMerge" },
},
["!!"] = { { icon = icon_state.icons.git_icons.ignored, hl = "NvimTreeGitIgnored" } },
dirty = { { icon = icon_state.icons.git_icons.unstaged, hl = "NvimTreeGitDirty" } },
}
get_git_icons = function(node, line, depth, icon_len)
local git_status = node.git_status
if not git_status then
return ""
end
local icon = ""
local icons = git_icon_state[git_status]
if not icons then
if vim.g.nvim_tree_git_hl ~= 1 then
utils.warn(
'Unrecognized git state "'
.. git_status
.. '". Please open up an issue on https://github.com/kyazdani42/nvim-tree.lua/issues with this message.'
)
end
icons = git_icon_state.dirty
end
for _, v in ipairs(icons) do
if #v.icon > 0 then
table.insert(hl, { v.hl, line, depth + icon_len + #icon, depth + icon_len + #icon + #v.icon })
icon = icon .. v.icon .. icon_padding
end
end
return icon
end
end
local picture = {
jpg = true,
jpeg = true,
png = true,
gif = true,
}
local function update_draw_data(tree, depth, markers)
local root_folder_modifier = vim.g.nvim_tree_root_folder_modifier or ":~"
local special = vim.g.nvim_tree_special_files
or {
["Cargo.toml"] = true,
Makefile = true,
["README.md"] = true,
["readme.md"] = true,
}
local hide_root_folder = view.View.hide_root_folder
if tree.cwd and tree.cwd ~= "/" and not hide_root_folder then
local root_name = utils.path_join {
utils.path_remove_trailing(vim.fn.fnamemodify(tree.cwd, root_folder_modifier)),
"..",
}
table.insert(lines, root_name)
table.insert(hl, { "NvimTreeRootFolder", index, 0, string.len(root_name) })
index = 1
end
for idx, node in ipairs(tree.nodes) do
local padding = _padding.get_padding(depth, idx, tree, node, markers)
local offset = string.len(padding)
if depth > 0 then
table.insert(hl, { "NvimTreeIndentMarker", index, 0, offset })
end
local git_hl = get_git_hl(node)
if node.nodes then
local has_children = #node.nodes ~= 0 or node.has_children
local icon = get_folder_icon(node.open, node.link_to ~= nil, has_children)
local git_icon = get_git_icons(node, index, offset, #icon) or ""
-- INFO: this is mandatory in order to keep gui attributes (bold/italics)
local folder_hl = "NvimTreeFolderName"
local name = node.name
local next = node.group_next
while next do
name = name .. "/" .. next.name
next = next.group_next
end
if not has_children then
folder_hl = "NvimTreeEmptyFolderName"
end
if node.open then
folder_hl = "NvimTreeOpenedFolderName"
end
if special[node.absolute_path] then
folder_hl = "NvimTreeSpecialFolderName"
end
set_folder_hl(index, offset, #icon + #git_icon, #name, folder_hl)
if git_hl then
set_folder_hl(index, offset, #icon + #git_icon, #name, git_hl)
end
index = index + 1
if node.open then
table.insert(lines, padding .. icon .. git_icon .. name .. (vim.g.nvim_tree_add_trailing == 1 and "/" or ""))
update_draw_data(node, depth + 2, markers)
else
table.insert(lines, padding .. icon .. git_icon .. name .. (vim.g.nvim_tree_add_trailing == 1 and "/" or ""))
end
elseif node.link_to then
local icon = get_symlink_icon()
local link_hl = git_hl or "NvimTreeSymlink"
local arrow = vim.g.nvim_tree_symlink_arrow or ""
table.insert(hl, { link_hl, index, offset, -1 })
table.insert(lines, padding .. icon .. node.name .. arrow .. node.link_to)
index = index + 1
else
local icon
local git_icons
if special[node.absolute_path] or special[node.name] then
icon = get_special_icon()
git_icons = get_git_icons(node, index, offset, 0)
table.insert(hl, { "NvimTreeSpecialFile", index, offset + #git_icons, -1 })
else
icon = get_file_icon(node.name, node.extension, index, offset)
git_icons = get_git_icons(node, index, offset, #icon)
end
table.insert(lines, padding .. icon .. git_icons .. node.name)
if node.executable then
table.insert(hl, { "NvimTreeExecFile", index, offset + #icon + #git_icons, -1 })
elseif picture[node.extension] then
table.insert(hl, { "NvimTreeImageFile", index, offset + #icon + #git_icons, -1 })
end
if should_hl_opened_files then
if vim.fn.bufloaded(node.absolute_path) > 0 then
if vim.g.nvim_tree_highlight_opened_files == 1 then
table.insert(hl, { "NvimTreeOpenedFile", index, offset, offset + #icon }) -- highlight icon only
elseif vim.g.nvim_tree_highlight_opened_files == 2 then
table.insert(hl, {
"NvimTreeOpenedFile",
index,
offset + #icon + #git_icons,
offset + #icon + #git_icons + #node.name,
}) -- highlight name only
elseif vim.g.nvim_tree_highlight_opened_files == 3 then
table.insert(hl, { "NvimTreeOpenedFile", index, offset, -1 }) -- highlight whole line
end
end
end
if git_hl then
table.insert(hl, { git_hl, index, offset + #icon + #git_icons, -1 })
end
index = index + 1
end
end
end
local M = {}
function M.draw()
local bufnr = view.get_bufnr()
if not TreeExplorer or not bufnr or not api.nvim_buf_is_loaded(bufnr) then
return
end
local cursor
if view.is_visible() then
cursor = api.nvim_win_get_cursor(view.get_winnr())
end
index = 0
lines = {}
hl = {}
icon_state = _icons.get_config()
local show_arrows = vim.g.nvim_tree_indent_markers ~= 1
and icon_state.show_folder_icon
and icon_state.show_folder_arrows
_padding.reload_padding_function()
update_draw_data(TreeExplorer, show_arrows and 2 or 0, {})
if view.is_help_ui() then
lines, hl = _help.compute_lines()
end
api.nvim_buf_set_option(bufnr, "modifiable", true)
api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
M.render_hl(bufnr)
api.nvim_buf_set_option(bufnr, "modifiable", false)
if cursor and #lines >= cursor[1] then
api.nvim_win_set_cursor(view.get_winnr(), cursor)
end
end
function M.render_hl(bufnr)
if not bufnr or not api.nvim_buf_is_loaded(bufnr) then
return
end
api.nvim_buf_clear_namespace(bufnr, namespace_id, 0, -1)
for _, data in ipairs(hl) do
api.nvim_buf_add_highlight(bufnr, namespace_id, data[1], data[2], data[3], data[4])
end
end
return M