mirror of
https://github.com/zoriya/jj.nvim.git
synced 2026-08-05 10:46:08 +00:00
refactor: create a new buffer module to centralize buffer operation logic
This commit is contained in:
@@ -0,0 +1,300 @@
|
||||
--- @class jj.core.buffer
|
||||
local M = {}
|
||||
|
||||
--- @class jj.core.buffer.opts
|
||||
--- @field name? string Buffer name
|
||||
--- @field split? "horizontal"|"vertical"|"tab"|"current" Split type (default: "horizontal")
|
||||
--- @field size? number Split size in lines/columns
|
||||
--- @field modifiable? boolean Whether buffer is modifiable (default: true)
|
||||
--- @field filetype? string Filetype to set
|
||||
--- @field buftype? string Buffer type (e.g., "nofile", "acwrite", etc. - optional, defaults to scratch buffer)
|
||||
--- @field on_exit? fun(buf: number) Callback when buffer is closed
|
||||
--- @field keymaps? jj.core.buffer.keymap[] Keymaps to set on the buffer
|
||||
|
||||
--- @class jj.core.buffer.keymap
|
||||
--- @field modes? string|string[] Modes for the keymap (default: "n")
|
||||
--- @field mode? string Alias for modes
|
||||
--- @field lhs string Left-hand side of the keymap
|
||||
--- @field rhs string|fun() Right-hand side of the keymap (string or function
|
||||
--- @field opts? table Additional keymap options
|
||||
|
||||
--- @class jj.core.buffer.float_opts
|
||||
--- @field width? number Window width (default: 80% of columns)
|
||||
--- @field height? number Window height (default: 80% of lines)
|
||||
--- @field row? number Window row position (default: centered)
|
||||
--- @field col? number Window column position (default: centered)
|
||||
--- @field relative? string Relative positioning (default: "editor")
|
||||
--- @field style? string Window style (default: "minimal")
|
||||
--- @field border? string Border style (default: "rounded")
|
||||
--- @field title? string Window title (default: none)
|
||||
--- @field title_pos? string Title position (default: "center")
|
||||
--- @field enter? boolean Whether to enter the window after creation (default: false)
|
||||
--- @field modifiable? boolean Whether buffer is modifiable (default: true)
|
||||
--- @field filetype? string Filetype to set
|
||||
--- @field buftype? string Buffer type (e.g., "nofile", "acwrite", etc. - optional, defaults to scratch buffer)
|
||||
--- @field bufhidden? string Buffer hidden behavior (default: "hide")
|
||||
--- @field on_exit? fun(buf: number) Callback when buffer is closed
|
||||
--- @field keymaps? jj.core.buffer.keymap[] Keymaps to set on the buffer
|
||||
--- @field win_options? table Window-specific options to set
|
||||
|
||||
--- Create and configure a new buffer
|
||||
--- @param opts jj.core.buffer.opts Buffer configuration options
|
||||
--- @return number buf Buffer handle
|
||||
--- @return number? win Window handle (nil if using current window)
|
||||
function M.create(opts)
|
||||
opts = opts or {}
|
||||
|
||||
local win = nil
|
||||
|
||||
-- Handle window/split creation
|
||||
if opts.split == "vertical" then
|
||||
local width = opts.size or math.floor(vim.o.columns / 2)
|
||||
vim.cmd(string.format("vsplit | vertical resize %d", width))
|
||||
win = vim.api.nvim_get_current_win()
|
||||
elseif opts.split == "tab" then
|
||||
vim.cmd("tabnew")
|
||||
win = vim.api.nvim_get_current_win()
|
||||
elseif opts.split == "current" then
|
||||
win = vim.api.nvim_get_current_win()
|
||||
else -- horizontal (default)
|
||||
local height = opts.size or math.floor(vim.o.lines / 2)
|
||||
vim.cmd(string.format("split | resize %d", height))
|
||||
win = vim.api.nvim_get_current_win()
|
||||
end
|
||||
|
||||
-- Create buffer
|
||||
local buf = vim.api.nvim_create_buf(false, true)
|
||||
|
||||
-- Set buffer in window if we created/got a window
|
||||
if win then
|
||||
vim.api.nvim_win_set_buf(win, buf)
|
||||
end
|
||||
|
||||
-- Set buffer name if provided (only if it doesn't already exist)
|
||||
if opts.name then
|
||||
pcall(vim.api.nvim_buf_set_name, buf, opts.name)
|
||||
end
|
||||
|
||||
-- Set buffer options
|
||||
if opts.buftype then
|
||||
vim.bo[buf].buftype = opts.buftype
|
||||
end
|
||||
vim.bo[buf].modifiable = opts.modifiable ~= nil and opts.modifiable or true
|
||||
vim.bo[buf].swapfile = false
|
||||
vim.bo[buf].buflisted = false
|
||||
|
||||
-- Set filetype if provided
|
||||
if opts.filetype then
|
||||
vim.bo[buf].filetype = opts.filetype
|
||||
end
|
||||
|
||||
-- Set keymaps if provided
|
||||
if opts.keymaps then
|
||||
M.set_keymaps(buf, opts.keymaps)
|
||||
end
|
||||
|
||||
-- Set up cleanup autocmd if on_exit callback provided
|
||||
if opts.on_exit then
|
||||
vim.api.nvim_create_autocmd({ "BufWipeout", "BufDelete" }, {
|
||||
buffer = buf,
|
||||
once = true,
|
||||
callback = function()
|
||||
opts.on_exit(buf)
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
return buf, win
|
||||
end
|
||||
|
||||
--- Create and configure a floating window buffer
|
||||
--- @param opts jj.core.buffer.float_opts Floating window configuration options
|
||||
--- @return number buf Buffer handle
|
||||
--- @return number win Window handle
|
||||
function M.create_float(opts)
|
||||
opts = opts or {}
|
||||
|
||||
-- Default config
|
||||
local width = opts.width or math.floor(vim.o.columns * 0.8)
|
||||
local height = opts.height or math.floor(vim.o.lines * 0.8)
|
||||
local row = opts.row or math.floor((vim.o.lines - height) / 2)
|
||||
local col = opts.col or math.floor((vim.o.columns - width) / 2)
|
||||
|
||||
local win_config = {
|
||||
relative = opts.relative or "editor",
|
||||
width = width,
|
||||
height = height,
|
||||
row = row,
|
||||
col = col,
|
||||
style = opts.style or "minimal",
|
||||
border = opts.border or "rounded",
|
||||
}
|
||||
|
||||
-- Add optional title
|
||||
if opts.title then
|
||||
win_config.title = opts.title
|
||||
win_config.title_pos = opts.title_pos or "center"
|
||||
end
|
||||
|
||||
-- Create buffer
|
||||
local buf = vim.api.nvim_create_buf(false, true)
|
||||
|
||||
-- Create floating window
|
||||
local win = vim.api.nvim_open_win(buf, opts.enter or false, win_config)
|
||||
|
||||
-- Set buffer options
|
||||
if opts.buftype then
|
||||
vim.bo[buf].buftype = opts.buftype
|
||||
end
|
||||
vim.bo[buf].modifiable = opts.modifiable ~= nil and opts.modifiable or true
|
||||
vim.bo[buf].swapfile = false
|
||||
vim.bo[buf].buflisted = false
|
||||
if opts.bufhidden then
|
||||
vim.bo[buf].bufhidden = opts.bufhidden
|
||||
end
|
||||
|
||||
-- Set filetype if provided
|
||||
if opts.filetype then
|
||||
vim.bo[buf].filetype = opts.filetype
|
||||
end
|
||||
|
||||
-- Set window options
|
||||
if opts.win_options then
|
||||
for option, value in pairs(opts.win_options) do
|
||||
vim.wo[win][option] = value
|
||||
end
|
||||
end
|
||||
|
||||
-- Set keymaps if provided
|
||||
if opts.keymaps then
|
||||
M.set_keymaps(buf, opts.keymaps)
|
||||
end
|
||||
|
||||
-- Set up cleanup autocmd if on_exit callback provided
|
||||
if opts.on_exit then
|
||||
vim.api.nvim_create_autocmd({ "BufWipeout", "BufDelete" }, {
|
||||
buffer = buf,
|
||||
once = true,
|
||||
callback = function()
|
||||
opts.on_exit(buf)
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
-- Set up auto-resize on VimResized
|
||||
vim.api.nvim_create_autocmd("VimResized", {
|
||||
buffer = buf,
|
||||
callback = function()
|
||||
if not vim.api.nvim_win_is_valid(win) then
|
||||
return true -- Remove autocmd if window is invalid
|
||||
end
|
||||
|
||||
-- Recalculate dimensions
|
||||
local new_width = opts.width or math.floor(vim.o.columns * 0.8)
|
||||
local new_height = opts.height or math.floor(vim.o.lines * 0.8)
|
||||
local new_row = opts.row or math.floor((vim.o.lines - new_height) / 2)
|
||||
local new_col = opts.col or math.floor((vim.o.columns - new_width) / 2)
|
||||
|
||||
-- Update window configuration
|
||||
vim.api.nvim_win_set_config(win, {
|
||||
relative = opts.relative or "editor",
|
||||
width = new_width,
|
||||
height = new_height,
|
||||
row = new_row,
|
||||
col = new_col,
|
||||
})
|
||||
end,
|
||||
})
|
||||
|
||||
return buf, win
|
||||
end
|
||||
|
||||
--- Close/wipe a buffer safely
|
||||
--- @param buf number Buffer handle
|
||||
--- @param force? boolean Force close (default: true)
|
||||
function M.close(buf, force)
|
||||
if not buf or not vim.api.nvim_buf_is_valid(buf) then
|
||||
return
|
||||
end
|
||||
|
||||
local cmd = force ~= false and "bwipeout!" or "bwipeout"
|
||||
vim.cmd(cmd .. " " .. buf)
|
||||
end
|
||||
|
||||
--- Add keymaps to a buffer
|
||||
--- @param buf number Buffer handle
|
||||
--- @param keymaps jj.core.buffer.keymap[] Array of keymap definitions
|
||||
function M.set_keymaps(buf, keymaps)
|
||||
if not vim.api.nvim_buf_is_valid(buf) then
|
||||
return
|
||||
end
|
||||
|
||||
for _, keymap in ipairs(keymaps) do
|
||||
local modes = keymap.modes or keymap.mode or "n"
|
||||
local lhs = keymap.lhs or keymap[1]
|
||||
local rhs = keymap.rhs or keymap[2]
|
||||
local opts = vim.tbl_extend("force", {
|
||||
buffer = buf,
|
||||
noremap = true,
|
||||
silent = true,
|
||||
}, keymap.opts or {})
|
||||
vim.keymap.set(modes, lhs, rhs, opts)
|
||||
end
|
||||
end
|
||||
|
||||
--- Remove keymaps from a buffer
|
||||
--- @param buf number Buffer handle
|
||||
--- @param keymaps jj.core.buffer.keymap[] Array of keymap definitions with modes and lhs
|
||||
function M.remove_keymaps(buf, keymaps)
|
||||
if not vim.api.nvim_buf_is_valid(buf) then
|
||||
return
|
||||
end
|
||||
|
||||
for _, keymap in ipairs(keymaps) do
|
||||
local modes = keymap.modes or keymap.mode or "n"
|
||||
local lhs = keymap.lhs or keymap[1]
|
||||
|
||||
local modes_list = type(modes) == "table" and modes or { modes }
|
||||
|
||||
for _, mode in ipairs(modes_list) do
|
||||
pcall(vim.keymap.del, mode, lhs, { buffer = buf })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Set buffer as modifiable or not
|
||||
--- @param buf number Buffer handle
|
||||
--- @param modifiable boolean Whether buffer should be modifiable
|
||||
function M.set_modifiable(buf, modifiable)
|
||||
if not vim.api.nvim_buf_is_valid(buf) then
|
||||
return
|
||||
end
|
||||
vim.bo[buf].modifiable = modifiable
|
||||
end
|
||||
|
||||
--- Stop insert mode if in the given buffer if the cursor is currently in that buffer
|
||||
--- @param buf number Buffer handle
|
||||
function M.stop_insert(buf)
|
||||
if not vim.api.nvim_buf_is_valid(buf) then
|
||||
return
|
||||
end
|
||||
if vim.api.nvim_get_current_buf() ~= buf then
|
||||
return
|
||||
end
|
||||
|
||||
vim.cmd("stopinsert")
|
||||
end
|
||||
|
||||
--- Start insert mode in the given buffer if the cursor is currently in that buffer
|
||||
--- @param buf number Buffer handle
|
||||
function M.start_insert(buf)
|
||||
if not vim.api.nvim_buf_is_valid(buf) then
|
||||
return
|
||||
end
|
||||
if vim.api.nvim_get_current_buf() ~= buf then
|
||||
return
|
||||
end
|
||||
vim.cmd("startinsert")
|
||||
end
|
||||
|
||||
return M
|
||||
|
||||
Reference in New Issue
Block a user