94 lines
3.2 KiB
Lua
94 lines
3.2 KiB
Lua
--- Git-aware directory sessions on top of resession's `dirsession` store.
|
|
---
|
|
--- Session identity is `<worktree root>@<ref>`, so every git worktree gets its
|
|
--- own session (worktrees live in distinct directories) and every branch
|
|
--- inside a worktree gets its own layout. Detached HEAD pins to the short
|
|
--- commit; outside a repository the plain cwd is used, matching AstroNvim's
|
|
--- default dirsession naming.
|
|
---
|
|
--- resession sanitizes `/` and `:` in session names, so slashes in worktree
|
|
--- paths and branch names (`feat/foo`) are safe.
|
|
--- @module config.session
|
|
|
|
local M = {}
|
|
|
|
local DIR = "dirsession"
|
|
|
|
--- Run git in `cwd`; nil on failure or empty output.
|
|
--- @param cwd string
|
|
--- @param ... string git arguments
|
|
--- @return string?
|
|
local function git(cwd, ...)
|
|
local cmd = { "git", "-C", cwd }
|
|
for _, arg in ipairs { ... } do
|
|
cmd[#cmd + 1] = arg
|
|
end
|
|
local out = vim.trim(vim.fn.system(cmd))
|
|
if vim.v.shell_error ~= 0 or out == "" then return nil end
|
|
return out
|
|
end
|
|
|
|
--- Session name for the current working directory.
|
|
--- @return string
|
|
function M.name()
|
|
local cwd = vim.fn.getcwd()
|
|
local root = git(cwd, "rev-parse", "--show-toplevel")
|
|
if not root then return cwd end
|
|
-- `branch --show-current` is empty on detached HEAD; an unborn branch in a
|
|
-- fresh repo has no commit to fall back to.
|
|
local ref = git(cwd, "branch", "--show-current")
|
|
or git(cwd, "rev-parse", "--short", "HEAD")
|
|
or "HEAD"
|
|
return root .. "@" .. ref
|
|
end
|
|
|
|
--- Whether a saved session file exists for `name`.
|
|
--- @param name string
|
|
--- @return boolean
|
|
function M.exists(name)
|
|
return vim.uv.fs_stat(require("resession.util").get_session_file(name, DIR)) ~= nil
|
|
end
|
|
|
|
--- Save the session for the current worktree/branch.
|
|
--- @param opts? table extra resession save options
|
|
function M.save(opts)
|
|
require("resession").save(M.name(), vim.tbl_extend("force", { dir = DIR }, opts or {}))
|
|
end
|
|
|
|
--- Load the session for the current worktree/branch.
|
|
--- @param opts? table extra resession load options
|
|
function M.load(opts)
|
|
require("resession").load(M.name(), vim.tbl_extend("force", { dir = DIR }, opts or {}))
|
|
end
|
|
|
|
--- Open the picker used when there is nothing to restore: git files inside a
|
|
--- repository, plain file search outside one.
|
|
local function pick_files()
|
|
local snacks = require "snacks"
|
|
local transform = require("config.ignore_patterns").create_picker_filter()
|
|
if snacks.git.get_root() then
|
|
snacks.picker.git_files { transform = transform }
|
|
else
|
|
snacks.picker.files { transform = transform }
|
|
end
|
|
end
|
|
|
|
--- VimEnter entry point: show neo-tree, then restore this worktree/branch
|
|
--- session, or fall back to the file picker when none has been saved yet.
|
|
---
|
|
--- Exception: when nvim was started with file arguments (`nvim foo.lua`, a
|
|
--- directory, or `-`) the requested buffer is what the user asked for, so
|
|
--- neither the tree, the session nor the picker may take the screen.
|
|
function M.restore()
|
|
if vim.fn.argc(-1) > 0 then return end
|
|
local restored = M.exists(M.name())
|
|
if restored then M.load { silence_errors = true } end
|
|
-- defer so the tree and picker open after startup finished drawing the UI
|
|
vim.schedule(function()
|
|
vim.cmd "Neotree show" -- no focus: the restored buffer / picker keeps it
|
|
if not restored then pick_files() end
|
|
end)
|
|
end
|
|
|
|
return M
|