refactor: remove overcomplicated meta_fields abstraction

This abstraction is overly generic for what it does. It's only ever used
to help us conditionally perform a fs_stat for the local files adapter.
We can replace that with a much dumber, much simpler bit of logic.
This commit is contained in:
Steven Arcangeli 2025-01-03 11:48:58 -08:00
parent ba858b6625
commit 1f7da07a3e
2 changed files with 77 additions and 99 deletions

View file

@ -12,6 +12,7 @@ local uv = vim.uv or vim.loop
local M = {}
local FIELD_NAME = constants.FIELD_NAME
local FIELD_TYPE = constants.FIELD_TYPE
local FIELD_META = constants.FIELD_META
local function read_link_data(path, cb)
@ -50,17 +51,8 @@ end
local file_columns = {}
local fs_stat_meta_fields = {
stat = function(parent_url, entry, cb)
local _, path = util.parse_url(parent_url)
assert(path)
local dir = fs.posix_to_os_path(path .. entry[FIELD_NAME])
uv.fs_stat(dir, cb)
end,
}
file_columns.size = {
meta_fields = fs_stat_meta_fields,
require_stat = true,
render = function(entry, conf)
local meta = entry[FIELD_META]
@ -97,7 +89,7 @@ file_columns.size = {
-- TODO support file permissions on windows
if not fs.is_windows then
file_columns.permissions = {
meta_fields = fs_stat_meta_fields,
require_stat = true,
render = function(entry, conf)
local meta = entry[FIELD_META]
@ -160,7 +152,7 @@ end)
for _, time_key in ipairs({ "ctime", "mtime", "atime", "birthtime" }) do
file_columns[time_key] = {
meta_fields = fs_stat_meta_fields,
require_stat = true,
render = function(entry, conf)
local meta = entry[FIELD_META]
@ -206,6 +198,20 @@ for _, time_key in ipairs({ "ctime", "mtime", "atime", "birthtime" }) do
}
end
---@param column_defs table[]
---@return boolean
local function columns_require_stat(column_defs)
for _, def in ipairs(column_defs) do
local name = util.split_config(def)
local column = M.get_column(name)
---@diagnostic disable-next-line: undefined-field We only put this on the files adapter columns
if column and column.require_stat then
return true
end
end
return false
end
---@param name string
---@return nil|oil.ColumnDefinition
M.get_column = function(name)
@ -283,12 +289,64 @@ M.get_entry_path = function(url, entry, cb)
end
end
---@param parent_dir string
---@param entry oil.InternalEntry
---@param require_stat boolean
---@param cb fun(err?: string)
local function fetch_entry_metadata(parent_dir, entry, require_stat, cb)
local entry_path = fs.posix_to_os_path(parent_dir .. entry[FIELD_NAME])
local meta = entry[FIELD_META]
if not meta then
meta = {}
entry[FIELD_META] = meta
end
-- Make sure we always get fs_stat info for links
if entry[FIELD_TYPE] == "link" then
read_link_data(entry_path, function(link_err, link, link_stat)
if link_err then
return cb(link_err)
end
meta.link = link
if link_stat then
-- Use the fstat of the linked file as the stat for the link
meta.link_stat = link_stat
meta.stat = link_stat
elseif require_stat then
-- The link is broken, so let's use the stat of the link itself
uv.fs_lstat(entry_path, function(stat_err, stat)
if stat_err then
return cb(stat_err)
end
meta.stat = stat
cb()
end)
return
end
cb()
end)
elseif require_stat then
uv.fs_stat(entry_path, function(stat_err, stat)
if stat_err then
return cb(stat_err)
end
assert(stat)
meta.stat = stat
cb()
end)
else
cb()
end
end
---@param url string
---@param column_defs string[]
---@param cb fun(err?: string, entries?: oil.InternalEntry[], fetch_more?: fun())
local function list_windows_drives(url, column_defs, cb)
---@cast M oil.FilesAdapter
local fetch_meta = columns.get_metadata_fetcher(M, column_defs)
local _, path = util.parse_url(url)
assert(path)
local require_stat = columns_require_stat(column_defs)
local stdout = ""
local jid = vim.fn.jobstart({ "wmic", "logicaldisk", "get", "name" }, {
stdout_buffered = true,
@ -318,14 +376,8 @@ local function list_windows_drives(url, column_defs, cb)
else
disk = disk:gsub(":%s*$", "")
local cache_entry = cache.create_entry(url, disk, "directory")
fetch_meta(url, cache_entry, function(err)
if err then
complete_disk_cb(err)
else
table.insert(internal_entries, cache_entry)
complete_disk_cb()
end
end)
table.insert(internal_entries, cache_entry)
fetch_entry_metadata(path, cache_entry, require_stat, complete_disk_cb)
end
end
end,
@ -345,8 +397,7 @@ M.list = function(url, column_defs, cb)
return list_windows_drives(url, column_defs, cb)
end
local dir = fs.posix_to_os_path(path)
---@cast M oil.Adapter
local fetch_meta = columns.get_metadata_fetcher(M, column_defs)
local require_stat = columns_require_stat(column_defs)
---@diagnostic disable-next-line: param-type-mismatch, discard-returns
uv.fs_opendir(dir, function(open_err, fd)
@ -378,28 +429,8 @@ M.list = function(url, column_defs, cb)
end)
for _, entry in ipairs(entries) do
local cache_entry = cache.create_entry(url, entry.name, entry.type)
fetch_meta(url, cache_entry, function(meta_err)
table.insert(internal_entries, cache_entry)
local meta = cache_entry[FIELD_META]
-- Make sure we always get fs_stat info for links
if entry.type == "link" then
read_link_data(fs.join(dir, entry.name), function(link_err, link, link_stat)
if link_err then
poll(link_err)
else
if not meta then
meta = {}
cache_entry[FIELD_META] = meta
end
meta.link = link
meta.link_stat = link_stat
poll()
end
end)
else
poll()
end
end)
table.insert(internal_entries, cache_entry)
fetch_entry_metadata(path, cache_entry, require_stat, poll)
end
else
uv.fs_closedir(fd, function(close_err)

View file

@ -14,7 +14,6 @@ local all_columns = {}
---@class (exact) oil.ColumnDefinition
---@field render fun(entry: oil.InternalEntry, conf: nil|table): nil|oil.TextChunk
---@field parse fun(line: string, conf: nil|table): nil|string, nil|string
---@field meta_fields? table<string, fun(parent_url: string, entry: oil.InternalEntry, cb: fun(err: nil|string))>
---@field compare? fun(entry: oil.InternalEntry, parsed_value: any): boolean
---@field render_action? fun(action: oil.ChangeAction): string
---@field perform_action? fun(action: oil.ChangeAction, callback: fun(err: nil|string))
@ -54,46 +53,6 @@ M.get_supported_columns = function(adapter_or_scheme)
return ret
end
---@param adapter oil.Adapter
---@param column_defs table[]
---@return fun(parent_url: string, entry: oil.InternalEntry, cb: fun(err: nil|string))
M.get_metadata_fetcher = function(adapter, column_defs)
local keyfetches = {}
local num_keys = 0
for _, def in ipairs(column_defs) do
local name = util.split_config(def)
local column = M.get_column(adapter, name)
if column and column.meta_fields then
for k, v in pairs(column.meta_fields) do
if not keyfetches[k] then
keyfetches[k] = v
num_keys = num_keys + 1
end
end
end
end
if num_keys == 0 then
return function(_, _, cb)
cb()
end
end
return function(parent_url, entry, cb)
cb = util.cb_collect(num_keys, cb)
local meta = {}
entry[FIELD_META] = meta
for k, v in pairs(keyfetches) do
v(parent_url, entry, function(err, value)
if err then
cb(err)
else
meta[k] = value
cb()
end
end)
end
end
end
local EMPTY = { "-", "Comment" }
M.EMPTY = EMPTY
@ -110,18 +69,6 @@ M.render_col = function(adapter, col_def, entry)
return EMPTY
end
-- Make sure all the required metadata exists before attempting to render
if column.meta_fields then
local meta = entry[FIELD_META]
if not meta then
return EMPTY
end
for k in pairs(column.meta_fields) do
if not meta[k] then
return EMPTY
end
end
end
local chunk = column.render(entry, conf)
if type(chunk) == "table" then
if chunk[1]:match("^%s*$") then