···11-remembers where you left off
11+# nivvie :seedline:
22+33+tiny neovim session keeper, keeping your place safe
44+55+## what it does ๐
66+77+nivvie remembers where you left off and restores it when you return (session
88+handling). it keeps sessions stored safely and lets you call on them whenever
99+you need, without clutter or extra ritual.
1010+1111+## why nivvie ๐
1212+1313+there are larger session managers with more features. nivvie is a small and
1414+quiet companion, meant for when you want something clean, simple, and reliable.
1515+1616+## features ๐ธ
1717+1818+- automatic save and restore (no extra steps)
1919+- manual save, load, and delete commands (direct control)
2020+- minimal configuration (defaults just work)
2121+- lightweight and unobtrusive (stays out of the way)
2222+2323+## installation ๐ฟ
2424+2525+add nivvie with your favourite plugin manager.
2626+2727+###### `vim.pack`
2828+2929+```lua
3030+vim.pack.add({ "comfysage/nivvie.nvim" })
3131+```
3232+3333+###### `lazy.nvim`
3434+3535+```lua
3636+{
3737+ "comfysage/nivvie.nvim",
3838+ lazy = false, -- nivvie takes care of its own lazy loading
3939+ config = function()
4040+ require("nivvie").setup()
4141+ end
4242+}
4343+```
4444+4545+## usage ๐ผ
4646+4747+sessions are saved automatically when you quit neovim and restored when you open it again. you can also manage sessions yourself:
4848+4949+```
5050+:Nivvie save [name] " save the current session for the current directory or with a custom name
5151+:Nivvie load [name] " load session for the current directory or with a custom name
5252+:Nivvie delete [name] " delete session for the current directory or with a custom name
5353+```
5454+5555+you may also map keys to these commands:
5656+5757+```lua
5858+vim.keymap.set("n", "<leader>ss", ":NivvieSave<CR>")
5959+vim.keymap.set("n", "<leader>sl", ":NivvieLoad<CR>")
6060+```
6161+6262+## configuration ๐พ
6363+6464+nivvie works without configuration (zero setup). you can still adjust its behavior:
6565+6666+```lua
6767+require("nivvie").setup({
6868+ -- where sessions are stored
6969+ session_dir = vim.fn.stdpath("state") .. "/sessions",
7070+ -- save on exit
7171+ autosave = true,
7272+ -- load on start
7373+ autorestore = true,
7474+})
7575+```
+122-40
plugin/nivvie.lua
···11if vim.g.loaded_session then
22- return
22+ return
33end
4455vim.g.loaded_session = true
6677local session = {}
8899+---@private
1010+session.stdin = false
1111+912function session.get_uri()
1010- return string.gsub(vim.fn.getcwd(), "[^a-zA-Z0-9_.-]", function(s)
1111- return "<" .. vim.fn.char2nr(s)
1212- end)
1313+ return string.gsub(vim.fn.getcwd(), '[^a-zA-Z0-9_.-]', function(s)
1414+ return '<' .. vim.fn.char2nr(s)
1515+ end)
1316end
14171515-local sessiondir = vim.g.sessiondir or vim.fs.joinpath(vim.fn.stdpath("state"), "sessions")
1818+local sessiondir = vim.g.sessiondir
1919+ or vim.fs.joinpath(vim.fn.stdpath 'state', 'sessions')
16201717-function session.get_path()
1818- return vim.fs.joinpath(sessiondir, session.get_uri() .. ".vim")
2121+---@param name? string
2222+function session.get_path(name)
2323+ return vim.fs.joinpath(sessiondir, (name or session.get_uri()) .. '.vim')
1924end
20252121-function session.save()
2222- local session_file = session.get_path()
2626+---@param name? string
2727+function session.save(name)
2828+ local session_file = session.get_path(name)
23292424- vim.fn.mkdir(sessiondir, "p")
3030+ vim.fn.mkdir(sessiondir, 'p')
25312626- vim.api.nvim_cmd({
2727- cmd = "mksession",
2828- bang = true,
2929- args = { session_file },
3030- }, {})
3232+ vim.api.nvim_cmd({
3333+ cmd = 'mksession',
3434+ bang = true,
3535+ args = { session_file },
3636+ }, {})
3137end
32383339function session.clean()
3434- vim.iter(ipairs(vim.api.nvim_list_bufs())):each(function(_, bufnr)
3535- if vim.bo[bufnr].buftype ~= "" then
3636- vim.api.nvim_buf_delete(bufnr, { force = true })
3737- end
3838- end)
4040+ vim.iter(ipairs(vim.api.nvim_list_bufs())):each(function(_, bufnr)
4141+ if vim.bo[bufnr].buftype ~= '' then
4242+ vim.api.nvim_buf_delete(bufnr, { force = true })
4343+ end
4444+ end)
3945end
40464141-function session.load()
4242- local path = session.get_path()
4343- if vim.uv.fs_stat(path) then
4444- vim.api.nvim_cmd({
4545- cmd = "source",
4646- args = { path },
4747- }, {})
4848- vim.api.nvim_exec_autocmds("SessionLoadPost", {})
4949- end
4747+---@param name? string
4848+function session.load(name)
4949+ local path = session.get_path(name)
5050+ if vim.uv.fs_stat(path) then
5151+ vim.api.nvim_cmd({
5252+ cmd = 'source',
5353+ args = { path },
5454+ }, {})
5555+ vim.api.nvim_exec_autocmds('SessionLoadPost', {})
5656+ end
5057end
51585252-local group = vim.api.nvim_create_augroup("nivvie", { clear = true })
5959+----
53605454-vim.api.nvim_create_autocmd({ "VimLeavePre" }, {
5555- group = group,
5656- callback = function()
5757- session.clean()
5858- session.save()
5959- end,
6060-})
6161+function session.shouldload()
6262+ if session.stdin then
6363+ return false
6464+ end
61656262-if vim.fn.argc() > 0 then
6363- return
6666+ if vim.fn.argc() > 0 then
6767+ return false
6868+ end
6969+7070+ local bufs = vim.api.nvim_list_bufs()
7171+ bufs = vim
7272+ .iter(bufs)
7373+ :filter(function(buf)
7474+ return vim.api.nvim_buf_is_valid(buf) and vim.bo[buf].buftype == ''
7575+ end)
7676+ :totable()
7777+7878+ return #bufs == 0
6479end
8080+8181+--- only load if necessary
8282+function session.autoload()
8383+ if not session.shouldload() then
8484+ return
8585+ end
8686+ session.load()
8787+end
8888+8989+local group = vim.api.nvim_create_augroup('nivvie', { clear = true })
9090+9191+vim.api.nvim_create_autocmd({ 'VimLeavePre' }, {
9292+ group = group,
9393+ callback = function()
9494+ session.clean()
9595+ session.save()
9696+ end,
9797+})
9898+9999+vim.api.nvim_create_autocmd({ 'StdinReadPost' }, {
100100+ group = group,
101101+ callback = function()
102102+ session.stdin = true
103103+ end,
104104+})
105105+65106vim.schedule(function()
6666- session.load()
107107+ vim.api.nvim_create_user_command('Nivvie', function(args)
108108+ if args.fargs[1] == 'save' then
109109+ session.save(args.fargs[2])
110110+ return
111111+ elseif args.fargs[1] == 'load' then
112112+ session.load(args.fargs[2])
113113+ return
114114+ elseif args.fargs[1] == 'delete' then
115115+ local path = session.get_path(args.fargs[2])
116116+ if vim.uv.fs_stat(path) then
117117+ vim.uv.fs_unlink(path)
118118+ end
119119+ end
120120+ end, {
121121+ complete = function()
122122+ return {
123123+ 'save',
124124+ 'load',
125125+ 'delete',
126126+ }
127127+ end,
128128+ })
67129end)
130130+131131+----
132132+133133+if vim.v.vim_did_enter > 0 then
134134+ vim.schedule(function()
135135+ session.autoload()
136136+ end)
137137+ return
138138+end
139139+140140+vim.api.nvim_create_autocmd({ 'VimEnter' }, {
141141+ group = group,
142142+ callback = function()
143143+ vim.schedule(function()
144144+ session.autoload()
145145+ end)
146146+ end,
147147+})
148148+149149+return session