A replacement for :mksession
with a better API
- No magic behavior. Only does what you tell it.
- Supports tab-scoped sessions.
- Extensive customizability in what gets saved/restored.
- Easy to write extensions for other plugins.
- Neovim 0.8+
resession supports all the usual plugin managers
lazy.nvim
{
'stevearc/resession.nvim',
opts = {},
}
Packer
require("packer").startup(function()
use({
"stevearc/resession.nvim",
config = function()
require("resession").setup()
end,
})
end)
Paq
require("paq")({
{ "stevearc/resession.nvim" },
})
Neovim native package
git clone --depth=1 https://github.com/stevearc/resession.nvim.git \
"${XDG_DATA_HOME:-$HOME/.local/share}"/nvim/site/pack/resession/start/resession.nvim
local resession = require("resession")
resession.setup()
-- Resession does NOTHING automagically, so we have to set up some keymaps
vim.keymap.set("n", "<leader>ss", resession.save)
vim.keymap.set("n", "<leader>sl", resession.load)
vim.keymap.set("n", "<leader>sd", resession.delete)
Now you can use <leader>ss
to save a session. When you want to load a session, use <leader>sl
.
vim.api.nvim_create_autocmd("VimLeavePre", {
callback = function()
-- Always save a special session named "last"
resession.save("last")
end,
})
When you are attached to a session (have saved or loaded a session), use this config to periodically re-save that session in the background.
require("resession").setup({
autosave = {
enabled = true,
interval = 60,
notify = true,
},
})
Load a dir-specific session when you open Neovim, save it when you exit.
vim.api.nvim_create_autocmd("VimEnter", {
callback = function()
-- Only load the session if nvim was started with no args
if vim.fn.argc(-1) == 0 then
-- Save these to a different directory, so our manual sessions don't get polluted
resession.load(vim.fn.getcwd(), { dir = "dirsession", silence_errors = true })
end
end,
})
vim.api.nvim_create_autocmd("VimLeavePre", {
callback = function()
resession.save(vim.fn.getcwd(), { dir = "dirsession", notify = false })
end,
})
Same as above, but have a separate session for each git branch in a directory.
local function get_session_name()
local name = vim.fn.getcwd()
local branch = vim.trim(vim.fn.system("git branch --show-current"))
if vim.v.shell_error == 0 then
return name .. branch
else
return name
end
end
vim.api.nvim_create_autocmd("VimEnter", {
callback = function()
-- Only load the session if nvim was started with no args
if vim.fn.argc(-1) == 0 then
resession.load(get_session_name(), { dir = "dirsession", silence_errors = true })
end
end,
})
vim.api.nvim_create_autocmd("VimLeavePre", {
callback = function()
resession.save(get_session_name(), { dir = "dirsession", notify = false })
end,
})
When saving a session, only save the current tab
-- Bind `save_tab` instead of `save`
vim.keymap.set("n", "<leader>ss", resession.save_tab)
vim.keymap.set("n", "<leader>sl", resession.load)
vim.keymap.set("n", "<leader>sd", resession.delete)
This will save only the current tabpage layout, but will save all of the open buffers. You can provide a filter to exclude buffers. For example, if you are using :tcd
to have tabs open for different directories, this will only save buffers in the current tabpage directory:
require("resession").setup({
tab_buf_filter = function(tabpage, bufnr)
local dir = vim.fn.getcwd(-1, vim.api.nvim_tabpage_get_number(tabpage))
-- ensure dir has trailing /
dir = dir:sub(-1) ~= "/" and dir .. "/" or dir
return vim.startswith(vim.api.nvim_buf_get_name(bufnr), dir)
end,
})
To create an extension, create a file in your runtimepath at lua/resession/extensions/myplugin.lua
. Add the following contents:
local M = {}
---Get the saved data for this extension
---@param opts resession.Extension.OnSaveOpts Information about the session being saved
---@return any
M.on_save = function(opts)
return {}
end
---Restore the extension state
---@param data The value returned from on_save
M.on_pre_load = function(data)
-- This is run before the buffers, windows, and tabs are restored
end
---Restore the extension state
---@param data The value returned from on_save
M.on_post_load = function(data)
-- This is run after the buffers, windows, and tabs are restored
end
---Called when resession gets configured
---This function is optional
---@param data table The configuration data passed in the config
M.config = function(data)
--
end
---Check if a window is supported by this extension
---This function is optional, but if provided save_win and load_win must
---also be present.
---@param winid integer
---@param bufnr integer
---@return boolean
M.is_win_supported = function(winid, bufnr)
return true
end
---Save data for a window
---@param winid integer
---@return any
M.save_win = function(winid)
-- This is used to save the data for a specific window that contains a non-file buffer (e.g. a filetree).
return {}
end
---Called with the data from save_win
---@param winid integer
---@param config any
---@return integer|nil If the original window has been replaced, return the new ID that should replace it
M.load_win = function(winid, config)
-- Restore the window from the config
end
return M
Then to activate it, users can add the extension to their call to setup
:
require("resession").setup({
extensions = {
myplugin = {
-- these args will get passed in to M.config()
},
},
})
For tab-scoped sessions, the on_save
and on_load
methods of extensions will be disabled by default. There is a special config argument always available that can override this:
require("resession").setup({
extensions = {
myplugin = {
enable_in_tab = true,
},
},
})
Refer to the quickfix extension for a complete example.
require("resession").setup({
-- Options for automatically saving sessions on a timer
autosave = {
enabled = false,
-- How often to save (in seconds)
interval = 60,
-- Notify when autosaved
notify = true,
},
-- Save and restore these options
options = {
"binary",
"bufhidden",
"buflisted",
"cmdheight",
"diff",
"filetype",
"modifiable",
"previewwindow",
"readonly",
"scrollbind",
"winfixheight",
"winfixwidth",
},
-- Custom logic for determining if the buffer should be included
buf_filter = require("resession").default_buf_filter,
-- Custom logic for determining if a buffer should be included in a tab-scoped session
tab_buf_filter = function(tabpage, bufnr)
return true
end,
-- The name of the directory to store sessions in
dir = "session",
-- Show more detail about the sessions when selecting one to load.
-- Disable if it causes lag.
load_detail = true,
-- List order ["modification_time", "creation_time", "filename"]
load_order = "modification_time",
-- Configuration for extensions
extensions = {
quickfix = {},
},
})
setup(config)
Initialize resession with configuration options
Param | Type | Desc |
---|---|---|
config | table |
load_extension(name, opts)
Load an extension some time after calling setup()
Param | Type | Desc |
---|---|---|
name | string |
Name of the extension |
opts | table |
Configuration options for extension |
get_current(): string
Get the name of the current session
Returns:
Type | Desc |
---|---|
string | ? |
get_current_session_info(): nil|resession.SessionInfo
Get information about the current session
detach()
Detach from the current session
list(opts): string[]
List all available saved sessions
Param | Type | Desc | |
---|---|---|---|
opts | nil|resession.ListOpts |
||
dir | nil|string |
Name of directory to list (overrides config.dir) |
delete(name, opts)
Delete a saved session
Param | Type | Desc | |
---|---|---|---|
name | nil|string |
If not provided, prompt for session to delete | |
opts | nil|resession.DeleteOpts |
||
dir | nil|string |
Name of directory to delete from (overrides config.dir) |
save(name, opts)
Save a session to disk
Param | Type | Desc | |
---|---|---|---|
name | nil|string |
||
opts | nil|resession.SaveOpts |
||
attach | nil|boolean |
Stay attached to session after saving (default true) | |
notify | nil|boolean |
Notify on success | |
dir | nil|string |
Name of directory to save to (overrides config.dir) |
save_tab(name, opts)
Save a tab-scoped session
Param | Type | Desc | |
---|---|---|---|
name | nil|string |
If not provided, will prompt user for session name | |
opts | nil|resession.SaveOpts |
||
attach | nil|boolean |
Stay attached to session after saving (default true) | |
notify | nil|boolean |
Notify on success | |
dir | nil|string |
Name of directory to save to (overrides config.dir) |
save_all(opts)
Save all current sessions to disk
Param | Type | Desc | |
---|---|---|---|
opts | nil|table |
||
notify | nil|boolean |
load(name, opts)
Load a session
Param | Type | Desc | |
---|---|---|---|
name | nil|string |
||
opts | nil|resession.LoadOpts |
||
attach | nil|boolean |
Stay attached to session after loading (default true) | |
reset | nil|boolean|"auto" |
Close everything before loading the session (default "auto") | |
silence_errors | nil|boolean |
Don't error when trying to load a missing session | |
dir | nil|string |
Name of directory to load from (overrides config.dir) |
Note:
The default value of `reset = "auto"` will reset when loading a normal session, but _not_ when loading a tab-scoped session.
add_hook(name, callback)
Add a callback that runs at a specific time
Param | Type | Desc |
---|---|---|
name | "pre_save"|"post_save"|"pre_load"|"post_load" |
|
callback | fun(...: any) |
remove_hook(name, callback)
Remove a hook callback
Param | Type | Desc |
---|---|---|
name | "pre_save"|"post_save"|"pre_load"|"post_load" |
|
callback | fun(...: any) |
default_buf_filter(bufnr): boolean
The default config.buf_filter (takes all buflisted files with "", "acwrite", or "help" buftype)
Param | Type | Desc |
---|---|---|
bufnr | integer |
is_loading(): boolean
Returns true if a session is currently being loaded
- quickfix (built-in)
- aerial.nvim
- overseer.nvim
Q: Why another session plugin?
A: All the other plugins use :mksession
under the hood
Q: Why don't you want to use :mksession
?
A: While it's amazing that this feature is built-in to vim, and it does an impressively good job for most situations, it is very difficult to customize. If :help sessionoptions
covers your use case, then you're golden. If you want anything else, you're out of luck.