From 1aa9852cad8a90cfe1dac549b0e550041e1268cf Mon Sep 17 00:00:00 2001 From: Mateusz Russak Date: Sat, 27 Jul 2024 04:58:02 +0200 Subject: [PATCH 1/3] docs: removed entry about macos rename (#2848) --- doc/nvim-tree-lua.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/doc/nvim-tree-lua.txt b/doc/nvim-tree-lua.txt index 8f03efab..19f36170 100644 --- a/doc/nvim-tree-lua.txt +++ b/doc/nvim-tree-lua.txt @@ -2691,10 +2691,6 @@ configurations for different types of prompts. ============================================================================== 11. OS SPECIFIC RESTRICTIONS *nvim-tree-os-specific* -macOS -- Rename to different case is not possible when using a case insensitive file - system. - Windows WSL and PowerShell - Trash is synchronized - Executable file detection is disabled as this is non-performant and can From 908478a0e05907a3b676ce77ffc97cc80de41cc3 Mon Sep 17 00:00:00 2001 From: Mateusz Russak Date: Sat, 27 Jul 2024 05:29:27 +0200 Subject: [PATCH 2/3] 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 --------- Co-authored-by: Alexander Courtis --- lua/nvim-tree.lua | 17 ++- lua/nvim-tree/actions/finders/search-node.lua | 10 +- .../actions/tree/modifiers/toggles.lua | 48 ++++--- lua/nvim-tree/explorer/explore.lua | 15 ++- lua/nvim-tree/explorer/filters.lua | 127 ++++++++++-------- lua/nvim-tree/explorer/init.lua | 6 +- lua/nvim-tree/explorer/reload.lua | 9 +- lua/nvim-tree/live-filter.lua | 4 +- 8 files changed, 142 insertions(+), 94 deletions(-) diff --git a/lua/nvim-tree.lua b/lua/nvim-tree.lua index f3260c47..42c4a4f0 100644 --- a/lua/nvim-tree.lua +++ b/lua/nvim-tree.lua @@ -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) diff --git a/lua/nvim-tree/actions/finders/search-node.lua b/lua/nvim-tree/actions/finders/search-node.lua index 5e3b612d..49678002 100644 --- a/lua/nvim-tree/actions/finders/search-node.lua +++ b/lua/nvim-tree/actions/finders/search-node.lua @@ -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 diff --git a/lua/nvim-tree/actions/tree/modifiers/toggles.lua b/lua/nvim-tree/actions/tree/modifiers/toggles.lua index 1d0c4e47..fdcb21d7 100644 --- a/lua/nvim-tree/actions/tree/modifiers/toggles.lua +++ b/lua/nvim-tree/actions/tree/modifiers/toggles.lua @@ -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 diff --git a/lua/nvim-tree/explorer/explore.lua b/lua/nvim-tree/explorer/explore.lua index 665c47c1..8cb4bc4a 100644 --- a/lua/nvim-tree/explorer/explore.lua +++ b/lua/nvim-tree/explorer/explore.lua @@ -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) diff --git a/lua/nvim-tree/explorer/filters.lua b/lua/nvim-tree/explorer/filters.lua index 7b8be131..a1eb80da 100644 --- a/lua/nvim-tree/explorer/filters.lua +++ b/lua/nvim-tree/explorer/filters.lua @@ -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 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 diff --git a/lua/nvim-tree/explorer/init.lua b/lua/nvim-tree/explorer/init.lua index 25ad242e..5b682b72 100644 --- a/lua/nvim-tree/explorer/init.lua +++ b/lua/nvim-tree/explorer/init.lua @@ -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) diff --git a/lua/nvim-tree/explorer/reload.lua b/lua/nvim-tree/explorer/reload.lua index 9d2ddcab..3243861e 100644 --- a/lua/nvim-tree/explorer/reload.lua +++ b/lua/nvim-tree/explorer/reload.lua @@ -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. diff --git a/lua/nvim-tree/live-filter.lua b/lua/nvim-tree/live-filter.lua index 350043aa..cc305da6 100644 --- a/lua/nvim-tree/live-filter.lua +++ b/lua/nvim-tree/live-filter.lua @@ -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 From 82ba116bbd0cc5f0482783d67ea969595e2b381b Mon Sep 17 00:00:00 2001 From: Mateusz Russak Date: Sat, 27 Jul 2024 05:54:40 +0200 Subject: [PATCH 3/3] refactor(#2829): multi instance nvim-tree.explorer.sorters (#2835) * refactor: multi instance nvim-tree.explorer.sorters * fix: linter errors * fix: style * fix: according to code review * chore: removed comment * fix: missing cfg params in sorters * tidy following rebase * tidy following rebase --------- Co-authored-by: Alexander Courtis --- lua/nvim-tree/explorer/explore.lua | 3 +- lua/nvim-tree/explorer/init.lua | 3 +- lua/nvim-tree/explorer/reload.lua | 3 +- lua/nvim-tree/explorer/sorters.lua | 92 ++++++++++++++++-------------- 4 files changed, 53 insertions(+), 48 deletions(-) diff --git a/lua/nvim-tree/explorer/explore.lua b/lua/nvim-tree/explorer/explore.lua index 8cb4bc4a..3d48f95a 100644 --- a/lua/nvim-tree/explorer/explore.lua +++ b/lua/nvim-tree/explorer/explore.lua @@ -2,7 +2,6 @@ local utils = require "nvim-tree.utils" 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 live_filter = require "nvim-tree.live-filter" local log = require "nvim-tree.log" @@ -82,7 +81,7 @@ function M.explore(node, status, parent) return ns end - sorters.sort(node.nodes) + parent.sorters:sort(node.nodes) live_filter.apply_filter(node) log.profile_end(profile) diff --git a/lua/nvim-tree/explorer/init.lua b/lua/nvim-tree/explorer/init.lua index 5b682b72..5dc085e7 100644 --- a/lua/nvim-tree/explorer/init.lua +++ b/lua/nvim-tree/explorer/init.lua @@ -4,6 +4,7 @@ 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 Sorters = require "nvim-tree.explorer.sorters" local M = {} @@ -40,6 +41,7 @@ function Explorer.new(path) nodes = {}, open = true, marks = Marks:new(), + sorters = Sorters:new(M.config), }, Explorer) explorer.watcher = watch.create_watcher(explorer) explorer.filters = Filters:new(M.config, explorer) @@ -76,7 +78,6 @@ 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.sorters").setup(opts) require("nvim-tree.explorer.reload").setup(opts) require("nvim-tree.explorer.watch").setup(opts) end diff --git a/lua/nvim-tree/explorer/reload.lua b/lua/nvim-tree/explorer/reload.lua index 3243861e..101a22cf 100644 --- a/lua/nvim-tree/explorer/reload.lua +++ b/lua/nvim-tree/explorer/reload.lua @@ -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 sorters = require "nvim-tree.explorer.sorters" local live_filter = require "nvim-tree.live-filter" local git = require "nvim-tree.git" local log = require "nvim-tree.log" @@ -165,7 +164,7 @@ function M.reload(node, git_status) return ns end - sorters.sort(node.nodes) + explorer.sorters:sort(node.nodes) live_filter.apply_filter(node) log.profile_end(profile) return node.nodes diff --git a/lua/nvim-tree/explorer/sorters.lua b/lua/nvim-tree/explorer/sorters.lua index cb7400f5..3fa40c9d 100644 --- a/lua/nvim-tree/explorer/sorters.lua +++ b/lua/nvim-tree/explorer/sorters.lua @@ -1,12 +1,27 @@ -local M = {} - local C = {} +---@class Sorter +local Sorter = {} + +function Sorter:new(opts) + local o = {} + setmetatable(o, self) + self.__index = self + o.config = vim.deepcopy(opts.sort) + + if type(o.config.sorter) == "function" then + o.user = o.config.sorter + end + return o +end + --- Predefined comparator, defaulting to name ---@param sorter string as per options ---@return function -local function get_comparator(sorter) - return C[sorter] or C.name +function Sorter:get_comparator(sorter) + return function(a, b) + return (C[sorter] or C.name)(a, b, self.config) + end end ---Create a shallow copy of a portion of a list. @@ -27,17 +42,17 @@ end ---@param a Node ---@param b Node ---@return boolean|nil -local function folders_or_files_first(a, b) - if not (M.config.sort.folders_first or M.config.sort.files_first) then +local function folders_or_files_first(a, b, cfg) + if not (cfg.folders_first or cfg.files_first) then return end if not a.nodes and b.nodes then -- file <> folder - return M.config.sort.files_first + return cfg.files_first elseif a.nodes and not b.nodes then -- folder <> file - return not M.config.sort.files_first + return not cfg.files_first end end @@ -97,8 +112,8 @@ end ---Perform a merge sort using sorter option. ---@param t table nodes -function M.sort(t) - if C.user then +function Sorter:sort(t) + if self.user then local t_user = {} local origin_index = {} @@ -115,9 +130,9 @@ function M.sort(t) table.insert(origin_index, n) end - local predefined = C.user(t_user) + local predefined = self.user(t_user) if predefined then - split_merge(t, 1, #t, get_comparator(predefined)) + split_merge(t, 1, #t, self:get_comparator(predefined)) return end @@ -142,7 +157,7 @@ function M.sort(t) split_merge(t, 1, #t, mini_comparator) -- sort by user order else - split_merge(t, 1, #t, get_comparator(M.config.sort.sorter)) + split_merge(t, 1, #t, self:get_comparator(self.config.sorter)) end end @@ -150,12 +165,12 @@ end ---@param b Node ---@param ignorecase boolean|nil ---@return boolean -local function node_comparator_name_ignorecase_or_not(a, b, ignorecase) +local function node_comparator_name_ignorecase_or_not(a, b, ignorecase, cfg) if not (a and b) then return true end - local early_return = folders_or_files_first(a, b) + local early_return = folders_or_files_first(a, b, cfg) if early_return ~= nil then return early_return end @@ -167,20 +182,20 @@ local function node_comparator_name_ignorecase_or_not(a, b, ignorecase) end end -function C.case_sensitive(a, b) - return node_comparator_name_ignorecase_or_not(a, b, false) +function C.case_sensitive(a, b, cfg) + return node_comparator_name_ignorecase_or_not(a, b, false, cfg) end -function C.name(a, b) - return node_comparator_name_ignorecase_or_not(a, b, true) +function C.name(a, b, cfg) + return node_comparator_name_ignorecase_or_not(a, b, true, cfg) end -function C.modification_time(a, b) +function C.modification_time(a, b, cfg) if not (a and b) then return true end - local early_return = folders_or_files_first(a, b) + local early_return = folders_or_files_first(a, b, cfg) if early_return ~= nil then return early_return end @@ -199,17 +214,17 @@ function C.modification_time(a, b) return last_modified_b <= last_modified_a end -function C.suffix(a, b) +function C.suffix(a, b, cfg) if not (a and b) then return true end -- directories go first - local early_return = folders_or_files_first(a, b) + local early_return = folders_or_files_first(a, b, cfg) if early_return ~= nil then return early_return elseif a.nodes and b.nodes then - return C.name(a, b) + return C.name(a, b, cfg) end -- dotfiles go second @@ -218,7 +233,7 @@ function C.suffix(a, b) elseif a.name:sub(1, 1) ~= "." and b.name:sub(1, 1) == "." then return false elseif a.name:sub(1, 1) == "." and b.name:sub(1, 1) == "." then - return C.name(a, b) + return C.name(a, b, cfg) end -- unsuffixed go third @@ -230,7 +245,7 @@ function C.suffix(a, b) elseif a_suffix_ndx and not b_suffix_ndx then return false elseif not (a_suffix_ndx and b_suffix_ndx) then - return C.name(a, b) + return C.name(a, b, cfg) end -- finally, compare by suffixes @@ -242,18 +257,18 @@ function C.suffix(a, b) elseif not a_suffix and b_suffix then return false elseif a_suffix:lower() == b_suffix:lower() then - return C.name(a, b) + return C.name(a, b, cfg) end return a_suffix:lower() < b_suffix:lower() end -function C.extension(a, b) +function C.extension(a, b, cfg) if not (a and b) then return true end - local early_return = folders_or_files_first(a, b) + local early_return = folders_or_files_first(a, b, cfg) if early_return ~= nil then return early_return end @@ -267,18 +282,18 @@ function C.extension(a, b) local a_ext = (a.extension or ""):lower() local b_ext = (b.extension or ""):lower() if a_ext == b_ext then - return C.name(a, b) + return C.name(a, b, cfg) end return a_ext < b_ext end -function C.filetype(a, b) +function C.filetype(a, b, cfg) local a_ft = vim.filetype.match { filename = a.name } local b_ft = vim.filetype.match { filename = b.name } -- directories first - local early_return = folders_or_files_first(a, b) + local early_return = folders_or_files_first(a, b, cfg) if early_return ~= nil then return early_return end @@ -292,19 +307,10 @@ function C.filetype(a, b) -- same filetype or both nil, sort by name if a_ft == b_ft then - return C.name(a, b) + return C.name(a, b, cfg) end return a_ft < b_ft end -function M.setup(opts) - M.config = {} - M.config.sort = opts.sort - - if type(M.config.sort.sorter) == "function" then - C.user = M.config.sort.sorter - end -end - -return M +return Sorter