refactor(#2828): multi instance nvim-tree.explorer.filters (#2841)

* refactor(#2828): multi instance nvim-tree.explorer.filters

* fix: style

* fix: apply suggestions from code review

Co-authored-by: Alexander Courtis <alex@courtis.org>

---------

Co-authored-by: Alexander Courtis <alex@courtis.org>
This commit is contained in:
Mateusz Russak 2024-07-27 05:29:27 +02:00 committed by GitHub
parent 1aa9852cad
commit 908478a0e0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 142 additions and 94 deletions

View File

@ -9,7 +9,6 @@ local actions = require "nvim-tree.actions"
local legacy = require "nvim-tree.legacy"
local core = require "nvim-tree.core"
local git = require "nvim-tree.git"
local filters = require "nvim-tree.explorer.filters"
local buffers = require "nvim-tree.buffers"
local notify = require "nvim-tree.notify"
@ -210,7 +209,13 @@ local function setup_autocommands(opts)
create_nvim_tree_autocmd("BufReadPost", {
callback = function(data)
-- update opened file buffers
if (filters.config.filter_no_buffer or renderer.config.highlight_opened_files ~= "none") and vim.bo[data.buf].buftype == "" then
local explorer = core.get_explorer()
if not explorer then
return
end
if
(explorer.filters.config.filter_no_buffer or renderer.config.highlight_opened_files ~= "none") and vim.bo[data.buf].buftype == ""
then
utils.debounce("Buf:filter_buffer", opts.view.debounce_delay, function()
actions.reloaders.reload_explorer()
end)
@ -221,7 +226,13 @@ local function setup_autocommands(opts)
create_nvim_tree_autocmd("BufUnload", {
callback = function(data)
-- update opened file buffers
if (filters.config.filter_no_buffer or renderer.config.highlight_opened_files ~= "none") and vim.bo[data.buf].buftype == "" then
local explorer = core.get_explorer()
if not explorer then
return
end
if
(explorer.filters.config.filter_no_buffer or renderer.config.highlight_opened_files ~= "none") and vim.bo[data.buf].buftype == ""
then
utils.debounce("Buf:filter_buffer", opts.view.debounce_delay, function()
actions.reloaders.reload_explorer()
end)

View File

@ -1,5 +1,4 @@
local core = require "nvim-tree.core"
local filters = require "nvim-tree.explorer.filters"
local find_file = require("nvim-tree.actions.finders.find-file").fn
local M = {}
@ -9,6 +8,11 @@ local M = {}
---@return string|nil
local function search(search_dir, input_path)
local realpaths_searched = {}
local explorer = core.get_explorer()
if not explorer then
return
end
if not search_dir then
return
@ -19,7 +23,7 @@ local function search(search_dir, input_path)
local function iter(dir)
local realpath, path, name, stat, handle, _
local filter_status = filters.prepare()
local filter_status = explorer.filters:prepare()
handle, _ = vim.loop.fs_scandir(dir)
if not handle then
@ -42,7 +46,7 @@ local function search(search_dir, input_path)
break
end
if not filters.should_filter(path, stat, filter_status) then
if not explorer.filters:should_filter(path, stat, filter_status) then
if string.find(path, "/" .. input_path .. "$") then
return path
end

View File

@ -1,8 +1,7 @@
local lib = require "nvim-tree.lib"
local utils = require "nvim-tree.utils"
local filters = require "nvim-tree.explorer.filters"
local reloaders = require "nvim-tree.actions.reloaders"
local core = require "nvim-tree.core"
local M = {}
local function reload()
@ -11,39 +10,56 @@ local function reload()
utils.focus_node_or_parent(node)
end
function M.custom()
filters.config.filter_custom = not filters.config.filter_custom
local function wrap_explorer(fn)
return function(...)
local explorer = core.get_explorer()
if explorer then
return fn(explorer, ...)
end
end
end
local function custom(explorer)
explorer.filters.config.filter_custom = not explorer.filters.config.filter_custom
reload()
end
function M.git_ignored()
filters.config.filter_git_ignored = not filters.config.filter_git_ignored
local function git_ignored(explorer)
explorer.filters.config.filter_git_ignored = not explorer.filters.config.filter_git_ignored
reload()
end
function M.git_clean()
filters.config.filter_git_clean = not filters.config.filter_git_clean
local function git_clean(explorer)
explorer.filters.config.filter_git_clean = not explorer.filters.config.filter_git_clean
reload()
end
function M.no_buffer()
filters.config.filter_no_buffer = not filters.config.filter_no_buffer
local function no_buffer(explorer)
explorer.filters.config.filter_no_buffer = not explorer.filters.config.filter_no_buffer
reload()
end
function M.no_bookmark()
filters.config.filter_no_bookmark = not filters.config.filter_no_bookmark
local function no_bookmark(explorer)
explorer.filters.config.filter_no_bookmark = not explorer.filters.config.filter_no_bookmark
reload()
end
function M.dotfiles()
filters.config.filter_dotfiles = not filters.config.filter_dotfiles
local function dotfiles(explorer)
explorer.filters.config.filter_dotfiles = not explorer.filters.config.filter_dotfiles
reload()
end
function M.enable()
filters.config.enable = not filters.config.enable
local function enable(explorer)
explorer.filters.config.enable = not explorer.filters.config.enable
reload()
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

View File

@ -3,7 +3,6 @@ local builders = require "nvim-tree.explorer.node-builders"
local explorer_node = require "nvim-tree.explorer.node"
local git = require "nvim-tree.git"
local sorters = require "nvim-tree.explorer.sorters"
local filters = require "nvim-tree.explorer.filters"
local live_filter = require "nvim-tree.live-filter"
local log = require "nvim-tree.log"
@ -15,10 +14,11 @@ local M = {}
---@param cwd string
---@param node Node
---@param git_status table
local function populate_children(handle, cwd, node, git_status)
---@param parent Explorer
local function populate_children(handle, cwd, node, git_status, parent)
local node_ignored = explorer_node.is_git_ignored(node)
local nodes_by_path = utils.bool_record(node.nodes, "absolute_path")
local filter_status = filters.prepare(git_status)
local filter_status = parent.filters:prepare(git_status)
while true do
local name, t = vim.loop.fs_scandir_next(handle)
if not name then
@ -31,7 +31,7 @@ local function populate_children(handle, cwd, node, git_status)
---@type uv.fs_stat.result|nil
local stat = vim.loop.fs_stat(abs)
if not filters.should_filter(abs, stat, filter_status) and not nodes_by_path[abs] and Watcher.is_fs_event_capable(abs) then
if not parent.filters:should_filter(abs, stat, filter_status) and not nodes_by_path[abs] and Watcher.is_fs_event_capable(abs) then
local child = nil
if t == "directory" and vim.loop.fs_access(abs, "R") then
child = builders.folder(node, abs, name, stat)
@ -56,8 +56,9 @@ end
---@param node Node
---@param status table
---@param parent Explorer
---@return Node[]|nil
function M.explore(node, status)
function M.explore(node, status, parent)
local cwd = node.link_to or node.absolute_path
local handle = vim.loop.fs_scandir(cwd)
if not handle then
@ -66,7 +67,7 @@ function M.explore(node, status)
local profile = log.profile_start("explore init %s", node.absolute_path)
populate_children(handle, cwd, node, status)
populate_children(handle, cwd, node, status, parent)
local is_root = not node.parent
local child_folder_only = explorer_node.has_one_child_folder(node) and node.nodes[1]
@ -74,7 +75,7 @@ function M.explore(node, status)
local child_cwd = child_folder_only.link_to or child_folder_only.absolute_path
local child_status = git.load_project_status(child_cwd)
node.group_next = child_folder_only
local ns = M.explore(child_folder_only, child_status)
local ns = M.explore(child_folder_only, child_status, parent)
node.nodes = ns or {}
log.profile_end(profile)

View File

@ -1,15 +1,52 @@
local utils = require "nvim-tree.utils"
local M = {
ignore_list = {},
exclude_list = {},
custom_function = nil,
}
---@class Filters to handle all opts.filters and related API
---@field config table hydrated user opts.filters
---@field private explorer Explorer
---@field private exclude_list string[] filters.exclude
---@field private ignore_list string[] filters.custom string table
---@field private custom_function (fun(absolute_path: string): boolean)|nil filters.custom function
local Filters = {}
---@param opts table user options
---@param explorer Explorer
---@return Filters
function Filters:new(opts, explorer)
local o = {
explorer = explorer,
ignore_list = {},
exclude_list = opts.filters.exclude,
custom_function = nil,
config = {
enable = opts.filters.enable,
filter_custom = true,
filter_dotfiles = opts.filters.dotfiles,
filter_git_ignored = opts.filters.git_ignored,
filter_git_clean = opts.filters.git_clean,
filter_no_buffer = opts.filters.no_buffer,
filter_no_bookmark = opts.filters.no_bookmark,
},
}
local custom_filter = opts.filters.custom
if type(custom_filter) == "function" then
o.custom_function = custom_filter
else
if custom_filter and #custom_filter > 0 then
for _, filter_name in pairs(custom_filter) do
o.ignore_list[filter_name] = true
end
end
end
setmetatable(o, self)
self.__index = self
return o
end
---@param path string
---@return boolean
local function is_excluded(path)
for _, node in ipairs(M.exclude_list) do
local function is_excluded(self, path)
for _, node in ipairs(self.exclude_list) do
if path:match(node) then
return true
end
@ -21,7 +58,7 @@ end
---@param path string Absolute path
---@param git_status table from prepare
---@return boolean
local function git(path, git_status)
local function git(self, path, git_status)
if type(git_status) ~= "table" or type(git_status.files) ~= "table" or type(git_status.dirs) ~= "table" then
return false
end
@ -32,12 +69,12 @@ local function git(path, git_status)
status = status or git_status.dirs.indirect[path] and git_status.dirs.indirect[path][1]
-- filter ignored; overrides clean as they are effectively dirty
if M.config.filter_git_ignored and status == "!!" then
if self.config.filter_git_ignored and status == "!!" then
return true
end
-- filter clean
if M.config.filter_git_clean and not status then
if self.config.filter_git_clean and not status then
return true
end
@ -48,8 +85,8 @@ end
---@param path string Absolute path
---@param bufinfo table vim.fn.getbufinfo { buflisted = 1 }
---@return boolean
local function buf(path, bufinfo)
if not M.config.filter_no_buffer or type(bufinfo) ~= "table" then
local function buf(self, path, bufinfo)
if not self.config.filter_no_buffer or type(bufinfo) ~= "table" then
return false
end
@ -65,15 +102,15 @@ end
---@param path string
---@return boolean
local function dotfile(path)
return M.config.filter_dotfiles and utils.path_basename(path):sub(1, 1) == "."
local function dotfile(self, path)
return self.config.filter_dotfiles and utils.path_basename(path):sub(1, 1) == "."
end
---@param path string
---@param path_type string|nil filetype of path
---@param bookmarks table<string, string|nil> path, filetype table of bookmarked files
local function bookmark(path, path_type, bookmarks)
if not M.config.filter_no_bookmark then
local function bookmark(self, path, path_type, bookmarks)
if not self.config.filter_no_bookmark then
return false
end
-- if bookmark is empty, we should see a empty filetree
@ -107,21 +144,21 @@ end
---@param path string
---@return boolean
local function custom(path)
if not M.config.filter_custom then
local function custom(self, path)
if not self.config.filter_custom then
return false
end
local basename = utils.path_basename(path)
-- filter user's custom function
if M.custom_function and M.custom_function(path) then
if self.custom_function and self.custom_function(path) then
return true
end
-- filter custom regexes
local relpath = utils.path_relative(path, vim.loop.cwd())
for pat, _ in pairs(M.ignore_list) do
for pat, _ in pairs(self.ignore_list) do
if vim.fn.match(relpath, pat) ~= -1 or vim.fn.match(basename, pat) ~= -1 then
return true
end
@ -129,7 +166,7 @@ local function custom(path)
local idx = path:match ".+()%.[^.]+$"
if idx then
if M.ignore_list["*" .. string.sub(path, idx)] == true then
if self.ignore_list["*" .. string.sub(path, idx)] == true then
return true
end
end
@ -143,14 +180,14 @@ end
--- git_status: reference
--- bufinfo: empty unless no_buffer set: vim.fn.getbufinfo { buflisted = 1 }
--- bookmarks: absolute paths to boolean
function M.prepare(git_status)
function Filters:prepare(git_status)
local status = {
git_status = git_status or {},
bufinfo = {},
bookmarks = {},
}
if M.config.filter_no_buffer then
if self.config.filter_no_buffer then
status.bufinfo = vim.fn.getbufinfo { buflisted = 1 }
end
@ -169,47 +206,21 @@ end
---@param fs_stat uv.fs_stat.result|nil fs_stat of file
---@param status table from prepare
---@return boolean
function M.should_filter(path, fs_stat, status)
if not M.config.enable then
function Filters:should_filter(path, fs_stat, status)
if not self.config.enable then
return false
end
-- exclusions override all filters
if is_excluded(path) then
if is_excluded(self, path) then
return false
end
return git(path, status.git_status)
or buf(path, status.bufinfo)
or dotfile(path)
or custom(path)
or bookmark(path, fs_stat and fs_stat.type, status.bookmarks)
return git(self, path, status.git_status)
or buf(self, path, status.bufinfo)
or dotfile(self, path)
or custom(self, path)
or bookmark(self, path, fs_stat and fs_stat.type, status.bookmarks)
end
function M.setup(opts)
M.config = {
enable = opts.filters.enable,
filter_custom = true,
filter_dotfiles = opts.filters.dotfiles,
filter_git_ignored = opts.filters.git_ignored,
filter_git_clean = opts.filters.git_clean,
filter_no_buffer = opts.filters.no_buffer,
filter_no_bookmark = opts.filters.no_bookmark,
}
M.ignore_list = {}
M.exclude_list = opts.filters.exclude
local custom_filter = opts.filters.custom
if type(custom_filter) == "function" then
M.custom_function = custom_filter
else
if custom_filter and #custom_filter > 0 then
for _, filter_name in pairs(custom_filter) do
M.ignore_list[filter_name] = true
end
end
end
end
return M
return Filters

View File

@ -2,6 +2,7 @@ local git = require "nvim-tree.git"
local notify = require "nvim-tree.notify"
local watch = require "nvim-tree.explorer.watch"
local explorer_node = require "nvim-tree.explorer.node"
local Filters = require "nvim-tree.explorer.filters"
local Marks = require "nvim-tree.marks"
local M = {}
@ -41,6 +42,7 @@ function Explorer.new(path)
marks = Marks:new(),
}, Explorer)
explorer.watcher = watch.create_watcher(explorer)
explorer.filters = Filters:new(M.config, explorer)
explorer:_load(explorer)
return explorer
end
@ -50,7 +52,7 @@ end
function Explorer:_load(node)
local cwd = node.link_to or node.absolute_path
local git_status = git.load_project_status(cwd)
M.explore(node, git_status)
M.explore(node, git_status, self)
end
---@param node Node
@ -71,9 +73,9 @@ function Explorer:destroy()
end
function M.setup(opts)
M.config = opts
require("nvim-tree.explorer.node").setup(opts)
require("nvim-tree.explorer.explore").setup(opts)
require("nvim-tree.explorer.filters").setup(opts)
require("nvim-tree.explorer.sorters").setup(opts)
require("nvim-tree.explorer.reload").setup(opts)
require("nvim-tree.explorer.watch").setup(opts)

View File

@ -1,7 +1,6 @@
local utils = require "nvim-tree.utils"
local builders = require "nvim-tree.explorer.node-builders"
local explorer_node = require "nvim-tree.explorer.node"
local filters = require "nvim-tree.explorer.filters"
local sorters = require "nvim-tree.explorer.sorters"
local live_filter = require "nvim-tree.live-filter"
local git = require "nvim-tree.git"
@ -70,6 +69,10 @@ end
---@param node Node
---@param git_status table
function M.reload(node, git_status)
local explorer = require("nvim-tree.core").get_explorer()
if not explorer then
return
end
local cwd = node.link_to or node.absolute_path
local handle = vim.loop.fs_scandir(cwd)
if not handle then
@ -78,7 +81,7 @@ function M.reload(node, git_status)
local profile = log.profile_start("reload %s", node.absolute_path)
local filter_status = filters.prepare(git_status)
local filter_status = explorer.filters:prepare(git_status)
if node.group_next then
node.nodes = { node.group_next }
@ -100,7 +103,7 @@ function M.reload(node, git_status)
---@type uv.fs_stat.result|nil
local stat = vim.loop.fs_stat(abs)
if not filters.should_filter(abs, stat, filter_status) then
if not explorer.filters:should_filter(abs, stat, filter_status) then
remain_childs[abs] = true
-- Recreate node if type changes.

View File

@ -1,7 +1,6 @@
local view = require "nvim-tree.view"
local utils = require "nvim-tree.utils"
local Iterator = require "nvim-tree.iterators.node-iterator"
local filters = require "nvim-tree.explorer.filters"
local M = {
filter = nil,
@ -57,7 +56,8 @@ end
---@param node Node
---@return boolean
local function matches(node)
if not filters.config.enable then
local explorer = require("nvim-tree.core").get_explorer()
if not explorer or not explorer.filters.config.enable then
return true
end