281 lines
7.0 KiB
Lua
281 lines
7.0 KiB
Lua
local utils = require("persisted.utils")
|
|
|
|
local M = {}
|
|
|
|
local config
|
|
local start_args = vim.fn.argc() > 0 or vim.g.started_with_stdin
|
|
|
|
local e = vim.fn.fnameescape
|
|
local uv = vim.uv or vim.loop
|
|
|
|
---Fire an event
|
|
---@param event string
|
|
function M.fire(event)
|
|
vim.api.nvim_exec_autocmds("User", { pattern = "Persisted" .. event })
|
|
end
|
|
|
|
---Get the current session for the current working directory and git branch
|
|
---@param opts? {branch?: boolean}
|
|
---@return string
|
|
function M.current(opts)
|
|
opts = opts or {}
|
|
local name = utils.make_fs_safe(vim.fn.getcwd())
|
|
|
|
if config.use_git_branch and opts.branch ~= false then
|
|
local branch = M.branch()
|
|
if branch then
|
|
branch = utils.make_fs_safe(branch)
|
|
name = name .. "@@" .. branch
|
|
end
|
|
end
|
|
|
|
return config.save_dir .. name .. ".vim"
|
|
end
|
|
|
|
---Automatically load the session for the current dir
|
|
---@param opts? { force?: boolean }
|
|
function M.autoload(opts)
|
|
opts = opts or {}
|
|
|
|
if not opts.force and start_args then
|
|
return
|
|
end
|
|
|
|
if config.autoload and M.allowed_dir() then
|
|
M.load({ autoload = true })
|
|
end
|
|
end
|
|
|
|
---Load a session
|
|
---@param opts? { last?: boolean, autoload?: boolean, session?: string }
|
|
function M.load(opts)
|
|
opts = opts or {}
|
|
local session
|
|
|
|
vim.api.nvim_create_autocmd("SessionLoadPost", {
|
|
callback = function()
|
|
if type(config.load_post) == "function" then
|
|
config.load_post()
|
|
end
|
|
M.fire("LoadPost")
|
|
return true -- returning deletes autocmd after fired
|
|
end,
|
|
})
|
|
if opts.last then
|
|
session = M.last()
|
|
elseif opts.session then
|
|
session = opts.session
|
|
else
|
|
session = M.current()
|
|
if vim.fn.filereadable(session) == 0 then
|
|
session = M.current({ branch = false })
|
|
end
|
|
end
|
|
|
|
if session and vim.fn.filereadable(session) ~= 0 then
|
|
vim.g.persisting_session = not config.follow_cwd and session or nil
|
|
vim.g.persisted_loaded_session = session
|
|
|
|
M.fire("LoadPre")
|
|
if type(config.load_pre) == "function" then
|
|
config.load_pre()
|
|
end
|
|
vim.cmd("silent! source " .. e(session))
|
|
elseif opts.autoload and type(config.on_autoload_no_session) == "function" then
|
|
config.on_autoload_no_session()
|
|
end
|
|
|
|
if config.autostart and M.allowed_dir() and not start_args then
|
|
M.start()
|
|
end
|
|
end
|
|
|
|
---Start a session
|
|
function M.start()
|
|
vim.api.nvim_create_autocmd("VimLeavePre", {
|
|
group = vim.api.nvim_create_augroup("Persisted", { clear = true }),
|
|
callback = function()
|
|
M.save()
|
|
end,
|
|
})
|
|
|
|
vim.g.persisting = true
|
|
M.fire("Start")
|
|
end
|
|
|
|
---Stop a session
|
|
function M.stop()
|
|
vim.g.persisting = false
|
|
pcall(vim.api.nvim_del_augroup_by_name, "Persisted")
|
|
M.fire("Stop")
|
|
end
|
|
|
|
vim.api.nvim_create_autocmd("SessionWritePost", {
|
|
callback = function()
|
|
if type(config.save_post) == "function" then
|
|
config.save_post()
|
|
end
|
|
M.fire("SavePost")
|
|
-- return true -- returning true deletes autocmd after fired
|
|
end,
|
|
})
|
|
|
|
---Save the session
|
|
---@param opts? { force?: boolean, session?: string }
|
|
function M.save(opts)
|
|
vim.schedule(function()
|
|
opts = opts or {}
|
|
|
|
-- Do not save the session if should_save evals to false...unless it's forced
|
|
if type(config.should_save) == "function" and not config.should_save() and not opts.force then
|
|
M.fire("SavePost")
|
|
return
|
|
end
|
|
|
|
M.fire("SavePre")
|
|
if type(config.save_pre) == "function" then
|
|
config.save_pre()
|
|
end
|
|
vim.api.nvim_command("wa")
|
|
vim.api.nvim_command("mksession! " .. e(opts.session or vim.g.persisting_session or M.current()))
|
|
end)
|
|
end
|
|
|
|
---Delete the current session
|
|
---@param opts? { session?: string }
|
|
function M.delete(opts)
|
|
opts = opts or {}
|
|
local session = opts.session or M.current()
|
|
|
|
if session and uv.fs_stat(session) ~= 0 then
|
|
M.fire("DeletePre")
|
|
vim.schedule(function()
|
|
M.stop()
|
|
vim.fn.delete(vim.fn.expand(session))
|
|
end)
|
|
M.fire("DeletePost")
|
|
end
|
|
end
|
|
|
|
---Get the current Git branch
|
|
---@return string?
|
|
function M.branch()
|
|
if uv.fs_stat(".git") then
|
|
local branch = vim.fn.systemlist("git branch --show-current")[1]
|
|
return vim.v.shell_error == 0 and branch or nil
|
|
end
|
|
end
|
|
|
|
-- Switch sessions
|
|
---@param session_file_path string
|
|
function M.switch(session_file_path)
|
|
vim.api.nvim_create_autocmd("User", {
|
|
pattern = "PersistedSavePost",
|
|
callback = function()
|
|
vim.schedule(function()
|
|
-- for _, buf in ipairs(vim.api.nvim_list_bufs()) do
|
|
-- if vim.api.nvim_buf_is_valid(buf) then
|
|
-- vim.api.nvim_buf_delete(buf, {})
|
|
-- end
|
|
-- end
|
|
vim.api.nvim_command("%bd")
|
|
M.load({ session = session_file_path })
|
|
end)
|
|
return true
|
|
end,
|
|
})
|
|
|
|
M.save({ session = vim.g.persisted_loaded_session })
|
|
end
|
|
|
|
---Select a session to load
|
|
function M.select()
|
|
---@type { session: string, dir: string, branch?: string }[]
|
|
local items = {}
|
|
local found = {} ---@type table<string, boolean>
|
|
for _, session in ipairs(M.list()) do
|
|
if uv.fs_stat(session) then
|
|
local file = session:sub(#M.config.save_dir + 1, -5)
|
|
local dir, branch = unpack(vim.split(file, "@@", { plain = true }))
|
|
dir = dir:gsub("%%", "/")
|
|
if jit.os:find("Windows") then
|
|
dir = dir:gsub("^(%w)/", "%1:/")
|
|
end
|
|
if not found[dir .. (branch or "")] then
|
|
found[dir .. (branch or "")] = true
|
|
items[#items + 1] = { session = session, dir = dir, branch = branch }
|
|
end
|
|
end
|
|
end
|
|
vim.ui.select(items, {
|
|
prompt = "Select a session: ",
|
|
format_item = function(item)
|
|
local name = vim.fn.fnamemodify(item.dir, ":p:~")
|
|
if item.branch then
|
|
name = name .. " (" .. item.branch .. ")"
|
|
end
|
|
return name
|
|
end,
|
|
}, function(item)
|
|
if item then
|
|
M.switch(item.session)
|
|
end
|
|
end)
|
|
end
|
|
|
|
---Determines whether to load, start or stop a session
|
|
function M.toggle()
|
|
M.fire("Toggle")
|
|
if vim.g.persisting == nil then
|
|
return M.load()
|
|
end
|
|
if vim.g.persisting then
|
|
return M.stop()
|
|
end
|
|
return M.start()
|
|
end
|
|
|
|
---Allow autosaving and autoloading for the given dir?
|
|
---@param opts? {dir?: string}
|
|
---@return boolean
|
|
function M.allowed_dir(opts)
|
|
opts = opts or {}
|
|
local dir = opts.dir or vim.fn.getcwd()
|
|
|
|
return (next(config.allowed_dirs) and utils.dirs_match(dir, config.allowed_dirs) or true)
|
|
and not (next(config.ignored_dirs) and utils.dirs_match(dir, config.ignored_dirs) or false)
|
|
end
|
|
|
|
---Get an ordered list of sessions, sorted by modified time
|
|
---@return string[]
|
|
function M.list()
|
|
local sessions = vim.fn.glob(config.save_dir .. "*.vim", true, true)
|
|
|
|
table.sort(sessions, function(a, b)
|
|
return uv.fs_stat(a).mtime.sec > uv.fs_stat(b).mtime.sec
|
|
end)
|
|
|
|
return sessions
|
|
end
|
|
|
|
---Get the last session that was saved
|
|
---@return string
|
|
function M.last()
|
|
return M.list()[1]
|
|
end
|
|
|
|
---Setup the plugin
|
|
---@param opts? table
|
|
function M.setup(opts)
|
|
config = vim.tbl_deep_extend("force", require("persisted.config"), opts or {})
|
|
M.config = config
|
|
|
|
vim.fn.mkdir(config.save_dir, "p")
|
|
|
|
if config.autostart and M.allowed_dir() and vim.g.persisting == nil and not start_args then
|
|
M.start()
|
|
end
|
|
end
|
|
|
|
return M
|