You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
347 lines
9.5 KiB
347 lines
9.5 KiB
local F = require("core.functions") |
|
|
|
-- Module for managing buffers, these are used for navigation |
|
|
|
local M = {} |
|
|
|
-- Buffers, categorized into groups |
|
-- { |
|
-- { group = "Project", buffers = { "buffer1", "buffer2" }, active_index = 1 }, |
|
-- { group = "Other", buffers = { "buffer3", "buffer4" }, active_index = 1 }, |
|
-- } |
|
M.buffers = {} |
|
|
|
M.buffer_default_group = "Other" |
|
|
|
M.buffer_matcher = { |
|
-- match = truthy, function() -> thruthy, or string matching buffer filename |
|
-- group = string, function() -> string |
|
{ match = F.find_project_root, group = F.find_project_root }, |
|
{ match = true, group = "Other" }, -- default |
|
} |
|
|
|
-------------------------------------------- |
|
|
|
local get_buffer_name = function() |
|
local buf = vim.api.nvim_get_current_buf() |
|
local buffer_name = vim.api.nvim_buf_get_name(buf) |
|
if not buffer_name or buffer_name == "" then return nil end |
|
return buffer_name |
|
end |
|
|
|
M.add_buffer = function() |
|
local buffer_name = get_buffer_name() |
|
if not buffer_name then return end |
|
|
|
-- Evaluate group |
|
local eval_group = function(config_group) |
|
if not config_group then return M.buffer_default_group end |
|
if type(config_group) == "function" then return config_group() end |
|
return config_group |
|
end |
|
|
|
-- Add if buffer does not exist yet |
|
local add = function(config_group) |
|
local group = eval_group(config_group) |
|
|
|
local index |
|
for i, buffer_group in ipairs(M.buffers) do |
|
if buffer_group.group == group then index = i end |
|
end |
|
|
|
-- Group is new entirely, so we only have 1 buffer |
|
if not index then |
|
table.insert(M.buffers, { group = group, buffers = { buffer_name }, active_index = 1 }) |
|
return #M.buffers, 1 -- group index, buffer index |
|
end |
|
|
|
-- Buffer previously added, set active |
|
for i, buffer in ipairs(M.buffers[index].buffers) do |
|
if buffer == buffer_name then |
|
M.buffers[index].active_index = i |
|
return index, i -- group index, buffer index |
|
end |
|
end |
|
|
|
-- Buffer is new, add it to the list |
|
table.insert(M.buffers[index].buffers, buffer_name) |
|
local count = #M.buffers[index].buffers |
|
M.buffers[index].active_index = count |
|
|
|
return index, count -- group index, buffer index |
|
end |
|
|
|
-- Walk matcher configuration |
|
local matcher = M.buffer_matcher or {} |
|
if matcher and type(matcher) == "function" then |
|
matcher = matcher() |
|
end |
|
if #matcher == 0 then add(M.buffer_default_group) end |
|
for _, config in ipairs(M.buffer_matcher) do |
|
if type(config.match) == "function" and config.match() then |
|
return add(config.group) |
|
end |
|
|
|
-- Match path's filename |
|
if type(config.match) == "string" and config.match == buffer_name:match("([^/]+)$") then |
|
return add(config.group) |
|
end |
|
|
|
if type(config.match) == "boolean" and config.match then |
|
return add(config.group) |
|
end |
|
end |
|
|
|
return add(M.buffer_default_group) |
|
end |
|
|
|
local get_group_from_buffer = function(buffer_name) |
|
for i, buffer_group in ipairs(M.buffers) do |
|
for j, buffer in ipairs(buffer_group.buffers) do |
|
if buffer == buffer_name then |
|
return i, j -- group index, buffer index |
|
end |
|
end |
|
end |
|
|
|
return nil |
|
end |
|
|
|
local focus_buffer = function(new_buffer_name) |
|
for _, buffer in ipairs(vim.api.nvim_list_bufs()) do |
|
if vim.api.nvim_buf_get_name(buffer) == new_buffer_name then |
|
vim.api.nvim_set_current_buf(buffer) |
|
break |
|
end |
|
end |
|
end |
|
|
|
M.remove_buffer = function(buffer_name) |
|
if not buffer_name or buffer_name == "" then return end |
|
|
|
local group_index, buffer_index = get_group_from_buffer(buffer_name) |
|
if not group_index or not buffer_index then return end |
|
|
|
local buffer_group = M.buffers[group_index] |
|
|
|
-- Remove the buffer and decrease active index |
|
table.remove(buffer_group.buffers, buffer_index) |
|
buffer_group.active_index = math.max(buffer_group.active_index - 1, 1) |
|
local new_buffer_name = buffer_group.buffers[buffer_group.active_index] |
|
|
|
-- If there are now no buffers in the group, delete the group |
|
if #buffer_group.buffers == 0 then |
|
table.remove(M.buffers, group_index) |
|
if #M.buffers == 0 then return end |
|
if #M.buffers < group_index then group_index = group_index - 1 end |
|
new_buffer_name = M.buffers[group_index].buffers[M.buffers[group_index].active_index] |
|
end |
|
|
|
-- Make the new index the active buffer |
|
focus_buffer(new_buffer_name) |
|
end |
|
|
|
local buffer_action_in_active_group = function(action, direction) |
|
local buffer_name = get_buffer_name() |
|
if not buffer_name then return end |
|
|
|
-- Get active group from the current buffer |
|
local group_index, buffer_index = get_group_from_buffer(buffer_name) |
|
if not group_index then group_index, buffer_index = M.add_buffer() end |
|
|
|
-- Get buffer set |
|
local buffers = M.buffers[group_index].buffers |
|
if #buffers < 2 then return end |
|
|
|
-- Determine the other index |
|
local other_index = buffer_index + direction |
|
if buffer_index == 1 and direction == -1 then |
|
other_index = #buffers |
|
elseif buffer_index == #buffers and direction == 1 then |
|
other_index = 1 |
|
end |
|
|
|
if action == "move" then |
|
for _, buffer in ipairs(vim.api.nvim_list_bufs()) do |
|
-- TODO: Probably not needed if the other autocommands are implemented |
|
-- Remove inactive buffers |
|
if not vim.api.nvim_buf_is_loaded(buffer) then |
|
for i, group_buffer in ipairs(buffers) do |
|
if buffer == group_buffer then |
|
table.remove(buffers, i) |
|
if i > 1 and i <= other_index then |
|
other_index = other_index - 1 |
|
end |
|
end |
|
end |
|
|
|
goto continue |
|
end |
|
|
|
-- Make buffer active |
|
if vim.api.nvim_buf_get_name(buffer) == buffers[other_index] then |
|
vim.api.nvim_set_current_buf(buffer) |
|
M.buffers[group_index].active_index = other_index |
|
break |
|
end |
|
|
|
::continue:: |
|
end |
|
elseif action == "swap" then |
|
local tmp = buffers[other_index] |
|
buffers[other_index] = buffers[buffer_index] |
|
buffers[buffer_index] = tmp |
|
end |
|
|
|
M.buffers[group_index].buffers = buffers |
|
end |
|
|
|
M.buffer_move_left = function() |
|
buffer_action_in_active_group("move", -1) |
|
end |
|
|
|
M.buffer_move_right = function() |
|
buffer_action_in_active_group("move", 1) |
|
end |
|
|
|
M.buffer_swap_left = function() |
|
buffer_action_in_active_group("swap", -1) |
|
end |
|
|
|
M.buffer_swap_right = function() |
|
buffer_action_in_active_group("swap", 1) |
|
end |
|
|
|
-------------------------------------------- |
|
-- Group move |
|
|
|
local buffer_group_move = function(direction) |
|
local buffer_name = get_buffer_name() |
|
if not buffer_name then return end |
|
|
|
-- Get active group from the current buffer |
|
local group_index, _ = get_group_from_buffer(buffer_name) |
|
if not group_index then group_index, _ = M.add_buffer() end |
|
|
|
-- Check groups bounds |
|
if #M.buffers < 2 then return end |
|
|
|
-- Determine the other index |
|
local other_index = group_index + direction |
|
if group_index == 1 and direction == -1 then |
|
other_index = #M.buffers |
|
elseif group_index == #M.buffers and direction == 1 then |
|
other_index = 1 |
|
end |
|
|
|
-- Get active index from other group |
|
local other_active_index = M.buffers[other_index].active_index |
|
|
|
-- Make buffer active |
|
-- TODO: What do if this buffer no longer exists? |
|
local other_buffer = M.buffers[other_index].buffers[other_active_index] |
|
focus_buffer(other_buffer) |
|
end |
|
|
|
M.buffer_group_move_up = function() |
|
buffer_group_move(-1) |
|
end |
|
|
|
M.buffer_group_move_down = function() |
|
buffer_group_move(1) |
|
end |
|
|
|
-------------------------------------------- |
|
-- Telescope functionss |
|
|
|
local pick = function(title, items, entry_maker, action) |
|
local actions = require("telescope.actions") |
|
local action_state = require("telescope.actions.state") |
|
local conf = require("telescope.config").values |
|
local finders = require("telescope.finders") |
|
local pickers = require("telescope.pickers") |
|
|
|
pickers.new({}, { |
|
prompt_title = title, |
|
finder = finders.new_table { |
|
results = items, |
|
entry_maker = entry_maker, |
|
}, |
|
previewer = false, |
|
sorter = conf.generic_sorter({}), |
|
attach_mappings = function(prompt_bufnr) |
|
actions.select_default:replace(function() |
|
actions.close(prompt_bufnr) |
|
|
|
local selection = action_state.get_selected_entry() |
|
action(selection) |
|
end) |
|
|
|
return true |
|
end, |
|
}):find() |
|
end |
|
|
|
M.buffer_pick_buffer = function() |
|
local buffer_name = get_buffer_name() |
|
if not buffer_name then return end |
|
|
|
-- Get active group from the current buffer |
|
local group_index, _ = get_group_from_buffer(buffer_name) |
|
if not group_index then group_index, _ = M.add_buffer() end |
|
|
|
-- Get all buffers from the active group |
|
local buffers = {} |
|
for i, buffer in ipairs(M.buffers[group_index].buffers) do |
|
table.insert(buffers, { key = buffer, value = tostring(i) }) |
|
end |
|
|
|
pick("Group buffers", |
|
buffers, |
|
function(buffer) |
|
-- Only show last part of the path |
|
local display = buffer.key:match("^.+/(.+)$") or buffer.key |
|
return { |
|
display = display, -- shown in the picker |
|
ordinal = buffer.value, -- used for sorting/filtering |
|
value = buffer.value, -- actual value |
|
} |
|
end, |
|
function(selection) |
|
local new_buffer_name = M.buffers[group_index].buffers[tonumber(selection.value)] |
|
|
|
-- Make new buffer active |
|
focus_buffer(new_buffer_name) |
|
end |
|
) |
|
end |
|
|
|
M.buffer_pick_group = function() |
|
-- Get all groups |
|
local groups = {} |
|
for i, buffer_group in ipairs(M.buffers) do |
|
table.insert(groups, { key = buffer_group.group, value = tostring(i) }) |
|
end |
|
|
|
pick("Groups", |
|
groups, |
|
function(group) |
|
-- Only show last part of the path |
|
local display = group.key:match("^.+/(.+)$") or group.key |
|
return { |
|
display = display, -- shown in the picker |
|
ordinal = group.value, -- used for sorting/filtering |
|
value = group.value, -- actual value |
|
} |
|
end, |
|
function(selection) |
|
local buffer_group = M.buffers[tonumber(selection.value)] |
|
local new_buffer_name = buffer_group.buffers[buffer_group.active_index] |
|
|
|
-- Make new buffer active |
|
focus_buffer(new_buffer_name) |
|
end |
|
) |
|
end |
|
|
|
return M
|
|
|