Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ require("bento").setup({
max_open_buffers = nil, -- Max buffers (nil = unlimited)
buffer_deletion_metric = "frecency_access", -- Metric for buffer deletion (see below)
buffer_notify_on_delete = true, -- Notify when deleting a buffer (false for silent deletion)
ordering_metric = "access", -- Buffer ordering: nil (arbitrary), "access", or "edit"
ordering_metric = "access", -- Buffer ordering: nil (arbitrary) | "access" | "edit" | "filename" | "directory"
default_action = "open", -- Action when pressing label directly

ui = {
Expand Down Expand Up @@ -212,7 +212,7 @@ require("bento").setup({
| `max_open_buffers` | number/nil | `nil` | Maximum number of buffers to keep open (`nil` = unlimited) |
| `buffer_deletion_metric` | string | `"frecency_access"` | Metric used to decide which buffer to delete when limit is reached (see below) |
| `buffer_notify_on_delete` | boolean | `true` | Whether to create a notification via `vim.notify` when a buffer is deleted by the plugin |
| `ordering_metric` | string/nil | `"access"` | Buffer ordering: `nil` (arbitrary), `"access"` (by last access time), or `"edit"` (by last edit time). Most recent first. |
| `ordering_metric` | string/nil | `"access"` | Buffer ordering: `nil` (arbitrary), `"access"` (by last access time, most recent first), `"edit"` (by last edit time, most recent first), `"filename"` (by filename alphabetically) or `"directory"` (by relative path alphabetically) |
| `default_action` | string | `"open"` | Default action mode when menu expands |
| `highlights` | table | See below | Highlight groups for all UI elements |
| `actions` | table | Built-in actions | Action definitions (see Actions section) |
Expand Down
120 changes: 107 additions & 13 deletions lua/bento/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -577,21 +577,83 @@ local function get_buffer_metric_value(buf_id, metric_type)
return 0
end

--- Get the ordering metric value for a buffer (used for sorting)
--- Returns higher values for more recently accessed/edited buffers
--- @param buf_id number Buffer ID
--- @return number Ordering value
function M.get_ordering_value(buf_id)
local config = M.get_config()
local ordering_metric = config.ordering_metric
--- Default stable sort comparator by buffer ID
--- @param a {buf_id: number}
--- @param b {buf_id: number}
--- @return boolean
local function sort_by_buf_id(a, b)
return a.buf_id < b.buf_id
end

if not ordering_metric then
return 0
--- Generic comparator using a value getter function
--- Falls back to buf_id sort when values are equal
--- @param a {buf_id: number}
--- @param b {buf_id: number}
--- @param value_getter fun(buf_id: number): any
--- @param asc? boolean Sort ascending (default: false)
--- @return boolean
local function sort_by_value(a, b, value_getter, asc)
local a_val = value_getter(a.buf_id)
local b_val = value_getter(b.buf_id)
if a_val == b_val then
return sort_by_buf_id(a, b)
elseif (asc or false) then
return a_val < b_val
else
return a_val > b_val
end
end

local metrics = M.buffer_metrics[buf_id]
--- Sort by filename
--- @param a {buf_id: number}
--- @param b {buf_id: number}
--- @return boolean
local function sort_buffers_by_filename(a, b)
--- Get the filename value for a buffer to compare by
--- @param buf_id number Buffer ID
--- @return string Filename
local function get_value(buf_id)
local buf_info = vim.fn.getbufinfo(buf_id)[1]
local filename = vim.fn.fnamemodify(buf_info.name or "", ":t")
return string.lower(filename)
end
return sort_by_value(a, b, get_value, true)
end

--- Sort by directory components (path segments)
--- @param a {buf_id: number}
--- @param b {buf_id: number}
--- @return boolean
local function sort_buffers_by_directory(a, b)
--- Get the relative path segments list for a buffer
--- @param buf_id number Buffer ID
--- @return string[] Path segments
local function get_values(buf_id)
local buf_info = vim.fn.getbufinfo(buf_id)[1]
local rel_path = vim.fn.fnamemodify(buf_info.name or "", ":.")
return utils.split_path(string.lower(rel_path))
end
local a_vals = get_values(a.buf_id)
local b_vals = get_values(b.buf_id)
local min_length = math.min(#a_vals, #b_vals)
for i = 1, min_length do
if i == min_length or a_vals[i] ~= b_vals[i] then
return a_vals[i] < b_vals[i]
end
end
end

if ordering_metric == "access" then
--- Sort by most recent access time
--- @param a {buf_id: number}
--- @param b {buf_id: number}
--- @return boolean
local function sort_buffers_by_access(a, b)
--- Get the ordering metric value for a buffer (used for sorting)
--- Returns higher values for more recently accessed buffers
--- @param buf_id number Buffer ID
--- @return number Ordering value
local function get_value(buf_id)
local metrics = M.buffer_metrics[buf_id]
if metrics and #metrics.access_times > 0 then
return metrics.access_times[#metrics.access_times]
end
Expand All @@ -600,14 +662,46 @@ function M.get_ordering_value(buf_id)
return buf_info.lastused or 0
end
return 0
elseif ordering_metric == "edit" then
end
return sort_by_value(a, b, get_value)
end

--- Sort by most recent edit time
--- @param a {buf_id: number}
--- @param b {buf_id: number}
--- @return boolean
local function sort_buffers_by_edit(a, b)
--- Get the ordering metric value for a buffer (used for sorting)
--- Returns higher values for more recently edited buffers
--- @param buf_id number Buffer ID
--- @return number Ordering value
local function get_value(buf_id)
local metrics = M.buffer_metrics[buf_id]
if metrics and #metrics.edit_times > 0 then
return metrics.edit_times[#metrics.edit_times]
end
return 0
end
return sort_by_value(a, b, get_value)
end

return 0
--- Returns the appropriate sort comparator function based on config.ordering_metric
--- @return fun(a: {buf_id: number}, b: {buf_id: number}): boolean
function M.get_buffers_sort_function()
local config = M.get_config()
local ordering_metric = config.ordering_metric
if not ordering_metric then
return sort_by_buf_id
elseif ordering_metric == "access" then
return sort_buffers_by_access
elseif ordering_metric == "edit" then
return sort_buffers_by_edit
elseif ordering_metric == "filename" then
return sort_buffers_by_filename
elseif ordering_metric == "directory" then
return sort_buffers_by_directory
end
return sort_by_buf_id
end

--- Initialize marks for all valid buffers
Expand Down
9 changes: 1 addition & 8 deletions lua/bento/ui.lua
Original file line number Diff line number Diff line change
Expand Up @@ -201,14 +201,7 @@ local function update_marks()

config = bento.get_config()
if config.ordering_metric then
table.sort(marks, function(a, b)
local a_val = bento.get_ordering_value(a.buf_id)
local b_val = bento.get_ordering_value(b.buf_id)
if a_val == b_val then
return a.buf_id < b.buf_id
end
return a_val > b_val
end)
table.sort(marks, bento.get_buffers_sort_function())
end
end

Expand Down
4 changes: 2 additions & 2 deletions lua/bento/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ end
--- Split a path into components (directories + filename)
--- @param path string File path
--- @return string[] Path components
local function split_path(path)
function M.split_path(path)
local components = {}
for part in string.gmatch(path, "[^/\\]+") do
table.insert(components, part)
Expand Down Expand Up @@ -54,7 +54,7 @@ function M.get_display_names(paths)
else
local path_components = {}
for _, path in ipairs(group) do
path_components[path] = split_path(path)
path_components[path] = M.split_path(path)
end

for _, path in ipairs(group) do
Expand Down