chore: migrate to classic (#2991)
* add classic, migrating nodes classes
* add mixins to classic
* typechecked optargs constructors for nodes
* typechecked optargs constructors for watcher and event
* luacheck
* typechecked optargs constructors for GitRunner
* typechecked optargs constructors for Sorter
* typechecked optargs constructors for decorators, WIP
* typechecked optargs constructors for decorators, WIP
* typechecked optargs constructors for decorators
* remove class
* replace enums with named maps
* Renderer and Builder use classic, tidy opts
* LiveFilter uses classic, tidy opts
* Filter uses classic, tidy opts
* add FilterTypes named map
* move toggles into filters
* Marks uses classic, tidy opts
* Sorter uses classic, tidy opts
* Clipboard uses classic, tidy opts
* use supers for node methods
* HighlightDisplay uses classic
* protected :new
* Watcher tidy
* Revert "use supers for node methods"
This reverts commit 9fc7a866ec.
* Watcher tidy
* format
* format
* Filters private methods
* format
* Sorter type safety
* Sorter type safety
* Sorter type safety
* Sorter type safety
* Sorter type safety
* Sorter type safety
* tidy Runner
* tidy hi-test name
This commit is contained in:
parent
610a1c189b
commit
3fc8de198c
@ -7,6 +7,7 @@ local notify = require("nvim-tree.notify")
|
|||||||
|
|
||||||
local find_file = require("nvim-tree.actions.finders.find-file").fn
|
local find_file = require("nvim-tree.actions.finders.find-file").fn
|
||||||
|
|
||||||
|
local Class = require("nvim-tree.classic")
|
||||||
local DirectoryNode = require("nvim-tree.node.directory")
|
local DirectoryNode = require("nvim-tree.node.directory")
|
||||||
|
|
||||||
---@alias ClipboardAction "copy" | "cut"
|
---@alias ClipboardAction "copy" | "cut"
|
||||||
@ -14,35 +15,31 @@ local DirectoryNode = require("nvim-tree.node.directory")
|
|||||||
|
|
||||||
---@alias ClipboardActionFn fun(source: string, dest: string): boolean, string?
|
---@alias ClipboardActionFn fun(source: string, dest: string): boolean, string?
|
||||||
|
|
||||||
---@class Clipboard to handle all actions.fs clipboard API
|
---@class (exact) Clipboard: Class
|
||||||
---@field config table hydrated user opts.filters
|
|
||||||
---@field private explorer Explorer
|
---@field private explorer Explorer
|
||||||
---@field private data ClipboardData
|
---@field private data ClipboardData
|
||||||
---@field private clipboard_name string
|
---@field private clipboard_name string
|
||||||
---@field private reg string
|
---@field private reg string
|
||||||
local Clipboard = {}
|
local Clipboard = Class:extend()
|
||||||
|
|
||||||
---@param opts table user options
|
---@class Clipboard
|
||||||
---@param explorer Explorer
|
---@overload fun(args: ClipboardArgs): Clipboard
|
||||||
---@return Clipboard
|
|
||||||
function Clipboard:new(opts, explorer)
|
---@class (exact) ClipboardArgs
|
||||||
---@type Clipboard
|
---@field explorer Explorer
|
||||||
local o = {
|
|
||||||
explorer = explorer,
|
---@protected
|
||||||
data = {
|
---@param args ClipboardArgs
|
||||||
copy = {},
|
function Clipboard:new(args)
|
||||||
cut = {},
|
self.explorer = args.explorer
|
||||||
},
|
|
||||||
clipboard_name = opts.actions.use_system_clipboard and "system" or "neovim",
|
self.data = {
|
||||||
reg = opts.actions.use_system_clipboard and "+" or "1",
|
copy = {},
|
||||||
config = {
|
cut = {},
|
||||||
filesystem_watchers = opts.filesystem_watchers,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setmetatable(o, self)
|
self.clipboard_name = self.explorer.opts.actions.use_system_clipboard and "system" or "neovim"
|
||||||
self.__index = self
|
self.reg = self.explorer.opts.actions.use_system_clipboard and "+" or "1"
|
||||||
return o
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param source string
|
---@param source string
|
||||||
@ -252,7 +249,7 @@ function Clipboard:do_paste(node, action, action_fn)
|
|||||||
end
|
end
|
||||||
|
|
||||||
self.data[action] = {}
|
self.data[action] = {}
|
||||||
if not self.config.filesystem_watchers.enable then
|
if not self.explorer.opts.filesystem_watchers.enable then
|
||||||
self.explorer:reload_explorer()
|
self.explorer:reload_explorer()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -2,7 +2,6 @@ local M = {}
|
|||||||
|
|
||||||
M.collapse_all = require("nvim-tree.actions.tree.modifiers.collapse-all")
|
M.collapse_all = require("nvim-tree.actions.tree.modifiers.collapse-all")
|
||||||
M.expand_all = require("nvim-tree.actions.tree.modifiers.expand-all")
|
M.expand_all = require("nvim-tree.actions.tree.modifiers.expand-all")
|
||||||
M.toggles = require("nvim-tree.actions.tree.modifiers.toggles")
|
|
||||||
|
|
||||||
function M.setup(opts)
|
function M.setup(opts)
|
||||||
M.expand_all.setup(opts)
|
M.expand_all.setup(opts)
|
||||||
|
|||||||
@ -1,73 +0,0 @@
|
|||||||
local utils = require("nvim-tree.utils")
|
|
||||||
local core = require("nvim-tree.core")
|
|
||||||
local M = {}
|
|
||||||
|
|
||||||
---@param explorer Explorer
|
|
||||||
local function reload(explorer)
|
|
||||||
local node = explorer:get_node_at_cursor()
|
|
||||||
explorer:reload_explorer()
|
|
||||||
if node then
|
|
||||||
utils.focus_node_or_parent(node)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function wrap_explorer(fn)
|
|
||||||
return function(...)
|
|
||||||
local explorer = core.get_explorer()
|
|
||||||
if explorer then
|
|
||||||
return fn(explorer, ...)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param explorer Explorer
|
|
||||||
local function custom(explorer)
|
|
||||||
explorer.filters.config.filter_custom = not explorer.filters.config.filter_custom
|
|
||||||
reload(explorer)
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param explorer Explorer
|
|
||||||
local function git_ignored(explorer)
|
|
||||||
explorer.filters.config.filter_git_ignored = not explorer.filters.config.filter_git_ignored
|
|
||||||
reload(explorer)
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param explorer Explorer
|
|
||||||
local function git_clean(explorer)
|
|
||||||
explorer.filters.config.filter_git_clean = not explorer.filters.config.filter_git_clean
|
|
||||||
reload(explorer)
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param explorer Explorer
|
|
||||||
local function no_buffer(explorer)
|
|
||||||
explorer.filters.config.filter_no_buffer = not explorer.filters.config.filter_no_buffer
|
|
||||||
reload(explorer)
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param explorer Explorer
|
|
||||||
local function no_bookmark(explorer)
|
|
||||||
explorer.filters.config.filter_no_bookmark = not explorer.filters.config.filter_no_bookmark
|
|
||||||
reload(explorer)
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param explorer Explorer
|
|
||||||
local function dotfiles(explorer)
|
|
||||||
explorer.filters.config.filter_dotfiles = not explorer.filters.config.filter_dotfiles
|
|
||||||
reload(explorer)
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param explorer Explorer
|
|
||||||
local function enable(explorer)
|
|
||||||
explorer.filters.config.enable = not explorer.filters.config.enable
|
|
||||||
reload(explorer)
|
|
||||||
end
|
|
||||||
|
|
||||||
M.custom = wrap_explorer(custom)
|
|
||||||
M.git_ignored = wrap_explorer(git_ignored)
|
|
||||||
M.git_clean = wrap_explorer(git_clean)
|
|
||||||
M.no_buffer = wrap_explorer(no_buffer)
|
|
||||||
M.no_bookmark = wrap_explorer(no_bookmark)
|
|
||||||
M.dotfiles = wrap_explorer(dotfiles)
|
|
||||||
M.enable = wrap_explorer(enable)
|
|
||||||
|
|
||||||
return M
|
|
||||||
@ -2,7 +2,7 @@ local core = require("nvim-tree.core")
|
|||||||
local view = require("nvim-tree.view")
|
local view = require("nvim-tree.view")
|
||||||
local utils = require("nvim-tree.utils")
|
local utils = require("nvim-tree.utils")
|
||||||
local actions = require("nvim-tree.actions")
|
local actions = require("nvim-tree.actions")
|
||||||
local appearance_diagnostics = require("nvim-tree.appearance.diagnostics")
|
local appearance_hi_test = require("nvim-tree.appearance.hi-test")
|
||||||
local events = require("nvim-tree.events")
|
local events = require("nvim-tree.events")
|
||||||
local help = require("nvim-tree.help")
|
local help = require("nvim-tree.help")
|
||||||
local keymap = require("nvim-tree.keymap")
|
local keymap = require("nvim-tree.keymap")
|
||||||
@ -89,6 +89,22 @@ local function wrap_node_or_nil(fn)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Invoke a member's method on the singleton explorer.
|
||||||
|
---Print error when setup not called.
|
||||||
|
---@param explorer_member string explorer member name
|
||||||
|
---@param member_method string method name to invoke on member
|
||||||
|
---@param ... any passed to method
|
||||||
|
---@return fun(...): any
|
||||||
|
local function wrap_explorer_member_args(explorer_member, member_method, ...)
|
||||||
|
local method_args = ...
|
||||||
|
return wrap(function(...)
|
||||||
|
local explorer = core.get_explorer()
|
||||||
|
if explorer then
|
||||||
|
return explorer[explorer_member][member_method](explorer[explorer_member], method_args, ...)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
---Invoke a member's method on the singleton explorer.
|
---Invoke a member's method on the singleton explorer.
|
||||||
---Print error when setup not called.
|
---Print error when setup not called.
|
||||||
---@param explorer_member string explorer member name
|
---@param explorer_member string explorer member name
|
||||||
@ -165,13 +181,13 @@ Api.tree.find_file = wrap(actions.tree.find_file.fn)
|
|||||||
Api.tree.search_node = wrap(actions.finders.search_node.fn)
|
Api.tree.search_node = wrap(actions.finders.search_node.fn)
|
||||||
Api.tree.collapse_all = wrap(actions.tree.modifiers.collapse_all.fn)
|
Api.tree.collapse_all = wrap(actions.tree.modifiers.collapse_all.fn)
|
||||||
Api.tree.expand_all = wrap_node(actions.tree.modifiers.expand_all.fn)
|
Api.tree.expand_all = wrap_node(actions.tree.modifiers.expand_all.fn)
|
||||||
Api.tree.toggle_enable_filters = wrap(actions.tree.modifiers.toggles.enable)
|
Api.tree.toggle_enable_filters = wrap_explorer_member("filters", "toggle")
|
||||||
Api.tree.toggle_gitignore_filter = wrap(actions.tree.modifiers.toggles.git_ignored)
|
Api.tree.toggle_gitignore_filter = wrap_explorer_member_args("filters", "toggle", "git_ignored")
|
||||||
Api.tree.toggle_git_clean_filter = wrap(actions.tree.modifiers.toggles.git_clean)
|
Api.tree.toggle_git_clean_filter = wrap_explorer_member_args("filters", "toggle", "git_clean")
|
||||||
Api.tree.toggle_no_buffer_filter = wrap(actions.tree.modifiers.toggles.no_buffer)
|
Api.tree.toggle_no_buffer_filter = wrap_explorer_member_args("filters", "toggle", "no_buffer")
|
||||||
Api.tree.toggle_custom_filter = wrap(actions.tree.modifiers.toggles.custom)
|
Api.tree.toggle_custom_filter = wrap_explorer_member_args("filters", "toggle", "custom")
|
||||||
Api.tree.toggle_hidden_filter = wrap(actions.tree.modifiers.toggles.dotfiles)
|
Api.tree.toggle_hidden_filter = wrap_explorer_member_args("filters", "toggle", "dotfiles")
|
||||||
Api.tree.toggle_no_bookmark_filter = wrap(actions.tree.modifiers.toggles.no_bookmark)
|
Api.tree.toggle_no_bookmark_filter = wrap_explorer_member_args("filters", "toggle", "no_bookmark")
|
||||||
Api.tree.toggle_help = wrap(help.toggle)
|
Api.tree.toggle_help = wrap(help.toggle)
|
||||||
Api.tree.is_tree_buf = wrap(utils.is_nvim_tree_buf)
|
Api.tree.is_tree_buf = wrap(utils.is_nvim_tree_buf)
|
||||||
|
|
||||||
@ -289,7 +305,7 @@ Api.config.mappings.get_keymap = wrap(keymap.get_keymap)
|
|||||||
Api.config.mappings.get_keymap_default = wrap(keymap.get_keymap_default)
|
Api.config.mappings.get_keymap_default = wrap(keymap.get_keymap_default)
|
||||||
Api.config.mappings.default_on_attach = keymap.default_on_attach
|
Api.config.mappings.default_on_attach = keymap.default_on_attach
|
||||||
|
|
||||||
Api.diagnostics.hi_test = wrap(appearance_diagnostics.hi_test)
|
Api.diagnostics.hi_test = wrap(appearance_hi_test)
|
||||||
|
|
||||||
Api.commands.get = wrap(function()
|
Api.commands.get = wrap(function()
|
||||||
return require("nvim-tree.commands").get()
|
return require("nvim-tree.commands").get()
|
||||||
|
|||||||
@ -1,45 +1,46 @@
|
|||||||
local appearance = require("nvim-tree.appearance")
|
local appearance = require("nvim-tree.appearance")
|
||||||
|
|
||||||
|
local Class = require("nvim-tree.classic")
|
||||||
|
|
||||||
-- others with name and links less than this arbitrary value are short
|
-- others with name and links less than this arbitrary value are short
|
||||||
local SHORT_LEN = 50
|
local SHORT_LEN = 50
|
||||||
|
|
||||||
local M = {}
|
---@class (exact) HighlightDisplay: Class for :NvimTreeHiTest
|
||||||
|
|
||||||
---@class HighlightDisplay for :NvimTreeHiTest
|
|
||||||
---@field group string nvim-tree highlight group name
|
---@field group string nvim-tree highlight group name
|
||||||
---@field links string link chain to a concretely defined group
|
---@field links string link chain to a concretely defined group
|
||||||
---@field def string :hi concrete definition after following any links
|
---@field def string :hi concrete definition after following any links
|
||||||
local HighlightDisplay = {}
|
local HighlightDisplay = Class:extend()
|
||||||
|
|
||||||
---@param group string nvim-tree highlight group name
|
---@class HighlightDisplay
|
||||||
---@return HighlightDisplay
|
---@overload fun(args: HighlightDisplayArgs): HighlightDisplay
|
||||||
function HighlightDisplay:new(group)
|
|
||||||
local o = {}
|
|
||||||
setmetatable(o, self)
|
|
||||||
self.__index = self
|
|
||||||
|
|
||||||
o.group = group
|
---@class (exact) HighlightDisplayArgs
|
||||||
local concrete = o.group
|
---@field group string nvim-tree highlight group name
|
||||||
|
|
||||||
|
---@protected
|
||||||
|
---@param args HighlightDisplayArgs
|
||||||
|
function HighlightDisplay:new(args)
|
||||||
|
self.group = args.group
|
||||||
|
|
||||||
|
local concrete = self.group
|
||||||
|
|
||||||
-- maybe follow links
|
-- maybe follow links
|
||||||
local links = {}
|
local links = {}
|
||||||
local link = vim.api.nvim_get_hl(0, { name = o.group }).link
|
local link = vim.api.nvim_get_hl(0, { name = self.group }).link
|
||||||
while link do
|
while link do
|
||||||
table.insert(links, link)
|
table.insert(links, link)
|
||||||
concrete = link
|
concrete = link
|
||||||
link = vim.api.nvim_get_hl(0, { name = link }).link
|
link = vim.api.nvim_get_hl(0, { name = link }).link
|
||||||
end
|
end
|
||||||
o.links = table.concat(links, " ")
|
self.links = table.concat(links, " ")
|
||||||
|
|
||||||
-- concrete definition
|
-- concrete definition
|
||||||
local ok, res = pcall(vim.api.nvim_cmd, { cmd = "highlight", args = { concrete } }, { output = true })
|
local ok, res = pcall(vim.api.nvim_cmd, { cmd = "highlight", args = { concrete } }, { output = true })
|
||||||
if ok and type(res) == "string" then
|
if ok and type(res) == "string" then
|
||||||
o.def = res:gsub(".*xxx *", "")
|
self.def = res:gsub(".*xxx *", "")
|
||||||
else
|
else
|
||||||
o.def = ""
|
self.def = ""
|
||||||
end
|
end
|
||||||
|
|
||||||
return o
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---Render one group.
|
---Render one group.
|
||||||
@ -87,7 +88,7 @@ end
|
|||||||
|
|
||||||
---Run a test similar to :so $VIMRUNTIME/syntax/hitest.vim
|
---Run a test similar to :so $VIMRUNTIME/syntax/hitest.vim
|
||||||
---Display all nvim-tree and neovim highlight groups, their link chain and actual definition
|
---Display all nvim-tree and neovim highlight groups, their link chain and actual definition
|
||||||
function M.hi_test()
|
return function()
|
||||||
-- create a buffer
|
-- create a buffer
|
||||||
local bufnr = vim.api.nvim_create_buf(false, true)
|
local bufnr = vim.api.nvim_create_buf(false, true)
|
||||||
|
|
||||||
@ -96,7 +97,7 @@ function M.hi_test()
|
|||||||
-- nvim-tree groups, ordered
|
-- nvim-tree groups, ordered
|
||||||
local displays = {}
|
local displays = {}
|
||||||
for _, highlight_group in ipairs(appearance.HIGHLIGHT_GROUPS) do
|
for _, highlight_group in ipairs(appearance.HIGHLIGHT_GROUPS) do
|
||||||
local display = HighlightDisplay:new(highlight_group.group)
|
local display = HighlightDisplay({ group = highlight_group.group })
|
||||||
table.insert(displays, display)
|
table.insert(displays, display)
|
||||||
end
|
end
|
||||||
l = render_displays("nvim-tree", displays, bufnr, l)
|
l = render_displays("nvim-tree", displays, bufnr, l)
|
||||||
@ -110,7 +111,7 @@ function M.hi_test()
|
|||||||
if ok then
|
if ok then
|
||||||
for group in string.gmatch(out, "(%w*)%s+xxx") do
|
for group in string.gmatch(out, "(%w*)%s+xxx") do
|
||||||
if group:find("NvimTree", 1, true) ~= 1 then
|
if group:find("NvimTree", 1, true) ~= 1 then
|
||||||
local display = HighlightDisplay:new(group)
|
local display = HighlightDisplay({ group = group })
|
||||||
if #display.group + #display.links > SHORT_LEN then
|
if #display.group + #display.links > SHORT_LEN then
|
||||||
table.insert(displays_long, display)
|
table.insert(displays_long, display)
|
||||||
else
|
else
|
||||||
@ -137,5 +138,3 @@ function M.hi_test()
|
|||||||
|
|
||||||
vim.cmd.buffer(bufnr)
|
vim.cmd.buffer(bufnr)
|
||||||
end
|
end
|
||||||
|
|
||||||
return M
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
---Generic class, useful for inheritence.
|
|
||||||
---@class (exact) Class
|
|
||||||
local Class = {}
|
|
||||||
|
|
||||||
---@generic T
|
|
||||||
---@param self T
|
|
||||||
---@param o T|nil
|
|
||||||
---@return T
|
|
||||||
function Class:new(o)
|
|
||||||
o = o or {}
|
|
||||||
|
|
||||||
setmetatable(o, self)
|
|
||||||
self.__index = self ---@diagnostic disable-line: inject-field
|
|
||||||
|
|
||||||
return o
|
|
||||||
end
|
|
||||||
|
|
||||||
---Object is an instance of class
|
|
||||||
---This will start with the lowest class and loop over all the superclasses.
|
|
||||||
---@generic T
|
|
||||||
---@param class T
|
|
||||||
---@return boolean
|
|
||||||
function Class:is(class)
|
|
||||||
local mt = getmetatable(self)
|
|
||||||
while mt do
|
|
||||||
if mt == class then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
mt = getmetatable(mt)
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
---Return object if it is an instance of class, otherwise nil
|
|
||||||
---@generic T
|
|
||||||
---@param class T
|
|
||||||
---@return T|nil
|
|
||||||
function Class:as(class)
|
|
||||||
return self:is(class) and self or nil
|
|
||||||
end
|
|
||||||
|
|
||||||
-- avoid unused param warnings in abstract methods
|
|
||||||
---@param ... any
|
|
||||||
function Class:nop(...) --luacheck: ignore 212
|
|
||||||
end
|
|
||||||
|
|
||||||
return Class
|
|
||||||
91
lua/nvim-tree/classic.lua
Normal file
91
lua/nvim-tree/classic.lua
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
--
|
||||||
|
-- classic
|
||||||
|
--
|
||||||
|
-- Copyright (c) 2014, rxi
|
||||||
|
--
|
||||||
|
-- This module is free software; you can redistribute it and/or modify it under
|
||||||
|
-- the terms of the MIT license. See LICENSE for details.
|
||||||
|
--
|
||||||
|
-- https://github.com/rxi/classic
|
||||||
|
--
|
||||||
|
|
||||||
|
---@class (exact) Class
|
||||||
|
---@field super Class
|
||||||
|
---@field private implements table<Class, boolean>
|
||||||
|
local Class = {}
|
||||||
|
Class.__index = Class ---@diagnostic disable-line: inject-field
|
||||||
|
|
||||||
|
---Default constructor
|
||||||
|
---@protected
|
||||||
|
function Class:new(...) --luacheck: ignore 212
|
||||||
|
end
|
||||||
|
|
||||||
|
---Extend a class, setting .super
|
||||||
|
function Class:extend()
|
||||||
|
local cls = {}
|
||||||
|
for k, v in pairs(self) do
|
||||||
|
if k:find("__") == 1 then
|
||||||
|
cls[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
cls.__index = cls
|
||||||
|
cls.super = self
|
||||||
|
setmetatable(cls, self)
|
||||||
|
return cls
|
||||||
|
end
|
||||||
|
|
||||||
|
---Implement the functions of a mixin
|
||||||
|
---Add the mixin to .implements
|
||||||
|
---@param mixin Class
|
||||||
|
function Class:implement(mixin)
|
||||||
|
if not rawget(self, "implements") then
|
||||||
|
-- set on the class itself instead of parents
|
||||||
|
rawset(self, "implements", {})
|
||||||
|
end
|
||||||
|
self.implements[mixin] = true
|
||||||
|
for k, v in pairs(mixin) do
|
||||||
|
if self[k] == nil and type(v) == "function" then
|
||||||
|
self[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---Object is an instance of class or implements a mixin
|
||||||
|
---@generic T
|
||||||
|
---@param class T
|
||||||
|
---@return boolean
|
||||||
|
function Class:is(class)
|
||||||
|
local mt = getmetatable(self)
|
||||||
|
while mt do
|
||||||
|
if mt == class then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
if mt.implements and mt.implements[class] then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
mt = getmetatable(mt)
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
---Return object if :is otherwise nil
|
||||||
|
---@generic T
|
||||||
|
---@param class T
|
||||||
|
---@return T|nil
|
||||||
|
function Class:as(class)
|
||||||
|
return self:is(class) and self or nil
|
||||||
|
end
|
||||||
|
|
||||||
|
---Constructor to create instance, call :new and return
|
||||||
|
function Class:__call(...)
|
||||||
|
local obj = setmetatable({}, self)
|
||||||
|
obj:new(...)
|
||||||
|
return obj
|
||||||
|
end
|
||||||
|
|
||||||
|
-- avoid unused param warnings in abstract methods
|
||||||
|
---@param ... any
|
||||||
|
function Class:nop(...) --luacheck: ignore 212
|
||||||
|
end
|
||||||
|
|
||||||
|
return Class
|
||||||
@ -1,4 +1,5 @@
|
|||||||
local events = require("nvim-tree.events")
|
local events = require("nvim-tree.events")
|
||||||
|
local notify = require("nvim-tree.notify")
|
||||||
local view = require("nvim-tree.view")
|
local view = require("nvim-tree.view")
|
||||||
local log = require("nvim-tree.log")
|
local log = require("nvim-tree.log")
|
||||||
|
|
||||||
@ -15,7 +16,21 @@ function M.init(foldername)
|
|||||||
if TreeExplorer then
|
if TreeExplorer then
|
||||||
TreeExplorer:destroy()
|
TreeExplorer:destroy()
|
||||||
end
|
end
|
||||||
TreeExplorer = require("nvim-tree.explorer"):create(foldername)
|
|
||||||
|
local err, path
|
||||||
|
|
||||||
|
if foldername then
|
||||||
|
path, err = vim.loop.fs_realpath(foldername)
|
||||||
|
else
|
||||||
|
path, err = vim.loop.cwd()
|
||||||
|
end
|
||||||
|
if path then
|
||||||
|
TreeExplorer = require("nvim-tree.explorer")({ path = path })
|
||||||
|
else
|
||||||
|
notify.error(err)
|
||||||
|
TreeExplorer = nil
|
||||||
|
end
|
||||||
|
|
||||||
if not first_init_done then
|
if not first_init_done then
|
||||||
events._dispatch_ready()
|
events._dispatch_ready()
|
||||||
first_init_done = true
|
first_init_done = true
|
||||||
|
|||||||
@ -1,32 +1,5 @@
|
|||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
---Must be synced with uv.fs_stat.result as it is compared with it
|
|
||||||
---@enum (key) NODE_TYPE
|
|
||||||
M.NODE_TYPE = {
|
|
||||||
directory = 1,
|
|
||||||
file = 2,
|
|
||||||
link = 4,
|
|
||||||
}
|
|
||||||
|
|
||||||
---Setup options for "highlight_*"
|
|
||||||
---@enum HL_POSITION
|
|
||||||
M.HL_POSITION = {
|
|
||||||
none = 0,
|
|
||||||
icon = 1,
|
|
||||||
name = 2,
|
|
||||||
all = 4,
|
|
||||||
}
|
|
||||||
|
|
||||||
---Setup options for "*_placement"
|
|
||||||
---@enum ICON_PLACEMENT
|
|
||||||
M.ICON_PLACEMENT = {
|
|
||||||
none = 0,
|
|
||||||
signcolumn = 1,
|
|
||||||
before = 2,
|
|
||||||
after = 3,
|
|
||||||
right_align = 4,
|
|
||||||
}
|
|
||||||
|
|
||||||
---Reason for filter in filter.lua
|
---Reason for filter in filter.lua
|
||||||
---@enum FILTER_REASON
|
---@enum FILTER_REASON
|
||||||
M.FILTER_REASON = {
|
M.FILTER_REASON = {
|
||||||
|
|||||||
@ -1,52 +1,59 @@
|
|||||||
local utils = require("nvim-tree.utils")
|
local utils = require("nvim-tree.utils")
|
||||||
local FILTER_REASON = require("nvim-tree.enum").FILTER_REASON
|
local FILTER_REASON = require("nvim-tree.enum").FILTER_REASON
|
||||||
|
|
||||||
---@class Filters to handle all opts.filters and related API
|
local Class = require("nvim-tree.classic")
|
||||||
---@field config table hydrated user opts.filters
|
|
||||||
|
---@alias FilterType "custom" | "dotfiles" | "git_ignored" | "git_clean" | "no_buffer" | "no_bookmark"
|
||||||
|
|
||||||
|
---@class (exact) Filters: Class
|
||||||
|
---@field enabled boolean
|
||||||
|
---@field state table<FilterType, boolean>
|
||||||
---@field private explorer Explorer
|
---@field private explorer Explorer
|
||||||
---@field private exclude_list string[] filters.exclude
|
---@field private exclude_list string[] filters.exclude
|
||||||
---@field private ignore_list string[] filters.custom string table
|
---@field private ignore_list table<string, boolean> filters.custom string table
|
||||||
---@field private custom_function (fun(absolute_path: string): boolean)|nil filters.custom function
|
---@field private custom_function (fun(absolute_path: string): boolean)|nil filters.custom function
|
||||||
local Filters = {}
|
local Filters = Class:extend()
|
||||||
|
|
||||||
---@param opts table user options
|
---@class Filters
|
||||||
---@param explorer Explorer
|
---@overload fun(args: FiltersArgs): Filters
|
||||||
---@return Filters
|
|
||||||
function Filters:new(opts, explorer)
|
---@class (exact) FiltersArgs
|
||||||
local o = {
|
---@field explorer Explorer
|
||||||
explorer = explorer,
|
|
||||||
ignore_list = {},
|
---@protected
|
||||||
exclude_list = opts.filters.exclude,
|
---@param args FiltersArgs
|
||||||
custom_function = nil,
|
function Filters:new(args)
|
||||||
config = {
|
self.explorer = args.explorer
|
||||||
enable = opts.filters.enable,
|
self.ignore_list = {}
|
||||||
filter_custom = true,
|
self.exclude_list = self.explorer.opts.filters.exclude
|
||||||
filter_dotfiles = opts.filters.dotfiles,
|
self.custom_function = nil
|
||||||
filter_git_ignored = opts.filters.git_ignored,
|
|
||||||
filter_git_clean = opts.filters.git_clean,
|
self.enabled = self.explorer.opts.filters.enable
|
||||||
filter_no_buffer = opts.filters.no_buffer,
|
self.state = {
|
||||||
filter_no_bookmark = opts.filters.no_bookmark,
|
custom = true,
|
||||||
},
|
dotfiles = self.explorer.opts.filters.dotfiles,
|
||||||
|
git_ignored = self.explorer.opts.filters.git_ignored,
|
||||||
|
git_clean = self.explorer.opts.filters.git_clean,
|
||||||
|
no_buffer = self.explorer.opts.filters.no_buffer,
|
||||||
|
no_bookmark = self.explorer.opts.filters.no_bookmark,
|
||||||
}
|
}
|
||||||
|
|
||||||
local custom_filter = opts.filters.custom
|
local custom_filter = self.explorer.opts.filters.custom
|
||||||
if type(custom_filter) == "function" then
|
if type(custom_filter) == "function" then
|
||||||
o.custom_function = custom_filter
|
self.custom_function = custom_filter
|
||||||
else
|
else
|
||||||
if custom_filter and #custom_filter > 0 then
|
if custom_filter and #custom_filter > 0 then
|
||||||
for _, filter_name in pairs(custom_filter) do
|
for _, filter_name in pairs(custom_filter) do
|
||||||
o.ignore_list[filter_name] = true
|
self.ignore_list[filter_name] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
setmetatable(o, self)
|
|
||||||
self.__index = self
|
|
||||||
return o
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@private
|
||||||
---@param path string
|
---@param path string
|
||||||
---@return boolean
|
---@return boolean
|
||||||
local function is_excluded(self, path)
|
function Filters:is_excluded(path)
|
||||||
for _, node in ipairs(self.exclude_list) do
|
for _, node in ipairs(self.exclude_list) do
|
||||||
if path:match(node) then
|
if path:match(node) then
|
||||||
return true
|
return true
|
||||||
@ -56,10 +63,11 @@ local function is_excluded(self, path)
|
|||||||
end
|
end
|
||||||
|
|
||||||
---Check if the given path is git clean/ignored
|
---Check if the given path is git clean/ignored
|
||||||
|
---@private
|
||||||
---@param path string Absolute path
|
---@param path string Absolute path
|
||||||
---@param project GitProject from prepare
|
---@param project GitProject from prepare
|
||||||
---@return boolean
|
---@return boolean
|
||||||
local function git(self, path, project)
|
function Filters:git(path, project)
|
||||||
if type(project) ~= "table" or type(project.files) ~= "table" or type(project.dirs) ~= "table" then
|
if type(project) ~= "table" or type(project.files) ~= "table" or type(project.dirs) ~= "table" then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
@ -70,12 +78,12 @@ local function git(self, path, project)
|
|||||||
xy = xy or project.dirs.indirect[path] and project.dirs.indirect[path][1]
|
xy = xy or project.dirs.indirect[path] and project.dirs.indirect[path][1]
|
||||||
|
|
||||||
-- filter ignored; overrides clean as they are effectively dirty
|
-- filter ignored; overrides clean as they are effectively dirty
|
||||||
if self.config.filter_git_ignored and xy == "!!" then
|
if self.state.git_ignored and xy == "!!" then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- filter clean
|
-- filter clean
|
||||||
if self.config.filter_git_clean and not xy then
|
if self.state.git_clean and not xy then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -83,11 +91,12 @@ local function git(self, path, project)
|
|||||||
end
|
end
|
||||||
|
|
||||||
---Check if the given path has no listed buffer
|
---Check if the given path has no listed buffer
|
||||||
|
---@private
|
||||||
---@param path string Absolute path
|
---@param path string Absolute path
|
||||||
---@param bufinfo table vim.fn.getbufinfo { buflisted = 1 }
|
---@param bufinfo table vim.fn.getbufinfo { buflisted = 1 }
|
||||||
---@return boolean
|
---@return boolean
|
||||||
local function buf(self, path, bufinfo)
|
function Filters:buf(path, bufinfo)
|
||||||
if not self.config.filter_no_buffer or type(bufinfo) ~= "table" then
|
if not self.state.no_buffer or type(bufinfo) ~= "table" then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -101,19 +110,21 @@ local function buf(self, path, bufinfo)
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@private
|
||||||
---@param path string
|
---@param path string
|
||||||
---@return boolean
|
---@return boolean
|
||||||
local function dotfile(self, path)
|
function Filters:dotfile(path)
|
||||||
return self.config.filter_dotfiles and utils.path_basename(path):sub(1, 1) == "."
|
return self.state.dotfiles and utils.path_basename(path):sub(1, 1) == "."
|
||||||
end
|
end
|
||||||
|
|
||||||
---Bookmark is present
|
---Bookmark is present
|
||||||
|
---@private
|
||||||
---@param path string
|
---@param path string
|
||||||
---@param path_type string|nil filetype of path
|
---@param path_type string|nil filetype of path
|
||||||
---@param bookmarks table<string, string|nil> path, filetype table of bookmarked files
|
---@param bookmarks table<string, string|nil> path, filetype table of bookmarked files
|
||||||
---@return boolean
|
---@return boolean
|
||||||
local function bookmark(self, path, path_type, bookmarks)
|
function Filters:bookmark(path, path_type, bookmarks)
|
||||||
if not self.config.filter_no_bookmark then
|
if not self.state.no_bookmark then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
-- if bookmark is empty, we should see a empty filetree
|
-- if bookmark is empty, we should see a empty filetree
|
||||||
@ -145,10 +156,11 @@ local function bookmark(self, path, path_type, bookmarks)
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@private
|
||||||
---@param path string
|
---@param path string
|
||||||
---@return boolean
|
---@return boolean
|
||||||
local function custom(self, path)
|
function Filters:custom(path)
|
||||||
if not self.config.filter_custom then
|
if not self.state.custom then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -190,7 +202,7 @@ function Filters:prepare(project)
|
|||||||
bookmarks = {},
|
bookmarks = {},
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.config.filter_no_buffer then
|
if self.state.no_buffer then
|
||||||
status.bufinfo = vim.fn.getbufinfo({ buflisted = 1 })
|
status.bufinfo = vim.fn.getbufinfo({ buflisted = 1 })
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -210,20 +222,20 @@ end
|
|||||||
---@param status table from prepare
|
---@param status table from prepare
|
||||||
---@return boolean
|
---@return boolean
|
||||||
function Filters:should_filter(path, fs_stat, status)
|
function Filters:should_filter(path, fs_stat, status)
|
||||||
if not self.config.enable then
|
if not self.enabled then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- exclusions override all filters
|
-- exclusions override all filters
|
||||||
if is_excluded(self, path) then
|
if self:is_excluded(path) then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
return git(self, path, status.project)
|
return self:git(path, status.project)
|
||||||
or buf(self, path, status.bufinfo)
|
or self:buf(path, status.bufinfo)
|
||||||
or dotfile(self, path)
|
or self:dotfile(path)
|
||||||
or custom(self, path)
|
or self:custom(path)
|
||||||
or bookmark(self, path, fs_stat and fs_stat.type, status.bookmarks)
|
or self:bookmark(path, fs_stat and fs_stat.type, status.bookmarks)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check if the given path should be filtered, and provide the reason why it was
|
--- Check if the given path should be filtered, and provide the reason why it was
|
||||||
@ -232,27 +244,44 @@ end
|
|||||||
---@param status table from prepare
|
---@param status table from prepare
|
||||||
---@return FILTER_REASON
|
---@return FILTER_REASON
|
||||||
function Filters:should_filter_as_reason(path, fs_stat, status)
|
function Filters:should_filter_as_reason(path, fs_stat, status)
|
||||||
if not self.config.enable then
|
if not self.enabled then
|
||||||
return FILTER_REASON.none
|
return FILTER_REASON.none
|
||||||
end
|
end
|
||||||
|
|
||||||
if is_excluded(self, path) then
|
if self:is_excluded(path) then
|
||||||
return FILTER_REASON.none
|
return FILTER_REASON.none
|
||||||
end
|
end
|
||||||
|
|
||||||
if git(self, path, status.project) then
|
if self:git(path, status.project) then
|
||||||
return FILTER_REASON.git
|
return FILTER_REASON.git
|
||||||
elseif buf(self, path, status.bufinfo) then
|
elseif self:buf(path, status.bufinfo) then
|
||||||
return FILTER_REASON.buf
|
return FILTER_REASON.buf
|
||||||
elseif dotfile(self, path) then
|
elseif self:dotfile(path) then
|
||||||
return FILTER_REASON.dotfile
|
return FILTER_REASON.dotfile
|
||||||
elseif custom(self, path) then
|
elseif self:custom(path) then
|
||||||
return FILTER_REASON.custom
|
return FILTER_REASON.custom
|
||||||
elseif bookmark(self, path, fs_stat and fs_stat.type, status.bookmarks) then
|
elseif self:bookmark(path, fs_stat and fs_stat.type, status.bookmarks) then
|
||||||
return FILTER_REASON.bookmark
|
return FILTER_REASON.bookmark
|
||||||
else
|
else
|
||||||
return FILTER_REASON.none
|
return FILTER_REASON.none
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Toggle a type and refresh
|
||||||
|
---@private
|
||||||
|
---@param type FilterType? nil to disable all
|
||||||
|
function Filters:toggle(type)
|
||||||
|
if not type or self.state[type] == nil then
|
||||||
|
self.enabled = not self.enabled
|
||||||
|
else
|
||||||
|
self.state[type] = not self.state[type]
|
||||||
|
end
|
||||||
|
|
||||||
|
local node = self.explorer:get_node_at_cursor()
|
||||||
|
self.explorer:reload_explorer()
|
||||||
|
if node then
|
||||||
|
utils.focus_node_or_parent(node)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return Filters
|
return Filters
|
||||||
|
|||||||
@ -3,7 +3,6 @@ local buffers = require("nvim-tree.buffers")
|
|||||||
local core = require("nvim-tree.core")
|
local core = require("nvim-tree.core")
|
||||||
local git = require("nvim-tree.git")
|
local git = require("nvim-tree.git")
|
||||||
local log = require("nvim-tree.log")
|
local log = require("nvim-tree.log")
|
||||||
local notify = require("nvim-tree.notify")
|
|
||||||
local utils = require("nvim-tree.utils")
|
local utils = require("nvim-tree.utils")
|
||||||
local view = require("nvim-tree.view")
|
local view = require("nvim-tree.view")
|
||||||
local node_factory = require("nvim-tree.node.factory")
|
local node_factory = require("nvim-tree.node.factory")
|
||||||
@ -18,7 +17,7 @@ local NodeIterator = require("nvim-tree.iterators.node-iterator")
|
|||||||
local Filters = require("nvim-tree.explorer.filters")
|
local Filters = require("nvim-tree.explorer.filters")
|
||||||
local Marks = require("nvim-tree.marks")
|
local Marks = require("nvim-tree.marks")
|
||||||
local LiveFilter = require("nvim-tree.explorer.live-filter")
|
local LiveFilter = require("nvim-tree.explorer.live-filter")
|
||||||
local Sorters = require("nvim-tree.explorer.sorters")
|
local Sorter = require("nvim-tree.explorer.sorter")
|
||||||
local Clipboard = require("nvim-tree.actions.fs.clipboard")
|
local Clipboard = require("nvim-tree.actions.fs.clipboard")
|
||||||
local Renderer = require("nvim-tree.renderer")
|
local Renderer = require("nvim-tree.renderer")
|
||||||
|
|
||||||
@ -36,51 +35,39 @@ local config
|
|||||||
---@field sorters Sorter
|
---@field sorters Sorter
|
||||||
---@field marks Marks
|
---@field marks Marks
|
||||||
---@field clipboard Clipboard
|
---@field clipboard Clipboard
|
||||||
local Explorer = RootNode:new()
|
local Explorer = RootNode:extend()
|
||||||
|
|
||||||
---Static factory method
|
---@class Explorer
|
||||||
---@param path string?
|
---@overload fun(args: ExplorerArgs): Explorer
|
||||||
---@return Explorer?
|
|
||||||
function Explorer:create(path)
|
|
||||||
local err
|
|
||||||
|
|
||||||
if path then
|
---@class (exact) ExplorerArgs
|
||||||
path, err = vim.loop.fs_realpath(path)
|
---@field path string
|
||||||
else
|
|
||||||
path, err = vim.loop.cwd()
|
|
||||||
end
|
|
||||||
if not path then
|
|
||||||
notify.error(err)
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
---@type Explorer
|
---@protected
|
||||||
local explorer_placeholder = nil
|
---@param args ExplorerArgs
|
||||||
|
function Explorer:new(args)
|
||||||
|
Explorer.super.new(self, {
|
||||||
|
explorer = self,
|
||||||
|
absolute_path = args.path,
|
||||||
|
name = "..",
|
||||||
|
})
|
||||||
|
|
||||||
local o = RootNode:create(explorer_placeholder, path, "..", nil)
|
self.uid_explorer = vim.loop.hrtime()
|
||||||
|
self.augroup_id = vim.api.nvim_create_augroup("NvimTree_Explorer_" .. self.uid_explorer, {})
|
||||||
|
|
||||||
o = self:new(o)
|
self.open = true
|
||||||
|
self.opts = config
|
||||||
|
|
||||||
o.explorer = o
|
self.sorters = Sorter({ explorer = self })
|
||||||
|
self.renderer = Renderer({ explorer = self })
|
||||||
|
self.filters = Filters({ explorer = self })
|
||||||
|
self.live_filter = LiveFilter({ explorer = self })
|
||||||
|
self.marks = Marks({ explorer = self })
|
||||||
|
self.clipboard = Clipboard({ explorer = self })
|
||||||
|
|
||||||
o.uid_explorer = vim.loop.hrtime()
|
self:create_autocmds()
|
||||||
o.augroup_id = vim.api.nvim_create_augroup("NvimTree_Explorer_" .. o.uid_explorer, {})
|
|
||||||
|
|
||||||
o.open = true
|
self:_load(self)
|
||||||
o.opts = config
|
|
||||||
|
|
||||||
o.sorters = Sorters:create(config)
|
|
||||||
o.renderer = Renderer:new(config, o)
|
|
||||||
o.filters = Filters:new(config, o)
|
|
||||||
o.live_filter = LiveFilter:new(config, o)
|
|
||||||
o.marks = Marks:new(config, o)
|
|
||||||
o.clipboard = Clipboard:new(config, o)
|
|
||||||
|
|
||||||
o:create_autocmds()
|
|
||||||
|
|
||||||
o:_load(o)
|
|
||||||
|
|
||||||
return o
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function Explorer:destroy()
|
function Explorer:destroy()
|
||||||
@ -114,7 +101,7 @@ function Explorer:create_autocmds()
|
|||||||
vim.api.nvim_create_autocmd("BufReadPost", {
|
vim.api.nvim_create_autocmd("BufReadPost", {
|
||||||
group = self.augroup_id,
|
group = self.augroup_id,
|
||||||
callback = function(data)
|
callback = function(data)
|
||||||
if (self.filters.config.filter_no_buffer or self.opts.highlight_opened_files ~= "none") and vim.bo[data.buf].buftype == "" then
|
if (self.filters.state.no_buffer or self.opts.highlight_opened_files ~= "none") and vim.bo[data.buf].buftype == "" then
|
||||||
utils.debounce("Buf:filter_buffer_" .. self.uid_explorer, self.opts.view.debounce_delay, function()
|
utils.debounce("Buf:filter_buffer_" .. self.uid_explorer, self.opts.view.debounce_delay, function()
|
||||||
self:reload_explorer()
|
self:reload_explorer()
|
||||||
end)
|
end)
|
||||||
@ -126,7 +113,7 @@ function Explorer:create_autocmds()
|
|||||||
vim.api.nvim_create_autocmd("BufUnload", {
|
vim.api.nvim_create_autocmd("BufUnload", {
|
||||||
group = self.augroup_id,
|
group = self.augroup_id,
|
||||||
callback = function(data)
|
callback = function(data)
|
||||||
if (self.filters.config.filter_no_buffer or self.opts.highlight_opened_files ~= "none") and vim.bo[data.buf].buftype == "" then
|
if (self.filters.state.no_buffer or self.opts.highlight_opened_files ~= "none") and vim.bo[data.buf].buftype == "" then
|
||||||
utils.debounce("Buf:filter_buffer_" .. self.uid_explorer, self.opts.view.debounce_delay, function()
|
utils.debounce("Buf:filter_buffer_" .. self.uid_explorer, self.opts.view.debounce_delay, function()
|
||||||
self:reload_explorer()
|
self:reload_explorer()
|
||||||
end)
|
end)
|
||||||
@ -213,10 +200,10 @@ function Explorer:reload(node, project)
|
|||||||
|
|
||||||
-- To reset we must 'zero' everything that we use
|
-- To reset we must 'zero' everything that we use
|
||||||
node.hidden_stats = vim.tbl_deep_extend("force", node.hidden_stats or {}, {
|
node.hidden_stats = vim.tbl_deep_extend("force", node.hidden_stats or {}, {
|
||||||
git = 0,
|
git = 0,
|
||||||
buf = 0,
|
buf = 0,
|
||||||
dotfile = 0,
|
dotfile = 0,
|
||||||
custom = 0,
|
custom = 0,
|
||||||
bookmark = 0,
|
bookmark = 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -246,7 +233,13 @@ function Explorer:reload(node, project)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if not nodes_by_path[abs] then
|
if not nodes_by_path[abs] then
|
||||||
local new_child = node_factory.create_node(self, node, abs, stat, name)
|
local new_child = node_factory.create({
|
||||||
|
explorer = self,
|
||||||
|
parent = node,
|
||||||
|
absolute_path = abs,
|
||||||
|
name = name,
|
||||||
|
fs_stat = stat
|
||||||
|
})
|
||||||
if new_child then
|
if new_child then
|
||||||
table.insert(node.nodes, new_child)
|
table.insert(node.nodes, new_child)
|
||||||
nodes_by_path[abs] = new_child
|
nodes_by_path[abs] = new_child
|
||||||
@ -362,10 +355,10 @@ function Explorer:populate_children(handle, cwd, node, project, parent)
|
|||||||
local filter_status = parent.filters:prepare(project)
|
local filter_status = parent.filters:prepare(project)
|
||||||
|
|
||||||
node.hidden_stats = vim.tbl_deep_extend("force", node.hidden_stats or {}, {
|
node.hidden_stats = vim.tbl_deep_extend("force", node.hidden_stats or {}, {
|
||||||
git = 0,
|
git = 0,
|
||||||
buf = 0,
|
buf = 0,
|
||||||
dotfile = 0,
|
dotfile = 0,
|
||||||
custom = 0,
|
custom = 0,
|
||||||
bookmark = 0,
|
bookmark = 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -384,7 +377,13 @@ function Explorer:populate_children(handle, cwd, node, project, parent)
|
|||||||
local stat = vim.loop.fs_lstat(abs)
|
local stat = vim.loop.fs_lstat(abs)
|
||||||
local filter_reason = parent.filters:should_filter_as_reason(abs, stat, filter_status)
|
local filter_reason = parent.filters:should_filter_as_reason(abs, stat, filter_status)
|
||||||
if filter_reason == FILTER_REASON.none and not nodes_by_path[abs] then
|
if filter_reason == FILTER_REASON.none and not nodes_by_path[abs] then
|
||||||
local child = node_factory.create_node(self, node, abs, stat, name)
|
local child = node_factory.create({
|
||||||
|
explorer = self,
|
||||||
|
parent = node,
|
||||||
|
absolute_path = abs,
|
||||||
|
name = name,
|
||||||
|
fs_stat = stat
|
||||||
|
})
|
||||||
if child then
|
if child then
|
||||||
table.insert(node.nodes, child)
|
table.insert(node.nodes, child)
|
||||||
nodes_by_path[child.absolute_path] = true
|
nodes_by_path[child.absolute_path] = true
|
||||||
|
|||||||
@ -1,29 +1,30 @@
|
|||||||
local view = require("nvim-tree.view")
|
local view = require("nvim-tree.view")
|
||||||
local utils = require("nvim-tree.utils")
|
local utils = require("nvim-tree.utils")
|
||||||
|
|
||||||
|
local Class = require("nvim-tree.classic")
|
||||||
local Iterator = require("nvim-tree.iterators.node-iterator")
|
local Iterator = require("nvim-tree.iterators.node-iterator")
|
||||||
local DirectoryNode = require("nvim-tree.node.directory")
|
local DirectoryNode = require("nvim-tree.node.directory")
|
||||||
|
|
||||||
---@class LiveFilter
|
---@class (exact) LiveFilter: Class
|
||||||
---@field explorer Explorer
|
---@field explorer Explorer
|
||||||
---@field prefix string
|
---@field prefix string
|
||||||
---@field always_show_folders boolean
|
---@field always_show_folders boolean
|
||||||
---@field filter string
|
---@field filter string
|
||||||
local LiveFilter = {}
|
local LiveFilter = Class:extend()
|
||||||
|
|
||||||
---@param opts table
|
---@class LiveFilter
|
||||||
---@param explorer Explorer
|
---@overload fun(args: LiveFilterArgs): LiveFilter
|
||||||
---@return LiveFilter
|
|
||||||
function LiveFilter:new(opts, explorer)
|
---@class (exact) LiveFilterArgs
|
||||||
local o = {
|
---@field explorer Explorer
|
||||||
explorer = explorer,
|
|
||||||
prefix = opts.live_filter.prefix,
|
---@protected
|
||||||
always_show_folders = opts.live_filter.always_show_folders,
|
---@param args LiveFilterArgs
|
||||||
filter = nil,
|
function LiveFilter:new(args)
|
||||||
}
|
self.explorer = args.explorer
|
||||||
setmetatable(o, self)
|
self.prefix = self.explorer.opts.live_filter.prefix
|
||||||
self.__index = self
|
self.always_show_folders = self.explorer.opts.live_filter.always_show_folders
|
||||||
return o
|
self.filter = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param node_ Node?
|
---@param node_ Node?
|
||||||
@ -81,7 +82,7 @@ end
|
|||||||
---@param node Node
|
---@param node Node
|
||||||
---@return boolean
|
---@return boolean
|
||||||
local function matches(self, node)
|
local function matches(self, node)
|
||||||
if not self.explorer.filters.config.enable then
|
if not self.explorer.filters.enabled then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -168,21 +169,21 @@ local function create_overlay(self)
|
|||||||
if view.View.float.enable then
|
if view.View.float.enable then
|
||||||
-- don't close nvim-tree float when focus is changed to filter window
|
-- don't close nvim-tree float when focus is changed to filter window
|
||||||
vim.api.nvim_clear_autocmds({
|
vim.api.nvim_clear_autocmds({
|
||||||
event = "WinLeave",
|
event = "WinLeave",
|
||||||
pattern = "NvimTree_*",
|
pattern = "NvimTree_*",
|
||||||
group = vim.api.nvim_create_augroup("NvimTree", { clear = false }),
|
group = vim.api.nvim_create_augroup("NvimTree", { clear = false }),
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
configure_buffer_overlay(self)
|
configure_buffer_overlay(self)
|
||||||
overlay_winnr = vim.api.nvim_open_win(overlay_bufnr, true, {
|
overlay_winnr = vim.api.nvim_open_win(overlay_bufnr, true, {
|
||||||
col = 1,
|
col = 1,
|
||||||
row = 0,
|
row = 0,
|
||||||
relative = "cursor",
|
relative = "cursor",
|
||||||
width = calculate_overlay_win_width(self),
|
width = calculate_overlay_win_width(self),
|
||||||
height = 1,
|
height = 1,
|
||||||
border = "none",
|
border = "none",
|
||||||
style = "minimal",
|
style = "minimal",
|
||||||
})
|
})
|
||||||
|
|
||||||
if vim.fn.has("nvim-0.10") == 1 then
|
if vim.fn.has("nvim-0.10") == 1 then
|
||||||
|
|||||||
@ -1,43 +1,25 @@
|
|||||||
local Class = require("nvim-tree.class")
|
local Class = require("nvim-tree.classic")
|
||||||
local DirectoryNode = require("nvim-tree.node.directory")
|
local DirectoryNode = require("nvim-tree.node.directory")
|
||||||
|
|
||||||
local C = {}
|
---@alias SorterType "name" | "case_sensitive" | "modification_time" | "extension" | "suffix" | "filetype"
|
||||||
|
---@alias SorterComparator fun(self: Sorter, a: Node, b: Node): boolean?
|
||||||
|
|
||||||
---@class (exact) SorterCfg
|
---@alias SorterUser fun(nodes: Node[]): SorterType?
|
||||||
---@field sorter string|fun(nodes: Node[])
|
|
||||||
---@field folders_first boolean
|
|
||||||
---@field files_first boolean
|
|
||||||
|
|
||||||
---@class (exact) Sorter: Class
|
---@class (exact) Sorter: Class
|
||||||
---@field cfg SorterCfg
|
---@field private explorer Explorer
|
||||||
---@field user fun(nodes: Node[])?
|
local Sorter = Class:extend()
|
||||||
---@field pre string?
|
|
||||||
local Sorter = Class:new()
|
|
||||||
|
|
||||||
---@param opts table user options
|
---@class Sorter
|
||||||
---@return Sorter
|
---@overload fun(args: SorterArgs): Sorter
|
||||||
function Sorter:create(opts)
|
|
||||||
---@type Sorter
|
|
||||||
local o = {
|
|
||||||
cfg = vim.deepcopy(opts.sort),
|
|
||||||
}
|
|
||||||
o = self:new(o)
|
|
||||||
|
|
||||||
if type(o.cfg.sorter) == "function" then
|
---@class (exact) SorterArgs
|
||||||
o.user = o.cfg.sorter --[[@as fun(nodes: Node[])]]
|
---@field explorer Explorer
|
||||||
elseif type(o.cfg.sorter) == "string" then
|
|
||||||
o.pre = o.cfg.sorter --[[@as string]]
|
|
||||||
end
|
|
||||||
return o
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Predefined comparator, defaulting to name
|
---@protected
|
||||||
---@param sorter string as per options
|
---@param args SorterArgs
|
||||||
---@return fun(a: Node, b: Node): boolean
|
function Sorter:new(args)
|
||||||
function Sorter:get_comparator(sorter)
|
self.explorer = args.explorer
|
||||||
return function(a, b)
|
|
||||||
return (C[sorter] or C.name)(a, b, self.cfg)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---Create a shallow copy of a portion of a list.
|
---Create a shallow copy of a portion of a list.
|
||||||
@ -54,31 +36,32 @@ local function tbl_slice(t, first, last)
|
|||||||
return slice
|
return slice
|
||||||
end
|
end
|
||||||
|
|
||||||
---Evaluate `sort.folders_first` and `sort.files_first`
|
---Evaluate folders_first and sort.files_first returning nil when no order is necessary
|
||||||
---@param a Node
|
---@private
|
||||||
---@param b Node
|
---@type SorterComparator
|
||||||
---@param cfg SorterCfg
|
function Sorter:folders_or_files_first(a, b)
|
||||||
---@return boolean|nil
|
if not (self.explorer.opts.sort.folders_first or self.explorer.opts.sort.files_first) then
|
||||||
local function folders_or_files_first(a, b, cfg)
|
return nil
|
||||||
if not (cfg.folders_first or cfg.files_first) then
|
|
||||||
return
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if not a:is(DirectoryNode) and b:is(DirectoryNode) then
|
if not a:is(DirectoryNode) and b:is(DirectoryNode) then
|
||||||
-- file <> folder
|
-- file <> folder
|
||||||
return cfg.files_first
|
return self.explorer.opts.sort.files_first
|
||||||
elseif a:is(DirectoryNode) and not b:is(DirectoryNode) then
|
elseif a:is(DirectoryNode) and not b:is(DirectoryNode) then
|
||||||
-- folder <> file
|
-- folder <> file
|
||||||
return not cfg.files_first
|
return not self.explorer.opts.sort.files_first
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param t table
|
---@private
|
||||||
|
---@param t Node[]
|
||||||
---@param first number
|
---@param first number
|
||||||
---@param mid number
|
---@param mid number
|
||||||
---@param last number
|
---@param last number
|
||||||
---@param comparator fun(a: Node, b: Node): boolean
|
---@param comparator SorterComparator
|
||||||
local function merge(t, first, mid, last, comparator)
|
function Sorter:merge(t, first, mid, last, comparator)
|
||||||
local n1 = mid - first + 1
|
local n1 = mid - first + 1
|
||||||
local n2 = last - mid
|
local n2 = last - mid
|
||||||
local ls = tbl_slice(t, first, mid)
|
local ls = tbl_slice(t, first, mid)
|
||||||
@ -88,7 +71,7 @@ local function merge(t, first, mid, last, comparator)
|
|||||||
local k = first
|
local k = first
|
||||||
|
|
||||||
while i <= n1 and j <= n2 do
|
while i <= n1 and j <= n2 do
|
||||||
if comparator(ls[i], rs[j]) then
|
if comparator(self, ls[i], rs[j]) then
|
||||||
t[k] = ls[i]
|
t[k] = ls[i]
|
||||||
i = i + 1
|
i = i + 1
|
||||||
else
|
else
|
||||||
@ -111,45 +94,49 @@ local function merge(t, first, mid, last, comparator)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param t table
|
---@private
|
||||||
|
---@param t Node[]
|
||||||
---@param first number
|
---@param first number
|
||||||
---@param last number
|
---@param last number
|
||||||
---@param comparator fun(a: Node, b: Node): boolean
|
---@param comparator SorterComparator
|
||||||
local function split_merge(t, first, last, comparator)
|
function Sorter:split_merge(t, first, last, comparator)
|
||||||
if (last - first) < 1 then
|
if (last - first) < 1 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local mid = math.floor((first + last) / 2)
|
local mid = math.floor((first + last) / 2)
|
||||||
|
|
||||||
split_merge(t, first, mid, comparator)
|
self:split_merge(t, first, mid, comparator)
|
||||||
split_merge(t, mid + 1, last, comparator)
|
self:split_merge(t, mid + 1, last, comparator)
|
||||||
merge(t, first, mid, last, comparator)
|
self:merge(t, first, mid, last, comparator)
|
||||||
end
|
end
|
||||||
|
|
||||||
---Perform a merge sort using sorter option.
|
---Perform a merge sort using sorter option.
|
||||||
---@param t Node[]
|
---@param t Node[]
|
||||||
function Sorter:sort(t)
|
function Sorter:sort(t)
|
||||||
if self.user then
|
if self[self.explorer.opts.sort.sorter] then
|
||||||
|
self:split_merge(t, 1, #t, self[self.explorer.opts.sort.sorter])
|
||||||
|
elseif type(self.explorer.opts.sort.sorter) == "function" then
|
||||||
local t_user = {}
|
local t_user = {}
|
||||||
local origin_index = {}
|
local origin_index = {}
|
||||||
|
|
||||||
for _, n in ipairs(t) do
|
for _, n in ipairs(t) do
|
||||||
table.insert(t_user, {
|
table.insert(t_user, {
|
||||||
absolute_path = n.absolute_path,
|
absolute_path = n.absolute_path,
|
||||||
executable = n.executable,
|
executable = n.executable,
|
||||||
extension = n.extension,
|
extension = n.extension,
|
||||||
filetype = vim.filetype.match({ filename = n.name }),
|
filetype = vim.filetype.match({ filename = n.name }),
|
||||||
link_to = n.link_to,
|
link_to = n.link_to,
|
||||||
name = n.name,
|
name = n.name,
|
||||||
type = n.type,
|
type = n.type,
|
||||||
})
|
})
|
||||||
table.insert(origin_index, n)
|
table.insert(origin_index, n)
|
||||||
end
|
end
|
||||||
|
|
||||||
local predefined = self.user(t_user)
|
-- user may return a SorterType
|
||||||
if predefined then
|
local ret = self.explorer.opts.sort.sorter(t_user)
|
||||||
split_merge(t, 1, #t, self:get_comparator(predefined))
|
if self[ret] then
|
||||||
|
self:split_merge(t, 1, #t, self[ret])
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -162,7 +149,7 @@ function Sorter:sort(t)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- if missing value found, then using origin_index
|
-- if missing value found, then using origin_index
|
||||||
local mini_comparator = function(a, b)
|
local mini_comparator = function(_, a, b)
|
||||||
local a_index = user_index[a.absolute_path] or origin_index[a.absolute_path]
|
local a_index = user_index[a.absolute_path] or origin_index[a.absolute_path]
|
||||||
local b_index = user_index[b.absolute_path] or origin_index[b.absolute_path]
|
local b_index = user_index[b.absolute_path] or origin_index[b.absolute_path]
|
||||||
|
|
||||||
@ -172,48 +159,52 @@ function Sorter:sort(t)
|
|||||||
return (a_index or 0) <= (b_index or 0)
|
return (a_index or 0) <= (b_index or 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
split_merge(t, 1, #t, mini_comparator) -- sort by user order
|
self:split_merge(t, 1, #t, mini_comparator) -- sort by user order
|
||||||
elseif self.pre then
|
|
||||||
split_merge(t, 1, #t, self:get_comparator(self.pre))
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@private
|
||||||
---@param a Node
|
---@param a Node
|
||||||
---@param b Node
|
---@param b Node
|
||||||
---@param ignorecase boolean|nil
|
---@param ignore_case boolean
|
||||||
---@param cfg SorterCfg
|
|
||||||
---@return boolean
|
---@return boolean
|
||||||
local function node_comparator_name_ignorecase_or_not(a, b, ignorecase, cfg)
|
function Sorter:name_case(a, b, ignore_case)
|
||||||
if not (a and b) then
|
if not (a and b) then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
local early_return = folders_or_files_first(a, b, cfg)
|
local early_return = self:folders_or_files_first(a, b)
|
||||||
if early_return ~= nil then
|
if early_return ~= nil then
|
||||||
return early_return
|
return early_return
|
||||||
end
|
end
|
||||||
|
|
||||||
if ignorecase then
|
if ignore_case then
|
||||||
return a.name:lower() <= b.name:lower()
|
return a.name:lower() <= b.name:lower()
|
||||||
else
|
else
|
||||||
return a.name <= b.name
|
return a.name <= b.name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function C.case_sensitive(a, b, cfg)
|
---@private
|
||||||
return node_comparator_name_ignorecase_or_not(a, b, false, cfg)
|
---@type SorterComparator
|
||||||
|
function Sorter:case_sensitive(a, b)
|
||||||
|
return self:name_case(a, b, false)
|
||||||
end
|
end
|
||||||
|
|
||||||
function C.name(a, b, cfg)
|
---@private
|
||||||
return node_comparator_name_ignorecase_or_not(a, b, true, cfg)
|
---@type SorterComparator
|
||||||
|
function Sorter:name(a, b)
|
||||||
|
return self:name_case(a, b, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
function C.modification_time(a, b, cfg)
|
---@private
|
||||||
|
---@type SorterComparator
|
||||||
|
function Sorter:modification_time(a, b)
|
||||||
if not (a and b) then
|
if not (a and b) then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
local early_return = folders_or_files_first(a, b, cfg)
|
local early_return = self:folders_or_files_first(a, b)
|
||||||
if early_return ~= nil then
|
if early_return ~= nil then
|
||||||
return early_return
|
return early_return
|
||||||
end
|
end
|
||||||
@ -232,17 +223,19 @@ function C.modification_time(a, b, cfg)
|
|||||||
return last_modified_b <= last_modified_a
|
return last_modified_b <= last_modified_a
|
||||||
end
|
end
|
||||||
|
|
||||||
function C.suffix(a, b, cfg)
|
---@private
|
||||||
|
---@type SorterComparator
|
||||||
|
function Sorter:suffix(a, b)
|
||||||
if not (a and b) then
|
if not (a and b) then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- directories go first
|
-- directories go first
|
||||||
local early_return = folders_or_files_first(a, b, cfg)
|
local early_return = self:folders_or_files_first(a, b)
|
||||||
if early_return ~= nil then
|
if early_return ~= nil then
|
||||||
return early_return
|
return early_return
|
||||||
elseif a.nodes and b.nodes then
|
elseif a.nodes and b.nodes then
|
||||||
return C.name(a, b, cfg)
|
return self:name(a, b)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- dotfiles go second
|
-- dotfiles go second
|
||||||
@ -251,7 +244,7 @@ function C.suffix(a, b, cfg)
|
|||||||
elseif a.name:sub(1, 1) ~= "." and b.name:sub(1, 1) == "." then
|
elseif a.name:sub(1, 1) ~= "." and b.name:sub(1, 1) == "." then
|
||||||
return false
|
return false
|
||||||
elseif a.name:sub(1, 1) == "." and b.name:sub(1, 1) == "." then
|
elseif a.name:sub(1, 1) == "." and b.name:sub(1, 1) == "." then
|
||||||
return C.name(a, b, cfg)
|
return self:name(a, b)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- unsuffixed go third
|
-- unsuffixed go third
|
||||||
@ -263,7 +256,7 @@ function C.suffix(a, b, cfg)
|
|||||||
elseif a_suffix_ndx and not b_suffix_ndx then
|
elseif a_suffix_ndx and not b_suffix_ndx then
|
||||||
return false
|
return false
|
||||||
elseif not (a_suffix_ndx and b_suffix_ndx) then
|
elseif not (a_suffix_ndx and b_suffix_ndx) then
|
||||||
return C.name(a, b, cfg)
|
return self:name(a, b)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- finally, compare by suffixes
|
-- finally, compare by suffixes
|
||||||
@ -275,18 +268,20 @@ function C.suffix(a, b, cfg)
|
|||||||
elseif not a_suffix and b_suffix then
|
elseif not a_suffix and b_suffix then
|
||||||
return false
|
return false
|
||||||
elseif a_suffix:lower() == b_suffix:lower() then
|
elseif a_suffix:lower() == b_suffix:lower() then
|
||||||
return C.name(a, b, cfg)
|
return self:name(a, b)
|
||||||
end
|
end
|
||||||
|
|
||||||
return a_suffix:lower() < b_suffix:lower()
|
return a_suffix:lower() < b_suffix:lower()
|
||||||
end
|
end
|
||||||
|
|
||||||
function C.extension(a, b, cfg)
|
---@private
|
||||||
|
---@type SorterComparator
|
||||||
|
function Sorter:extension(a, b)
|
||||||
if not (a and b) then
|
if not (a and b) then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
local early_return = folders_or_files_first(a, b, cfg)
|
local early_return = self:folders_or_files_first(a, b)
|
||||||
if early_return ~= nil then
|
if early_return ~= nil then
|
||||||
return early_return
|
return early_return
|
||||||
end
|
end
|
||||||
@ -300,18 +295,20 @@ function C.extension(a, b, cfg)
|
|||||||
local a_ext = (a.extension or ""):lower()
|
local a_ext = (a.extension or ""):lower()
|
||||||
local b_ext = (b.extension or ""):lower()
|
local b_ext = (b.extension or ""):lower()
|
||||||
if a_ext == b_ext then
|
if a_ext == b_ext then
|
||||||
return C.name(a, b, cfg)
|
return self:name(a, b)
|
||||||
end
|
end
|
||||||
|
|
||||||
return a_ext < b_ext
|
return a_ext < b_ext
|
||||||
end
|
end
|
||||||
|
|
||||||
function C.filetype(a, b, cfg)
|
---@private
|
||||||
|
---@type SorterComparator
|
||||||
|
function Sorter:filetype(a, b)
|
||||||
local a_ft = vim.filetype.match({ filename = a.name })
|
local a_ft = vim.filetype.match({ filename = a.name })
|
||||||
local b_ft = vim.filetype.match({ filename = b.name })
|
local b_ft = vim.filetype.match({ filename = b.name })
|
||||||
|
|
||||||
-- directories first
|
-- directories first
|
||||||
local early_return = folders_or_files_first(a, b, cfg)
|
local early_return = self:folders_or_files_first(a, b)
|
||||||
if early_return ~= nil then
|
if early_return ~= nil then
|
||||||
return early_return
|
return early_return
|
||||||
end
|
end
|
||||||
@ -325,7 +322,7 @@ function C.filetype(a, b, cfg)
|
|||||||
|
|
||||||
-- same filetype or both nil, sort by name
|
-- same filetype or both nil, sort by name
|
||||||
if a_ft == b_ft then
|
if a_ft == b_ft then
|
||||||
return C.name(a, b, cfg)
|
return self:name(a, b)
|
||||||
end
|
end
|
||||||
|
|
||||||
return a_ft < b_ft
|
return a_ft < b_ft
|
||||||
@ -83,8 +83,12 @@ function M.create_watcher(node)
|
|||||||
end
|
end
|
||||||
|
|
||||||
M.uid = M.uid + 1
|
M.uid = M.uid + 1
|
||||||
return Watcher:create(path, nil, callback, {
|
return Watcher:create({
|
||||||
context = "explorer:watch:" .. path .. ":" .. M.uid,
|
path = path,
|
||||||
|
callback = callback,
|
||||||
|
data = {
|
||||||
|
context = "explorer:watch:" .. path .. ":" .. M.uid
|
||||||
|
}
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -128,25 +128,25 @@ function M.reload_project(toplevel, path, callback)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
---@type GitRunnerOpts
|
---@type GitRunnerArgs
|
||||||
local runner_opts = {
|
local args = {
|
||||||
toplevel = toplevel,
|
toplevel = toplevel,
|
||||||
path = path,
|
path = path,
|
||||||
list_untracked = git_utils.should_show_untracked(toplevel),
|
list_untracked = git_utils.should_show_untracked(toplevel),
|
||||||
list_ignored = true,
|
list_ignored = true,
|
||||||
timeout = M.config.git.timeout,
|
timeout = M.config.git.timeout,
|
||||||
}
|
}
|
||||||
|
|
||||||
if callback then
|
if callback then
|
||||||
---@param path_xy GitPathXY
|
---@param path_xy GitPathXY
|
||||||
runner_opts.callback = function(path_xy)
|
args.callback = function(path_xy)
|
||||||
reload_git_project(toplevel, path, project, path_xy)
|
reload_git_project(toplevel, path, project, path_xy)
|
||||||
callback()
|
callback()
|
||||||
end
|
end
|
||||||
GitRunner:run(runner_opts)
|
GitRunner:run(args)
|
||||||
else
|
else
|
||||||
-- TODO #1974 use callback once async/await is available
|
-- TODO #1974 use callback once async/await is available
|
||||||
reload_git_project(toplevel, path, project, GitRunner:run(runner_opts))
|
reload_git_project(toplevel, path, project, GitRunner:run(args))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -276,10 +276,10 @@ function M.load_project(path)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local path_xys = GitRunner:run({
|
local path_xys = GitRunner:run({
|
||||||
toplevel = toplevel,
|
toplevel = toplevel,
|
||||||
list_untracked = git_utils.should_show_untracked(toplevel),
|
list_untracked = git_utils.should_show_untracked(toplevel),
|
||||||
list_ignored = true,
|
list_ignored = true,
|
||||||
timeout = M.config.git.timeout,
|
timeout = M.config.git.timeout,
|
||||||
})
|
})
|
||||||
|
|
||||||
local watcher = nil
|
local watcher = nil
|
||||||
@ -298,15 +298,20 @@ function M.load_project(path)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local git_dir = vim.env.GIT_DIR or M._git_dirs_by_toplevel[toplevel] or utils.path_join({ toplevel, ".git" })
|
local git_dir = vim.env.GIT_DIR or M._git_dirs_by_toplevel[toplevel] or utils.path_join({ toplevel, ".git" })
|
||||||
watcher = Watcher:create(git_dir, WATCHED_FILES, callback, {
|
watcher = Watcher:create({
|
||||||
toplevel = toplevel,
|
path = git_dir,
|
||||||
|
files = WATCHED_FILES,
|
||||||
|
callback = callback,
|
||||||
|
data = {
|
||||||
|
toplevel = toplevel,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
if path_xys then
|
if path_xys then
|
||||||
M._projects_by_toplevel[toplevel] = {
|
M._projects_by_toplevel[toplevel] = {
|
||||||
files = path_xys,
|
files = path_xys,
|
||||||
dirs = git_utils.project_files_to_project_dirs(path_xys, toplevel),
|
dirs = git_utils.project_files_to_project_dirs(path_xys, toplevel),
|
||||||
watcher = watcher,
|
watcher = watcher,
|
||||||
}
|
}
|
||||||
return M._projects_by_toplevel[toplevel]
|
return M._projects_by_toplevel[toplevel]
|
||||||
|
|||||||
@ -2,9 +2,23 @@ local log = require("nvim-tree.log")
|
|||||||
local utils = require("nvim-tree.utils")
|
local utils = require("nvim-tree.utils")
|
||||||
local notify = require("nvim-tree.notify")
|
local notify = require("nvim-tree.notify")
|
||||||
|
|
||||||
local Class = require("nvim-tree.class")
|
local Class = require("nvim-tree.classic")
|
||||||
|
|
||||||
---@class (exact) GitRunnerOpts
|
---@class (exact) GitRunner: Class
|
||||||
|
---@field private toplevel string absolute path
|
||||||
|
---@field private path string? absolute path
|
||||||
|
---@field private list_untracked boolean
|
||||||
|
---@field private list_ignored boolean
|
||||||
|
---@field private timeout integer
|
||||||
|
---@field private callback fun(path_xy: GitPathXY)?
|
||||||
|
---@field private path_xy GitPathXY
|
||||||
|
---@field private rc integer? -- -1 indicates timeout
|
||||||
|
local GitRunner = Class:extend()
|
||||||
|
|
||||||
|
---@class GitRunner
|
||||||
|
---@overload fun(args: GitRunnerArgs): GitRunner
|
||||||
|
|
||||||
|
---@class (exact) GitRunnerArgs
|
||||||
---@field toplevel string absolute path
|
---@field toplevel string absolute path
|
||||||
---@field path string? absolute path
|
---@field path string? absolute path
|
||||||
---@field list_untracked boolean
|
---@field list_untracked boolean
|
||||||
@ -12,15 +26,23 @@ local Class = require("nvim-tree.class")
|
|||||||
---@field timeout integer
|
---@field timeout integer
|
||||||
---@field callback fun(path_xy: GitPathXY)?
|
---@field callback fun(path_xy: GitPathXY)?
|
||||||
|
|
||||||
---@class (exact) GitRunner: Class
|
|
||||||
---@field private opts GitRunnerOpts
|
|
||||||
---@field private path_xy GitPathXY
|
|
||||||
---@field private rc integer? -- -1 indicates timeout
|
|
||||||
local GitRunner = Class:new()
|
|
||||||
|
|
||||||
local timeouts = 0
|
local timeouts = 0
|
||||||
local MAX_TIMEOUTS = 5
|
local MAX_TIMEOUTS = 5
|
||||||
|
|
||||||
|
---@protected
|
||||||
|
---@param args GitRunnerArgs
|
||||||
|
function GitRunner:new(args)
|
||||||
|
self.toplevel = args.toplevel
|
||||||
|
self.path = args.path
|
||||||
|
self.list_untracked = args.list_untracked
|
||||||
|
self.list_ignored = args.list_ignored
|
||||||
|
self.timeout = args.timeout
|
||||||
|
self.callback = args.callback
|
||||||
|
|
||||||
|
self.path_xy = {}
|
||||||
|
self.rc = nil
|
||||||
|
end
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
---@param status string
|
---@param status string
|
||||||
---@param path string|nil
|
---@param path string|nil
|
||||||
@ -34,7 +56,7 @@ function GitRunner:parse_status_output(status, path)
|
|||||||
path = path:gsub("/", "\\")
|
path = path:gsub("/", "\\")
|
||||||
end
|
end
|
||||||
if #status > 0 and #path > 0 then
|
if #status > 0 and #path > 0 then
|
||||||
self.path_xy[utils.path_remove_trailing(utils.path_join({ self.opts.toplevel, path }))] = status
|
self.path_xy[utils.path_remove_trailing(utils.path_join({ self.toplevel, path }))] = status
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -81,11 +103,11 @@ end
|
|||||||
---@param stderr_handle uv.uv_pipe_t
|
---@param stderr_handle uv.uv_pipe_t
|
||||||
---@return uv.spawn.options
|
---@return uv.spawn.options
|
||||||
function GitRunner:get_spawn_options(stdout_handle, stderr_handle)
|
function GitRunner:get_spawn_options(stdout_handle, stderr_handle)
|
||||||
local untracked = self.opts.list_untracked and "-u" or nil
|
local untracked = self.list_untracked and "-u" or nil
|
||||||
local ignored = (self.opts.list_untracked and self.opts.list_ignored) and "--ignored=matching" or "--ignored=no"
|
local ignored = (self.list_untracked and self.list_ignored) and "--ignored=matching" or "--ignored=no"
|
||||||
return {
|
return {
|
||||||
args = { "--no-optional-locks", "status", "--porcelain=v1", "-z", ignored, untracked, self.opts.path },
|
args = { "--no-optional-locks", "status", "--porcelain=v1", "-z", ignored, untracked, self.path },
|
||||||
cwd = self.opts.toplevel,
|
cwd = self.toplevel,
|
||||||
stdio = { nil, stdout_handle, stderr_handle },
|
stdio = { nil, stdout_handle, stderr_handle },
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
@ -139,7 +161,7 @@ function GitRunner:run_git_job(callback)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local spawn_options = self:get_spawn_options(stdout, stderr)
|
local spawn_options = self:get_spawn_options(stdout, stderr)
|
||||||
log.line("git", "running job with timeout %dms", self.opts.timeout)
|
log.line("git", "running job with timeout %dms", self.timeout)
|
||||||
log.line("git", "git %s", table.concat(utils.array_remove_nils(spawn_options.args), " "))
|
log.line("git", "git %s", table.concat(utils.array_remove_nils(spawn_options.args), " "))
|
||||||
|
|
||||||
handle, pid = vim.loop.spawn(
|
handle, pid = vim.loop.spawn(
|
||||||
@ -151,7 +173,7 @@ function GitRunner:run_git_job(callback)
|
|||||||
)
|
)
|
||||||
|
|
||||||
timer:start(
|
timer:start(
|
||||||
self.opts.timeout,
|
self.timeout,
|
||||||
0,
|
0,
|
||||||
vim.schedule_wrap(function()
|
vim.schedule_wrap(function()
|
||||||
on_finish(-1)
|
on_finish(-1)
|
||||||
@ -191,17 +213,17 @@ end
|
|||||||
---@private
|
---@private
|
||||||
function GitRunner:finalise()
|
function GitRunner:finalise()
|
||||||
if self.rc == -1 then
|
if self.rc == -1 then
|
||||||
log.line("git", "job timed out %s %s", self.opts.toplevel, self.opts.path)
|
log.line("git", "job timed out %s %s", self.toplevel, self.path)
|
||||||
timeouts = timeouts + 1
|
timeouts = timeouts + 1
|
||||||
if timeouts == MAX_TIMEOUTS then
|
if timeouts == MAX_TIMEOUTS then
|
||||||
notify.warn(string.format("%d git jobs have timed out after git.timeout %dms, disabling git integration.", timeouts,
|
notify.warn(string.format("%d git jobs have timed out after git.timeout %dms, disabling git integration.", timeouts,
|
||||||
self.opts.timeout))
|
self.timeout))
|
||||||
require("nvim-tree.git").disable_git_integration()
|
require("nvim-tree.git").disable_git_integration()
|
||||||
end
|
end
|
||||||
elseif self.rc ~= 0 then
|
elseif self.rc ~= 0 then
|
||||||
log.line("git", "job fail rc %d %s %s", self.rc, self.opts.toplevel, self.opts.path)
|
log.line("git", "job fail rc %d %s %s", self.rc, self.toplevel, self.path)
|
||||||
else
|
else
|
||||||
log.line("git", "job success %s %s", self.opts.toplevel, self.opts.path)
|
log.line("git", "job success %s %s", self.toplevel, self.path)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -209,17 +231,17 @@ end
|
|||||||
---@private
|
---@private
|
||||||
---@return GitPathXY?
|
---@return GitPathXY?
|
||||||
function GitRunner:execute()
|
function GitRunner:execute()
|
||||||
local async = self.opts.callback ~= nil
|
local async = self.callback ~= nil
|
||||||
local profile = log.profile_start("git %s job %s %s", async and "async" or "sync", self.opts.toplevel, self.opts.path)
|
local profile = log.profile_start("git %s job %s %s", async and "async" or "sync", self.toplevel, self.path)
|
||||||
|
|
||||||
if async and self.opts.callback then
|
if async and self.callback then
|
||||||
-- async, always call back
|
-- async, always call back
|
||||||
self:run_git_job(function()
|
self:run_git_job(function()
|
||||||
log.profile_end(profile)
|
log.profile_end(profile)
|
||||||
|
|
||||||
self:finalise()
|
self:finalise()
|
||||||
|
|
||||||
self.opts.callback(self.path_xy)
|
self.callback(self.path_xy)
|
||||||
end)
|
end)
|
||||||
else
|
else
|
||||||
-- sync, maybe call back
|
-- sync, maybe call back
|
||||||
@ -230,8 +252,8 @@ function GitRunner:execute()
|
|||||||
|
|
||||||
self:finalise()
|
self:finalise()
|
||||||
|
|
||||||
if self.opts.callback then
|
if self.callback then
|
||||||
self.opts.callback(self.path_xy)
|
self.callback(self.path_xy)
|
||||||
else
|
else
|
||||||
return self.path_xy
|
return self.path_xy
|
||||||
end
|
end
|
||||||
@ -240,15 +262,10 @@ end
|
|||||||
|
|
||||||
---Static method to run a git process, which will be killed if it takes more than timeout
|
---Static method to run a git process, which will be killed if it takes more than timeout
|
||||||
---Return nil when callback present
|
---Return nil when callback present
|
||||||
---@param opts GitRunnerOpts
|
---@param args GitRunnerArgs
|
||||||
---@return GitPathXY?
|
---@return GitPathXY?
|
||||||
function GitRunner:run(opts)
|
function GitRunner:run(args)
|
||||||
---@type GitRunner
|
local runner = GitRunner(args)
|
||||||
local runner = {
|
|
||||||
opts = opts,
|
|
||||||
path_xy = {},
|
|
||||||
}
|
|
||||||
runner = GitRunner:new(runner)
|
|
||||||
|
|
||||||
return runner:execute()
|
return runner:execute()
|
||||||
end
|
end
|
||||||
|
|||||||
@ -172,8 +172,8 @@ function M.git_status_dir(parent_ignored, project, path, path_fallback)
|
|||||||
elseif project then
|
elseif project then
|
||||||
ns = {
|
ns = {
|
||||||
file = project.files and (project.files[path] or project.files[path_fallback]),
|
file = project.files and (project.files[path] or project.files[path_fallback]),
|
||||||
dir = project.dirs and {
|
dir = project.dirs and {
|
||||||
direct = project.dirs.direct and project.dirs.direct[path],
|
direct = project.dirs.direct and project.dirs.direct[path],
|
||||||
indirect = project.dirs.indirect and project.dirs.indirect[path],
|
indirect = project.dirs.indirect and project.dirs.indirect[path],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,37 +8,33 @@ local rename_file = require("nvim-tree.actions.fs.rename-file")
|
|||||||
local trash = require("nvim-tree.actions.fs.trash")
|
local trash = require("nvim-tree.actions.fs.trash")
|
||||||
local utils = require("nvim-tree.utils")
|
local utils = require("nvim-tree.utils")
|
||||||
|
|
||||||
|
local Class = require("nvim-tree.classic")
|
||||||
local DirectoryNode = require("nvim-tree.node.directory")
|
local DirectoryNode = require("nvim-tree.node.directory")
|
||||||
|
|
||||||
---@class Marks
|
---@class (exact) Marks: Class
|
||||||
---@field config table hydrated user opts.filters
|
|
||||||
---@field private explorer Explorer
|
---@field private explorer Explorer
|
||||||
---@field private marks table<string, Node> by absolute path
|
---@field private marks table<string, Node> by absolute path
|
||||||
local Marks = {}
|
local Marks = Class:extend()
|
||||||
|
|
||||||
---@return Marks
|
---@class Marks
|
||||||
---@param opts table user options
|
---@overload fun(args: MarksArgs): Marks
|
||||||
---@param explorer Explorer
|
|
||||||
function Marks:new(opts, explorer)
|
|
||||||
local o = {
|
|
||||||
explorer = explorer,
|
|
||||||
config = {
|
|
||||||
ui = opts.ui,
|
|
||||||
filesystem_watchers = opts.filesystem_watchers,
|
|
||||||
},
|
|
||||||
marks = {},
|
|
||||||
}
|
|
||||||
|
|
||||||
setmetatable(o, self)
|
---@class (exact) MarksArgs
|
||||||
self.__index = self
|
---@field explorer Explorer
|
||||||
return o
|
|
||||||
|
---@protected
|
||||||
|
---@param args MarksArgs
|
||||||
|
function Marks:new(args)
|
||||||
|
self.explorer = args.explorer
|
||||||
|
|
||||||
|
self.marks = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
---Clear all marks and reload if watchers disabled
|
---Clear all marks and reload if watchers disabled
|
||||||
---@private
|
---@private
|
||||||
function Marks:clear_reload()
|
function Marks:clear_reload()
|
||||||
self:clear()
|
self:clear()
|
||||||
if not self.config.filesystem_watchers.enable then
|
if not self.explorer.opts.filesystem_watchers.enable then
|
||||||
self.explorer:reload_explorer()
|
self.explorer:reload_explorer()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -100,7 +96,7 @@ function Marks:bulk_delete()
|
|||||||
self:clear_reload()
|
self:clear_reload()
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.config.ui.confirm.remove then
|
if self.explorer.opts.ui.confirm.remove then
|
||||||
local prompt_select = "Remove bookmarked ?"
|
local prompt_select = "Remove bookmarked ?"
|
||||||
local prompt_input = prompt_select .. " y/N: "
|
local prompt_input = prompt_select .. " y/N: "
|
||||||
lib.prompt(prompt_input, prompt_select, { "", "y" }, { "No", "Yes" }, "nvimtree_bulk_delete", function(item_short)
|
lib.prompt(prompt_input, prompt_select, { "", "y" }, { "No", "Yes" }, "nvimtree_bulk_delete", function(item_short)
|
||||||
@ -129,7 +125,7 @@ function Marks:bulk_trash()
|
|||||||
self:clear_reload()
|
self:clear_reload()
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.config.ui.confirm.trash then
|
if self.explorer.opts.ui.confirm.trash then
|
||||||
local prompt_select = "Trash bookmarked ?"
|
local prompt_select = "Trash bookmarked ?"
|
||||||
local prompt_input = prompt_select .. " y/N: "
|
local prompt_input = prompt_select .. " y/N: "
|
||||||
lib.prompt(prompt_input, prompt_select, { "", "y" }, { "No", "Yes" }, "nvimtree_bulk_trash", function(item_short)
|
lib.prompt(prompt_input, prompt_select, { "", "y" }, { "No", "Yes" }, "nvimtree_bulk_trash", function(item_short)
|
||||||
|
|||||||
@ -2,35 +2,29 @@ local git_utils = require("nvim-tree.git.utils")
|
|||||||
local utils = require("nvim-tree.utils")
|
local utils = require("nvim-tree.utils")
|
||||||
|
|
||||||
local DirectoryNode = require("nvim-tree.node.directory")
|
local DirectoryNode = require("nvim-tree.node.directory")
|
||||||
|
local LinkNode = require("nvim-tree.node.link")
|
||||||
|
|
||||||
---@class (exact) DirectoryLinkNode: DirectoryNode
|
---@class (exact) DirectoryLinkNode: DirectoryNode, LinkNode
|
||||||
---@field link_to string absolute path
|
local DirectoryLinkNode = DirectoryNode:extend()
|
||||||
---@field private fs_stat_target uv.fs_stat.result
|
DirectoryLinkNode:implement(LinkNode)
|
||||||
local DirectoryLinkNode = DirectoryNode:new()
|
|
||||||
|
|
||||||
---Static factory method
|
---@class DirectoryLinkNode
|
||||||
---@param explorer Explorer
|
---@overload fun(args: LinkNodeArgs): DirectoryLinkNode
|
||||||
---@param parent DirectoryNode
|
|
||||||
---@param absolute_path string
|
|
||||||
---@param link_to string
|
|
||||||
---@param name string
|
|
||||||
---@param fs_stat uv.fs_stat.result?
|
|
||||||
---@param fs_stat_target uv.fs_stat.result
|
|
||||||
---@return DirectoryLinkNode? nil on vim.loop.fs_realpath failure
|
|
||||||
function DirectoryLinkNode:create(explorer, parent, absolute_path, link_to, name, fs_stat, fs_stat_target)
|
|
||||||
-- create DirectoryNode with the target path for the watcher
|
|
||||||
local o = DirectoryNode:create(explorer, parent, link_to, name, fs_stat)
|
|
||||||
|
|
||||||
o = self:new(o)
|
---@protected
|
||||||
|
---@param args LinkNodeArgs
|
||||||
|
function DirectoryLinkNode:new(args)
|
||||||
|
LinkNode.new(self, args)
|
||||||
|
|
||||||
|
-- create DirectoryNode with watcher on link_to
|
||||||
|
local absolute_path = args.absolute_path
|
||||||
|
args.absolute_path = args.link_to
|
||||||
|
DirectoryLinkNode.super.new(self, args)
|
||||||
|
|
||||||
|
self.type = "link"
|
||||||
|
|
||||||
-- reset absolute path to the link itself
|
-- reset absolute path to the link itself
|
||||||
o.absolute_path = absolute_path
|
self.absolute_path = absolute_path
|
||||||
|
|
||||||
o.type = "link"
|
|
||||||
o.link_to = link_to
|
|
||||||
o.fs_stat_target = fs_stat_target
|
|
||||||
|
|
||||||
return o
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function DirectoryLinkNode:destroy()
|
function DirectoryLinkNode:destroy()
|
||||||
@ -54,10 +48,10 @@ function DirectoryLinkNode:highlighted_icon()
|
|||||||
|
|
||||||
if self.open then
|
if self.open then
|
||||||
str = self.explorer.opts.renderer.icons.glyphs.folder.symlink_open
|
str = self.explorer.opts.renderer.icons.glyphs.folder.symlink_open
|
||||||
hl = "NvimTreeOpenedFolderIcon"
|
hl = "NvimTreeOpenedFolderIcon"
|
||||||
else
|
else
|
||||||
str = self.explorer.opts.renderer.icons.glyphs.folder.symlink
|
str = self.explorer.opts.renderer.icons.glyphs.folder.symlink
|
||||||
hl = "NvimTreeClosedFolderIcon"
|
hl = "NvimTreeClosedFolderIcon"
|
||||||
end
|
end
|
||||||
|
|
||||||
return { str = str, hl = { hl } }
|
return { str = str, hl = { hl } }
|
||||||
@ -70,8 +64,9 @@ function DirectoryLinkNode:highlighted_name()
|
|||||||
|
|
||||||
if self.explorer.opts.renderer.symlink_destination then
|
if self.explorer.opts.renderer.symlink_destination then
|
||||||
local link_to = utils.path_relative(self.link_to, self.explorer.absolute_path)
|
local link_to = utils.path_relative(self.link_to, self.explorer.absolute_path)
|
||||||
name.str = string.format("%s%s%s", name.str, self.explorer.opts.renderer.icons.symlink_arrow, link_to)
|
|
||||||
name.hl = { "NvimTreeSymlinkFolderName" }
|
name.str = string.format("%s%s%s", name.str, self.explorer.opts.renderer.icons.symlink_arrow, link_to)
|
||||||
|
name.hl = { "NvimTreeSymlinkFolderName" }
|
||||||
end
|
end
|
||||||
|
|
||||||
return name
|
return name
|
||||||
@ -82,7 +77,6 @@ end
|
|||||||
function DirectoryLinkNode:clone()
|
function DirectoryLinkNode:clone()
|
||||||
local clone = DirectoryNode.clone(self) --[[@as DirectoryLinkNode]]
|
local clone = DirectoryNode.clone(self) --[[@as DirectoryLinkNode]]
|
||||||
|
|
||||||
clone.type = self.type
|
|
||||||
clone.link_to = self.link_to
|
clone.link_to = self.link_to
|
||||||
clone.fs_stat_target = self.fs_stat_target
|
clone.fs_stat_target = self.fs_stat_target
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
local git_utils = require("nvim-tree.git.utils")
|
local git_utils = require("nvim-tree.git.utils")
|
||||||
local icons = require("nvim-tree.renderer.components.devicons")
|
local icons = require("nvim-tree.renderer.components.devicons")
|
||||||
local notify = require("nvim-tree.notify")
|
local notify = require("nvim-tree.notify")
|
||||||
|
|
||||||
local Node = require("nvim-tree.node")
|
local Node = require("nvim-tree.node")
|
||||||
|
|
||||||
---@class (exact) DirectoryNode: Node
|
---@class (exact) DirectoryNode: Node
|
||||||
@ -10,45 +11,28 @@ local Node = require("nvim-tree.node")
|
|||||||
---@field open boolean
|
---@field open boolean
|
||||||
---@field hidden_stats table? -- Each field of this table is a key for source and value for count
|
---@field hidden_stats table? -- Each field of this table is a key for source and value for count
|
||||||
---@field private watcher Watcher?
|
---@field private watcher Watcher?
|
||||||
local DirectoryNode = Node:new()
|
local DirectoryNode = Node:extend()
|
||||||
|
|
||||||
---Static factory method
|
---@class DirectoryNode
|
||||||
---@param explorer Explorer
|
---@overload fun(args: NodeArgs): DirectoryNode
|
||||||
---@param parent DirectoryNode?
|
|
||||||
---@param absolute_path string
|
---@protected
|
||||||
---@param name string
|
---@param args NodeArgs
|
||||||
---@param fs_stat uv.fs_stat.result|nil
|
function DirectoryNode:new(args)
|
||||||
---@return DirectoryNode
|
DirectoryNode.super.new(self, args)
|
||||||
function DirectoryNode:create(explorer, parent, absolute_path, name, fs_stat)
|
|
||||||
local handle = vim.loop.fs_scandir(absolute_path)
|
local handle = vim.loop.fs_scandir(args.absolute_path)
|
||||||
local has_children = handle and vim.loop.fs_scandir_next(handle) ~= nil or false
|
local has_children = handle and vim.loop.fs_scandir_next(handle) ~= nil or false
|
||||||
|
|
||||||
---@type DirectoryNode
|
self.type = "directory"
|
||||||
local o = {
|
|
||||||
type = "directory",
|
|
||||||
explorer = explorer,
|
|
||||||
absolute_path = absolute_path,
|
|
||||||
executable = false,
|
|
||||||
fs_stat = fs_stat,
|
|
||||||
git_status = nil,
|
|
||||||
hidden = false,
|
|
||||||
name = name,
|
|
||||||
parent = parent,
|
|
||||||
watcher = nil,
|
|
||||||
diag_status = nil,
|
|
||||||
is_dot = false,
|
|
||||||
|
|
||||||
has_children = has_children,
|
self.has_children = has_children
|
||||||
group_next = nil,
|
self.group_next = nil
|
||||||
nodes = {},
|
self.nodes = {}
|
||||||
open = false,
|
self.open = false
|
||||||
hidden_stats = nil,
|
self.hidden_stats = nil
|
||||||
}
|
|
||||||
o = self:new(o)
|
|
||||||
|
|
||||||
o.watcher = require("nvim-tree.explorer.watch").create_watcher(o)
|
self.watcher = require("nvim-tree.explorer.watch").create_watcher(self)
|
||||||
|
|
||||||
return o
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function DirectoryNode:destroy()
|
function DirectoryNode:destroy()
|
||||||
@ -289,12 +273,12 @@ end
|
|||||||
---Create a sanitized partial copy of a node, populating children recursively.
|
---Create a sanitized partial copy of a node, populating children recursively.
|
||||||
---@return DirectoryNode cloned
|
---@return DirectoryNode cloned
|
||||||
function DirectoryNode:clone()
|
function DirectoryNode:clone()
|
||||||
local clone = Node.clone(self) --[[@as DirectoryNode]]
|
local clone = Node.clone(self) --[[@as DirectoryNode]]
|
||||||
|
|
||||||
clone.has_children = self.has_children
|
clone.has_children = self.has_children
|
||||||
clone.group_next = nil
|
clone.group_next = nil
|
||||||
clone.nodes = {}
|
clone.nodes = {}
|
||||||
clone.open = self.open
|
clone.open = self.open
|
||||||
clone.hidden_stats = nil
|
clone.hidden_stats = nil
|
||||||
|
|
||||||
for _, child in ipairs(self.nodes) do
|
for _, child in ipairs(self.nodes) do
|
||||||
|
|||||||
@ -7,38 +7,38 @@ local Watcher = require("nvim-tree.watcher")
|
|||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
---Factory function to create the appropriate Node
|
---Factory function to create the appropriate Node
|
||||||
---@param explorer Explorer
|
---nil on invalid stat or invalid link target stat
|
||||||
---@param parent DirectoryNode
|
---@param args NodeArgs
|
||||||
---@param absolute_path string
|
|
||||||
---@param stat uv.fs_stat.result? -- on nil stat return nil Node
|
|
||||||
---@param name string
|
|
||||||
---@return Node?
|
---@return Node?
|
||||||
function M.create_node(explorer, parent, absolute_path, stat, name)
|
function M.create(args)
|
||||||
if not stat then
|
if not args.fs_stat then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
if stat.type == "directory" then
|
if args.fs_stat.type == "directory" then
|
||||||
-- directory must be readable and enumerable
|
-- directory must be readable and enumerable
|
||||||
if vim.loop.fs_access(absolute_path, "R") and Watcher.is_fs_event_capable(absolute_path) then
|
if vim.loop.fs_access(args.absolute_path, "R") and Watcher.is_fs_event_capable(args.absolute_path) then
|
||||||
return DirectoryNode:create(explorer, parent, absolute_path, name, stat)
|
return DirectoryNode(args)
|
||||||
end
|
end
|
||||||
elseif stat.type == "file" then
|
elseif args.fs_stat.type == "file" then
|
||||||
-- any file
|
return FileNode(args)
|
||||||
return FileNode:create(explorer, parent, absolute_path, name, stat)
|
elseif args.fs_stat.type == "link" then
|
||||||
elseif stat.type == "link" then
|
|
||||||
-- link target path and stat must resolve
|
-- link target path and stat must resolve
|
||||||
local link_to = vim.loop.fs_realpath(absolute_path)
|
local link_to = vim.loop.fs_realpath(args.absolute_path)
|
||||||
local link_to_stat = link_to and vim.loop.fs_stat(link_to)
|
local link_to_stat = link_to and vim.loop.fs_stat(link_to)
|
||||||
if not link_to or not link_to_stat then
|
if not link_to or not link_to_stat then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@cast args LinkNodeArgs
|
||||||
|
args.link_to = link_to
|
||||||
|
args.fs_stat_target = link_to_stat
|
||||||
|
|
||||||
-- choose directory or file
|
-- choose directory or file
|
||||||
if link_to_stat.type == "directory" then
|
if link_to_stat.type == "directory" then
|
||||||
return DirectoryLinkNode:create(explorer, parent, absolute_path, link_to, name, stat, link_to_stat)
|
return DirectoryLinkNode(args)
|
||||||
else
|
else
|
||||||
return FileLinkNode:create(explorer, parent, absolute_path, link_to, name, stat, link_to_stat)
|
return FileLinkNode(args)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -2,31 +2,22 @@ local git_utils = require("nvim-tree.git.utils")
|
|||||||
local utils = require("nvim-tree.utils")
|
local utils = require("nvim-tree.utils")
|
||||||
|
|
||||||
local FileNode = require("nvim-tree.node.file")
|
local FileNode = require("nvim-tree.node.file")
|
||||||
|
local LinkNode = require("nvim-tree.node.link")
|
||||||
|
|
||||||
---@class (exact) FileLinkNode: FileNode
|
---@class (exact) FileLinkNode: FileNode, LinkNode
|
||||||
---@field link_to string absolute path
|
local FileLinkNode = FileNode:extend()
|
||||||
---@field private fs_stat_target uv.fs_stat.result
|
FileLinkNode:implement(LinkNode)
|
||||||
local FileLinkNode = FileNode:new()
|
|
||||||
|
|
||||||
---Static factory method
|
---@class FileLinkNode
|
||||||
---@param explorer Explorer
|
---@overload fun(args: LinkNodeArgs): FileLinkNode
|
||||||
---@param parent DirectoryNode
|
|
||||||
---@param absolute_path string
|
|
||||||
---@param link_to string
|
|
||||||
---@param name string
|
|
||||||
---@param fs_stat uv.fs_stat.result?
|
|
||||||
---@param fs_stat_target uv.fs_stat.result
|
|
||||||
---@return FileLinkNode? nil on vim.loop.fs_realpath failure
|
|
||||||
function FileLinkNode:create(explorer, parent, absolute_path, link_to, name, fs_stat, fs_stat_target)
|
|
||||||
local o = FileNode:create(explorer, parent, absolute_path, name, fs_stat)
|
|
||||||
|
|
||||||
o = self:new(o)
|
---@protected
|
||||||
|
---@param args LinkNodeArgs
|
||||||
|
function FileLinkNode:new(args)
|
||||||
|
LinkNode.new(self, args)
|
||||||
|
FileLinkNode.super.new(self, args)
|
||||||
|
|
||||||
o.type = "link"
|
self.type = "link"
|
||||||
o.link_to = link_to
|
|
||||||
o.fs_stat_target = fs_stat_target
|
|
||||||
|
|
||||||
return o
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function FileLinkNode:destroy()
|
function FileLinkNode:destroy()
|
||||||
@ -71,7 +62,6 @@ end
|
|||||||
function FileLinkNode:clone()
|
function FileLinkNode:clone()
|
||||||
local clone = FileNode.clone(self) --[[@as FileLinkNode]]
|
local clone = FileNode.clone(self) --[[@as FileLinkNode]]
|
||||||
|
|
||||||
clone.type = self.type
|
|
||||||
clone.link_to = self.link_to
|
clone.link_to = self.link_to
|
||||||
clone.fs_stat_target = self.fs_stat_target
|
clone.fs_stat_target = self.fs_stat_target
|
||||||
|
|
||||||
|
|||||||
@ -15,35 +15,19 @@ local PICTURE_MAP = {
|
|||||||
|
|
||||||
---@class (exact) FileNode: Node
|
---@class (exact) FileNode: Node
|
||||||
---@field extension string
|
---@field extension string
|
||||||
local FileNode = Node:new()
|
local FileNode = Node:extend()
|
||||||
|
|
||||||
---Static factory method
|
---@class FileNode
|
||||||
---@param explorer Explorer
|
---@overload fun(args: NodeArgs): FileNode
|
||||||
---@param parent DirectoryNode
|
|
||||||
---@param absolute_path string
|
|
||||||
---@param name string
|
|
||||||
---@param fs_stat uv.fs_stat.result?
|
|
||||||
---@return FileNode
|
|
||||||
function FileNode:create(explorer, parent, absolute_path, name, fs_stat)
|
|
||||||
---@type FileNode
|
|
||||||
local o = {
|
|
||||||
type = "file",
|
|
||||||
explorer = explorer,
|
|
||||||
absolute_path = absolute_path,
|
|
||||||
executable = utils.is_executable(absolute_path),
|
|
||||||
fs_stat = fs_stat,
|
|
||||||
git_status = nil,
|
|
||||||
hidden = false,
|
|
||||||
name = name,
|
|
||||||
parent = parent,
|
|
||||||
diag_status = nil,
|
|
||||||
is_dot = false,
|
|
||||||
|
|
||||||
extension = string.match(name, ".?[^.]+%.(.*)") or "",
|
---@protected
|
||||||
}
|
---@param args NodeArgs
|
||||||
o = self:new(o)
|
function FileNode:new(args)
|
||||||
|
FileNode.super.new(self, args)
|
||||||
|
|
||||||
return o
|
self.type = "file"
|
||||||
|
self.extension = string.match(args.name, ".?[^.]+%.(.*)") or ""
|
||||||
|
self.executable = utils.is_executable(args.absolute_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
function FileNode:destroy()
|
function FileNode:destroy()
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
local Class = require("nvim-tree.class")
|
local Class = require("nvim-tree.classic")
|
||||||
|
|
||||||
---Abstract Node class.
|
---Abstract Node class.
|
||||||
---Uses the abstract factory pattern to instantiate child instances.
|
|
||||||
---@class (exact) Node: Class
|
---@class (exact) Node: Class
|
||||||
---@field type NODE_TYPE
|
---@field type "file" | "directory" | "link" uv.fs_stat.result.type
|
||||||
---@field explorer Explorer
|
---@field explorer Explorer
|
||||||
---@field absolute_path string
|
---@field absolute_path string
|
||||||
---@field executable boolean
|
---@field executable boolean
|
||||||
@ -14,7 +13,29 @@ local Class = require("nvim-tree.class")
|
|||||||
---@field parent DirectoryNode?
|
---@field parent DirectoryNode?
|
||||||
---@field diag_status DiagStatus?
|
---@field diag_status DiagStatus?
|
||||||
---@field private is_dot boolean cached is_dotfile
|
---@field private is_dot boolean cached is_dotfile
|
||||||
local Node = Class:new()
|
local Node = Class:extend()
|
||||||
|
|
||||||
|
---@class (exact) NodeArgs
|
||||||
|
---@field explorer Explorer
|
||||||
|
---@field parent DirectoryNode?
|
||||||
|
---@field absolute_path string
|
||||||
|
---@field name string
|
||||||
|
---@field fs_stat uv.fs_stat.result?
|
||||||
|
|
||||||
|
---@protected
|
||||||
|
---@param args NodeArgs
|
||||||
|
function Node:new(args)
|
||||||
|
self.explorer = args.explorer
|
||||||
|
self.absolute_path = args.absolute_path
|
||||||
|
self.executable = false
|
||||||
|
self.fs_stat = args.fs_stat
|
||||||
|
self.git_status = nil
|
||||||
|
self.hidden = false
|
||||||
|
self.name = args.name
|
||||||
|
self.parent = args.parent
|
||||||
|
self.diag_status = nil
|
||||||
|
self.is_dot = false
|
||||||
|
end
|
||||||
|
|
||||||
function Node:destroy()
|
function Node:destroy()
|
||||||
end
|
end
|
||||||
@ -104,17 +125,17 @@ function Node:clone()
|
|||||||
|
|
||||||
---@type Node
|
---@type Node
|
||||||
local clone = {
|
local clone = {
|
||||||
type = self.type,
|
type = self.type,
|
||||||
explorer = explorer_placeholder,
|
explorer = explorer_placeholder,
|
||||||
absolute_path = self.absolute_path,
|
absolute_path = self.absolute_path,
|
||||||
executable = self.executable,
|
executable = self.executable,
|
||||||
fs_stat = self.fs_stat,
|
fs_stat = self.fs_stat,
|
||||||
git_status = self.git_status,
|
git_status = self.git_status,
|
||||||
hidden = self.hidden,
|
hidden = self.hidden,
|
||||||
name = self.name,
|
name = self.name,
|
||||||
parent = nil,
|
parent = nil,
|
||||||
diag_status = nil,
|
diag_status = nil,
|
||||||
is_dot = self.is_dot,
|
is_dot = self.is_dot,
|
||||||
}
|
}
|
||||||
|
|
||||||
return clone
|
return clone
|
||||||
|
|||||||
19
lua/nvim-tree/node/link.lua
Normal file
19
lua/nvim-tree/node/link.lua
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
local Class = require("nvim-tree.classic")
|
||||||
|
|
||||||
|
---@class (exact) LinkNode: Class
|
||||||
|
---@field link_to string
|
||||||
|
---@field protected fs_stat_target uv.fs_stat.result
|
||||||
|
local LinkNode = Class:extend()
|
||||||
|
|
||||||
|
---@class (exact) LinkNodeArgs: NodeArgs
|
||||||
|
---@field link_to string
|
||||||
|
---@field fs_stat_target uv.fs_stat.result
|
||||||
|
|
||||||
|
---@protected
|
||||||
|
---@param args LinkNodeArgs
|
||||||
|
function LinkNode:new(args)
|
||||||
|
self.link_to = args.link_to
|
||||||
|
self.fs_stat_target = args.fs_stat_target
|
||||||
|
end
|
||||||
|
|
||||||
|
return LinkNode
|
||||||
@ -1,20 +1,15 @@
|
|||||||
local DirectoryNode = require("nvim-tree.node.directory")
|
local DirectoryNode = require("nvim-tree.node.directory")
|
||||||
|
|
||||||
---@class (exact) RootNode: DirectoryNode
|
---@class (exact) RootNode: DirectoryNode
|
||||||
local RootNode = DirectoryNode:new()
|
local RootNode = DirectoryNode:extend()
|
||||||
|
|
||||||
---Static factory method
|
---@class RootNode
|
||||||
---@param explorer Explorer
|
---@overload fun(args: NodeArgs): RootNode
|
||||||
---@param absolute_path string
|
|
||||||
---@param name string
|
|
||||||
---@param fs_stat uv.fs_stat.result|nil
|
|
||||||
---@return RootNode
|
|
||||||
function RootNode:create(explorer, absolute_path, name, fs_stat)
|
|
||||||
local o = DirectoryNode:create(explorer, nil, absolute_path, name, fs_stat)
|
|
||||||
|
|
||||||
o = self:new(o)
|
---@protected
|
||||||
|
---@param args NodeArgs
|
||||||
return o
|
function RootNode:new(args)
|
||||||
|
RootNode.super.new(self, args)
|
||||||
end
|
end
|
||||||
|
|
||||||
---Root is never a dotfile
|
---Root is never a dotfile
|
||||||
|
|||||||
@ -2,6 +2,7 @@ local notify = require("nvim-tree.notify")
|
|||||||
local utils = require("nvim-tree.utils")
|
local utils = require("nvim-tree.utils")
|
||||||
local view = require("nvim-tree.view")
|
local view = require("nvim-tree.view")
|
||||||
|
|
||||||
|
local Class = require("nvim-tree.classic")
|
||||||
local DirectoryNode = require("nvim-tree.node.directory")
|
local DirectoryNode = require("nvim-tree.node.directory")
|
||||||
|
|
||||||
local DecoratorBookmarks = require("nvim-tree.renderer.decorator.bookmarks")
|
local DecoratorBookmarks = require("nvim-tree.renderer.decorator.bookmarks")
|
||||||
@ -26,57 +27,51 @@ local pad = require("nvim-tree.renderer.components.padding")
|
|||||||
---@field col_end number
|
---@field col_end number
|
||||||
|
|
||||||
---@class (exact) Builder
|
---@class (exact) Builder
|
||||||
---@field private __index? table
|
|
||||||
---@field lines string[] includes icons etc.
|
---@field lines string[] includes icons etc.
|
||||||
---@field hl_args AddHighlightArgs[] line highlights
|
---@field hl_args AddHighlightArgs[] line highlights
|
||||||
---@field signs string[] line signs
|
---@field signs string[] line signs
|
||||||
---@field extmarks table[] extra marks for right icon placement
|
---@field extmarks table[] extra marks for right icon placement
|
||||||
---@field virtual_lines table[] virtual lines for hidden count display
|
---@field virtual_lines table[] virtual lines for hidden count display
|
||||||
---@field private explorer Explorer
|
---@field private explorer Explorer
|
||||||
---@field private opts table
|
|
||||||
---@field private index number
|
---@field private index number
|
||||||
---@field private depth number
|
---@field private depth number
|
||||||
---@field private combined_groups table<string, boolean> combined group names
|
---@field private combined_groups table<string, boolean> combined group names
|
||||||
---@field private markers boolean[] indent markers
|
---@field private markers boolean[] indent markers
|
||||||
---@field private decorators Decorator[]
|
---@field private decorators Decorator[]
|
||||||
---@field private hidden_display fun(node: Node): string|nil
|
---@field private hidden_display fun(node: Node): string|nil
|
||||||
local Builder = {}
|
local Builder = Class:extend()
|
||||||
|
|
||||||
---@param opts table user options
|
---@class Builder
|
||||||
---@param explorer Explorer
|
---@overload fun(args: BuilderArgs): Builder
|
||||||
---@return Builder
|
|
||||||
function Builder:new(opts, explorer)
|
---@class (exact) BuilderArgs
|
||||||
---@type Builder
|
---@field explorer Explorer
|
||||||
local o = {
|
|
||||||
opts = opts,
|
---@protected
|
||||||
explorer = explorer,
|
---@param args BuilderArgs
|
||||||
index = 0,
|
function Builder:new(args)
|
||||||
depth = 0,
|
self.explorer = args.explorer
|
||||||
hl_args = {},
|
self.index = 0
|
||||||
combined_groups = {},
|
self.depth = 0
|
||||||
lines = {},
|
self.hl_args = {}
|
||||||
markers = {},
|
self.combined_groups = {}
|
||||||
signs = {},
|
self.lines = {}
|
||||||
extmarks = {},
|
self.markers = {}
|
||||||
virtual_lines = {},
|
self.signs = {}
|
||||||
decorators = {
|
self.extmarks = {}
|
||||||
-- priority order
|
self.virtual_lines = {}
|
||||||
DecoratorCut:create(opts, explorer),
|
self.decorators = {
|
||||||
DecoratorCopied:create(opts, explorer),
|
-- priority order
|
||||||
DecoratorDiagnostics:create(opts, explorer),
|
DecoratorCut({ explorer = args.explorer }),
|
||||||
DecoratorBookmarks:create(opts, explorer),
|
DecoratorCopied({ explorer = args.explorer }),
|
||||||
DecoratorModified:create(opts, explorer),
|
DecoratorDiagnostics({ explorer = args.explorer }),
|
||||||
DecoratorHidden:create(opts, explorer),
|
DecoratorBookmarks({ explorer = args.explorer }),
|
||||||
DecoratorOpened:create(opts, explorer),
|
DecoratorModified({ explorer = args.explorer }),
|
||||||
DecoratorGit:create(opts, explorer),
|
DecoratorHidden({ explorer = args.explorer }),
|
||||||
},
|
DecoratorOpened({ explorer = args.explorer }),
|
||||||
hidden_display = Builder:setup_hidden_display_function(opts),
|
DecoratorGit({ explorer = args.explorer })
|
||||||
}
|
}
|
||||||
|
self.hidden_display = Builder:setup_hidden_display_function(self.explorer.opts)
|
||||||
setmetatable(o, self)
|
|
||||||
self.__index = self
|
|
||||||
|
|
||||||
return o
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---Insert ranged highlight groups into self.highlights
|
---Insert ranged highlight groups into self.highlights
|
||||||
@ -123,7 +118,7 @@ function Builder:format_line(indent_markers, arrows, icon, name, node)
|
|||||||
end
|
end
|
||||||
for _, v in ipairs(t2) do
|
for _, v in ipairs(t2) do
|
||||||
if added_len > 0 then
|
if added_len > 0 then
|
||||||
table.insert(t1, { str = self.opts.renderer.icons.padding })
|
table.insert(t1, { str = self.explorer.opts.renderer.icons.padding })
|
||||||
end
|
end
|
||||||
table.insert(t1, v)
|
table.insert(t1, v)
|
||||||
end
|
end
|
||||||
@ -284,7 +279,7 @@ function Builder:add_hidden_count_string(node, idx, num_children)
|
|||||||
local hidden_count_string = self.hidden_display(node.hidden_stats)
|
local hidden_count_string = self.hidden_display(node.hidden_stats)
|
||||||
if hidden_count_string and hidden_count_string ~= "" then
|
if hidden_count_string and hidden_count_string ~= "" then
|
||||||
local indent_markers = pad.get_indent_markers(self.depth, idx or 0, num_children or 0, node, self.markers, 1)
|
local indent_markers = pad.get_indent_markers(self.depth, idx or 0, num_children or 0, node, self.markers, 1)
|
||||||
local indent_width = self.opts.renderer.indent_width
|
local indent_width = self.explorer.opts.renderer.indent_width
|
||||||
|
|
||||||
local indent_padding = string.rep(" ", indent_width)
|
local indent_padding = string.rep(" ", indent_width)
|
||||||
local indent_string = indent_padding .. indent_markers.str
|
local indent_string = indent_padding .. indent_markers.str
|
||||||
@ -354,16 +349,16 @@ end
|
|||||||
---@private
|
---@private
|
||||||
function Builder:build_header()
|
function Builder:build_header()
|
||||||
if view.is_root_folder_visible(self.explorer.absolute_path) then
|
if view.is_root_folder_visible(self.explorer.absolute_path) then
|
||||||
local root_name = self:format_root_name(self.opts.renderer.root_folder_label)
|
local root_name = self:format_root_name(self.explorer.opts.renderer.root_folder_label)
|
||||||
table.insert(self.lines, root_name)
|
table.insert(self.lines, root_name)
|
||||||
self:insert_highlight({ "NvimTreeRootFolder" }, 0, string.len(root_name))
|
self:insert_highlight({ "NvimTreeRootFolder" }, 0, string.len(root_name))
|
||||||
self.index = 1
|
self.index = 1
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.explorer.live_filter.filter then
|
if self.explorer.live_filter.filter then
|
||||||
local filter_line = string.format("%s/%s/", self.opts.live_filter.prefix, self.explorer.live_filter.filter)
|
local filter_line = string.format("%s/%s/", self.explorer.opts.live_filter.prefix, self.explorer.live_filter.filter)
|
||||||
table.insert(self.lines, filter_line)
|
table.insert(self.lines, filter_line)
|
||||||
local prefix_length = string.len(self.opts.live_filter.prefix)
|
local prefix_length = string.len(self.explorer.opts.live_filter.prefix)
|
||||||
self:insert_highlight({ "NvimTreeLiveFilterPrefix" }, 0, prefix_length)
|
self:insert_highlight({ "NvimTreeLiveFilterPrefix" }, 0, prefix_length)
|
||||||
self:insert_highlight({ "NvimTreeLiveFilterValue" }, prefix_length, string.len(filter_line))
|
self:insert_highlight({ "NvimTreeLiveFilterValue" }, prefix_length, string.len(filter_line))
|
||||||
self.index = self.index + 1
|
self.index = self.index + 1
|
||||||
@ -413,11 +408,11 @@ function Builder:setup_hidden_display_function(opts)
|
|||||||
-- In case of missing field such as live_filter we zero it, otherwise keep field as is
|
-- In case of missing field such as live_filter we zero it, otherwise keep field as is
|
||||||
hidden_stats = vim.tbl_deep_extend("force", {
|
hidden_stats = vim.tbl_deep_extend("force", {
|
||||||
live_filter = 0,
|
live_filter = 0,
|
||||||
git = 0,
|
git = 0,
|
||||||
buf = 0,
|
buf = 0,
|
||||||
dotfile = 0,
|
dotfile = 0,
|
||||||
custom = 0,
|
custom = 0,
|
||||||
bookmark = 0,
|
bookmark = 0,
|
||||||
}, hidden_stats or {})
|
}, hidden_stats or {})
|
||||||
|
|
||||||
local ok, result = pcall(hidden_display, hidden_stats)
|
local ok, result = pcall(hidden_display, hidden_stats)
|
||||||
|
|||||||
@ -1,95 +0,0 @@
|
|||||||
local HL_POSITION = require("nvim-tree.enum").HL_POSITION
|
|
||||||
local diagnostics = require("nvim-tree.diagnostics")
|
|
||||||
|
|
||||||
local DirectoryNode = require("nvim-tree.node.directory")
|
|
||||||
|
|
||||||
local M = {
|
|
||||||
-- highlight strings for the icons
|
|
||||||
HS_ICON = {},
|
|
||||||
|
|
||||||
-- highlight groups for HL
|
|
||||||
HG_FILE = {},
|
|
||||||
HG_FOLDER = {},
|
|
||||||
|
|
||||||
-- position for HL
|
|
||||||
HL_POS = HL_POSITION.none,
|
|
||||||
}
|
|
||||||
|
|
||||||
---Diagnostics highlight group and position when highlight_diagnostics.
|
|
||||||
---@param node Node
|
|
||||||
---@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
|
|
||||||
return HL_POSITION.none, nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local group
|
|
||||||
local diag_status = diagnostics.get_diag_status(node)
|
|
||||||
if node:is(DirectoryNode) then
|
|
||||||
group = M.HS_FOLDER[diag_status and diag_status.value]
|
|
||||||
else
|
|
||||||
group = M.HS_FILE[diag_status and diag_status.value]
|
|
||||||
end
|
|
||||||
|
|
||||||
if group then
|
|
||||||
return M.HL_POS, group
|
|
||||||
else
|
|
||||||
return HL_POSITION.none, nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
---diagnostics icon if there is a status
|
|
||||||
---@param node Node
|
|
||||||
---@return HighlightedString|nil modified icon
|
|
||||||
function M.get_icon(node)
|
|
||||||
if node and M.config.diagnostics.enable and M.config.renderer.icons.show.diagnostics then
|
|
||||||
local diag_status = diagnostics.get_diag_status(node)
|
|
||||||
return M.ICON[diag_status and diag_status.value]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.setup(opts)
|
|
||||||
M.config = {
|
|
||||||
diagnostics = opts.diagnostics,
|
|
||||||
renderer = opts.renderer,
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.diagnostics.enable and opts.renderer.highlight_diagnostics then
|
|
||||||
M.HL_POS = HL_POSITION[opts.renderer.highlight_diagnostics]
|
|
||||||
end
|
|
||||||
|
|
||||||
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.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.HS_ICON[vim.diagnostic.severity.ERROR] = {
|
|
||||||
str = M.config.diagnostics.icons.error,
|
|
||||||
hl = { "NvimTreeDiagnosticErrorIcon" },
|
|
||||||
}
|
|
||||||
|
|
||||||
M.HS_ICON[vim.diagnostic.severity.WARN] = {
|
|
||||||
str = M.config.diagnostics.icons.warning,
|
|
||||||
hl = { "NvimTreeDiagnosticWarningIcon" },
|
|
||||||
}
|
|
||||||
M.HS_ICON[vim.diagnostic.severity.INFO] = {
|
|
||||||
str = M.config.diagnostics.icons.info,
|
|
||||||
hl = { "NvimTreeDiagnosticInfoIcon" },
|
|
||||||
}
|
|
||||||
M.HS_ICON[vim.diagnostic.severity.HINT] = {
|
|
||||||
str = M.config.diagnostics.icons.hint,
|
|
||||||
hl = { "NvimTreeDiagnosticHintIcon" },
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, i in ipairs(M.HS_ICON) do
|
|
||||||
vim.fn.sign_define(i.hl[1], { text = i.str, texthl = i.hl[1] })
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return M
|
|
||||||
@ -57,13 +57,13 @@ local function show()
|
|||||||
end
|
end
|
||||||
|
|
||||||
M.popup_win = vim.api.nvim_open_win(vim.api.nvim_create_buf(false, false), false, {
|
M.popup_win = vim.api.nvim_open_win(vim.api.nvim_create_buf(false, false), false, {
|
||||||
relative = "win",
|
relative = "win",
|
||||||
row = 0,
|
row = 0,
|
||||||
bufpos = { vim.api.nvim_win_get_cursor(0)[1] - 1, 0 },
|
bufpos = { vim.api.nvim_win_get_cursor(0)[1] - 1, 0 },
|
||||||
width = math.min(text_width, vim.o.columns - 2),
|
width = math.min(text_width, vim.o.columns - 2),
|
||||||
height = 1,
|
height = 1,
|
||||||
noautocmd = true,
|
noautocmd = true,
|
||||||
style = "minimal",
|
style = "minimal",
|
||||||
})
|
})
|
||||||
|
|
||||||
local ns_id = vim.api.nvim_get_namespaces()["NvimTreeHighlights"]
|
local ns_id = vim.api.nvim_get_namespaces()["NvimTreeHighlights"]
|
||||||
|
|||||||
@ -1,12 +1,10 @@
|
|||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
M.diagnostics = require("nvim-tree.renderer.components.diagnostics")
|
|
||||||
M.full_name = require("nvim-tree.renderer.components.full-name")
|
M.full_name = require("nvim-tree.renderer.components.full-name")
|
||||||
M.devicons = require("nvim-tree.renderer.components.devicons")
|
M.devicons = require("nvim-tree.renderer.components.devicons")
|
||||||
M.padding = require("nvim-tree.renderer.components.padding")
|
M.padding = require("nvim-tree.renderer.components.padding")
|
||||||
|
|
||||||
function M.setup(opts)
|
function M.setup(opts)
|
||||||
M.diagnostics.setup(opts)
|
|
||||||
M.full_name.setup(opts)
|
M.full_name.setup(opts)
|
||||||
M.devicons.setup(opts)
|
M.devicons.setup(opts)
|
||||||
M.padding.setup(opts)
|
M.padding.setup(opts)
|
||||||
|
|||||||
@ -1,35 +1,29 @@
|
|||||||
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")
|
local Decorator = require("nvim-tree.renderer.decorator")
|
||||||
|
|
||||||
---@class (exact) DecoratorBookmarks: Decorator
|
---@class (exact) DecoratorBookmarks: Decorator
|
||||||
---@field icon HighlightedString?
|
---@field icon HighlightedString?
|
||||||
local DecoratorBookmarks = Decorator:new()
|
local DecoratorBookmarks = Decorator:extend()
|
||||||
|
|
||||||
---Static factory method
|
---@class DecoratorBookmarks
|
||||||
---@param opts table
|
---@overload fun(explorer: DecoratorArgs): DecoratorBookmarks
|
||||||
---@param explorer Explorer
|
|
||||||
---@return DecoratorBookmarks
|
|
||||||
function DecoratorBookmarks:create(opts, explorer)
|
|
||||||
---@type DecoratorBookmarks
|
|
||||||
local o = {
|
|
||||||
explorer = explorer,
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
o = self:new(o)
|
|
||||||
|
|
||||||
if opts.renderer.icons.show.bookmarks then
|
---@protected
|
||||||
o.icon = {
|
---@param args DecoratorArgs
|
||||||
str = opts.renderer.icons.glyphs.bookmark,
|
function DecoratorBookmarks:new(args)
|
||||||
|
Decorator.new(self, {
|
||||||
|
explorer = args.explorer,
|
||||||
|
enabled = true,
|
||||||
|
hl_pos = args.explorer.opts.renderer.highlight_bookmarks or "none",
|
||||||
|
icon_placement = args.explorer.opts.renderer.icons.bookmarks_placement or "none",
|
||||||
|
})
|
||||||
|
|
||||||
|
if self.explorer.opts.renderer.icons.show.bookmarks then
|
||||||
|
self.icon = {
|
||||||
|
str = self.explorer.opts.renderer.icons.glyphs.bookmark,
|
||||||
hl = { "NvimTreeBookmarkIcon" },
|
hl = { "NvimTreeBookmarkIcon" },
|
||||||
}
|
}
|
||||||
o:define_sign(o.icon)
|
self:define_sign(self.icon)
|
||||||
end
|
end
|
||||||
|
|
||||||
return o
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---Bookmark icon: renderer.icons.show.bookmarks and node is marked
|
---Bookmark icon: renderer.icons.show.bookmarks and node is marked
|
||||||
@ -45,7 +39,7 @@ end
|
|||||||
---@param node Node
|
---@param node Node
|
||||||
---@return string|nil group
|
---@return string|nil group
|
||||||
function DecoratorBookmarks:calculate_highlight(node)
|
function DecoratorBookmarks:calculate_highlight(node)
|
||||||
if self.hl_pos ~= HL_POSITION.none and self.explorer.marks:get(node) then
|
if self.range ~= "none" and self.explorer.marks:get(node) then
|
||||||
return "NvimTreeBookmarkHL"
|
return "NvimTreeBookmarkHL"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,34 +1,27 @@
|
|||||||
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")
|
local Decorator = require("nvim-tree.renderer.decorator")
|
||||||
|
|
||||||
---@class (exact) DecoratorCopied: Decorator
|
---@class (exact) DecoratorCopied: Decorator
|
||||||
---@field icon HighlightedString?
|
local DecoratorCopied = Decorator:extend()
|
||||||
local DecoratorCopied = Decorator:new()
|
|
||||||
|
|
||||||
---Static factory method
|
---@class DecoratorCopied
|
||||||
---@param opts table
|
---@overload fun(explorer: DecoratorArgs): DecoratorCopied
|
||||||
---@param explorer Explorer
|
|
||||||
---@return DecoratorCopied
|
|
||||||
function DecoratorCopied:create(opts, explorer)
|
|
||||||
---@type DecoratorCopied
|
|
||||||
local o = {
|
|
||||||
explorer = explorer,
|
|
||||||
enabled = true,
|
|
||||||
hl_pos = HL_POSITION[opts.renderer.highlight_clipboard] or HL_POSITION.none,
|
|
||||||
icon_placement = ICON_PLACEMENT.none,
|
|
||||||
}
|
|
||||||
o = self:new(o)
|
|
||||||
|
|
||||||
return o
|
---@protected
|
||||||
|
---@param args DecoratorArgs
|
||||||
|
function DecoratorCopied:new(args)
|
||||||
|
Decorator.new(self, {
|
||||||
|
explorer = args.explorer,
|
||||||
|
enabled = true,
|
||||||
|
hl_pos = args.explorer.opts.renderer.highlight_clipboard or "none",
|
||||||
|
icon_placement = "none",
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
---Copied highlight: renderer.highlight_clipboard and node is copied
|
---Copied highlight: renderer.highlight_clipboard and node is copied
|
||||||
---@param node Node
|
---@param node Node
|
||||||
---@return string|nil group
|
---@return string|nil group
|
||||||
function DecoratorCopied:calculate_highlight(node)
|
function DecoratorCopied:calculate_highlight(node)
|
||||||
if self.hl_pos ~= HL_POSITION.none and self.explorer.clipboard:is_copied(node) then
|
if self.range ~= "none" and self.explorer.clipboard:is_copied(node) then
|
||||||
return "NvimTreeCopiedHL"
|
return "NvimTreeCopiedHL"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,33 +1,27 @@
|
|||||||
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")
|
local Decorator = require("nvim-tree.renderer.decorator")
|
||||||
|
|
||||||
---@class (exact) DecoratorCut: Decorator
|
---@class (exact) DecoratorCut: Decorator
|
||||||
local DecoratorCut = Decorator:new()
|
local DecoratorCut = Decorator:extend()
|
||||||
|
|
||||||
---Static factory method
|
---@class DecoratorCut
|
||||||
---@param opts table
|
---@overload fun(explorer: DecoratorArgs): DecoratorCut
|
||||||
---@param explorer Explorer
|
|
||||||
---@return DecoratorCut
|
|
||||||
function DecoratorCut:create(opts, explorer)
|
|
||||||
---@type DecoratorCut
|
|
||||||
local o = {
|
|
||||||
explorer = explorer,
|
|
||||||
enabled = true,
|
|
||||||
hl_pos = HL_POSITION[opts.renderer.highlight_clipboard] or HL_POSITION.none,
|
|
||||||
icon_placement = ICON_PLACEMENT.none,
|
|
||||||
}
|
|
||||||
o = self:new(o)
|
|
||||||
|
|
||||||
return o
|
---@protected
|
||||||
|
---@param args DecoratorArgs
|
||||||
|
function DecoratorCut:new(args)
|
||||||
|
Decorator.new(self, {
|
||||||
|
explorer = args.explorer,
|
||||||
|
enabled = true,
|
||||||
|
hl_pos = args.explorer.opts.renderer.highlight_clipboard or "none",
|
||||||
|
icon_placement = "none",
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
---Cut highlight: renderer.highlight_clipboard and node is cut
|
---Cut highlight: renderer.highlight_clipboard and node is cut
|
||||||
---@param node Node
|
---@param node Node
|
||||||
---@return string|nil group
|
---@return string|nil group
|
||||||
function DecoratorCut:calculate_highlight(node)
|
function DecoratorCut:calculate_highlight(node)
|
||||||
if self.hl_pos ~= HL_POSITION.none and self.explorer.clipboard:is_cut(node) then
|
if self.range ~= "none" and self.explorer.clipboard:is_cut(node) then
|
||||||
return "NvimTreeCutHL"
|
return "NvimTreeCutHL"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,8 +1,5 @@
|
|||||||
local diagnostics = require("nvim-tree.diagnostics")
|
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")
|
local Decorator = require("nvim-tree.renderer.decorator")
|
||||||
local DirectoryNode = require("nvim-tree.node.directory")
|
local DirectoryNode = require("nvim-tree.node.directory")
|
||||||
|
|
||||||
@ -35,38 +32,35 @@ local ICON_KEYS = {
|
|||||||
|
|
||||||
---@class (exact) DecoratorDiagnostics: Decorator
|
---@class (exact) DecoratorDiagnostics: Decorator
|
||||||
---@field icons HighlightedString[]?
|
---@field icons HighlightedString[]?
|
||||||
local DecoratorDiagnostics = Decorator:new()
|
local DecoratorDiagnostics = Decorator:extend()
|
||||||
|
|
||||||
---Static factory method
|
---@class DecoratorDiagnostics
|
||||||
---@param opts table
|
---@overload fun(explorer: DecoratorArgs): DecoratorDiagnostics
|
||||||
---@param explorer Explorer
|
|
||||||
---@return DecoratorDiagnostics
|
|
||||||
function DecoratorDiagnostics:create(opts, explorer)
|
|
||||||
---@type DecoratorDiagnostics
|
|
||||||
local o = {
|
|
||||||
explorer = explorer,
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
o = self:new(o)
|
|
||||||
|
|
||||||
if not o.enabled then
|
---@protected
|
||||||
return o
|
---@param args DecoratorArgs
|
||||||
|
function DecoratorDiagnostics:new(args)
|
||||||
|
Decorator.new(self, {
|
||||||
|
explorer = args.explorer,
|
||||||
|
enabled = true,
|
||||||
|
hl_pos = args.explorer.opts.renderer.highlight_diagnostics or "none",
|
||||||
|
icon_placement = args.explorer.opts.renderer.icons.diagnostics_placement or "none",
|
||||||
|
})
|
||||||
|
|
||||||
|
if not self.enabled then
|
||||||
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if opts.renderer.icons.show.diagnostics then
|
if self.explorer.opts.renderer.icons.show.diagnostics then
|
||||||
o.icons = {}
|
self.icons = {}
|
||||||
for name, sev in pairs(ICON_KEYS) do
|
for name, sev in pairs(ICON_KEYS) do
|
||||||
o.icons[sev] = {
|
self.icons[sev] = {
|
||||||
str = opts.diagnostics.icons[name],
|
str = self.explorer.opts.diagnostics.icons[name],
|
||||||
hl = { HG_ICON[sev] },
|
hl = { HG_ICON[sev] },
|
||||||
}
|
}
|
||||||
o:define_sign(o.icons[sev])
|
self:define_sign(self.icons[sev])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return o
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---Diagnostic icon: diagnostics.enable, renderer.icons.show.diagnostics and node has status
|
---Diagnostic icon: diagnostics.enable, renderer.icons.show.diagnostics and node has status
|
||||||
@ -87,7 +81,7 @@ end
|
|||||||
---@param node Node
|
---@param node Node
|
||||||
---@return string|nil group
|
---@return string|nil group
|
||||||
function DecoratorDiagnostics:calculate_highlight(node)
|
function DecoratorDiagnostics:calculate_highlight(node)
|
||||||
if not node or not self.enabled or self.hl_pos == HL_POSITION.none then
|
if not node or not self.enabled or self.range == "none" then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,5 @@
|
|||||||
local notify = require("nvim-tree.notify")
|
local notify = require("nvim-tree.notify")
|
||||||
|
|
||||||
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")
|
local Decorator = require("nvim-tree.renderer.decorator")
|
||||||
local DirectoryNode = require("nvim-tree.node.directory")
|
local DirectoryNode = require("nvim-tree.node.directory")
|
||||||
|
|
||||||
@ -20,52 +17,49 @@ local DirectoryNode = require("nvim-tree.node.directory")
|
|||||||
---@field folder_hl_by_xy table<GitXY, string>?
|
---@field folder_hl_by_xy table<GitXY, string>?
|
||||||
---@field icons_by_status GitIconsByStatus?
|
---@field icons_by_status GitIconsByStatus?
|
||||||
---@field icons_by_xy GitIconsByXY?
|
---@field icons_by_xy GitIconsByXY?
|
||||||
local DecoratorGit = Decorator:new()
|
local DecoratorGit = Decorator:extend()
|
||||||
|
|
||||||
---Static factory method
|
---@class DecoratorGit
|
||||||
---@param opts table
|
---@overload fun(explorer: DecoratorArgs): DecoratorGit
|
||||||
---@param explorer Explorer
|
|
||||||
---@return DecoratorGit
|
|
||||||
function DecoratorGit:create(opts, explorer)
|
|
||||||
---@type DecoratorGit
|
|
||||||
local o = {
|
|
||||||
explorer = explorer,
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
o = self:new(o)
|
|
||||||
|
|
||||||
if not o.enabled then
|
---@protected
|
||||||
return o
|
---@param args DecoratorArgs
|
||||||
|
function DecoratorGit:new(args)
|
||||||
|
Decorator.new(self, {
|
||||||
|
explorer = args.explorer,
|
||||||
|
enabled = args.explorer.opts.git.enable,
|
||||||
|
hl_pos = args.explorer.opts.renderer.highlight_git or "none",
|
||||||
|
icon_placement = args.explorer.opts.renderer.icons.git_placement or "none",
|
||||||
|
})
|
||||||
|
|
||||||
|
if not self.enabled then
|
||||||
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if o.hl_pos ~= HL_POSITION.none then
|
if self.range ~= "none" then
|
||||||
o:build_file_folder_hl_by_xy()
|
self:build_file_folder_hl_by_xy()
|
||||||
end
|
end
|
||||||
|
|
||||||
if opts.renderer.icons.show.git then
|
if self.explorer.opts.renderer.icons.show.git then
|
||||||
o:build_icons_by_status(opts.renderer.icons.glyphs.git)
|
self:build_icons_by_status(self.explorer.opts.renderer.icons.glyphs.git)
|
||||||
o:build_icons_by_xy(o.icons_by_status)
|
self:build_icons_by_xy(self.icons_by_status)
|
||||||
|
|
||||||
for _, icon in pairs(o.icons_by_status) do
|
for _, icon in pairs(self.icons_by_status) do
|
||||||
self:define_sign(icon)
|
self:define_sign(icon)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return o
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param glyphs GitGlyphsByStatus
|
---@param glyphs GitGlyphsByStatus
|
||||||
function DecoratorGit:build_icons_by_status(glyphs)
|
function DecoratorGit:build_icons_by_status(glyphs)
|
||||||
self.icons_by_status = {}
|
self.icons_by_status = {}
|
||||||
self.icons_by_status.staged = { str = glyphs.staged, hl = { "NvimTreeGitStagedIcon" }, ord = 1 }
|
self.icons_by_status.staged = { str = glyphs.staged, hl = { "NvimTreeGitStagedIcon" }, ord = 1 }
|
||||||
self.icons_by_status.unstaged = { str = glyphs.unstaged, hl = { "NvimTreeGitDirtyIcon" }, ord = 2 }
|
self.icons_by_status.unstaged = { str = glyphs.unstaged, hl = { "NvimTreeGitDirtyIcon" }, ord = 2 }
|
||||||
self.icons_by_status.renamed = { str = glyphs.renamed, hl = { "NvimTreeGitRenamedIcon" }, ord = 3 }
|
self.icons_by_status.renamed = { str = glyphs.renamed, hl = { "NvimTreeGitRenamedIcon" }, ord = 3 }
|
||||||
self.icons_by_status.deleted = { str = glyphs.deleted, hl = { "NvimTreeGitDeletedIcon" }, ord = 4 }
|
self.icons_by_status.deleted = { str = glyphs.deleted, hl = { "NvimTreeGitDeletedIcon" }, ord = 4 }
|
||||||
self.icons_by_status.unmerged = { str = glyphs.unmerged, hl = { "NvimTreeGitMergeIcon" }, ord = 5 }
|
self.icons_by_status.unmerged = { str = glyphs.unmerged, hl = { "NvimTreeGitMergeIcon" }, ord = 5 }
|
||||||
self.icons_by_status.untracked = { str = glyphs.untracked, hl = { "NvimTreeGitNewIcon" }, ord = 6 }
|
self.icons_by_status.untracked = { str = glyphs.untracked, hl = { "NvimTreeGitNewIcon" }, ord = 6 }
|
||||||
self.icons_by_status.ignored = { str = glyphs.ignored, hl = { "NvimTreeGitIgnoredIcon" }, ord = 7 }
|
self.icons_by_status.ignored = { str = glyphs.ignored, hl = { "NvimTreeGitIgnoredIcon" }, ord = 7 }
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param icons GitIconsByXY
|
---@param icons GitIconsByXY
|
||||||
@ -102,7 +96,7 @@ function DecoratorGit:build_icons_by_xy(icons)
|
|||||||
["DD"] = { icons.deleted },
|
["DD"] = { icons.deleted },
|
||||||
["DU"] = { icons.deleted, icons.unmerged },
|
["DU"] = { icons.deleted, icons.unmerged },
|
||||||
["!!"] = { icons.ignored },
|
["!!"] = { icons.ignored },
|
||||||
dirty = { icons.unstaged },
|
dirty = { icons.unstaged },
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -121,7 +115,7 @@ function DecoratorGit:build_file_folder_hl_by_xy()
|
|||||||
[" T"] = "NvimTreeGitFileDirtyHL",
|
[" T"] = "NvimTreeGitFileDirtyHL",
|
||||||
["MM"] = "NvimTreeGitFileDirtyHL",
|
["MM"] = "NvimTreeGitFileDirtyHL",
|
||||||
["AM"] = "NvimTreeGitFileDirtyHL",
|
["AM"] = "NvimTreeGitFileDirtyHL",
|
||||||
dirty = "NvimTreeGitFileDirtyHL",
|
dirty = "NvimTreeGitFileDirtyHL",
|
||||||
["A "] = "NvimTreeGitFileStagedHL",
|
["A "] = "NvimTreeGitFileStagedHL",
|
||||||
["??"] = "NvimTreeGitFileNewHL",
|
["??"] = "NvimTreeGitFileNewHL",
|
||||||
["AU"] = "NvimTreeGitFileMergeHL",
|
["AU"] = "NvimTreeGitFileMergeHL",
|
||||||
@ -165,7 +159,7 @@ function DecoratorGit:calculate_icons(node)
|
|||||||
for _, s in pairs(git_xy) do
|
for _, s in pairs(git_xy) do
|
||||||
local icons = self.icons_by_xy[s]
|
local icons = self.icons_by_xy[s]
|
||||||
if not icons then
|
if not icons then
|
||||||
if self.hl_pos == HL_POSITION.none then
|
if self.range == "none" then
|
||||||
notify.warn(string.format("Unrecognized git state '%s'", git_xy))
|
notify.warn(string.format("Unrecognized git state '%s'", git_xy))
|
||||||
end
|
end
|
||||||
return nil
|
return nil
|
||||||
@ -197,7 +191,7 @@ end
|
|||||||
---@param node Node
|
---@param node Node
|
||||||
---@return string|nil name
|
---@return string|nil name
|
||||||
function DecoratorGit:sign_name(node)
|
function DecoratorGit:sign_name(node)
|
||||||
if self.icon_placement ~= ICON_PLACEMENT.signcolumn then
|
if self.icon_placement ~= "signcolumn" then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -211,7 +205,7 @@ end
|
|||||||
---@param node Node
|
---@param node Node
|
||||||
---@return string|nil group
|
---@return string|nil group
|
||||||
function DecoratorGit:calculate_highlight(node)
|
function DecoratorGit:calculate_highlight(node)
|
||||||
if not node or not self.enabled or self.hl_pos == HL_POSITION.none then
|
if not node or not self.enabled or self.range == "none" then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -1,36 +1,30 @@
|
|||||||
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")
|
local Decorator = require("nvim-tree.renderer.decorator")
|
||||||
local DirectoryNode = require("nvim-tree.node.directory")
|
local DirectoryNode = require("nvim-tree.node.directory")
|
||||||
|
|
||||||
---@class (exact) DecoratorHidden: Decorator
|
---@class (exact) DecoratorHidden: Decorator
|
||||||
---@field icon HighlightedString?
|
---@field icon HighlightedString?
|
||||||
local DecoratorHidden = Decorator:new()
|
local DecoratorHidden = Decorator:extend()
|
||||||
|
|
||||||
---Static factory method
|
---@class DecoratorHidden
|
||||||
---@param opts table
|
---@overload fun(explorer: DecoratorArgs): DecoratorHidden
|
||||||
---@param explorer Explorer
|
|
||||||
---@return DecoratorHidden
|
|
||||||
function DecoratorHidden:create(opts, explorer)
|
|
||||||
---@type DecoratorHidden
|
|
||||||
local o = {
|
|
||||||
explorer = explorer,
|
|
||||||
enabled = true,
|
|
||||||
hl_pos = HL_POSITION[opts.renderer.highlight_hidden] or HL_POSITION.none,
|
|
||||||
icon_placement = ICON_PLACEMENT[opts.renderer.icons.hidden_placement] or ICON_PLACEMENT.none,
|
|
||||||
}
|
|
||||||
o = self:new(o)
|
|
||||||
|
|
||||||
if opts.renderer.icons.show.hidden then
|
---@protected
|
||||||
o.icon = {
|
---@param args DecoratorArgs
|
||||||
str = opts.renderer.icons.glyphs.hidden,
|
function DecoratorHidden:new(args)
|
||||||
|
Decorator.new(self, {
|
||||||
|
explorer = args.explorer,
|
||||||
|
enabled = true,
|
||||||
|
hl_pos = args.explorer.opts.renderer.highlight_hidden or "none",
|
||||||
|
icon_placement = args.explorer.opts.renderer.icons.hidden_placement or "none",
|
||||||
|
})
|
||||||
|
|
||||||
|
if self.explorer.opts.renderer.icons.show.hidden then
|
||||||
|
self.icon = {
|
||||||
|
str = self.explorer.opts.renderer.icons.glyphs.hidden,
|
||||||
hl = { "NvimTreeHiddenIcon" },
|
hl = { "NvimTreeHiddenIcon" },
|
||||||
}
|
}
|
||||||
o:define_sign(o.icon)
|
self:define_sign(self.icon)
|
||||||
end
|
end
|
||||||
|
|
||||||
return o
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---Hidden icon: renderer.icons.show.hidden and node starts with `.` (dotfile).
|
---Hidden icon: renderer.icons.show.hidden and node starts with `.` (dotfile).
|
||||||
@ -46,7 +40,7 @@ end
|
|||||||
---@param node Node
|
---@param node Node
|
||||||
---@return string|nil group
|
---@return string|nil group
|
||||||
function DecoratorHidden:calculate_highlight(node)
|
function DecoratorHidden:calculate_highlight(node)
|
||||||
if not self.enabled or self.hl_pos == HL_POSITION.none or not node:is_dotfile() then
|
if not self.enabled or self.range == "none" or not node:is_dotfile() then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -1,16 +1,33 @@
|
|||||||
local Class = require("nvim-tree.class")
|
local Class = require("nvim-tree.classic")
|
||||||
|
|
||||||
local HL_POSITION = require("nvim-tree.enum").HL_POSITION
|
---@alias DecoratorRange "none" | "icon" | "name" | "all"
|
||||||
local ICON_PLACEMENT = require("nvim-tree.enum").ICON_PLACEMENT
|
---@alias DecoratorIconPlacement "none" | "before" | "after" | "signcolumn" | "right_align"
|
||||||
|
|
||||||
---Abstract Decorator
|
---Abstract Decorator
|
||||||
---Uses the factory pattern to instantiate child instances.
|
---Uses the factory pattern to instantiate child instances.
|
||||||
---@class (exact) Decorator: Class
|
---@class (exact) Decorator: Class
|
||||||
---@field protected explorer Explorer
|
---@field protected explorer Explorer
|
||||||
---@field protected enabled boolean
|
---@field protected enabled boolean
|
||||||
---@field protected hl_pos HL_POSITION
|
---@field protected range DecoratorRange
|
||||||
---@field protected icon_placement ICON_PLACEMENT
|
---@field protected icon_placement DecoratorIconPlacement
|
||||||
local Decorator = Class:new()
|
local Decorator = Class:extend()
|
||||||
|
|
||||||
|
---@class (exact) DecoratorArgs
|
||||||
|
---@field explorer Explorer
|
||||||
|
|
||||||
|
---@class (exact) AbstractDecoratorArgs: DecoratorArgs
|
||||||
|
---@field enabled boolean
|
||||||
|
---@field hl_pos DecoratorRange
|
||||||
|
---@field icon_placement DecoratorIconPlacement
|
||||||
|
|
||||||
|
---@protected
|
||||||
|
---@param args AbstractDecoratorArgs
|
||||||
|
function Decorator:new(args)
|
||||||
|
self.explorer = args.explorer
|
||||||
|
self.enabled = args.enabled
|
||||||
|
self.range = args.hl_pos
|
||||||
|
self.icon_placement = args.icon_placement
|
||||||
|
end
|
||||||
|
|
||||||
---Maybe highlight groups
|
---Maybe highlight groups
|
||||||
---@param node Node
|
---@param node Node
|
||||||
@ -19,13 +36,13 @@ local Decorator = Class:new()
|
|||||||
function Decorator:groups_icon_name(node)
|
function Decorator:groups_icon_name(node)
|
||||||
local icon_hl, name_hl
|
local icon_hl, name_hl
|
||||||
|
|
||||||
if self.enabled and self.hl_pos ~= HL_POSITION.none then
|
if self.enabled and self.range ~= "none" then
|
||||||
local hl = self:calculate_highlight(node)
|
local hl = self:calculate_highlight(node)
|
||||||
|
|
||||||
if self.hl_pos == HL_POSITION.all or self.hl_pos == HL_POSITION.icon then
|
if self.range == "all" or self.range == "icon" then
|
||||||
icon_hl = hl
|
icon_hl = hl
|
||||||
end
|
end
|
||||||
if self.hl_pos == HL_POSITION.all or self.hl_pos == HL_POSITION.name then
|
if self.range == "all" or self.range == "name" then
|
||||||
name_hl = hl
|
name_hl = hl
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -37,7 +54,7 @@ end
|
|||||||
---@param node Node
|
---@param node Node
|
||||||
---@return string|nil name
|
---@return string|nil name
|
||||||
function Decorator:sign_name(node)
|
function Decorator:sign_name(node)
|
||||||
if not self.enabled or self.icon_placement ~= ICON_PLACEMENT.signcolumn then
|
if not self.enabled or self.icon_placement ~= "signcolumn" then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -47,33 +64,33 @@ function Decorator:sign_name(node)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---Icons when ICON_PLACEMENT.before
|
---Icons when "before"
|
||||||
---@param node Node
|
---@param node Node
|
||||||
---@return HighlightedString[]|nil icons
|
---@return HighlightedString[]|nil icons
|
||||||
function Decorator:icons_before(node)
|
function Decorator:icons_before(node)
|
||||||
if not self.enabled or self.icon_placement ~= ICON_PLACEMENT.before then
|
if not self.enabled or self.icon_placement ~= "before" then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
return self:calculate_icons(node)
|
return self:calculate_icons(node)
|
||||||
end
|
end
|
||||||
|
|
||||||
---Icons when ICON_PLACEMENT.after
|
---Icons when "after"
|
||||||
---@param node Node
|
---@param node Node
|
||||||
---@return HighlightedString[]|nil icons
|
---@return HighlightedString[]|nil icons
|
||||||
function Decorator:icons_after(node)
|
function Decorator:icons_after(node)
|
||||||
if not self.enabled or self.icon_placement ~= ICON_PLACEMENT.after then
|
if not self.enabled or self.icon_placement ~= "after" then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
return self:calculate_icons(node)
|
return self:calculate_icons(node)
|
||||||
end
|
end
|
||||||
|
|
||||||
---Icons when ICON_PLACEMENT.right_align
|
---Icons when "right_align"
|
||||||
---@param node Node
|
---@param node Node
|
||||||
---@return HighlightedString[]|nil icons
|
---@return HighlightedString[]|nil icons
|
||||||
function Decorator:icons_right_align(node)
|
function Decorator:icons_right_align(node)
|
||||||
if not self.enabled or self.icon_placement ~= ICON_PLACEMENT.right_align then
|
if not self.enabled or self.icon_placement ~= "right_align" then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -109,7 +126,7 @@ function Decorator:define_sign(icon)
|
|||||||
|
|
||||||
-- don't use sign if not defined
|
-- don't use sign if not defined
|
||||||
if #icon.str < 1 then
|
if #icon.str < 1 then
|
||||||
self.icon_placement = ICON_PLACEMENT.none
|
self.icon_placement = "none"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -1,42 +1,36 @@
|
|||||||
local buffers = require("nvim-tree.buffers")
|
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")
|
local Decorator = require("nvim-tree.renderer.decorator")
|
||||||
local DirectoryNode = require("nvim-tree.node.directory")
|
local DirectoryNode = require("nvim-tree.node.directory")
|
||||||
|
|
||||||
---@class (exact) DecoratorModified: Decorator
|
---@class (exact) DecoratorModified: Decorator
|
||||||
---@field icon HighlightedString|nil
|
---@field icon HighlightedString?
|
||||||
local DecoratorModified = Decorator:new()
|
local DecoratorModified = Decorator:extend()
|
||||||
|
|
||||||
---Static factory method
|
---@class DecoratorModified
|
||||||
---@param opts table
|
---@overload fun(explorer: DecoratorArgs): DecoratorModified
|
||||||
---@param explorer Explorer
|
|
||||||
---@return DecoratorModified
|
|
||||||
function DecoratorModified:create(opts, explorer)
|
|
||||||
---@type DecoratorModified
|
|
||||||
local o = {
|
|
||||||
explorer = explorer,
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
o = self:new(o)
|
|
||||||
|
|
||||||
if not o.enabled then
|
---@protected
|
||||||
return o
|
---@param args DecoratorArgs
|
||||||
|
function DecoratorModified:new(args)
|
||||||
|
Decorator.new(self, {
|
||||||
|
explorer = args.explorer,
|
||||||
|
enabled = true,
|
||||||
|
hl_pos = args.explorer.opts.renderer.highlight_modified or "none",
|
||||||
|
icon_placement = args.explorer.opts.renderer.icons.modified_placement or "none",
|
||||||
|
})
|
||||||
|
|
||||||
|
if not self.enabled then
|
||||||
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if opts.renderer.icons.show.modified then
|
if self.explorer.opts.renderer.icons.show.modified then
|
||||||
o.icon = {
|
self.icon = {
|
||||||
str = opts.renderer.icons.glyphs.modified,
|
str = self.explorer.opts.renderer.icons.glyphs.modified,
|
||||||
hl = { "NvimTreeModifiedIcon" },
|
hl = { "NvimTreeModifiedIcon" },
|
||||||
}
|
}
|
||||||
o:define_sign(o.icon)
|
self:define_sign(self.icon)
|
||||||
end
|
end
|
||||||
|
|
||||||
return o
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---Modified icon: modified.enable, renderer.icons.show.modified and node is modified
|
---Modified icon: modified.enable, renderer.icons.show.modified and node is modified
|
||||||
@ -52,7 +46,7 @@ end
|
|||||||
---@param node Node
|
---@param node Node
|
||||||
---@return string|nil group
|
---@return string|nil group
|
||||||
function DecoratorModified:calculate_highlight(node)
|
function DecoratorModified:calculate_highlight(node)
|
||||||
if not self.enabled or self.hl_pos == HL_POSITION.none or not buffers.is_modified(node) then
|
if not self.enabled or self.range == "none" or not buffers.is_modified(node) then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -1,36 +1,30 @@
|
|||||||
local buffers = require("nvim-tree.buffers")
|
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")
|
local Decorator = require("nvim-tree.renderer.decorator")
|
||||||
|
|
||||||
---@class (exact) DecoratorOpened: Decorator
|
---@class (exact) DecoratorOpened: Decorator
|
||||||
---@field icon HighlightedString|nil
|
---@field icon HighlightedString|nil
|
||||||
local DecoratorOpened = Decorator:new()
|
local DecoratorOpened = Decorator:extend()
|
||||||
|
|
||||||
---Static factory method
|
---@class DecoratorOpened
|
||||||
---@param opts table
|
---@overload fun(explorer: DecoratorArgs): DecoratorOpened
|
||||||
---@param explorer Explorer
|
|
||||||
---@return DecoratorOpened
|
|
||||||
function DecoratorOpened:create(opts, explorer)
|
|
||||||
---@type DecoratorOpened
|
|
||||||
local o = {
|
|
||||||
explorer = explorer,
|
|
||||||
enabled = true,
|
|
||||||
hl_pos = HL_POSITION[opts.renderer.highlight_opened_files] or HL_POSITION.none,
|
|
||||||
icon_placement = ICON_PLACEMENT.none,
|
|
||||||
}
|
|
||||||
o = self:new(o)
|
|
||||||
|
|
||||||
return o
|
---@protected
|
||||||
|
---@param args DecoratorArgs
|
||||||
|
function DecoratorOpened:new(args)
|
||||||
|
Decorator.new(self, {
|
||||||
|
explorer = args.explorer,
|
||||||
|
enabled = true,
|
||||||
|
hl_pos = args.explorer.opts.renderer.highlight_opened_files or "none",
|
||||||
|
icon_placement = "none",
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
---Opened highlight: renderer.highlight_opened_files and node has an open buffer
|
---Opened highlight: renderer.highlight_opened_files and node has an open buffer
|
||||||
---@param node Node
|
---@param node Node
|
||||||
---@return string|nil group
|
---@return string|nil group
|
||||||
function DecoratorOpened:calculate_highlight(node)
|
function DecoratorOpened:calculate_highlight(node)
|
||||||
if self.hl_pos ~= HL_POSITION.none and buffers.is_opened(node) then
|
if self.range ~= "none" and buffers.is_opened(node) then
|
||||||
return "NvimTreeOpenedHL"
|
return "NvimTreeOpenedHL"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -2,6 +2,7 @@ local log = require("nvim-tree.log")
|
|||||||
local view = require("nvim-tree.view")
|
local view = require("nvim-tree.view")
|
||||||
local events = require("nvim-tree.events")
|
local events = require("nvim-tree.events")
|
||||||
|
|
||||||
|
local Class = require("nvim-tree.classic")
|
||||||
local Builder = require("nvim-tree.renderer.builder")
|
local Builder = require("nvim-tree.renderer.builder")
|
||||||
|
|
||||||
local SIGN_GROUP = "NvimTreeRendererSigns"
|
local SIGN_GROUP = "NvimTreeRendererSigns"
|
||||||
@ -10,26 +11,20 @@ local namespace_highlights_id = vim.api.nvim_create_namespace("NvimTreeHighlight
|
|||||||
local namespace_extmarks_id = vim.api.nvim_create_namespace("NvimTreeExtmarks")
|
local namespace_extmarks_id = vim.api.nvim_create_namespace("NvimTreeExtmarks")
|
||||||
local namespace_virtual_lines_id = vim.api.nvim_create_namespace("NvimTreeVirtualLines")
|
local namespace_virtual_lines_id = vim.api.nvim_create_namespace("NvimTreeVirtualLines")
|
||||||
|
|
||||||
---@class (exact) Renderer
|
---@class (exact) Renderer: Class
|
||||||
---@field private __index? table
|
---@field explorer Explorer
|
||||||
---@field private opts table user options
|
local Renderer = Class:extend()
|
||||||
---@field private explorer Explorer
|
|
||||||
local Renderer = {}
|
|
||||||
|
|
||||||
---@param opts table user options
|
---@class Renderer
|
||||||
---@param explorer Explorer
|
---@overload fun(args: RendererArgs): Renderer
|
||||||
---@return Renderer
|
|
||||||
function Renderer:new(opts, explorer)
|
|
||||||
---@type Renderer
|
|
||||||
local o = {
|
|
||||||
opts = opts,
|
|
||||||
explorer = explorer,
|
|
||||||
}
|
|
||||||
|
|
||||||
setmetatable(o, self)
|
---@class (exact) RendererArgs
|
||||||
self.__index = self
|
---@field explorer Explorer
|
||||||
|
|
||||||
return o
|
---@protected
|
||||||
|
---@param args RendererArgs
|
||||||
|
function Renderer:new(args)
|
||||||
|
self.explorer = args.explorer
|
||||||
end
|
end
|
||||||
|
|
||||||
---@private
|
---@private
|
||||||
@ -64,9 +59,9 @@ function Renderer:_draw(bufnr, lines, hl_args, signs, extmarks, virtual_lines)
|
|||||||
for i, extname in pairs(extmarks) do
|
for i, extname in pairs(extmarks) do
|
||||||
for _, mark in ipairs(extname) do
|
for _, mark in ipairs(extname) do
|
||||||
vim.api.nvim_buf_set_extmark(bufnr, namespace_extmarks_id, i, -1, {
|
vim.api.nvim_buf_set_extmark(bufnr, namespace_extmarks_id, i, -1, {
|
||||||
virt_text = { { mark.str, mark.hl } },
|
virt_text = { { mark.str, mark.hl } },
|
||||||
virt_text_pos = "right_align",
|
virt_text_pos = "right_align",
|
||||||
hl_mode = "combine",
|
hl_mode = "combine",
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -74,8 +69,8 @@ function Renderer:_draw(bufnr, lines, hl_args, signs, extmarks, virtual_lines)
|
|||||||
vim.api.nvim_buf_clear_namespace(bufnr, namespace_virtual_lines_id, 0, -1)
|
vim.api.nvim_buf_clear_namespace(bufnr, namespace_virtual_lines_id, 0, -1)
|
||||||
for line_nr, vlines in pairs(virtual_lines) do
|
for line_nr, vlines in pairs(virtual_lines) do
|
||||||
vim.api.nvim_buf_set_extmark(bufnr, namespace_virtual_lines_id, line_nr, 0, {
|
vim.api.nvim_buf_set_extmark(bufnr, namespace_virtual_lines_id, line_nr, 0, {
|
||||||
virt_lines = vlines,
|
virt_lines = vlines,
|
||||||
virt_lines_above = false,
|
virt_lines_above = false,
|
||||||
virt_lines_leftcol = true,
|
virt_lines_leftcol = true,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
@ -106,7 +101,7 @@ function Renderer:draw()
|
|||||||
|
|
||||||
local cursor = vim.api.nvim_win_get_cursor(view.get_winnr() or 0)
|
local cursor = vim.api.nvim_win_get_cursor(view.get_winnr() or 0)
|
||||||
|
|
||||||
local builder = Builder:new(self.opts, self.explorer):build()
|
local builder = Builder(self.explorer):build()
|
||||||
|
|
||||||
self:_draw(bufnr, builder.lines, builder.hl_args, builder.signs, builder.extmarks, builder.virtual_lines)
|
self:_draw(bufnr, builder.lines, builder.hl_args, builder.signs, builder.extmarks, builder.virtual_lines)
|
||||||
|
|
||||||
|
|||||||
@ -15,31 +15,31 @@ local DEFAULT_MAX_WIDTH = -1
|
|||||||
local DEFAULT_PADDING = 1
|
local DEFAULT_PADDING = 1
|
||||||
|
|
||||||
M.View = {
|
M.View = {
|
||||||
adaptive_size = false,
|
adaptive_size = false,
|
||||||
centralize_selection = false,
|
centralize_selection = false,
|
||||||
tabpages = {},
|
tabpages = {},
|
||||||
cursors = {},
|
cursors = {},
|
||||||
hide_root_folder = false,
|
hide_root_folder = false,
|
||||||
live_filter = {
|
live_filter = {
|
||||||
prev_focused_node = nil,
|
prev_focused_node = nil,
|
||||||
},
|
},
|
||||||
winopts = {
|
winopts = {
|
||||||
relativenumber = false,
|
relativenumber = false,
|
||||||
number = false,
|
number = false,
|
||||||
list = false,
|
list = false,
|
||||||
foldenable = false,
|
foldenable = false,
|
||||||
winfixwidth = true,
|
winfixwidth = true,
|
||||||
winfixheight = true,
|
winfixheight = true,
|
||||||
spell = false,
|
spell = false,
|
||||||
signcolumn = "yes",
|
signcolumn = "yes",
|
||||||
foldmethod = "manual",
|
foldmethod = "manual",
|
||||||
foldcolumn = "0",
|
foldcolumn = "0",
|
||||||
cursorcolumn = false,
|
cursorcolumn = false,
|
||||||
cursorline = true,
|
cursorline = true,
|
||||||
cursorlineopt = "both",
|
cursorlineopt = "both",
|
||||||
colorcolumn = "0",
|
colorcolumn = "0",
|
||||||
wrap = false,
|
wrap = false,
|
||||||
winhl = table.concat({
|
winhl = table.concat({
|
||||||
"EndOfBuffer:NvimTreeEndOfBuffer",
|
"EndOfBuffer:NvimTreeEndOfBuffer",
|
||||||
"CursorLine:NvimTreeCursorLine",
|
"CursorLine:NvimTreeCursorLine",
|
||||||
"CursorLineNr:NvimTreeCursorLineNr",
|
"CursorLineNr:NvimTreeCursorLineNr",
|
||||||
@ -152,7 +152,6 @@ local function set_window_options_and_buffer()
|
|||||||
pcall(vim.api.nvim_command, "buffer " .. M.get_bufnr())
|
pcall(vim.api.nvim_command, "buffer " .. M.get_bufnr())
|
||||||
|
|
||||||
if vim.fn.has("nvim-0.10") == 1 then
|
if vim.fn.has("nvim-0.10") == 1 then
|
||||||
|
|
||||||
local eventignore = vim.api.nvim_get_option_value("eventignore", {})
|
local eventignore = vim.api.nvim_get_option_value("eventignore", {})
|
||||||
vim.api.nvim_set_option_value("eventignore", "all", {})
|
vim.api.nvim_set_option_value("eventignore", "all", {})
|
||||||
|
|
||||||
@ -161,9 +160,7 @@ local function set_window_options_and_buffer()
|
|||||||
end
|
end
|
||||||
|
|
||||||
vim.api.nvim_set_option_value("eventignore", eventignore, {})
|
vim.api.nvim_set_option_value("eventignore", eventignore, {})
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
local eventignore = vim.api.nvim_get_option("eventignore") ---@diagnostic disable-line: deprecated
|
local eventignore = vim.api.nvim_get_option("eventignore") ---@diagnostic disable-line: deprecated
|
||||||
vim.api.nvim_set_option("eventignore", "all") ---@diagnostic disable-line: deprecated
|
vim.api.nvim_set_option("eventignore", "all") ---@diagnostic disable-line: deprecated
|
||||||
|
|
||||||
@ -172,7 +169,6 @@ local function set_window_options_and_buffer()
|
|||||||
end
|
end
|
||||||
|
|
||||||
vim.api.nvim_set_option("eventignore", eventignore) ---@diagnostic disable-line: deprecated
|
vim.api.nvim_set_option("eventignore", eventignore) ---@diagnostic disable-line: deprecated
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@ local notify = require("nvim-tree.notify")
|
|||||||
local log = require("nvim-tree.log")
|
local log = require("nvim-tree.log")
|
||||||
local utils = require("nvim-tree.utils")
|
local utils = require("nvim-tree.utils")
|
||||||
|
|
||||||
local Class = require("nvim-tree.class")
|
local Class = require("nvim-tree.classic")
|
||||||
|
|
||||||
local FS_EVENT_FLAGS = {
|
local FS_EVENT_FLAGS = {
|
||||||
-- inotify or equivalent will be used; fallback to stat has not yet been implemented
|
-- inotify or equivalent will be used; fallback to stat has not yet been implemented
|
||||||
@ -15,36 +15,45 @@ local M = {
|
|||||||
config = {},
|
config = {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
---Registry of all events
|
||||||
|
---@type Event[]
|
||||||
|
local events = {}
|
||||||
|
|
||||||
---@class (exact) Event: Class
|
---@class (exact) Event: Class
|
||||||
---@field destroyed boolean
|
---@field destroyed boolean
|
||||||
---@field private path string
|
---@field private path string
|
||||||
---@field private fs_event uv.uv_fs_event_t?
|
---@field private fs_event uv.uv_fs_event_t?
|
||||||
---@field private listeners function[]
|
---@field private listeners function[]
|
||||||
local Event = Class:new()
|
local Event = Class:extend()
|
||||||
|
|
||||||
---Registry of all events
|
---@class Event
|
||||||
---@type Event[]
|
---@overload fun(args: EventArgs): Event
|
||||||
local events = {}
|
|
||||||
|
---@class (exact) EventArgs
|
||||||
|
---@field path string
|
||||||
|
|
||||||
|
---@protected
|
||||||
|
---@param args EventArgs
|
||||||
|
function Event:new(args)
|
||||||
|
self.destroyed = false
|
||||||
|
self.path = args.path
|
||||||
|
self.fs_event = nil
|
||||||
|
self.listeners = {}
|
||||||
|
end
|
||||||
|
|
||||||
---Static factory method
|
---Static factory method
|
||||||
---Creates and starts an Event
|
---Creates and starts an Event
|
||||||
---@param path string
|
---nil on failure to start
|
||||||
---@return Event|nil
|
---@param args EventArgs
|
||||||
function Event:create(path)
|
---@return Event?
|
||||||
log.line("watcher", "Event:create '%s'", path)
|
function Event:create(args)
|
||||||
|
log.line("watcher", "Event:create '%s'", args.path)
|
||||||
|
|
||||||
---@type Event
|
local event = Event(args)
|
||||||
local o = {
|
|
||||||
destroyed = false,
|
|
||||||
path = path,
|
|
||||||
fs_event = nil,
|
|
||||||
listeners = {},
|
|
||||||
}
|
|
||||||
o = self:new(o)
|
|
||||||
|
|
||||||
if o:start() then
|
if event:start() then
|
||||||
events[path] = o
|
events[event.path] = event
|
||||||
return o
|
return event
|
||||||
else
|
else
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
@ -128,8 +137,10 @@ function Event:destroy(message)
|
|||||||
events[self.path] = nil
|
events[self.path] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
---Static factory method
|
---Registry of all watchers
|
||||||
---Creates and starts a Watcher
|
---@type Watcher[]
|
||||||
|
local watchers = {}
|
||||||
|
|
||||||
---@class (exact) Watcher: Class
|
---@class (exact) Watcher: Class
|
||||||
---@field data table user data
|
---@field data table user data
|
||||||
---@field destroyed boolean
|
---@field destroyed boolean
|
||||||
@ -138,43 +149,50 @@ end
|
|||||||
---@field private files string[]?
|
---@field private files string[]?
|
||||||
---@field private listener fun(filename: string)?
|
---@field private listener fun(filename: string)?
|
||||||
---@field private event Event
|
---@field private event Event
|
||||||
local Watcher = Class:new()
|
local Watcher = Class:extend()
|
||||||
|
|
||||||
---Registry of all watchers
|
---@class Watcher
|
||||||
---@type Watcher[]
|
---@overload fun(args: WatcherArgs): Watcher
|
||||||
local watchers = {}
|
|
||||||
|
---@class (exact) WatcherArgs
|
||||||
|
---@field path string
|
||||||
|
---@field files string[]|nil
|
||||||
|
---@field callback fun(watcher: Watcher)
|
||||||
|
---@field data table? user data
|
||||||
|
|
||||||
|
---@protected
|
||||||
|
---@param args WatcherArgs
|
||||||
|
function Watcher:new(args)
|
||||||
|
self.data = args.data
|
||||||
|
self.destroyed = false
|
||||||
|
self.path = args.path
|
||||||
|
self.callback = args.callback
|
||||||
|
self.files = args.files
|
||||||
|
self.listener = nil
|
||||||
|
end
|
||||||
|
|
||||||
---Static factory method
|
---Static factory method
|
||||||
---@param path string
|
---Creates and starts a Watcher
|
||||||
---@param files string[]|nil
|
---nil on failure to create Event
|
||||||
---@param callback fun(watcher: Watcher)
|
---@param args WatcherArgs
|
||||||
---@param data table user data
|
|
||||||
---@return Watcher|nil
|
---@return Watcher|nil
|
||||||
function Watcher:create(path, files, callback, data)
|
function Watcher:create(args)
|
||||||
log.line("watcher", "Watcher:create '%s' %s", path, vim.inspect(files))
|
log.line("watcher", "Watcher:create '%s' %s", args.path, vim.inspect(args.files))
|
||||||
|
|
||||||
local event = events[path] or Event:create(path)
|
local event = events[args.path] or Event:create({ path = args.path })
|
||||||
if not event then
|
if not event then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
---@type Watcher
|
local watcher = Watcher(args)
|
||||||
local o = {
|
|
||||||
data = data,
|
|
||||||
destroyed = false,
|
|
||||||
path = path,
|
|
||||||
callback = callback,
|
|
||||||
files = files,
|
|
||||||
listener = nil,
|
|
||||||
event = event,
|
|
||||||
}
|
|
||||||
o = self:new(o)
|
|
||||||
|
|
||||||
o:start()
|
watcher.event = event
|
||||||
|
|
||||||
table.insert(watchers, o)
|
watcher:start()
|
||||||
|
|
||||||
return o
|
table.insert(watchers, watcher)
|
||||||
|
|
||||||
|
return watcher
|
||||||
end
|
end
|
||||||
|
|
||||||
function Watcher:start()
|
function Watcher:start()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user