feat: api to sort directory contents (#169)
This commit is contained in:
parent
ca2560cae8
commit
879d280617
11 changed files with 256 additions and 54 deletions
|
|
@ -179,6 +179,7 @@ require("oil").setup({
|
||||||
["_"] = "actions.open_cwd",
|
["_"] = "actions.open_cwd",
|
||||||
["`"] = "actions.cd",
|
["`"] = "actions.cd",
|
||||||
["~"] = "actions.tcd",
|
["~"] = "actions.tcd",
|
||||||
|
["gs"] = "actions.change_sort",
|
||||||
["g."] = "actions.toggle_hidden",
|
["g."] = "actions.toggle_hidden",
|
||||||
},
|
},
|
||||||
-- Set to false to disable all of the above keymaps
|
-- Set to false to disable all of the above keymaps
|
||||||
|
|
@ -194,6 +195,12 @@ require("oil").setup({
|
||||||
is_always_hidden = function(name, bufnr)
|
is_always_hidden = function(name, bufnr)
|
||||||
return false
|
return false
|
||||||
end,
|
end,
|
||||||
|
sort = {
|
||||||
|
-- sort order can be "asc" or "desc"
|
||||||
|
-- see :help oil-columns to see which columns are sortable
|
||||||
|
{ "type", "asc" },
|
||||||
|
{ "name", "asc" },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
-- Configuration for the floating window in oil.open_float
|
-- Configuration for the floating window in oil.open_float
|
||||||
float = {
|
float = {
|
||||||
|
|
@ -277,6 +284,7 @@ Note that at the moment the ssh adapter does not support Windows machines, and i
|
||||||
- [get_cursor_entry()](doc/api.md#get_cursor_entry)
|
- [get_cursor_entry()](doc/api.md#get_cursor_entry)
|
||||||
- [discard_all_changes()](doc/api.md#discard_all_changes)
|
- [discard_all_changes()](doc/api.md#discard_all_changes)
|
||||||
- [set_columns(cols)](doc/api.md#set_columnscols)
|
- [set_columns(cols)](doc/api.md#set_columnscols)
|
||||||
|
- [set_sort(sort)](doc/api.md#set_sortsort)
|
||||||
- [set_is_hidden_file(is_hidden_file)](doc/api.md#set_is_hidden_fileis_hidden_file)
|
- [set_is_hidden_file(is_hidden_file)](doc/api.md#set_is_hidden_fileis_hidden_file)
|
||||||
- [toggle_hidden()](doc/api.md#toggle_hidden)
|
- [toggle_hidden()](doc/api.md#toggle_hidden)
|
||||||
- [get_current_dir()](doc/api.md#get_current_dir)
|
- [get_current_dir()](doc/api.md#get_current_dir)
|
||||||
|
|
|
||||||
10
doc/api.md
10
doc/api.md
|
|
@ -6,6 +6,7 @@
|
||||||
- [get_cursor_entry()](#get_cursor_entry)
|
- [get_cursor_entry()](#get_cursor_entry)
|
||||||
- [discard_all_changes()](#discard_all_changes)
|
- [discard_all_changes()](#discard_all_changes)
|
||||||
- [set_columns(cols)](#set_columnscols)
|
- [set_columns(cols)](#set_columnscols)
|
||||||
|
- [set_sort(sort)](#set_sortsort)
|
||||||
- [set_is_hidden_file(is_hidden_file)](#set_is_hidden_fileis_hidden_file)
|
- [set_is_hidden_file(is_hidden_file)](#set_is_hidden_fileis_hidden_file)
|
||||||
- [toggle_hidden()](#toggle_hidden)
|
- [toggle_hidden()](#toggle_hidden)
|
||||||
- [get_current_dir()](#get_current_dir)
|
- [get_current_dir()](#get_current_dir)
|
||||||
|
|
@ -52,6 +53,15 @@ Change the display columns for oil
|
||||||
| ----- | ------------------ | ---- |
|
| ----- | ------------------ | ---- |
|
||||||
| cols | `oil.ColumnSpec[]` | |
|
| cols | `oil.ColumnSpec[]` | |
|
||||||
|
|
||||||
|
## set_sort(sort)
|
||||||
|
|
||||||
|
`set_sort(sort)` \
|
||||||
|
Change the sort order for oil
|
||||||
|
|
||||||
|
| Param | Type | Desc |
|
||||||
|
| ----- | ---------- | ---- |
|
||||||
|
| sort | `string[]` | [] |
|
||||||
|
|
||||||
## set_is_hidden_file(is_hidden_file)
|
## set_is_hidden_file(is_hidden_file)
|
||||||
|
|
||||||
`set_is_hidden_file(is_hidden_file)` \
|
`set_is_hidden_file(is_hidden_file)` \
|
||||||
|
|
|
||||||
24
doc/oil.txt
24
doc/oil.txt
|
|
@ -12,7 +12,7 @@ CONTENTS *oil-content
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
OPTIONS *oil-options*
|
OPTIONS *oil-options*
|
||||||
|
|
||||||
>
|
>lua
|
||||||
require("oil").setup({
|
require("oil").setup({
|
||||||
-- Oil will take over directory buffers (e.g. `vim .` or `:e src/`)
|
-- Oil will take over directory buffers (e.g. `vim .` or `:e src/`)
|
||||||
-- Set to false if you still want to use netrw.
|
-- Set to false if you still want to use netrw.
|
||||||
|
|
@ -70,6 +70,7 @@ OPTIONS *oil-option
|
||||||
["_"] = "actions.open_cwd",
|
["_"] = "actions.open_cwd",
|
||||||
["`"] = "actions.cd",
|
["`"] = "actions.cd",
|
||||||
["~"] = "actions.tcd",
|
["~"] = "actions.tcd",
|
||||||
|
["gs"] = "actions.change_sort",
|
||||||
["g."] = "actions.toggle_hidden",
|
["g."] = "actions.toggle_hidden",
|
||||||
},
|
},
|
||||||
-- Set to false to disable all of the above keymaps
|
-- Set to false to disable all of the above keymaps
|
||||||
|
|
@ -85,6 +86,12 @@ OPTIONS *oil-option
|
||||||
is_always_hidden = function(name, bufnr)
|
is_always_hidden = function(name, bufnr)
|
||||||
return false
|
return false
|
||||||
end,
|
end,
|
||||||
|
sort = {
|
||||||
|
-- sort order can be "asc" or "desc"
|
||||||
|
-- see :help oil-columns to see which columns are sortable
|
||||||
|
{ "type", "asc" },
|
||||||
|
{ "name", "asc" },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
-- Configuration for the floating window in oil.open_float
|
-- Configuration for the floating window in oil.open_float
|
||||||
float = {
|
float = {
|
||||||
|
|
@ -166,6 +173,12 @@ set_columns({cols}) *oil.set_column
|
||||||
Parameters:
|
Parameters:
|
||||||
{cols} `oil.ColumnSpec[]`
|
{cols} `oil.ColumnSpec[]`
|
||||||
|
|
||||||
|
set_sort({sort}) *oil.set_sort*
|
||||||
|
Change the sort order for oil
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
{sort} `string[]` []
|
||||||
|
|
||||||
set_is_hidden_file({is_hidden_file}) *oil.set_is_hidden_file*
|
set_is_hidden_file({is_hidden_file}) *oil.set_is_hidden_file*
|
||||||
Change how oil determines if the file is hidden
|
Change how oil determines if the file is hidden
|
||||||
|
|
||||||
|
|
@ -244,6 +257,7 @@ or as a table to pass parameters (e.g. `{"size", highlight = "Special"}`)
|
||||||
|
|
||||||
type *column-type*
|
type *column-type*
|
||||||
Adapters: *
|
Adapters: *
|
||||||
|
Sortable: this column can be used in view_props.sort
|
||||||
The type of the entry (file, directory, link, etc)
|
The type of the entry (file, directory, link, etc)
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
@ -266,6 +280,7 @@ icon *column-ico
|
||||||
|
|
||||||
size *column-size*
|
size *column-size*
|
||||||
Adapters: files, ssh
|
Adapters: files, ssh
|
||||||
|
Sortable: this column can be used in view_props.sort
|
||||||
The size of the file
|
The size of the file
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
@ -283,6 +298,7 @@ permissions *column-permission
|
||||||
|
|
||||||
ctime *column-ctime*
|
ctime *column-ctime*
|
||||||
Adapters: files
|
Adapters: files
|
||||||
|
Sortable: this column can be used in view_props.sort
|
||||||
Change timestamp of the file
|
Change timestamp of the file
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
@ -292,6 +308,7 @@ ctime *column-ctim
|
||||||
|
|
||||||
mtime *column-mtime*
|
mtime *column-mtime*
|
||||||
Adapters: files
|
Adapters: files
|
||||||
|
Sortable: this column can be used in view_props.sort
|
||||||
Last modified time of the file
|
Last modified time of the file
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
@ -301,6 +318,7 @@ mtime *column-mtim
|
||||||
|
|
||||||
atime *column-atime*
|
atime *column-atime*
|
||||||
Adapters: files
|
Adapters: files
|
||||||
|
Sortable: this column can be used in view_props.sort
|
||||||
Last access time of the file
|
Last access time of the file
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
@ -310,6 +328,7 @@ atime *column-atim
|
||||||
|
|
||||||
birthtime *column-birthtime*
|
birthtime *column-birthtime*
|
||||||
Adapters: files
|
Adapters: files
|
||||||
|
Sortable: this column can be used in view_props.sort
|
||||||
The time the file was created
|
The time the file was created
|
||||||
|
|
||||||
Parameters:
|
Parameters:
|
||||||
|
|
@ -325,6 +344,9 @@ These are actions that can be used in the `keymaps` section of config options.
|
||||||
cd *actions.cd*
|
cd *actions.cd*
|
||||||
:cd to the current oil directory
|
:cd to the current oil directory
|
||||||
|
|
||||||
|
change_sort *actions.change_sort*
|
||||||
|
Change the sort order
|
||||||
|
|
||||||
close *actions.close*
|
close *actions.close*
|
||||||
Close oil and restore original buffer
|
Close oil and restore original buffer
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -237,6 +237,32 @@ M.open_cmdline_dir = {
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
M.change_sort = {
|
||||||
|
desc = "Change the sort order",
|
||||||
|
callback = function()
|
||||||
|
local sort_cols = { "name", "size", "atime", "mtime", "ctime", "birthtime" }
|
||||||
|
vim.ui.select(sort_cols, { prompt = "Sort by", kind = "oil_sort_col" }, function(col)
|
||||||
|
if not col then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
vim.ui.select(
|
||||||
|
{ "ascending", "descending" },
|
||||||
|
{ prompt = "Sort order", kind = "oil_sort_order" },
|
||||||
|
function(order)
|
||||||
|
if not order then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
order = order == "ascending" and "asc" or "desc"
|
||||||
|
oil.set_sort({
|
||||||
|
{ "type", "asc" },
|
||||||
|
{ col, order },
|
||||||
|
})
|
||||||
|
end
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
||||||
---List actions for documentation generation
|
---List actions for documentation generation
|
||||||
---@private
|
---@private
|
||||||
M._get_actions = function()
|
M._get_actions = function()
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,11 @@ local FIELD_NAME = constants.FIELD_NAME
|
||||||
local FIELD_META = constants.FIELD_META
|
local FIELD_META = constants.FIELD_META
|
||||||
|
|
||||||
local function read_link_data(path, cb)
|
local function read_link_data(path, cb)
|
||||||
uv.fs_readlink(
|
uv.fs_readlink(path, function(link_err, link)
|
||||||
path,
|
|
||||||
vim.schedule_wrap(function(link_err, link)
|
|
||||||
if link_err then
|
if link_err then
|
||||||
cb(link_err)
|
cb(link_err)
|
||||||
else
|
else
|
||||||
|
assert(link)
|
||||||
local stat_path = link
|
local stat_path = link
|
||||||
if not fs.is_absolute(link) then
|
if not fs.is_absolute(link) then
|
||||||
stat_path = fs.join(vim.fn.fnamemodify(path, ":h"), link)
|
stat_path = fs.join(vim.fn.fnamemodify(path, ":h"), link)
|
||||||
|
|
@ -28,7 +27,6 @@ local function read_link_data(path, cb)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param path string
|
---@param path string
|
||||||
|
|
@ -60,7 +58,7 @@ file_columns.size = {
|
||||||
local meta = entry[FIELD_META]
|
local meta = entry[FIELD_META]
|
||||||
local stat = meta.stat
|
local stat = meta.stat
|
||||||
if not stat then
|
if not stat then
|
||||||
return ""
|
return columns.EMPTY
|
||||||
end
|
end
|
||||||
if stat.size >= 1e9 then
|
if stat.size >= 1e9 then
|
||||||
return string.format("%.1fG", stat.size / 1e9)
|
return string.format("%.1fG", stat.size / 1e9)
|
||||||
|
|
@ -73,6 +71,16 @@ file_columns.size = {
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
get_sort_value = function(entry)
|
||||||
|
local meta = entry[FIELD_META]
|
||||||
|
local stat = meta.stat
|
||||||
|
if stat then
|
||||||
|
return stat.size
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
parse = function(line, conf)
|
parse = function(line, conf)
|
||||||
return line:match("^(%d+%S*)%s+(.*)$")
|
return line:match("^(%d+%S*)%s+(.*)$")
|
||||||
end,
|
end,
|
||||||
|
|
@ -87,7 +95,7 @@ if not fs.is_windows then
|
||||||
local meta = entry[FIELD_META]
|
local meta = entry[FIELD_META]
|
||||||
local stat = meta.stat
|
local stat = meta.stat
|
||||||
if not stat then
|
if not stat then
|
||||||
return ""
|
return columns.EMPTY
|
||||||
end
|
end
|
||||||
return permissions.mode_to_str(stat.mode)
|
return permissions.mode_to_str(stat.mode)
|
||||||
end,
|
end,
|
||||||
|
|
@ -145,6 +153,9 @@ for _, time_key in ipairs({ "ctime", "mtime", "atime", "birthtime" }) do
|
||||||
render = function(entry, conf)
|
render = function(entry, conf)
|
||||||
local meta = entry[FIELD_META]
|
local meta = entry[FIELD_META]
|
||||||
local stat = meta.stat
|
local stat = meta.stat
|
||||||
|
if not stat then
|
||||||
|
return columns.EMPTY
|
||||||
|
end
|
||||||
local fmt = conf and conf.format
|
local fmt = conf and conf.format
|
||||||
local ret
|
local ret
|
||||||
if fmt then
|
if fmt then
|
||||||
|
|
@ -170,6 +181,16 @@ for _, time_key in ipairs({ "ctime", "mtime", "atime", "birthtime" }) do
|
||||||
end
|
end
|
||||||
return line:match("^(" .. pattern .. ")%s+(.+)$")
|
return line:match("^(" .. pattern .. ")%s+(.+)$")
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
get_sort_value = function(entry)
|
||||||
|
local meta = entry[FIELD_META]
|
||||||
|
local stat = meta.stat
|
||||||
|
if stat then
|
||||||
|
return stat[time_key].sec
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,15 @@ ssh_columns.size = {
|
||||||
parse = function(line, conf)
|
parse = function(line, conf)
|
||||||
return line:match("^(%d+%S*)%s+(.*)$")
|
return line:match("^(%d+%S*)%s+(.*)$")
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
get_sort_value = function(entry)
|
||||||
|
local meta = entry[FIELD_META]
|
||||||
|
if meta.size then
|
||||||
|
return meta.size
|
||||||
|
else
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
---@param name string
|
---@param name string
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,11 @@ local all_columns = {}
|
||||||
---@class (exact) oil.ColumnDefinition
|
---@class (exact) oil.ColumnDefinition
|
||||||
---@field render fun(entry: oil.InternalEntry, conf: nil|table): nil|oil.TextChunk
|
---@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 parse fun(line: string, conf: nil|table): nil|string, nil|string
|
||||||
---@field meta_fields nil|table<string, fun(parent_url: string, entry: oil.InternalEntry, cb: fun(err: 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 compare? fun(entry: oil.InternalEntry, parsed_value: any): boolean
|
||||||
---@field render_action? fun(action: oil.ChangeAction): string
|
---@field render_action? fun(action: oil.ChangeAction): string
|
||||||
---@field perform_action? fun(action: oil.ChangeAction, callback: fun(err: nil|string))
|
---@field perform_action? fun(action: oil.ChangeAction, callback: fun(err: nil|string))
|
||||||
|
---@field get_sort_value? fun(entry: oil.InternalEntry): number|string
|
||||||
|
|
||||||
---@param name string
|
---@param name string
|
||||||
---@param column oil.ColumnDefinition
|
---@param column oil.ColumnDefinition
|
||||||
|
|
@ -29,7 +30,7 @@ end
|
||||||
---@param adapter oil.Adapter
|
---@param adapter oil.Adapter
|
||||||
---@param defn oil.ColumnSpec
|
---@param defn oil.ColumnSpec
|
||||||
---@return nil|oil.ColumnDefinition
|
---@return nil|oil.ColumnDefinition
|
||||||
local function get_column(adapter, defn)
|
M.get_column = function(adapter, defn)
|
||||||
local name = util.split_config(defn)
|
local name = util.split_config(defn)
|
||||||
return all_columns[name] or adapter.get_column(name)
|
return all_columns[name] or adapter.get_column(name)
|
||||||
end
|
end
|
||||||
|
|
@ -46,7 +47,7 @@ M.get_supported_columns = function(adapter_or_scheme)
|
||||||
assert(adapter)
|
assert(adapter)
|
||||||
local ret = {}
|
local ret = {}
|
||||||
for _, def in ipairs(config.columns) do
|
for _, def in ipairs(config.columns) do
|
||||||
if get_column(adapter, def) then
|
if M.get_column(adapter, def) then
|
||||||
table.insert(ret, def)
|
table.insert(ret, def)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -61,7 +62,7 @@ M.get_metadata_fetcher = function(adapter, column_defs)
|
||||||
local num_keys = 0
|
local num_keys = 0
|
||||||
for _, def in ipairs(column_defs) do
|
for _, def in ipairs(column_defs) do
|
||||||
local name = util.split_config(def)
|
local name = util.split_config(def)
|
||||||
local column = get_column(adapter, name)
|
local column = M.get_column(adapter, name)
|
||||||
if column and column.meta_fields then
|
if column and column.meta_fields then
|
||||||
for k, v in pairs(column.meta_fields) do
|
for k, v in pairs(column.meta_fields) do
|
||||||
if not keyfetches[k] then
|
if not keyfetches[k] then
|
||||||
|
|
@ -95,13 +96,15 @@ end
|
||||||
|
|
||||||
local EMPTY = { "-", "Comment" }
|
local EMPTY = { "-", "Comment" }
|
||||||
|
|
||||||
|
M.EMPTY = EMPTY
|
||||||
|
|
||||||
---@param adapter oil.Adapter
|
---@param adapter oil.Adapter
|
||||||
---@param col_def oil.ColumnSpec
|
---@param col_def oil.ColumnSpec
|
||||||
---@param entry oil.InternalEntry
|
---@param entry oil.InternalEntry
|
||||||
---@return oil.TextChunk
|
---@return oil.TextChunk
|
||||||
M.render_col = function(adapter, col_def, entry)
|
M.render_col = function(adapter, col_def, entry)
|
||||||
local name, conf = util.split_config(col_def)
|
local name, conf = util.split_config(col_def)
|
||||||
local column = get_column(adapter, name)
|
local column = M.get_column(adapter, name)
|
||||||
if not column then
|
if not column then
|
||||||
-- This shouldn't be possible because supports_col should return false
|
-- This shouldn't be possible because supports_col should return false
|
||||||
return EMPTY
|
return EMPTY
|
||||||
|
|
@ -150,7 +153,7 @@ M.parse_col = function(adapter, line, col_def)
|
||||||
if vim.startswith(line, "- ") then
|
if vim.startswith(line, "- ") then
|
||||||
return nil, line:sub(3)
|
return nil, line:sub(3)
|
||||||
end
|
end
|
||||||
local column = get_column(adapter, name)
|
local column = M.get_column(adapter, name)
|
||||||
if column then
|
if column then
|
||||||
return column.parse(line, conf)
|
return column.parse(line, conf)
|
||||||
end
|
end
|
||||||
|
|
@ -162,7 +165,7 @@ end
|
||||||
---@param parsed_value any
|
---@param parsed_value any
|
||||||
---@return boolean
|
---@return boolean
|
||||||
M.compare = function(adapter, col_name, entry, parsed_value)
|
M.compare = function(adapter, col_name, entry, parsed_value)
|
||||||
local column = get_column(adapter, col_name)
|
local column = M.get_column(adapter, col_name)
|
||||||
if column and column.compare then
|
if column and column.compare then
|
||||||
return column.compare(entry, parsed_value)
|
return column.compare(entry, parsed_value)
|
||||||
else
|
else
|
||||||
|
|
@ -174,7 +177,7 @@ end
|
||||||
---@param action oil.ChangeAction
|
---@param action oil.ChangeAction
|
||||||
---@return string
|
---@return string
|
||||||
M.render_change_action = function(adapter, action)
|
M.render_change_action = function(adapter, action)
|
||||||
local column = get_column(adapter, action.column)
|
local column = M.get_column(adapter, action.column)
|
||||||
if not column then
|
if not column then
|
||||||
error(string.format("Received change action for nonexistant column %s", action.column))
|
error(string.format("Received change action for nonexistant column %s", action.column))
|
||||||
end
|
end
|
||||||
|
|
@ -189,7 +192,7 @@ end
|
||||||
---@param action oil.ChangeAction
|
---@param action oil.ChangeAction
|
||||||
---@param callback fun(err: nil|string)
|
---@param callback fun(err: nil|string)
|
||||||
M.perform_change_action = function(adapter, action, callback)
|
M.perform_change_action = function(adapter, action, callback)
|
||||||
local column = get_column(adapter, action.column)
|
local column = M.get_column(adapter, action.column)
|
||||||
if not column then
|
if not column then
|
||||||
return callback(
|
return callback(
|
||||||
string.format("Received change action for nonexistant column %s", action.column)
|
string.format("Received change action for nonexistant column %s", action.column)
|
||||||
|
|
@ -236,6 +239,19 @@ local default_type_icons = {
|
||||||
directory = "dir",
|
directory = "dir",
|
||||||
socket = "sock",
|
socket = "sock",
|
||||||
}
|
}
|
||||||
|
---@param entry oil.InternalEntry
|
||||||
|
---@return boolean
|
||||||
|
local function is_entry_directory(entry)
|
||||||
|
local type = entry[FIELD_TYPE]
|
||||||
|
if type == "directory" then
|
||||||
|
return true
|
||||||
|
elseif type == "link" then
|
||||||
|
local meta = entry[FIELD_META]
|
||||||
|
return meta and meta.link_stat and meta.link_stat.type == "directory"
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
M.register("type", {
|
M.register("type", {
|
||||||
render = function(entry, conf)
|
render = function(entry, conf)
|
||||||
local entry_type = entry[FIELD_TYPE]
|
local entry_type = entry[FIELD_TYPE]
|
||||||
|
|
@ -249,6 +265,28 @@ M.register("type", {
|
||||||
parse = function(line, conf)
|
parse = function(line, conf)
|
||||||
return line:match("^(%S+)%s+(.*)$")
|
return line:match("^(%S+)%s+(.*)$")
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
get_sort_value = function(entry)
|
||||||
|
if is_entry_directory(entry) then
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
return 2
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
M.register("name", {
|
||||||
|
render = function(entry, conf)
|
||||||
|
error("Do not use the name column. It is for sorting only")
|
||||||
|
end,
|
||||||
|
|
||||||
|
parse = function(line, conf)
|
||||||
|
error("Do not use the name column. It is for sorting only")
|
||||||
|
end,
|
||||||
|
|
||||||
|
get_sort_value = function(entry)
|
||||||
|
return entry[FIELD_NAME]
|
||||||
|
end,
|
||||||
})
|
})
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ local default_config = {
|
||||||
["_"] = "actions.open_cwd",
|
["_"] = "actions.open_cwd",
|
||||||
["`"] = "actions.cd",
|
["`"] = "actions.cd",
|
||||||
["~"] = "actions.tcd",
|
["~"] = "actions.tcd",
|
||||||
|
["gs"] = "actions.change_sort",
|
||||||
["g."] = "actions.toggle_hidden",
|
["g."] = "actions.toggle_hidden",
|
||||||
},
|
},
|
||||||
-- Set to false to disable all of the above keymaps
|
-- Set to false to disable all of the above keymaps
|
||||||
|
|
@ -70,6 +71,12 @@ local default_config = {
|
||||||
is_always_hidden = function(name, bufnr)
|
is_always_hidden = function(name, bufnr)
|
||||||
return false
|
return false
|
||||||
end,
|
end,
|
||||||
|
sort = {
|
||||||
|
-- sort order can be "asc" or "desc"
|
||||||
|
-- see :help oil-columns to see which columns are sortable
|
||||||
|
{ "type", "asc" },
|
||||||
|
{ "name", "asc" },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
-- Configuration for the floating window in oil.open_float
|
-- Configuration for the floating window in oil.open_float
|
||||||
float = {
|
float = {
|
||||||
|
|
|
||||||
|
|
@ -142,6 +142,12 @@ M.set_columns = function(cols)
|
||||||
require("oil.view").set_columns(cols)
|
require("oil.view").set_columns(cols)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Change the sort order for oil
|
||||||
|
---@param sort string[][]
|
||||||
|
M.set_sort = function(sort)
|
||||||
|
require("oil.view").set_sort(sort)
|
||||||
|
end
|
||||||
|
|
||||||
---Change how oil determines if the file is hidden
|
---Change how oil determines if the file is hidden
|
||||||
---@param is_hidden_file fun(filename: string, bufnr: nil|integer): boolean Return true if the file/dir should be hidden
|
---@param is_hidden_file fun(filename: string, bufnr: nil|integer): boolean Return true if the file/dir should be hidden
|
||||||
M.set_is_hidden_file = function(is_hidden_file)
|
M.set_is_hidden_file = function(is_hidden_file)
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,17 @@ M.set_columns = function(cols)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
M.set_sort = function(new_sort)
|
||||||
|
local any_modified = are_any_modified()
|
||||||
|
if any_modified then
|
||||||
|
vim.notify("Cannot change sorting when you have unsaved changes", vim.log.levels.WARN)
|
||||||
|
else
|
||||||
|
config.view_options.sort = new_sort
|
||||||
|
-- TODO only refetch if we don't have all the necessary data for the columns
|
||||||
|
M.rerender_all_oil_buffers({ refetch = true })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- List of bufnrs
|
-- List of bufnrs
|
||||||
local session = {}
|
local session = {}
|
||||||
|
|
||||||
|
|
@ -351,17 +362,46 @@ M.initialize = function(bufnr)
|
||||||
keymap_util.set_keymaps("", config.keymaps, bufnr)
|
keymap_util.set_keymaps("", config.keymaps, bufnr)
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param entry oil.InternalEntry
|
---@param adapter oil.Adapter
|
||||||
---@return boolean
|
---@return fun(a: oil.InternalEntry, b: oil.InternalEntry): boolean
|
||||||
local function is_entry_directory(entry)
|
local function get_sort_function(adapter)
|
||||||
local type = entry[FIELD_TYPE]
|
local idx_funs = {}
|
||||||
if type == "directory" then
|
for _, sort_pair in ipairs(config.view_options.sort) do
|
||||||
return true
|
local col_name, order = unpack(sort_pair)
|
||||||
elseif type == "link" then
|
if order ~= "asc" and order ~= "desc" then
|
||||||
local meta = entry[FIELD_META]
|
vim.notify_once(
|
||||||
return meta and meta.link_stat and meta.link_stat.type == "directory"
|
string.format(
|
||||||
|
"Column '%s' has invalid sort order '%s'. Should be either 'asc' or 'desc'",
|
||||||
|
col_name,
|
||||||
|
order
|
||||||
|
),
|
||||||
|
vim.log.levels.WARN
|
||||||
|
)
|
||||||
|
end
|
||||||
|
local col = columns.get_column(adapter, col_name)
|
||||||
|
if col and col.get_sort_value then
|
||||||
|
table.insert(idx_funs, { col.get_sort_value, order })
|
||||||
else
|
else
|
||||||
return false
|
vim.notify_once(
|
||||||
|
string.format("Column '%s' does not support sorting", col_name),
|
||||||
|
vim.log.levels.WARN
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return function(a, b)
|
||||||
|
for _, sort_fn in ipairs(idx_funs) do
|
||||||
|
local get_sort_value, order = unpack(sort_fn)
|
||||||
|
local a_val = get_sort_value(a)
|
||||||
|
local b_val = get_sort_value(b)
|
||||||
|
if a_val ~= b_val then
|
||||||
|
if order == "desc" then
|
||||||
|
return a_val > b_val
|
||||||
|
else
|
||||||
|
return a_val < b_val
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return a[FIELD_NAME] < b[FIELD_NAME]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -390,14 +430,7 @@ local function render_buffer(bufnr, opts)
|
||||||
local entries = cache.list_url(bufname)
|
local entries = cache.list_url(bufname)
|
||||||
local entry_list = vim.tbl_values(entries)
|
local entry_list = vim.tbl_values(entries)
|
||||||
|
|
||||||
table.sort(entry_list, function(a, b)
|
table.sort(entry_list, get_sort_function(adapter))
|
||||||
local a_isdir = is_entry_directory(a)
|
|
||||||
local b_isdir = is_entry_directory(b)
|
|
||||||
if a_isdir ~= b_isdir then
|
|
||||||
return a_isdir
|
|
||||||
end
|
|
||||||
return a[FIELD_NAME] < b[FIELD_NAME]
|
|
||||||
end)
|
|
||||||
|
|
||||||
local jump_idx
|
local jump_idx
|
||||||
if opts.jump_first then
|
if opts.jump_first then
|
||||||
|
|
@ -512,6 +545,21 @@ M.format_entry_cols = function(entry, column_defs, col_width, adapter)
|
||||||
return cols
|
return cols
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Get the column names that are used for view and sort
|
||||||
|
---@return string[]
|
||||||
|
local function get_used_columns()
|
||||||
|
local cols = {}
|
||||||
|
for _, def in ipairs(config.columns) do
|
||||||
|
local name = util.split_config(def)
|
||||||
|
table.insert(cols, name)
|
||||||
|
end
|
||||||
|
for _, sort_pair in ipairs(config.view_options.sort) do
|
||||||
|
local name = sort_pair[1]
|
||||||
|
table.insert(cols, name)
|
||||||
|
end
|
||||||
|
return cols
|
||||||
|
end
|
||||||
|
|
||||||
---@param bufnr integer
|
---@param bufnr integer
|
||||||
---@param opts nil|table
|
---@param opts nil|table
|
||||||
--- preserve_undo nil|boolean
|
--- preserve_undo nil|boolean
|
||||||
|
|
@ -579,7 +627,7 @@ M.render_buffer_async = function(bufnr, opts, callback)
|
||||||
end
|
end
|
||||||
|
|
||||||
cache.begin_update_url(bufname)
|
cache.begin_update_url(bufname)
|
||||||
adapter.list(bufname, config.columns, function(err, entries, fetch_more)
|
adapter.list(bufname, get_used_columns(), function(err, entries, fetch_more)
|
||||||
loading.set_loading(bufnr, false)
|
loading.set_loading(bufnr, false)
|
||||||
if err then
|
if err then
|
||||||
cache.end_update_url(bufname)
|
cache.end_update_url(bufname)
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,7 @@ class ColumnDef:
|
||||||
name: str
|
name: str
|
||||||
adapters: str
|
adapters: str
|
||||||
editable: bool
|
editable: bool
|
||||||
|
sortable: bool
|
||||||
summary: str
|
summary: str
|
||||||
params: List["LuaParam"] = field(default_factory=list)
|
params: List["LuaParam"] = field(default_factory=list)
|
||||||
|
|
||||||
|
|
@ -107,6 +108,7 @@ COL_DEFS = [
|
||||||
"type",
|
"type",
|
||||||
"*",
|
"*",
|
||||||
False,
|
False,
|
||||||
|
True,
|
||||||
"The type of the entry (file, directory, link, etc)",
|
"The type of the entry (file, directory, link, etc)",
|
||||||
HL
|
HL
|
||||||
+ [LuaParam("icons", "table<string, string>", "Mapping of entry type to icon")],
|
+ [LuaParam("icons", "table<string, string>", "Mapping of entry type to icon")],
|
||||||
|
|
@ -115,6 +117,7 @@ COL_DEFS = [
|
||||||
"icon",
|
"icon",
|
||||||
"*",
|
"*",
|
||||||
False,
|
False,
|
||||||
|
False,
|
||||||
"An icon for the entry's type (requires nvim-web-devicons)",
|
"An icon for the entry's type (requires nvim-web-devicons)",
|
||||||
HL
|
HL
|
||||||
+ [
|
+ [
|
||||||
|
|
@ -123,17 +126,17 @@ COL_DEFS = [
|
||||||
LuaParam("add_padding", "boolean", "Set to false to remove the extra whitespace after the icon"),
|
LuaParam("add_padding", "boolean", "Set to false to remove the extra whitespace after the icon"),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
ColumnDef("size", "files, ssh", False, "The size of the file", HL + []),
|
ColumnDef("size", "files, ssh", False, True, "The size of the file", HL + []),
|
||||||
ColumnDef(
|
ColumnDef(
|
||||||
"permissions", "files, ssh", True, "Access permissions of the file", HL + []
|
"permissions", "files, ssh", True, False, "Access permissions of the file", HL + []
|
||||||
),
|
),
|
||||||
ColumnDef("ctime", "files", False, "Change timestamp of the file", HL + TIME + []),
|
ColumnDef("ctime", "files", False, True, "Change timestamp of the file", HL + TIME + []),
|
||||||
ColumnDef(
|
ColumnDef(
|
||||||
"mtime", "files", False, "Last modified time of the file", HL + TIME + []
|
"mtime", "files", False, True, "Last modified time of the file", HL + TIME + []
|
||||||
),
|
),
|
||||||
ColumnDef("atime", "files", False, "Last access time of the file", HL + TIME + []),
|
ColumnDef("atime", "files", False, True, "Last access time of the file", HL + TIME + []),
|
||||||
ColumnDef(
|
ColumnDef(
|
||||||
"birthtime", "files", False, "The time the file was created", HL + TIME + []
|
"birthtime", "files", False, True, "The time the file was created", HL + TIME + []
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -142,7 +145,7 @@ def get_options_vimdoc() -> "VimdocSection":
|
||||||
section = VimdocSection("options", "oil-options")
|
section = VimdocSection("options", "oil-options")
|
||||||
config_file = os.path.join(ROOT, "lua", "oil", "config.lua")
|
config_file = os.path.join(ROOT, "lua", "oil", "config.lua")
|
||||||
opt_lines = read_section(config_file, r"^local default_config =", r"^}$")
|
opt_lines = read_section(config_file, r"^local default_config =", r"^}$")
|
||||||
lines = ["\n", ">\n", ' require("oil").setup({\n']
|
lines = ["\n", ">lua\n", ' require("oil").setup({\n']
|
||||||
lines.extend(indent(opt_lines, 4))
|
lines.extend(indent(opt_lines, 4))
|
||||||
lines.extend([" })\n", "<\n"])
|
lines.extend([" })\n", "<\n"])
|
||||||
section.body = lines
|
section.body = lines
|
||||||
|
|
@ -193,6 +196,10 @@ def get_columns_vimdoc() -> "VimdocSection":
|
||||||
for col in COL_DEFS:
|
for col in COL_DEFS:
|
||||||
section.body.append(leftright(col.name, f"*column-{col.name}*"))
|
section.body.append(leftright(col.name, f"*column-{col.name}*"))
|
||||||
section.body.extend(wrap(f"Adapters: {col.adapters}", 4))
|
section.body.extend(wrap(f"Adapters: {col.adapters}", 4))
|
||||||
|
if col.sortable:
|
||||||
|
section.body.extend(
|
||||||
|
wrap(f"Sortable: this column can be used in view_props.sort", 4)
|
||||||
|
)
|
||||||
if col.editable:
|
if col.editable:
|
||||||
section.body.extend(wrap(f"Editable: this column is read/write", 4))
|
section.body.extend(wrap(f"Editable: this column is read/write", 4))
|
||||||
section.body.extend(wrap(col.summary, 4))
|
section.body.extend(wrap(col.summary, 4))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue