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:
Alexander Courtis
2024-11-09 14:14:04 +11:00
committed by GitHub
parent 610a1c189b
commit 3fc8de198c
42 changed files with 995 additions and 1115 deletions

View File

@@ -1,52 +1,59 @@
local utils = require("nvim-tree.utils")
local FILTER_REASON = require("nvim-tree.enum").FILTER_REASON
---@class Filters to handle all opts.filters and related API
---@field config table hydrated user opts.filters
local Class = require("nvim-tree.classic")
---@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 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
local Filters = {}
local Filters = Class:extend()
---@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,
},
---@class Filters
---@overload fun(args: FiltersArgs): Filters
---@class (exact) FiltersArgs
---@field explorer Explorer
---@protected
---@param args FiltersArgs
function Filters:new(args)
self.explorer = args.explorer
self.ignore_list = {}
self.exclude_list = self.explorer.opts.filters.exclude
self.custom_function = nil
self.enabled = self.explorer.opts.filters.enable
self.state = {
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
o.custom_function = custom_filter
self.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
self.ignore_list[filter_name] = true
end
end
end
setmetatable(o, self)
self.__index = self
return o
end
---@private
---@param path string
---@return boolean
local function is_excluded(self, path)
function Filters:is_excluded(path)
for _, node in ipairs(self.exclude_list) do
if path:match(node) then
return true
@@ -56,10 +63,11 @@ local function is_excluded(self, path)
end
---Check if the given path is git clean/ignored
---@private
---@param path string Absolute path
---@param project GitProject from prepare
---@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
return false
end
@@ -70,12 +78,12 @@ local function git(self, path, project)
xy = xy or project.dirs.indirect[path] and project.dirs.indirect[path][1]
-- 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
end
-- filter clean
if self.config.filter_git_clean and not xy then
if self.state.git_clean and not xy then
return true
end
@@ -83,11 +91,12 @@ local function git(self, path, project)
end
---Check if the given path has no listed buffer
---@private
---@param path string Absolute path
---@param bufinfo table vim.fn.getbufinfo { buflisted = 1 }
---@return boolean
local function buf(self, path, bufinfo)
if not self.config.filter_no_buffer or type(bufinfo) ~= "table" then
function Filters:buf(path, bufinfo)
if not self.state.no_buffer or type(bufinfo) ~= "table" then
return false
end
@@ -101,19 +110,21 @@ local function buf(self, path, bufinfo)
return true
end
---@private
---@param path string
---@return boolean
local function dotfile(self, path)
return self.config.filter_dotfiles and utils.path_basename(path):sub(1, 1) == "."
function Filters:dotfile(path)
return self.state.dotfiles and utils.path_basename(path):sub(1, 1) == "."
end
---Bookmark is present
---@private
---@param path string
---@param path_type string|nil filetype of path
---@param bookmarks table<string, string|nil> path, filetype table of bookmarked files
---@return boolean
local function bookmark(self, path, path_type, bookmarks)
if not self.config.filter_no_bookmark then
function Filters:bookmark(path, path_type, bookmarks)
if not self.state.no_bookmark then
return false
end
-- if bookmark is empty, we should see a empty filetree
@@ -145,10 +156,11 @@ local function bookmark(self, path, path_type, bookmarks)
return true
end
---@private
---@param path string
---@return boolean
local function custom(self, path)
if not self.config.filter_custom then
function Filters:custom(path)
if not self.state.custom then
return false
end
@@ -190,7 +202,7 @@ function Filters:prepare(project)
bookmarks = {},
}
if self.config.filter_no_buffer then
if self.state.no_buffer then
status.bufinfo = vim.fn.getbufinfo({ buflisted = 1 })
end
@@ -210,20 +222,20 @@ end
---@param status table from prepare
---@return boolean
function Filters:should_filter(path, fs_stat, status)
if not self.config.enable then
if not self.enabled then
return false
end
-- exclusions override all filters
if is_excluded(self, path) then
if self:is_excluded(path) then
return false
end
return git(self, path, status.project)
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)
return self:git(path, status.project)
or self:buf(path, status.bufinfo)
or self:dotfile(path)
or self:custom(path)
or self:bookmark(path, fs_stat and fs_stat.type, status.bookmarks)
end
--- 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
---@return FILTER_REASON
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
end
if is_excluded(self, path) then
if self:is_excluded(path) then
return FILTER_REASON.none
end
if git(self, path, status.project) then
if self:git(path, status.project) then
return FILTER_REASON.git
elseif buf(self, path, status.bufinfo) then
elseif self:buf(path, status.bufinfo) then
return FILTER_REASON.buf
elseif dotfile(self, path) then
elseif self:dotfile(path) then
return FILTER_REASON.dotfile
elseif custom(self, path) then
elseif self:custom(path) then
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
else
return FILTER_REASON.none
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

View File

@@ -3,7 +3,6 @@ local buffers = require("nvim-tree.buffers")
local core = require("nvim-tree.core")
local git = require("nvim-tree.git")
local log = require("nvim-tree.log")
local notify = require("nvim-tree.notify")
local utils = require("nvim-tree.utils")
local view = require("nvim-tree.view")
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 Marks = require("nvim-tree.marks")
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 Renderer = require("nvim-tree.renderer")
@@ -36,51 +35,39 @@ local config
---@field sorters Sorter
---@field marks Marks
---@field clipboard Clipboard
local Explorer = RootNode:new()
local Explorer = RootNode:extend()
---Static factory method
---@param path string?
---@return Explorer?
function Explorer:create(path)
local err
---@class Explorer
---@overload fun(args: ExplorerArgs): Explorer
if path then
path, err = vim.loop.fs_realpath(path)
else
path, err = vim.loop.cwd()
end
if not path then
notify.error(err)
return nil
end
---@class (exact) ExplorerArgs
---@field path string
---@type Explorer
local explorer_placeholder = nil
---@protected
---@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()
o.augroup_id = vim.api.nvim_create_augroup("NvimTree_Explorer_" .. o.uid_explorer, {})
self:create_autocmds()
o.open = true
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
self:_load(self)
end
function Explorer:destroy()
@@ -114,7 +101,7 @@ function Explorer:create_autocmds()
vim.api.nvim_create_autocmd("BufReadPost", {
group = self.augroup_id,
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()
self:reload_explorer()
end)
@@ -126,7 +113,7 @@ function Explorer:create_autocmds()
vim.api.nvim_create_autocmd("BufUnload", {
group = self.augroup_id,
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()
self:reload_explorer()
end)
@@ -213,10 +200,10 @@ function Explorer:reload(node, project)
-- To reset we must 'zero' everything that we use
node.hidden_stats = vim.tbl_deep_extend("force", node.hidden_stats or {}, {
git = 0,
buf = 0,
dotfile = 0,
custom = 0,
git = 0,
buf = 0,
dotfile = 0,
custom = 0,
bookmark = 0,
})
@@ -246,7 +233,13 @@ function Explorer:reload(node, project)
end
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
table.insert(node.nodes, 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)
node.hidden_stats = vim.tbl_deep_extend("force", node.hidden_stats or {}, {
git = 0,
buf = 0,
dotfile = 0,
custom = 0,
git = 0,
buf = 0,
dotfile = 0,
custom = 0,
bookmark = 0,
})
@@ -384,7 +377,13 @@ function Explorer:populate_children(handle, cwd, node, project, parent)
local stat = vim.loop.fs_lstat(abs)
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
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
table.insert(node.nodes, child)
nodes_by_path[child.absolute_path] = true

View File

@@ -1,29 +1,30 @@
local view = require("nvim-tree.view")
local utils = require("nvim-tree.utils")
local Class = require("nvim-tree.classic")
local Iterator = require("nvim-tree.iterators.node-iterator")
local DirectoryNode = require("nvim-tree.node.directory")
---@class LiveFilter
---@class (exact) LiveFilter: Class
---@field explorer Explorer
---@field prefix string
---@field always_show_folders boolean
---@field filter string
local LiveFilter = {}
local LiveFilter = Class:extend()
---@param opts table
---@param explorer Explorer
---@return LiveFilter
function LiveFilter:new(opts, explorer)
local o = {
explorer = explorer,
prefix = opts.live_filter.prefix,
always_show_folders = opts.live_filter.always_show_folders,
filter = nil,
}
setmetatable(o, self)
self.__index = self
return o
---@class LiveFilter
---@overload fun(args: LiveFilterArgs): LiveFilter
---@class (exact) LiveFilterArgs
---@field explorer Explorer
---@protected
---@param args LiveFilterArgs
function LiveFilter:new(args)
self.explorer = args.explorer
self.prefix = self.explorer.opts.live_filter.prefix
self.always_show_folders = self.explorer.opts.live_filter.always_show_folders
self.filter = nil
end
---@param node_ Node?
@@ -81,7 +82,7 @@ end
---@param node Node
---@return boolean
local function matches(self, node)
if not self.explorer.filters.config.enable then
if not self.explorer.filters.enabled then
return true
end
@@ -168,21 +169,21 @@ local function create_overlay(self)
if view.View.float.enable then
-- don't close nvim-tree float when focus is changed to filter window
vim.api.nvim_clear_autocmds({
event = "WinLeave",
event = "WinLeave",
pattern = "NvimTree_*",
group = vim.api.nvim_create_augroup("NvimTree", { clear = false }),
group = vim.api.nvim_create_augroup("NvimTree", { clear = false }),
})
end
configure_buffer_overlay(self)
overlay_winnr = vim.api.nvim_open_win(overlay_bufnr, true, {
col = 1,
row = 0,
col = 1,
row = 0,
relative = "cursor",
width = calculate_overlay_win_width(self),
height = 1,
border = "none",
style = "minimal",
width = calculate_overlay_win_width(self),
height = 1,
border = "none",
style = "minimal",
})
if vim.fn.has("nvim-0.10") == 1 then

View File

@@ -1,43 +1,25 @@
local Class = require("nvim-tree.class")
local Class = require("nvim-tree.classic")
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
---@field sorter string|fun(nodes: Node[])
---@field folders_first boolean
---@field files_first boolean
---@alias SorterUser fun(nodes: Node[]): SorterType?
---@class (exact) Sorter: Class
---@field cfg SorterCfg
---@field user fun(nodes: Node[])?
---@field pre string?
local Sorter = Class:new()
---@field private explorer Explorer
local Sorter = Class:extend()
---@param opts table user options
---@return Sorter
function Sorter:create(opts)
---@type Sorter
local o = {
cfg = vim.deepcopy(opts.sort),
}
o = self:new(o)
---@class Sorter
---@overload fun(args: SorterArgs): Sorter
if type(o.cfg.sorter) == "function" then
o.user = o.cfg.sorter --[[@as fun(nodes: Node[])]]
elseif type(o.cfg.sorter) == "string" then
o.pre = o.cfg.sorter --[[@as string]]
end
return o
end
---@class (exact) SorterArgs
---@field explorer Explorer
--- Predefined comparator, defaulting to name
---@param sorter string as per options
---@return fun(a: Node, b: Node): boolean
function Sorter:get_comparator(sorter)
return function(a, b)
return (C[sorter] or C.name)(a, b, self.cfg)
end
---@protected
---@param args SorterArgs
function Sorter:new(args)
self.explorer = args.explorer
end
---Create a shallow copy of a portion of a list.
@@ -54,31 +36,32 @@ local function tbl_slice(t, first, last)
return slice
end
---Evaluate `sort.folders_first` and `sort.files_first`
---@param a Node
---@param b Node
---@param cfg SorterCfg
---@return boolean|nil
local function folders_or_files_first(a, b, cfg)
if not (cfg.folders_first or cfg.files_first) then
return
---Evaluate folders_first and sort.files_first returning nil when no order is necessary
---@private
---@type SorterComparator
function Sorter:folders_or_files_first(a, b)
if not (self.explorer.opts.sort.folders_first or self.explorer.opts.sort.files_first) then
return nil
end
if not a:is(DirectoryNode) and b:is(DirectoryNode) then
-- file <> folder
return cfg.files_first
return self.explorer.opts.sort.files_first
elseif a:is(DirectoryNode) and not b:is(DirectoryNode) then
-- folder <> file
return not cfg.files_first
return not self.explorer.opts.sort.files_first
end
return nil
end
---@param t table
---@private
---@param t Node[]
---@param first number
---@param mid number
---@param last number
---@param comparator fun(a: Node, b: Node): boolean
local function merge(t, first, mid, last, comparator)
---@param comparator SorterComparator
function Sorter:merge(t, first, mid, last, comparator)
local n1 = mid - first + 1
local n2 = last - mid
local ls = tbl_slice(t, first, mid)
@@ -88,7 +71,7 @@ local function merge(t, first, mid, last, comparator)
local k = first
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]
i = i + 1
else
@@ -111,45 +94,49 @@ local function merge(t, first, mid, last, comparator)
end
end
---@param t table
---@private
---@param t Node[]
---@param first number
---@param last number
---@param comparator fun(a: Node, b: Node): boolean
local function split_merge(t, first, last, comparator)
---@param comparator SorterComparator
function Sorter:split_merge(t, first, last, comparator)
if (last - first) < 1 then
return
end
local mid = math.floor((first + last) / 2)
split_merge(t, first, mid, comparator)
split_merge(t, mid + 1, last, comparator)
merge(t, first, mid, last, comparator)
self:split_merge(t, first, mid, comparator)
self:split_merge(t, mid + 1, last, comparator)
self:merge(t, first, mid, last, comparator)
end
---Perform a merge sort using sorter option.
---@param t Node[]
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 origin_index = {}
for _, n in ipairs(t) do
table.insert(t_user, {
absolute_path = n.absolute_path,
executable = n.executable,
extension = n.extension,
filetype = vim.filetype.match({ filename = n.name }),
link_to = n.link_to,
name = n.name,
type = n.type,
executable = n.executable,
extension = n.extension,
filetype = vim.filetype.match({ filename = n.name }),
link_to = n.link_to,
name = n.name,
type = n.type,
})
table.insert(origin_index, n)
end
local predefined = self.user(t_user)
if predefined then
split_merge(t, 1, #t, self:get_comparator(predefined))
-- user may return a SorterType
local ret = self.explorer.opts.sort.sorter(t_user)
if self[ret] then
self:split_merge(t, 1, #t, self[ret])
return
end
@@ -162,7 +149,7 @@ function Sorter:sort(t)
end
-- 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 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)
end
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))
self:split_merge(t, 1, #t, mini_comparator) -- sort by user order
end
end
---@private
---@param a Node
---@param b Node
---@param ignorecase boolean|nil
---@param cfg SorterCfg
---@param ignore_case 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
return true
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
return early_return
end
if ignorecase then
if ignore_case then
return a.name:lower() <= b.name:lower()
else
return a.name <= b.name
end
end
function C.case_sensitive(a, b, cfg)
return node_comparator_name_ignorecase_or_not(a, b, false, cfg)
---@private
---@type SorterComparator
function Sorter:case_sensitive(a, b)
return self:name_case(a, b, false)
end
function C.name(a, b, cfg)
return node_comparator_name_ignorecase_or_not(a, b, true, cfg)
---@private
---@type SorterComparator
function Sorter:name(a, b)
return self:name_case(a, b, true)
end
function C.modification_time(a, b, cfg)
---@private
---@type SorterComparator
function Sorter:modification_time(a, b)
if not (a and b) then
return true
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
return early_return
end
@@ -232,17 +223,19 @@ function C.modification_time(a, b, cfg)
return last_modified_b <= last_modified_a
end
function C.suffix(a, b, cfg)
---@private
---@type SorterComparator
function Sorter:suffix(a, b)
if not (a and b) then
return true
end
-- 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
return early_return
elseif a.nodes and b.nodes then
return C.name(a, b, cfg)
return self:name(a, b)
end
-- 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
return false
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
-- unsuffixed go third
@@ -263,7 +256,7 @@ function C.suffix(a, b, cfg)
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, cfg)
return self:name(a, b)
end
-- finally, compare by suffixes
@@ -275,18 +268,20 @@ function C.suffix(a, b, cfg)
elseif not a_suffix and b_suffix then
return false
elseif a_suffix:lower() == b_suffix:lower() then
return C.name(a, b, cfg)
return self:name(a, b)
end
return a_suffix:lower() < b_suffix:lower()
end
function C.extension(a, b, cfg)
---@private
---@type SorterComparator
function Sorter:extension(a, b)
if not (a and b) then
return true
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
return early_return
end
@@ -300,18 +295,20 @@ function C.extension(a, b, cfg)
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, cfg)
return self:name(a, b)
end
return a_ext < b_ext
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 b_ft = vim.filetype.match({ filename = b.name })
-- 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
return early_return
end
@@ -325,7 +322,7 @@ function C.filetype(a, b, cfg)
-- same filetype or both nil, sort by name
if a_ft == b_ft then
return C.name(a, b, cfg)
return self:name(a, b)
end
return a_ft < b_ft

View File

@@ -83,8 +83,12 @@ function M.create_watcher(node)
end
M.uid = M.uid + 1
return Watcher:create(path, nil, callback, {
context = "explorer:watch:" .. path .. ":" .. M.uid,
return Watcher:create({
path = path,
callback = callback,
data = {
context = "explorer:watch:" .. path .. ":" .. M.uid
}
})
end