doc/sessions.txt — rendered from the plugin's own vimdoc
*sessions.txt* Branch- and project-aware Neovim sessions *sessions.nvim* Author: Stefan Bartl Homepage: https://github.com/stefanbartl/sessions.nvim
CONTENTS
1. Introduction ................. |sessions-introduction| 2. Requirements ................. |sessions-requirements| 3. Setup ........................ |sessions-setup| 4. Quick Start .................. |sessions-quick-start| 5. Configuration ................ |sessions-config| 6. Commands ..................... |sessions-commands| 7. Keymaps ...................... |sessions-keymaps| 8. Public API ................... |sessions-api| 9. Session naming ............... |sessions-naming| 10. Metadata ..................... |sessions-metadata| 11. Git integration .............. |sessions-git| 12. Health check ................ |sessions-health|
1. INTRODUCTION
sessions.nvim saves and restores Neovim sessions using only built-in Neovim primitives (|:mksession|/|:source|). Key features: - Branch-aware naming: automatically appends the current git branch to the session name, so switching branches restores a different workspace. - Project-aware naming: detects the project root from markers like.gitand prefixes the session name with the project basename. - Session metadata: writes a companion JSON file with the save timestamp, working directory, branch, and buffer list for statusline/picker use. - Clean save: blacklisted buftypes/filetypes/paths are wiped before|:mksession|so they are never serialised. - E445 fix: modified buffers are hidden (not discarded) before loading a session to avoid the "cannot close, buffer modified" error.
2. REQUIREMENTS
- Neovim 0.9+ - lib.nvim — REQUIRED: the |:Session|/|:LastSession| commands are built on lib.nvim.bindings.usercmd.composer. lib.nvim.notify, lib.nvim.bindings.keymap, and lib.nvim.git stay soft-guarded (used when available, native fallback otherwise).
3. SETUP
Minimal setup:
require("sessions").setup()
With lazy.nvim (lazy-loaded on command use):
{
"stefanbartl/sessions.nvim",
dependencies = { "stefanbartl/lib.nvim" },
cmd = { "Session", "LastSession" },
opts = {},
}
With lazy.nvim (loaded at VimEnter, recommended for autoload/autosave):
{
"stefanbartl/sessions.nvim",
dependencies = { "stefanbartl/lib.nvim" },
event = "VimEnter",
opts = {},
}
With lazy.nvim (eager, needed fornvim +LastSession/nvim '+Session load'command-line args):
{
"stefanbartl/sessions.nvim",
dependencies = { "stefanbartl/lib.nvim" },
lazy = false,
opts = {},
}
Note: Command-line arguments likenvim +LastSessionexecute **before** lazy-loading triggers, so they requirelazy = falsein the plugin spec. For all other workflows,event = "VimEnter"orcmd = { ... }is preferred. lib.nvim is now a REQUIRED dependency (the |:Session| / |:LastSession| commands are built on lib.nvim.bindings.usercmd.composer).
4. QUICK START
Save a session (either via :Session save or autosave on exit):
:Session save
:quit
Restore the session:
# Wherever you left off (project + branch aware) -- no quoting, own command
nvim +LastSession
# The same resolution, spelled as a :Session subcommand -- needs quoting
nvim '+Session load'
# Explicit session name -- needs quoting
nvim '+Session load myntest'
nvim '+Session load myapp_feature-login'
:Session is one command with subcommands, built via
lib.nvim.bindings.usercmd.composer. A bare Neovim CLI +cmd flag is a single shell
word, so any invocation containing a space needs to be quoted as one shell
argument. |:LastSession| is a plain, separate, single-word command
specifically so the single most common case (restore wherever you left
off) never needs quoting -- it resolves exactly like a bare
|:Session-load|, not a hardcoded name (see |sessions-naming|).
Common workflow — branch-aware sessions:
# Work on main branch (auto-named: myapp_main)
git checkout main
nvim '+Session load' # loads myapp_main
# Switch to feature branch (auto-named: myapp_feature-auth)
git checkout feature-auth
nvim '+Session load' # loads myapp_feature-auth (different workspace)
**Important:** To usenvim +LastSession/nvim '+Session load'(command-line arguments), your plugin spec must havelazy = false. See |sessions-setup| for details.
5. CONFIGURATION
Default values (pass to setup(opts)):
{
-- Absolute path for session storage (user-local by default).
root = vim.fn.stdpath("data") .. "/sessions",
-- Fallback session name when auto-resolve yields nothing.
default_name = "last",
-- Append current git branch to the auto-resolved name.
branch_aware = true,
-- Prefix auto-resolved name with the detected project root basename.
project_aware = true,
-- Files searched upward from cwd to detect a project root.
project_markers = {
".git", "pyproject.toml", "package.json",
"Makefile", "Cargo.toml", "go.mod",
},
-- Passed to vim.opt.sessionoptions before every save/load.
sessionoptions = "buffers,curdir,tabpages,winsize,help,folds",
-- Rewrite the saved cwd to a portable placeholder, re-anchored to cwd
-- on load. See |sessions-portability| / docs/portability.md.
relative_paths = false,
-- Old-root -> new-root path prefixes translated when loading, for
-- cross-OS/cross-machine sync. See docs/portability.md.
root_remap = {},
-- Auto-load the contextual session when Neovim starts without file args.
-- Set to "ask" to show a floating y/n prompt before loading instead of
-- loading silently.
autoload = false,
-- Auto-save on VimLeavePre (false = disabled).
autosave = true,
-- Autosave target, used when autosave = true:
-- true -- resolve like a bare :Session save (branch/project-aware
-- when configured), so different projects don't clobber
-- each other's autosave.
-- "name" -- pin autosave to this one fixed name regardless of project.
-- false -- no autosave despite autosave = true.
autosave_name = true,
-- Write a .{name}.json companion file next to each .vim session.
metadata = true,
-- Callbacks invoked after save/load (errors swallowed via pcall).
hooks = {
on_save = nil, -- fun(name: string, path: string)
on_load = nil, -- fun(name: string, path: string)
},
-- Buffers matching these criteria are wiped before :mksession.
blacklist = {
buftypes = { "quickfix", "nofile", "prompt" },
filetypes = { "gitcommit", "gitrebase" },
paths = { "/tmp/", "/private/tmp/" },
-- %TEMP% is automatically added on Windows.
},
-- Normal-mode keymaps. Disabled by default (keymaps = false).
-- Set to a table to enable specific keymaps.
keymaps = false,
-- or: { save = "<leader>ssa", load = "<leader>slo", ... }
-- Register a which-key group label for the keymap prefix, if
-- which-key is installed and at least one keymap is configured.
which_key = { enable = true },
}
6. COMMANDS
One command, :Session <subcommand> (built via lib.nvim.bindings.usercmd.composer, with <Tab> completion — session-name args complete dynamically from the current saved-session list), plus a standalone |:LastSession| convenience command. *:Session* :Session save [name] *:Session-save* Save the current session. If [name] is omitted the name is auto-resolved from project root and/or git branch (see |sessions-naming|). :Session save-timestamp *:Session-save-timestamp* Save a snapshot with a timestamp suffix (e.g.sess-20251231-120000). Useful for creating restore points before a risky refactor. :Session load [name] *:Session-load* Load a session. [name] supports tab completion from saved sessions. If [name] is omitted, the current project/branch's own session is preferred when it exists (the same auto-resolution |:Session-save| uses), else the remembered last-loaded/saved session, elsedefault_name-- see |sessions-naming|. :Session delete <name> *:Session-delete* Permanently delete a session file (and its metadata companion). :Session rename <old> <new> *:Session-rename* Rename a session, including its metadata file. :Session list *:Session-list* Print all saved sessions, annotated with the active session (*), save timestamp, and branch name from metadata when available. :Session current *:Session-current* Print the name of the currently active session, or report that none is active. :Session toggle-track [name] *:Session-toggle-track* Togglegit update-index --skip-worktreeon the named session file. Useful when |sessions-config|rootlives inside a git-tracked config directory: mark transient sessions as skip-worktree so they are not committed, but keep permanent sessions tracked. If [name] is omitted, the currently active session (ordefault_name) is used. Therootdirectory must be inside a git repository. *:LastSession* :LastSession Load wherever you left off -- the same resolution as a bare |:Session-load| (no [name]): the current project/branch's own session first, else the remembered last-loaded/saved one, elsedefault_name(see |sessions-naming|). A plain, separate, single-word command specifically sonvim +LastSessionworks without CLI-arg quoting — the generalnvim '+Session load ...'form needs quoting since it is multiple shell words.
7. KEYMAPS
Keymaps are **disabled by default**. Enable them in setup:
require("sessions").setup({
keymaps = {
save = "<leader>ssa",
load = "<leader>slo",
save_ts = "<leader>sst",
list = "<leader>sli",
},
})
To disable individual keymaps:
keymaps = {
save = "<leader>ssa",
load = false, -- disabled
save_ts = "<leader>sst",
list = "<leader>sli",
}
8. PUBLIC API
All functions return explicit booleans plus a result/error string so callers
can react without parsing notification messages.
require("sessions").setup(opts?)
Configure and activate the plugin (idempotent).
require("sessions").save(name?) -> ok, path_or_err
Save a session. Returns (true, path) on success, (false, err) on failure.
require("sessions").load(name?) -> ok, path_or_err, hidden
Load a session. Third return value lists buffer names that had unsaved
changes and were hidden (not discarded) to allow the load.
require("sessions").list() -> string[]
Return absolute paths to all stored .vim session files.
require("sessions").delete(name) -> ok, err
Delete a session and its metadata.
require("sessions").rename(old, new) -> ok, err
Rename a session and its metadata.
require("sessions").current() -> string|nil
Return the name of the active session, or nil. Suitable for statusline
use:
local sessions = require("sessions")
-- in your statusline component:
local name = sessions.current()
return name and (" " .. name) or ""
require("sessions").metadata(name) -> Sessions.Meta|nil
Return the metadata table for a saved session, or nil if no metadata
file exists. Metadata structure:
{ saved_at = "2025-12-31T12:00:00Z", cwd = "/path/to/project",
branch = "feature-branch", buffers = [...] }
9. SESSION NAMING
When no explicit name is passed to |:Session-save| or |:Session-load|, the name is derived from the current context: project_aware=true, branch_aware=true (default):{project}_{branch}e.g.myapp_feature-loginproject_aware=true, branch_aware=false:{project}e.g.myappproject_aware=false, branch_aware=true:{branch}e.g.feature-loginboth false:default_namee.g.lastNot inside a git repo / no project marker found: Falls back towarddefault_name. Characters unsafe for filenames (slashes, spaces, etc.) are replaced with-or_. Example:feature/login→feature-login,my project→my-project. The table above governs |:Session-save| with no [name], and |:Session-load| when a [name] *is* given. A bare |:Session-load| (no [name]) and autoload resolve in priority order instead: 1. The auto-resolved name for the CURRENT project/branch (the table above) -- but only when that session's file already exists. 2. The remembered last-loaded/saved session:.state.jsoninroot, one pointer shared by every project -- wins only when (1) has nothing to offer (naming is off, or this project has never been saved). 3.default_name, if neither resolves to an existing file. (1) is checked first deliberately: preferring the remembered pointer unconditionally meant opening a *different* project withautoload = truecould silently load whatever session was last touched elsewhere.
10. METADATA
Whenmetadata = true(default), sessions.nvim writes a hidden JSON file alongside each.vimsession file: {root}/.{name}.json Example contents: { "saved_at": "2025-12-31T12:00:00Z", "cwd": "/home/user/myproject", "branch": "feature/login", "buffers": ["/home/user/myproject/src/main.lua", ...] } The metadata is used by |:Session-list| to display timestamps and branches. You can read it programmatically via require("sessions").metadata(name).
11. GIT INTEGRATION
|:Session-toggle-track| is designed for the workflow whererootpoints inside a git-tracked config directory (e.g.stdpath("config") .. "/sessions"). Scenario: you want to sync named sessions across machines via git but exclude thelastauto-save, becauselastcontains absolute paths that only exist on one machine. 1. Set root to a path inside your config repo: root = vim.fn.stdpath("config") .. "/sessions" 2. Commit your named sessions normally. 3. Run:Session toggle-track lastto marklast.vimas skip-worktree. The file stays on disk but git ignores local changes to it. 4. On a different machine, run:Session toggle-track lastagain to start tracking it there too if you want.
12. HEALTH CHECK
Run:
:checkhealth sessions
Reports: Neovim version, optional dependency availability, active configuration, session root status, session count, and command registration.