diff --git a/README.md b/README.md index b0e7f63..16aaf27 100644 --- a/README.md +++ b/README.md @@ -7,14 +7,14 @@ The plugin was forked from the fantastic [Persistence.nvim](https://github.com/f ## ✨ Features - Automatically saves the active session under `.local/share/nvim/sessions` on exiting Neovim -- Simple API to restore the current or last session +- Supports auto saving and loading of sessions with allowed/ignored directories +- Simple API to save/stop/restore/delete/list the current session(s) - Support for sessions across git branches -- Specify custom directory to save sessions -- Stop or even delete the current session +- Specify custom directory to save/load sessions from ## ⚡️ Requirements -- Neovim >= 0.5.0 +- Neovim >= 0.6.0 ## 📦 Installation @@ -55,9 +55,11 @@ The plugin comes with the following defaults: { dir = vim.fn.expand(vim.fn.stdpath("data") .. "/sessions/"), -- directory where session files are saved use_git_branch = false, -- create session files based on the branch of the git enabled repository - autosave = true, -- automatically save session files - autoload = false, -- automatically load the current session on Neovim startup + autosave = true, -- automatically save session files when exiting Neovim + autoload = false, -- automatically load the session for the cwd on Neovim startup options = { "buffers", "curdir", "tabpages", "winsize" }, -- session options used for saving + allowed_dirs = nil, -- table of dirs that the plugin will auto-save and auto-load from + ignored_dirs = nil, -- table of dirs that are ignored when auto-saving and auto-loading before_save = function() end, -- function to run before the session is saved to disk after_save = function() end, -- function to run after the session is saved to disk } @@ -65,13 +67,17 @@ The plugin comes with the following defaults: These can be overwritten by calling the `setup` method and passing in the appropriate value. +> **Note:** `autosave` and `autoload` may not work if you've lazy loaded the plugin. + ## 🚀 Usage -The plugin works well with others like [vim-startify](https://github.com/mhinz/vim-startify) or [dashboard](https://github.com/glepnir/dashboard-nvim). It will never restore a session automatically but the commands below, or a custom autocmd, may be used. +The plugin is designed to work with startup screens like [vim-startify](https://github.com/mhinz/vim-startify) or [dashboard](https://github.com/glepnir/dashboard-nvim) out of the box. It will never load a session automatically by default. + +To use the plugin, the commands below may be used: ### Commands -- `SessionStart` - Start a session. Useful if `autosave = false` +- `SessionStart` - Start recording a session. Useful if `autosave = false` - `SessionStop` - Stop recording a session - `SessionSave` - Save the current session - `SessionLoad` - Load the session for the current directory and current branch if `git_use_branch = true` @@ -79,6 +85,8 @@ The plugin works well with others like [vim-startify](https://github.com/mhinz/v - `SessionDelete` - Delete the current session - `SessionToggle` - Determines whether to load, start or stop a session +> **Note:** The author only binds `SessionToggle` to a keymap for simplicity. + ### Callbacks The plugin allows for _before_ and _after_ callbacks to be executed relative to the session. This is achieved via the `before_save` and `after_save` configuration options. diff --git a/lua/persisted/config.lua b/lua/persisted/config.lua index dce688a..bf1610c 100644 --- a/lua/persisted/config.lua +++ b/lua/persisted/config.lua @@ -4,9 +4,11 @@ local M = {} local defaults = { dir = vim.fn.expand(vim.fn.stdpath("data") .. "/sessions/"), -- directory where session files are saved use_git_branch = false, -- create session files based on the branch of the git enabled repository - autosave = true, -- automatically save session files - autoload = false, -- automatically load the session in the cwd on Neovim startup + autosave = true, -- automatically save session files when exiting Neovim + autoload = false, -- automatically load the session for the cwd on Neovim startup options = { "buffers", "curdir", "tabpages", "winsize" }, -- session options used for saving + allowed_dirs = nil, -- table of dirs that the plugin will auto-save and auto-load from + ignored_dirs = nil, -- table of dirs that are ignored for auto-saving and auto-loading before_save = function() end, -- function to run before the session is saved to disk after_save = function() end, -- function to run after the session is saved to disk } diff --git a/lua/persisted/init.lua b/lua/persisted/init.lua index c1952e8..08a1b3c 100644 --- a/lua/persisted/init.lua +++ b/lua/persisted/init.lua @@ -3,7 +3,18 @@ local config = require("persisted.config") local M = {} local e = vim.fn.fnameescape +local echo = vim.api.nvim_echo +local echoerr = function(msg, error) + echo({ + { "[persisted.nvim]: ", "ErrorMsg" }, + { msg, "WarningMsg" }, + { error, "Normal" }, + }, true, {}) +end + +---Setup the plugin's commands +---@return nil local function setup_commands() vim.cmd([[ command! SessionStart :lua require("persisted").start() @@ -16,16 +27,55 @@ local function setup_commands() ]]) end -function M.get_current() - local pattern = "/" - if vim.fn.has("win32") == 1 then - pattern = "[\\:]" +---Check if a target directory exists in a given table +---@param dir_target string +---@param dir_table table +---@return boolean +local function dirs_match(dir_target, dir_table) + for _, dir in pairs(dir_table) do + dir = string.gsub(vim.fn.expand(dir), "/+$", "") + if dir_target == dir then + return true + end end - local name = vim.fn.getcwd():gsub(pattern, "%%") - return config.options.dir .. name .. get_branch() .. ".vim" + return false end -function get_branch() +---Does the current working directory allow for the auto-saving and loading? +---@return boolean +local function allow_dir() + local allowed_dirs = config.options.allowed_dirs + + if allowed_dirs == nil then + return true + end + return dirs_match(vim.fn.getcwd(), allowed_dirs) +end + +---Is the current working directory ignored for auto-saving and loading? +---@return boolean +local function ignore_dir() + local ignored_dirs = config.options.ignored_dirs + + if ignored_dirs == nil then + return false + end + return dirs_match(vim.fn.getcwd(), ignored_dirs) +end + +---Get the session that was saved last +---@return string +local function get_last() + local sessions = M.list() + table.sort(sessions, function(a, b) + return vim.loop.fs_stat(a).mtime.sec > vim.loop.fs_stat(b).mtime.sec + end) + return sessions[1] +end + +---Get the current Git branch +---@return string +local function get_branch() local git_enabled = (vim.fn.isdirectory(vim.fn.getcwd() .. "/.git") == 1) if config.options.use_git_branch and git_enabled then @@ -42,25 +92,52 @@ function get_branch() return "" end -function M.get_last() - local sessions = M.list() - table.sort(sessions, function(a, b) - return vim.loop.fs_stat(a).mtime.sec > vim.loop.fs_stat(b).mtime.sec - end) - return sessions[1] +---Get the current session for the current working directory and git branch +---@return string +local function get_current() + local pattern = "/" + if vim.fn.has("win32") == 1 then + pattern = "[\\:]" + end + local name = vim.fn.getcwd():gsub(pattern, "%%") + return config.options.dir .. name .. get_branch() .. ".vim" end +---Setup the plugin based on the intersect of the default and the user's config +---@param opts table +---@return nil function M.setup(opts) config.setup(opts) setup_commands() - if config.options.autoload then + if config.options.autoload and (allow_dir() and not ignore_dir()) then M.load() end - if config.options.autosave then + if config.options.autosave and (allow_dir() and not ignore_dir() and vim.g.persisting == nil) then M.start() end end +---Load a session +---@param opt table +---@return nil +function M.load(opt) + opt = opt or {} + local session = opt.last and get_last() or get_current() + + if session and vim.fn.filereadable(session) ~= 0 then + local ok, result = pcall(vim.cmd, "source " .. e(session)) + if not ok then + echoerr("Error loading the session! ", result) + end + end + + if config.options.autosave and (allow_dir() and not ignore_dir()) then + M.start() + end +end + +---Start recording a session and write it to disk when exiting Neovim +---@return nil function M.start() vim.cmd([[ augroup Persisted @@ -71,6 +148,8 @@ function M.start() vim.g.persisting = true end +---Stop recording a session +---@return nil function M.stop() vim.cmd([[ autocmd! Persisted @@ -79,39 +158,38 @@ function M.stop() vim.g.persisting = false end +---Save the session to disk +---@return nil function M.save() config.options.before_save() local tmp = vim.o.sessionoptions vim.o.sessionoptions = table.concat(config.options.options, ",") - vim.cmd("mks! " .. e(M.get_current())) + vim.cmd("mks! " .. e(get_current())) vim.o.sessionoptions = tmp vim.g.persisting = true config.options.after_save() end +---Delete the current session from disk +---@return nil function M.delete() - local session = M.get_current() + local session = get_current() if session and vim.loop.fs_stat(session) ~= 0 then M.stop() vim.fn.system("rm " .. e(session)) end end -function M.load(opt) - opt = opt or {} - local session = opt.last and M.get_last() or M.get_current() - if session and vim.fn.filereadable(session) ~= 0 then - vim.cmd("source " .. e(session)) - vim.g.persisting = true - end -end - +---List all of the sessions in the session directory +---@return table function M.list() return vim.fn.glob(config.options.dir .. "*.vim", true, true) end +---Determines whether to load, start or stop a session +---@return function function M.toggle() if vim.g.persisting == nil then return M.load()