1

Update generated nvim config

This commit is contained in:
2024-06-05 22:05:42 +02:00
parent 859ee3a2ba
commit 075fe5f587
1292 changed files with 152601 additions and 0 deletions

View File

@ -0,0 +1,138 @@
name: Bugs or configuration issues
description: If you've already asked for help with a problem and confirmed something is broken with the plugin itself, create a bug report.
title: "[Bug]: "
labels: ["bug"]
assignees: olimorris
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report! This form may seem onerous but it makes a resolution much quicker for you and I.
- type: markdown
attributes:
value: |
## Test with a `minimal.lua` file
I know the temptation is to skip this step but please don't. With the quantity of plugins you have in your Neovim config, it's like searching for a needle in a haystack.
Firstly, test and run Neovim with the minimal config below. Be sure to add your Persisted.nvim config, saving to a `minimal.lua` file and adding any additional plugins you may need.
```lua
local root = "/tmp/persisted"
-- Set stdpaths to use root dir
for _, name in ipairs({ "config", "data", "state", "cache" }) do
vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name
end
-- Bootstrap lazy
local lazypath = root .. "/plugins/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
vim.fn.system({
"git",
"clone",
"--filter=blob:none",
"--single-branch",
"https://github.com/folke/lazy.nvim.git",
lazypath,
})
end
vim.opt.runtimepath:prepend(lazypath)
vim.opt.sessionoptions = "buffers,curdir,folds,globals,tabpages,winpos,winsize" -- Session options to store in the session
-- Install plugins
local plugins = {
{
"olimorris/persisted.nvim",
opts = {
-- Your custom config here
}
},
-- Put any other plugins here
}
require("lazy").setup(plugins, {
root = root .. "/plugins",
})
```
Once you've updated for your config, run Neovim with this command:
```sh
nvim --clean -u minimal.lua
```
Open some buffers and save the session with:
```sh
:SessionSave
```
Then re-open neovim and load the session with:
```sh
:SessionLoad
```
- type: textarea
id: persisted-config
attributes:
label: Your `minimal.lua` config
description: Please paste your minimal.lua config here
placeholder: |
```lua
-- Your minimal.lua config here
```
validations:
required: true
- type: textarea
id: errors
attributes:
label: Error messages
description: Please paste any error messages you receive
placeholder: |
```lua
-- Error messages here
```
validations:
required: false
- type: textarea
id: bug
attributes:
label: Describe the bug
description: Please describe the bug and include any screenshots
placeholder: |
### What I expect to happen
[Your text here]
### What actually happens
[Your text here]
validations:
required: true
- type: textarea
id: reproduce
attributes:
label: Reproduce the bug
description: Please include the steps to reproduce the bug
placeholder: |
Steps to reproduce:
1.
2.
3.
validations:
required: false
- type: checkboxes
id: final-checks
attributes:
label: Final checks
description: |
Before you submit, please make sure you have completed the following steps:
options:
- label: I have made sure this issue exists in the latest version of the plugin
required: true
- label: I have tested with the `minimal.lua` config file above and still get the issue
required: true
- label: I have used `SessionSave` to save the session before restarting Neovim and using `SessionLoad`
required: true
- label: I have made sure this is not a duplicate issue
required: true

View File

@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Get Help
url: https://github.com/olimorris/persisted.nvim/discussions/new?category=q-a
about: If you can't get something to work the way you expect, open a question in the discussion forums.
- name: Feature Request
url: https://github.com/olimorris/persisted.nvim/discussions/new?category=ideas
about: Suggest any ideas you have using the discussion forums.

View File

@ -0,0 +1,54 @@
name: Continuous Integration
on:
push:
branches: [main]
pull_request:
branches: [main]
# Cancel any in-progress CI runs for a PR if it is updated
concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }}
cancel-in-progress: true
jobs:
tests:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-20.04
url: https://github.com/neovim/neovim/releases/download/nightly/nvim-linux64.tar.gz
- os: ubuntu-20.04
url: https://github.com/neovim/neovim/releases/download/v0.10.0/nvim-linux64.tar.gz
- os: ubuntu-20.04
url: https://github.com/neovim/neovim/releases/download/v0.9.0/nvim-linux64.tar.gz
- os: ubuntu-20.04
url: https://github.com/neovim/neovim/releases/download/v0.8.0/nvim-linux64.tar.gz
steps:
- uses: actions/checkout@v4
- run: date +%F > todays-date
- name: Restore from todays cache
uses: actions/cache@v4
with:
path: _neovim
key: ${{ runner.os }}-${{ matrix.url }}-${{ hashFiles('todays-date') }}
- name: Prepare
run: |
test -d _neovim || {
mkdir -p _neovim
curl -sL ${{ matrix.url }} | tar xzf - --strip-components=1 -C "${PWD}/_neovim"
}
mkdir -p ~/.local/share/nvim/site/pack/vendor/start
git clone --depth 1 https://github.com/nvim-lua/plenary.nvim ~/.local/share/nvim/site/pack/vendor/start/plenary.nvim
ln -s $(pwd) ~/.local/share/nvim/site/pack/vendor/start
- name: Run tests
run: |
export PATH="${PWD}/_neovim/bin:${PATH}"
export VIM="${PWD}/_neovim/share/nvim/runtime"
nvim --version
make test

View File

@ -0,0 +1,7 @@
misc/
!tests/**/*
.luarc.json
todo.md
tests/.DS_Store
tests/dummy_data/

View File

@ -0,0 +1,10 @@
# Contributing
If you open a PR please consider the following:
- If you're adding new functionality, please open an issue to discuss the design of the new feature
- How the end user will consume your new feature. It's often easier to start with the API and work backwards
- Consider the use of [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) to make your commit messages more descriptive
- Add new tests to the plugin (use the existing ones as a template)
The mantra for this plugin is "simple session management" and as such the code base is relatively simple. Please consider this in your code.

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Oli Morris
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,42 @@
PANVIMDOC_DIR = misc/panvimdoc
PANVIMDOC_URL = https://github.com/kdheepak/panvimdoc
PLENARY_DIR = misc/plenary
PLENARY_URL = https://github.com/nvim-lua/plenary.nvim
all: format test docs
docs: $(PANVIMDOC_DIR)
@cd $(PANVIMDOC_DIR) && \
pandoc \
--metadata="project:persisted.nvim" \
--metadata="description:Simple session management for Neovim" \
--metadata="toc:true" \
--metadata="incrementheadinglevelby:0" \
--metadata="treesitter:true" \
--lua-filter scripts/skip-blocks.lua \
--lua-filter scripts/include-files.lua \
--lua-filter scripts/remove-emojis.lua \
-t scripts/panvimdoc.lua \
../../README.md \
-o ../../doc/persisted.nvim.txt
$(PANVIMDOC_DIR):
git clone --depth=1 --no-single-branch $(PANVIMDOC_URL) $(PANVIMDOC_DIR)
@rm -rf doc/panvimdoc/.git
check:
stylua --check lua/ tests/ -f ./stylua.toml
format:
stylua lua/ tests/ -f ./stylua.toml
test: $(PLENARY_DIR)
nvim --headless --noplugin -u tests/minimal.vim +Setup
# nvim --headless --noplugin -u tests/minimal.vim +TestAutoloading
nvim --headless --noplugin -u tests/minimal.vim +TestGitBranching
nvim --headless --noplugin -u tests/minimal.vim +TestDefaults
nvim --headless --noplugin -u tests/minimal.vim +TearDown
$(PLENARY_DIR):
git clone --depth=1 --branch v0.1.3 $(PLENARY_URL) $(PLENARY_DIR)
@rm -rf $(PLENARY_DIR)/.git

View File

@ -0,0 +1,412 @@
<!-- panvimdoc-ignore-start -->
<p align="center">
<img src="https://user-images.githubusercontent.com/9512444/179085825-7c3bc1f7-c86b-4119-96e2-1a581e9bfffc.png" alt="Persisted.nvim" />
</p>
<h1 align="center">Persisted.nvim</h1>
<p align="center">
<a href="https://github.com/olimorris/persisted.nvim/stargazers"><img src="https://img.shields.io/github/stars/olimorris/persisted.nvim?color=c678dd&logoColor=e06c75&style=for-the-badge"></a>
<a href="https://github.com/olimorris/persisted.nvim/issues"><img src="https://img.shields.io/github/issues/olimorris/persisted.nvim?color=%23d19a66&style=for-the-badge"></a>
<a href="https://github.com/olimorris/persisted.nvim/blob/main/LICENSE"><img src="https://img.shields.io/github/license/olimorris/persisted.nvim?style=for-the-badge"></a>
<a href="https://github.com/olimorris/persisted.nvim/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/olimorris/persisted.nvim/ci.yml?branch=main&label=tests&style=for-the-badge"></a>
</p>
<p align="center">
<b>Persisted.nvim</b> is a simple lua plugin for working with sessions in Neovim<br>
(Forked from <a href="https://github.com/folke/persistence.nvim">Persistence.nvim</a>)
</p>
<!-- panvimdoc-ignore-end -->
## :sparkles: Features
- :evergreen_tree: Supports sessions across multiple git branches
- :telescope: Telescope extension to work with saved sessions
- :tickets: Custom events which users can hook into for tighter integration
- :memo: Simple API to save/stop/restore/delete/list the current session(s)
- :open_file_folder: Supports autosaving and autoloading of sessions with allowed/ignored directories
- :floppy_disk: Automatically saves the active session under `.local/share/nvim/sessions` on exiting Neovim
## :zap: Requirements
- Neovim >= 0.8.0
## :package: Installation
Install the plugin with your preferred package manager:
**[Lazy.nvim](https://github.com/folke/lazy.nvim)**
```lua
-- Lua
{
"olimorris/persisted.nvim",
lazy = false, -- make sure the plugin is always loaded at startup
config = true
}
```
> [!NOTE]
> Setting `lazy = true` option may be useful if you use a dashboard
**[Packer](https://github.com/wbthomason/packer.nvim)**
```lua
-- Lua
use({
"olimorris/persisted.nvim",
config = function()
require("persisted").setup()
end,
})
```
**[Vim Plug](https://github.com/junegunn/vim-plug)**
```vim
" Vim Script
Plug 'olimorris/persisted.nvim'
lua << EOF
require("persisted").setup {
-- your configuration comes here
-- or leave it empty to use the default settings
-- refer to the configuration section below
}
EOF
```
If you wish to use session _autoloading_ alongside a dashboard plugin, it is recommended that you give this plugin a greater loading priority. With **Packer** the `after` config option can be used and in **Lazy.nvim**, the `priority` property.
### Telescope extension
Ensure that the telescope extension is loaded with:
```lua
require("telescope").load_extension("persisted")
```
The layout can then be customised from within Telescope:
```lua
require('telescope').setup({
defaults = {
},
extensions = {
persisted = {
layout_config = { width = 0.55, height = 0.55 }
}
}
})
```
## :rocket: Usage
### Commands
The plugin comes with a number of commands:
- `:SessionToggle` - Determines whether to load, start or stop a session
- `: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`)
- `:SessionLoadLast` - Load the most recent session
- `:SessionLoadFromFile` - Load a session from a given path
- `:SessionDelete` - Delete the current session
### Telescope extension
<!-- panvimdoc-ignore-start -->
<p align="center">
<img src="https://github.com/olimorris/persisted.nvim/assets/9512444/5bfd6f94-ff70-4f2b-9193-53cdf7140d75" alt="Telescope">
</p>
<!-- panvimdoc-ignore-end -->
The Telescope extension may be opened via `:Telescope persisted`. The available actions are:
- `<CR>` - Open/source the session file
- `<C-b>` - Add/update the git branch for the session file
- `<C-c>` - Copy the session file
- `<C-d>` - Delete the session file
### Global variables
The plugin sets a number of global variables throughout its lifecycle:
- `vim.g.persisting` - (bool) Determines if the plugin is active for the current session
- `vim.g.persisted_exists` - (bool) Determines if a session exists for the current working directory
- `vim.g.persisted_loaded_session` - (string) The file path to the current session
## :wrench: Configuration
### Defaults
The plugin comes with the following defaults:
```lua
require("persisted").setup({
save_dir = vim.fn.expand(vim.fn.stdpath("data") .. "/sessions/"), -- directory where session files are saved
silent = false, -- silent nvim message when sourcing session file
use_git_branch = false, -- create session files based on the branch of a git enabled repository
default_branch = "main", -- the branch to load if a session file is not found for the current branch
autosave = true, -- automatically save session files when exiting Neovim
should_autosave = nil, -- function to determine if a session should be autosaved
autoload = false, -- automatically load the session for the cwd on Neovim startup
on_autoload_no_session = nil, -- function to run when `autoload = true` but there is no session to load
follow_cwd = true, -- change session file name to match current working directory if it changes
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
ignored_branches = nil, -- table of branch patterns that are ignored for auto-saving and auto-loading
telescope = {
reset_prompt = true, -- Reset the Telescope prompt after an action?
mappings = { -- table of mappings for the Telescope extension
change_branch = "<c-b>",
copy_session = "<c-c>",
delete_session = "<c-d>",
},
icons = { -- icons displayed in the picker, set to nil to disable entirely
branch = " ",
dir = " ",
selected = " ",
},
},
})
```
### What is saved in the session?
As the plugin uses Vim's `:mksession` command then you may change the `vim.o.sessionoptions` value to determine what to write into the session. Please see `:h sessionoptions` for more information.
> [!NOTE]
> The author uses: `vim.o.sessionoptions = "buffers,curdir,folds,tabpages,winpos,winsize"`
### Session save location
The location of the session files may be changed by altering the `save_dir` configuration option. For example:
```lua
require("persisted").setup({
save_dir = vim.fn.expand(vim.fn.stdpath("data") .. "/sessions/"), -- Resolves to ~/.local/share/nvim/sessions/
})
```
> [!NOTE]
> The plugin may be unable to find existing sessions if the `save_dir` value is changed
### Git branching
One of the plugin's core features is the ability to have multiple session files for a given project, by using git branches. To enable git branching:
```lua
require("persisted").setup({
use_git_branch = true,
})
```
### Autosaving
By default, the plugin will automatically save a Neovim session to disk when the `VimLeavePre` event is triggered. Autosaving can be turned off by:
```lua
require("persisted").setup({
autosave = false,
})
```
Autosaving can be further controlled for certain directories by specifying `allowed_dirs` and `ignored_dirs`.
There may be occasions when you do not wish to autosave; perhaps when a dashboard or a certain buftype is present. To control this, a callback function, `should_autosave`, may be used which should return a boolean value.
```lua
require("persisted").setup({
should_autosave = function()
-- do not autosave if the alpha dashboard is the current filetype
if vim.bo.filetype == "alpha" then
return false
end
return true
end,
})
```
Of course, if you wish to manually save the session when autosaving is disabled, the `:SessionSave` command can be used.
> [!NOTE]
> If `autosave = false` then the `should_autosave` callback will not be executed.
### Autoloading
The plugin can be enabled to automatically load sessions when Neovim is started. Whilst off by default, this can be turned on by:
```lua
require("persisted").setup({
autoload = true,
})
```
You can also provide a function to run when `autoload = true` but there is no session to be loaded:
```lua
require("persisted").setup({
autoload = true,
on_autoload_no_session = function()
vim.notify("No existing session to load.")
end
})
```
Autoloading can be further controlled for certain directories by specifying `allowed_dirs` and `ignored_dirs`.
> [!NOTE]
> Autoloading will not occur if the plugin is lazy loaded or a user opens Neovim with arguments other than a single directory argument. For example: `nvim some_file.rb` will not result in autoloading but `nvim some/existing/path` or `nvim .` will.
### Following current working directory
There may be a need to change the working directory to quickly access files in other directories without changing the current session's name on save. This behavior can be configured with `follow_cwd = false`.
By default, the session name will match the current working directory:
```lua
require("persisted").setup({
follow_cwd = true,
})
```
> [!NOTE]
> If `follow_cwd = false` the session name is stored upon loading under the global variable `vim.g.persisting_session`. This variable can be manually adjusted if changes to the session name are needed. Alternatively, if `follow_cwd = true` then `vim.g.persisting_session = nil`.
### Allowed directories
You may specify a table of directories for which the plugin will autosave and/or autoload from. For example:
```lua
require("persisted").setup({
allowed_dirs = {
"~/.dotfiles",
"~/Code",
},
})
```
Specifying `~/Code` will autosave and autoload from that directory as well as all its sub-directories.
> [!NOTE]
> If `allowed_dirs` is left at its default value and `autosave` and/or `autoload` are set to `true`, then the plugin will autoload/autosave from _any_ directory
### Ignored directories
You may specify a table of directories for which the plugin will **never** autosave and autoload from. For example:
```lua
require("persisted").setup({
ignored_dirs = {
"~/.config",
"~/.local/nvim"
},
})
```
Specifying `~/.config` will prevent any autosaving and autoloading from that directory as well as all its sub-directories.
You can also specify exact directory matches to ignore. In this case, unlike the default behavior which ignores all children of the ignored directory, this will ignore only the specified child. For example:
```lua
require("persisted").setup({
ignored_dirs = {
"~/.config",
"~/.local/nvim",
{ "/", exact = true },
{ "/tmp", exact = true }
},
})
```
In this setup, `~/.config` and `~/.local/nvim` are still going to behave in their default setting (ignoring all listed directory and its children), however `/` and `/tmp` will only ignore those directories exactly.
### Ignored branches
You may specify a table of patterns that match against branches for which the plugin will **never** autosave and autoload from:
```lua
require("persisted").setup({
ignored_branches = {
"^master",
"feature/%u"
},
})
```
### Events / Callbacks
The plugin fires events at various points during its lifecycle:
- `PersistedLoadPre` - For _before_ a session is loaded
- `PersistedLoadPost` - For _after_ a session is loaded
- `PersistedTelescopeLoadPre` - For _before_ a session is loaded via Telescope
- `PersistedTelescopeLoadPost` - For _after_ a session is loaded via Telescope
- `PersistedSavePre` - For _before_ a session is saved
- `PersistedSavePost` - For _after_ a session is saved
- `PersistedDeletePre` - For _before_ a session is deleted
- `PersistedDeletePost` - For _after_ a session is deleted
- `PersistedStateChange` - For when a session is _started_ or _stopped_
- `PersistedToggled` - For when a session is toggled
These events can be consumed anywhere within your configuration by utilising the `vim.api.nvim_create_autocmd` function.
#### Example use case
A commonly requested example is to use the Telescope extension to load a session, saving the current session before clearing all of the open buffers:
```lua
local group = vim.api.nvim_create_augroup("PersistedHooks", {})
vim.api.nvim_create_autocmd({ "User" }, {
pattern = "PersistedTelescopeLoadPre",
group = group,
callback = function(session)
-- Save the currently loaded session using a global variable
require("persisted").save({ session = vim.g.persisted_loaded_session })
-- Delete all of the open buffers
vim.api.nvim_input("<ESC>:%bd!<CR>")
end,
})
```
#### Using callback data
When certain events are fired, session data is made available for the user to consume, for example:
```lua
{
branch = "main",
dir_path = "Code/Neovim/persisted.nvim",
file_path = "/Users/Oli/.local/share/nvim/sessions/%Users%Oli%Code%Neovim%persisted.nvim@@main.vim",
name = "Code/Neovim/persisted.nvim@@main",
}
```
To consume this data, use the `session.data` table in your autocmd:
```lua
vim.api.nvim_create_autocmd({ "User" }, {
pattern = "PersistedLoadPost",
group = group,
callback = function(session)
print(session.data.branch)
end,
})
```
> [!NOTE]
> This data is available for the `PersistedLoad`, `PersistedDelete` and `PersistedTelescope` events
## :page_with_curl: License
[MIT](https://github.com/olimorris/persisted.nvim/blob/main/LICENSE)

View File

@ -0,0 +1,460 @@
*persisted.nvim.txt* Simple session management for Neovim
==============================================================================
Table of Contents *persisted.nvim-table-of-contents*
- Features |persisted.nvim-features|
- Requirements |persisted.nvim-requirements|
- Installation |persisted.nvim-installation|
- Usage |persisted.nvim-usage|
- Configuration |persisted.nvim-configuration|
- License |persisted.nvim-license|
FEATURES *persisted.nvim-features*
- Supports sessions across multiple git branches
- Telescope extension to work with saved sessions
- Custom events which users can hook into for tighter integration
- Simple API to save/stop/restore/delete/list the current session(s)
- Supports autosaving and autoloading of sessions with allowed/ignored directories
- Automatically saves the active session under `.local/share/nvim/sessions` on exiting Neovim
REQUIREMENTS *persisted.nvim-requirements*
- Neovim >= 0.8.0
INSTALLATION *persisted.nvim-installation*
Install the plugin with your preferred package manager:
**Lazy.nvim**
>lua
-- Lua
{
"olimorris/persisted.nvim",
lazy = false, -- make sure the plugin is always loaded at startup
config = true
}
<
[!NOTE] Setting `lazy = true` option may be useful if you use a dashboard
**Packer**
>lua
-- Lua
use({
"olimorris/persisted.nvim",
config = function()
require("persisted").setup()
end,
})
<
**Vim Plug**
>vim
" Vim Script
Plug 'olimorris/persisted.nvim'
lua << EOF
require("persisted").setup {
-- your configuration comes here
-- or leave it empty to use the default settings
-- refer to the configuration section below
}
EOF
<
If you wish to use session _autoloading_ alongside a dashboard plugin, it is
recommended that you give this plugin a greater loading priority. With
**Packer** the `after` config option can be used and in **Lazy.nvim**, the
`priority` property.
TELESCOPE EXTENSION ~
Ensure that the telescope extension is loaded with:
>lua
require("telescope").load_extension("persisted")
<
The layout can then be customised from within Telescope:
>lua
require('telescope').setup({
defaults = {
},
extensions = {
persisted = {
layout_config = { width = 0.55, height = 0.55 }
}
}
})
<
USAGE *persisted.nvim-usage*
COMMANDS ~
The plugin comes with a number of commands:
- `:SessionToggle` - Determines whether to load, start or stop a session
- `: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`)
- `:SessionLoadLast` - Load the most recent session
- `:SessionLoadFromFile` - Load a session from a given path
- `:SessionDelete` - Delete the current session
TELESCOPE EXTENSION ~
The Telescope extension may be opened via `:Telescope persisted`. The available
actions are:
- `<CR>` - Open/source the session file
- `<C-b>` - Add/update the git branch for the session file
- `<C-c>` - Copy the session file
- `<C-d>` - Delete the session file
GLOBAL VARIABLES ~
The plugin sets a number of global variables throughout its lifecycle:
- `vim.g.persisting` - (bool) Determines if the plugin is active for the current session
- `vim.g.persisted_exists` - (bool) Determines if a session exists for the current working directory
- `vim.g.persisted_loaded_session` - (string) The file path to the current session
CONFIGURATION *persisted.nvim-configuration*
DEFAULTS ~
The plugin comes with the following defaults:
>lua
require("persisted").setup({
save_dir = vim.fn.expand(vim.fn.stdpath("data") .. "/sessions/"), -- directory where session files are saved
silent = false, -- silent nvim message when sourcing session file
use_git_branch = false, -- create session files based on the branch of a git enabled repository
default_branch = "main", -- the branch to load if a session file is not found for the current branch
autosave = true, -- automatically save session files when exiting Neovim
should_autosave = nil, -- function to determine if a session should be autosaved
autoload = false, -- automatically load the session for the cwd on Neovim startup
on_autoload_no_session = nil, -- function to run when `autoload = true` but there is no session to load
follow_cwd = true, -- change session file name to match current working directory if it changes
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
ignored_branches = nil, -- table of branch patterns that are ignored for auto-saving and auto-loading
telescope = {
reset_prompt = true, -- Reset the Telescope prompt after an action?
mappings = { -- table of mappings for the Telescope extension
change_branch = "<c-b>",
copy_session = "<c-c>",
delete_session = "<c-d>",
},
icons = { -- icons displayed in the picker, set to nil to disable entirely
branch = " ",
dir = " ",
selected = " ",
},
},
})
<
WHAT IS SAVED IN THE SESSION? ~
As the plugin uses Vims `:mksession` command then you may change the
`vim.o.sessionoptions` value to determine what to write into the session.
Please see `:h sessionoptions` for more information.
[!NOTE] The author uses: `vim.o.sessionoptions =
"buffers,curdir,folds,tabpages,winpos,winsize"`
SESSION SAVE LOCATION ~
The location of the session files may be changed by altering the `save_dir`
configuration option. For example:
>lua
require("persisted").setup({
save_dir = vim.fn.expand(vim.fn.stdpath("data") .. "/sessions/"), -- Resolves to ~/.local/share/nvim/sessions/
})
<
[!NOTE] The plugin may be unable to find existing sessions if the `save_dir`
value is changed
GIT BRANCHING ~
One of the plugins core features is the ability to have multiple session
files for a given project, by using git branches. To enable git branching:
>lua
require("persisted").setup({
use_git_branch = true,
})
<
AUTOSAVING ~
By default, the plugin will automatically save a Neovim session to disk when
the `VimLeavePre` event is triggered. Autosaving can be turned off by:
>lua
require("persisted").setup({
autosave = false,
})
<
Autosaving can be further controlled for certain directories by specifying
`allowed_dirs` and `ignored_dirs`.
There may be occasions when you do not wish to autosave; perhaps when a
dashboard or a certain buftype is present. To control this, a callback
function, `should_autosave`, may be used which should return a boolean value.
>lua
require("persisted").setup({
should_autosave = function()
-- do not autosave if the alpha dashboard is the current filetype
if vim.bo.filetype == "alpha" then
return false
end
return true
end,
})
<
Of course, if you wish to manually save the session when autosaving is
disabled, the `:SessionSave` command can be used.
[!NOTE] If `autosave = false` then the `should_autosave` callback will not be
executed.
AUTOLOADING ~
The plugin can be enabled to automatically load sessions when Neovim is
started. Whilst off by default, this can be turned on by:
>lua
require("persisted").setup({
autoload = true,
})
<
You can also provide a function to run when `autoload = true` but there is no
session to be loaded:
>lua
require("persisted").setup({
autoload = true,
on_autoload_no_session = function()
vim.notify("No existing session to load.")
end
})
<
Autoloading can be further controlled for certain directories by specifying
`allowed_dirs` and `ignored_dirs`.
[!NOTE] Autoloading will not occur if the plugin is lazy loaded or a user opens
Neovim with arguments other than a single directory argument. For example:
`nvim some_file.rb` will not result in autoloading but `nvim
some/existing/path` or `nvim .` will.
FOLLOWING CURRENT WORKING DIRECTORY ~
There may be a need to change the working directory to quickly access files in
other directories without changing the current sessions name on save. This
behavior can be configured with `follow_cwd = false`.
By default, the session name will match the current working directory:
>lua
require("persisted").setup({
follow_cwd = true,
})
<
[!NOTE] If `follow_cwd = false` the session name is stored upon loading under
the global variable `vim.g.persisting_session`. This variable can be manually
adjusted if changes to the session name are needed. Alternatively, if
`follow_cwd = true` then `vim.g.persisting_session = nil`.
ALLOWED DIRECTORIES ~
You may specify a table of directories for which the plugin will autosave
and/or autoload from. For example:
>lua
require("persisted").setup({
allowed_dirs = {
"~/.dotfiles",
"~/Code",
},
})
<
Specifying `~/Code` will autosave and autoload from that directory as well as
all its sub-directories.
[!NOTE] If `allowed_dirs` is left at its default value and `autosave` and/or
`autoload` are set to `true`, then the plugin will autoload/autosave from _any_
directory
IGNORED DIRECTORIES ~
You may specify a table of directories for which the plugin will **never**
autosave and autoload from. For example:
>lua
require("persisted").setup({
ignored_dirs = {
"~/.config",
"~/.local/nvim"
},
})
<
Specifying `~/.config` will prevent any autosaving and autoloading from that
directory as well as all its sub-directories.
You can also specify exact directory matches to ignore. In this case, unlike
the default behavior which ignores all children of the ignored directory, this
will ignore only the specified child. For example:
>lua
require("persisted").setup({
ignored_dirs = {
"~/.config",
"~/.local/nvim",
{ "/", exact = true },
{ "/tmp", exact = true }
},
})
<
In this setup, `~/.config` and `~/.local/nvim` are still going to behave in
their default setting (ignoring all listed directory and its children), however
`/` and `/tmp` will only ignore those directories exactly.
IGNORED BRANCHES ~
You may specify a table of patterns that match against branches for which the
plugin will **never** autosave and autoload from:
>lua
require("persisted").setup({
ignored_branches = {
"^master",
"feature/%u"
},
})
<
EVENTS / CALLBACKS ~
The plugin fires events at various points during its lifecycle:
- `PersistedLoadPre` - For _before_ a session is loaded
- `PersistedLoadPost` - For _after_ a session is loaded
- `PersistedTelescopeLoadPre` - For _before_ a session is loaded via Telescope
- `PersistedTelescopeLoadPost` - For _after_ a session is loaded via Telescope
- `PersistedSavePre` - For _before_ a session is saved
- `PersistedSavePost` - For _after_ a session is saved
- `PersistedDeletePre` - For _before_ a session is deleted
- `PersistedDeletePost` - For _after_ a session is deleted
- `PersistedStateChange` - For when a session is _started_ or _stopped_
- `PersistedToggled` - For when a session is toggled
These events can be consumed anywhere within your configuration by utilising
the `vim.api.nvim_create_autocmd` function.
EXAMPLE USE CASE
A commonly requested example is to use the Telescope extension to load a
session, saving the current session before clearing all of the open buffers:
>lua
local group = vim.api.nvim_create_augroup("PersistedHooks", {})
vim.api.nvim_create_autocmd({ "User" }, {
pattern = "PersistedTelescopeLoadPre",
group = group,
callback = function(session)
-- Save the currently loaded session using a global variable
require("persisted").save({ session = vim.g.persisted_loaded_session })
-- Delete all of the open buffers
vim.api.nvim_input("<ESC>:%bd!<CR>")
end,
})
<
USING CALLBACK DATA
When certain events are fired, session data is made available for the user to
consume, for example:
>lua
{
branch = "main",
dir_path = "Code/Neovim/persisted.nvim",
file_path = "/Users/Oli/.local/share/nvim/sessions/%Users%Oli%Code%Neovim%persisted.nvim@@main.vim",
name = "Code/Neovim/persisted.nvim@@main",
}
<
To consume this data, use the `session.data` table in your autocmd:
>lua
vim.api.nvim_create_autocmd({ "User" }, {
pattern = "PersistedLoadPost",
group = group,
callback = function(session)
print(session.data.branch)
end,
})
<
[!NOTE] This data is available for the `PersistedLoad`, `PersistedDelete` and
`PersistedTelescope` events
LICENSE *persisted.nvim-license*
MIT <https://github.com/olimorris/persisted.nvim/blob/main/LICENSE>
Generated by panvimdoc <https://github.com/kdheepak/panvimdoc>
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@ -0,0 +1,8 @@
persisted.nvim-configuration persisted.nvim.txt /*persisted.nvim-configuration*
persisted.nvim-features persisted.nvim.txt /*persisted.nvim-features*
persisted.nvim-installation persisted.nvim.txt /*persisted.nvim-installation*
persisted.nvim-license persisted.nvim.txt /*persisted.nvim-license*
persisted.nvim-requirements persisted.nvim.txt /*persisted.nvim-requirements*
persisted.nvim-table-of-contents persisted.nvim.txt /*persisted.nvim-table-of-contents*
persisted.nvim-usage persisted.nvim.txt /*persisted.nvim-usage*
persisted.nvim.txt persisted.nvim.txt /*persisted.nvim.txt*

View File

@ -0,0 +1,45 @@
local M = {}
local defaults = {
save_dir = vim.fn.expand(vim.fn.stdpath("data") .. "/sessions/"), -- directory where session files are saved
silent = false, -- silent nvim message when sourcing session file
use_git_branch = false, -- create session files based on the branch of a git enabled repository
branch_separator = "@@", -- string used to separate session directory name from branch name
default_branch = "main", -- the branch to load if a session file is not found for the current branch
autosave = true, -- automatically save session files when exiting Neovim
should_autosave = nil, -- function to determine if a session should be autosaved (resolve to a boolean)
autoload = false, -- automatically load the session for the cwd on Neovim startup
on_autoload_no_session = nil, -- function to run when `autoload = true` but there is no session to load
follow_cwd = true, -- change session file name with changes in current working directory
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
ignored_branches = nil, -- table of branch patterns that are ignored for auto-saving and auto-loading
telescope = {
reset_prompt = true, -- Reset prompt after a telescope action?
--TODO: We should add a deprecation notice for the old API here
mappings = {
change_branch = "<c-b>",
copy_session = "<c-c>",
delete_session = "<c-d>",
},
icons = { -- icons displayed in the picker
branch = "",
dir = "",
selected = "",
},
},
}
M.options = {}
function M.setup(opts)
M.options = vim.tbl_deep_extend("force", defaults, opts or {})
vim.fn.mkdir(M.options.save_dir, "p")
end
return M

View File

@ -0,0 +1,38 @@
---[[
--Courtesy of the awesome work in Nightfox.nvim
--https://github.com/EdenEast/nightfox.nvim/blob/main/lua/nightfox/lib/deprecation.lua
--]
local M = {
_list = { { "[Persisted.nvim]\n", "Question" }, { "The following have been " }, { "deprecated:\n", "WarningMsg" } },
_has_registered = false,
}
function M.write(...)
for _, e in ipairs({ ... }) do
table.insert(M._list, type(e) == "string" and { e } or e)
end
M._list[#M._list][1] = M._list[#M._list][1] .. "\n"
if not M._has_registered then
vim.cmd([[
augroup PersistedDeprecations
au!
autocmd VimEnter * ++once lua require("persisted.deprecate").flush()
augroup END
]])
M._has_registered = true
end
end
function M.flush()
M.write(
"----------\n",
"See ",
{ "https://github.com/olimorris/persisted.nvim/issues/51", "Title" },
" for more information."
)
vim.api.nvim_echo(M._list, true, {})
end
return M

View File

@ -0,0 +1,381 @@
local utils = require("persisted.utils")
local config = require("persisted.config")
local M = {}
local e = vim.fn.fnameescape
---Escapes special characters before performing string substitution
---@param str string
---@param pattern string
---@param replace string
---@param n? integer
---@return string
---@return integer
local function escape_pattern(str, pattern, replace, n)
pattern = string.gsub(pattern, "[%(%)%.%+%-%*%?%[%]%^%$%%]", "%%%1") -- escape pattern
replace = string.gsub(replace, "[%%]", "%%%%") -- escape replacement
return string.gsub(str, pattern, replace, n)
end
---Gets the directory from the file/path argument passed to Neovim if there's
---exactly one and it resolves to a valid directory
---@return string|nil
local function args_path()
if vim.fn.argc() ~= 1 then
return nil
end
-- Use expand() to resolve '~' and use fs_realpath to resolve both '.' and
-- relative paths passed as arguments. Note that argv() will only ever return
-- paths/files passed as arguments and does not include other
-- parameters/arguments. fs_realpath() returns nil if the path doesn't exist.
-- Use isdirectory to validate it's a directory and not a file.
local dir = vim.loop.fs_realpath(vim.fn.expand(vim.fn.argv(0)))
if dir ~= nil and vim.fn.isdirectory(dir) ~= 0 then
return dir
end
return nil
end
---Check any arguments passed to Neovim and verify if they're a directory
---@return boolean
local function args_check()
-- Args are valid if a single directory was resolved or if no args were used.
return args_path() ~= nil or vim.fn.argc() == 0
end
---Get the directory to be used for the session
---@return string
local function session_dir()
-- Use specified directory from arguments or the working directory otherwise.
return args_path() or vim.fn.getcwd()
end
---Does the current working directory allow for the auto-saving and loading?
---@param dir string Directory to be used for the session
---@return boolean
local function allow_dir(dir)
local allowed_dirs = config.options.allowed_dirs
if allowed_dirs == nil then
return true
end
return utils.dirs_match(dir, allowed_dirs)
end
---Is the current working directory ignored for auto-saving and loading?
---@param dir string Directory to be used for the session
---@return boolean
local function ignore_dir(dir)
local ignored_dirs = config.options.ignored_dirs
if ignored_dirs == nil then
return false
end
return utils.dirs_match(dir, ignored_dirs)
end
---Is the current branch ignored for auto-saving and loading?
---@param dir string Branch to be used for the session
---@return boolean
local function ignore_branch(branch)
local ignored_branches = config.options.ignored_branches
if ignored_branches == nil then
return false
end
return utils.table_match(branch, ignored_branches) ~= nil
end
---Get the session that was saved last
---@return string
local function get_last()
local sessions = vim.fn.glob(config.options.save_dir .. "*.vim", true, true)
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 name, untouched
---@param dir? string Directory to be used for the session
---@return string|nil
local function get_branchname(dir)
dir = dir or session_dir()
vim.fn.system('git -C "' .. dir .. '" rev-parse 2>/dev/null')
local git_enabled = (vim.v.shell_error == 0)
if git_enabled then
local git_branch = vim.fn.systemlist('git -C "' .. dir .. '" rev-parse --abbrev-ref HEAD 2>/dev/null')
return git_branch[1]
end
return nil
end
---Get the current Git branch
---@param dir? string Directory to be used for the session
---@return string|nil
function M.get_branch(dir)
dir = dir or session_dir()
if config.options.use_git_branch then
vim.fn.system('git -C "' .. dir .. '" rev-parse 2>/dev/null')
local git_enabled = (vim.v.shell_error == 0)
if git_enabled then
local git_branch = vim.fn.systemlist('git -C "' .. dir .. '" rev-parse --abbrev-ref HEAD 2>/dev/null')
if vim.v.shell_error == 0 then
local branch = config.options.branch_separator .. git_branch[1]:gsub("/", "%%")
local branch_session = config.options.save_dir .. dir:gsub(utils.get_dir_pattern(), "%%") .. branch .. ".vim"
-- Try to load the session for the current branch
if vim.fn.filereadable(branch_session) ~= 0 then
return branch
else
vim.notify(
string.format("[Persisted.nvim]: Trying to load a session for branch %s", config.options.default_branch),
vim.log.levels.INFO
)
vim.notify(
string.format("[Persisted.nvim]: Could not load a session for branch %s.", git_branch[1]),
vim.log.levels.WARN
)
vim.g.persisted_branch_session = branch_session
return config.options.branch_separator .. config.options.default_branch
end
end
end
end
end
---Get the current session for the current working directory and git branch
---@param dir string Directory to be used for the session
---@return string
local function get_current(dir)
local name = dir:gsub(utils.get_dir_pattern(), "%%")
local branch = M.get_branch(dir)
return config.options.save_dir .. name .. (branch or "") .. ".vim"
end
---Determine if a session for the current wording directory, exists
---@param dir? string Directory to be used for the session
---@return boolean
function M.session_exists(dir)
dir = dir or session_dir()
return vim.fn.filereadable(get_current(dir)) ~= 0
end
---Setup the plugin
---@param opts? table
---@return nil
function M.setup(opts)
config.setup(opts)
local dir = session_dir()
local branch = get_branchname()
if
config.options.autosave
and (allow_dir(dir) and not ignore_dir(dir) and vim.g.persisting == nil)
and not ignore_branch(branch)
and args_check()
then
M.start()
end
end
---Load a session
---@param opt? table
---@param dir? string Directory to be used for the session
---@return nil
function M.load(opt, dir)
opt = opt or {}
dir = dir or session_dir()
local branch = get_branchname()
local session = opt.session or (opt.last and get_last() or get_current(dir))
local session_exists = vim.fn.filereadable(session) ~= 0
if session then
if session_exists then
vim.g.persisting_session = config.options.follow_cwd and nil or session
utils.load_session(session, config.options.silent)
elseif type(config.options.on_autoload_no_session) == "function" then
config.options.on_autoload_no_session()
end
end
if config.options.autosave and (allow_dir(dir) and not ignore_dir(dir)) and not ignore_branch(branch) then
M.start()
end
end
---Automatically load the session for the current dir
---@return nil
function M.autoload()
local dir = session_dir()
local branch = get_branchname()
if config.options.autoload and args_check() then
if allow_dir(dir) and not ignore_dir(dir) and not ignore_branch(branch) then
M.load({}, dir)
end
end
end
---Start recording a session
---@return nil
function M.start()
vim.g.persisting = true
vim.api.nvim_exec_autocmds("User", { pattern = "PersistedStateChange", data = { action = "start" } })
end
---Stop recording a session
---@return nil
function M.stop()
vim.g.persisting = false
vim.g.persisting_session = nil
vim.api.nvim_exec_autocmds("User", { pattern = "PersistedStateChange", data = { action = "stop" } })
end
---Write the session to disk
---@param session string
---@return nil
local function write(session)
vim.api.nvim_exec_autocmds("User", { pattern = "PersistedSavePre" })
vim.cmd("mks! " .. e(session))
vim.g.persisting = true
vim.api.nvim_exec_autocmds("User", { pattern = "PersistedSavePost" })
end
---Save the session
---@param opt? table
---@param dir? string Directory to be used for the session
---@return nil
function M.save(opt, dir)
opt = opt or {}
dir = dir or session_dir()
if not opt.session then
-- Do not save the session if the user has manually stopped it...unless it's forced
if (vim.g.persisting == false or vim.g.persisting == nil) and not opt.force then
return
end
-- Do not save the session if autosave is turned off...unless it's forced
if not config.options.autosave and not opt.force then
return
end
-- Do not save the session if the callback returns false...unless it's forced
if
not opt.force
and type(config.options.should_autosave) == "function"
and not config.options.should_autosave()
then
return
end
end
local session = opt.session or (vim.g.persisted_branch_session or vim.g.persisting_session or get_current(dir))
write(session)
end
---Delete the current session
---@param dir? string Directory to be used for the session
---@return nil
function M.delete(dir)
dir = dir or session_dir()
local session = get_current(dir)
if session and vim.loop.fs_stat(session) ~= 0 then
local session_data = utils.make_session_data(session)
vim.api.nvim_exec_autocmds("User", { pattern = "PersistedDeletePre", data = session_data })
vim.schedule(function()
M.stop()
vim.fn.system("rm " .. e(session))
end)
vim.api.nvim_exec_autocmds("User", { pattern = "PersistedDeletePost", data = session_data })
end
end
---Determines whether to load, start or stop a session
---@param dir? string The directory whose associated session saving should be toggled. If not set, the current working directory is used.
---@return nil
function M.toggle(dir)
vim.api.nvim_exec_autocmds("User", { pattern = "PersistedToggled" })
dir = dir or session_dir()
if vim.g.persisting == nil then
return M.load({}, dir)
end
if vim.g.persisting then
return M.stop()
end
return M.start()
end
---List all of the sessions
---@return table
function M.list()
local save_dir = config.options.save_dir
local session_files = vim.fn.glob(save_dir .. "*.vim", true, true)
local branch_separator = config.options.branch_separator
local dir_separator = utils.get_dir_pattern()
local sessions = {}
for _, session in pairs(session_files) do
local session_name = escape_pattern(session, save_dir, "")
:gsub("%%", dir_separator)
:gsub(vim.fn.expand("~"), dir_separator)
:gsub("//", "")
:sub(1, -5)
if vim.fn.has("win32") == 1 then
-- format drive letter (no trailing separator)
session_name = escape_pattern(session_name, dir_separator, ":", 1)
-- format remaining filepath separator(s)
session_name = escape_pattern(session_name, dir_separator, "\\")
end
local branch, dir_path
if string.find(session_name, branch_separator, 1, true) then
local splits = vim.split(session_name, branch_separator, { plain = true })
branch = table.remove(splits, #splits)
dir_path = vim.fn.join(splits, branch_separator)
else
dir_path = session_name
end
table.insert(sessions, {
["name"] = session_name,
["file_path"] = session,
["branch"] = branch,
["dir_path"] = dir_path,
})
end
return sessions
end
return M

View File

@ -0,0 +1,144 @@
local M = {}
local e = vim.fn.fnameescape
local fp_sep = vim.loop.os_uname().sysname:lower():match("windows") and "\\" or "/" -- \ for windows, mac and linux both use \
---Print an error message
--@param msg string
--@param error string
--@return string
local function echoerr(msg, error)
vim.api.nvim_echo({
{ "[Persisted.nvim]: ", "ErrorMsg" },
{ msg, "WarningMsg" },
{ error, "Normal" },
}, true, {})
end
--- Escape special pattern matching characters in a string
---@param input string
---@return string
local function escape_pattern(input)
local magic_chars = { "%", "(", ")", ".", "+", "-", "*", "?", "[", "^", "$" }
for _, char in ipairs(magic_chars) do
input = input:gsub("%" .. char, "%%" .. char)
end
return input
end
---Form a table of session data
---@param session string
---@return table|nil
function M.make_session_data(session)
local config = require("persisted.config").options
local home = os.getenv("HOME") or os.getenv("USERPROFILE") or ""
-- Split the session string into path and branch parts
local separator_index = session:find(config.branch_separator)
if not separator_index then
return nil
end
local branch = session:sub(separator_index + 2):gsub("%.vim$", ""):gsub("%%", "/")
-- Removing the home directory from the path and cleaning leading `/`
local name = session:gsub(config.save_dir, ""):gsub("%%", "/"):gsub(home, "")
-- Remove the .vim extension
name = name:sub(1, #name - 4)
if name:sub(1, 1) == "/" then
name = name:sub(2)
end
local dir_path = name:gsub(branch, ""):gsub(config.branch_separator, ""):gsub(home, "")
return {
name = name,
dir_path = dir_path,
file_path = session,
branch = branch,
}
end
--- Get the last element in a table
---@param table table
---@return string
function M.get_last_item(table)
local last
for _, _ in pairs(table) do
last = #table - 0
end
return table[last]
end
---Check if a target directory exists in a given table
---@param dir string
---@param dirs_table table
---@return boolean
function M.dirs_match(dir, dirs_table)
dir = vim.fn.expand(dir)
return M.table_match(dir, dirs_table, function(pattern)
return escape_pattern(vim.fn.expand(pattern))
end)
end
---Check if a string matches and entry in a given table
---@param needle string
---@param heystack table
---@return boolean
function M.table_match(needle, heystack, escape_fct)
if needle == nil then
return false
end
return heystack
and next(vim.tbl_filter(function(pattern)
if pattern.exact then
-- The pattern is actually a table
pattern = pattern[1]
-- Stripping off the trailing backslash that a user might put here,
-- but only if we aren't looking at the root directory
if pattern:sub(-1) == fp_sep and pattern:len() > 1 then
pattern = pattern:sub(1, -2)
end
return needle == pattern
else
if escape_fct then
pattern = escape_fct(pattern)
end
return needle:match(pattern)
end
end, heystack))
end
---Get the directory pattern based on OS
---@return string
function M.get_dir_pattern()
local pattern = "/"
if vim.fn.has("win32") == 1 then
pattern = "[\\:]"
end
return pattern
end
---Load the given session
---@param session string
---@param silent boolean Load the session silently?
---@return nil|string
function M.load_session(session, silent)
local session_data = M.make_session_data(session)
vim.api.nvim_exec_autocmds("User", { pattern = "PersistedLoadPre", data = session_data })
local ok, result = pcall(vim.cmd, (silent and "silent " or "") .. "source " .. e(session))
if not ok then
return echoerr("Error loading the session! ", result)
end
vim.g.persisted_exists = true
vim.g.persisted_loaded_session = session
vim.api.nvim_exec_autocmds("User", { pattern = "PersistedLoadPost", data = session_data })
end
return M

View File

@ -0,0 +1,64 @@
local actions = require("telescope.actions")
local pickers = require("telescope.pickers")
local conf = require("telescope.config").values
local action_state = require("telescope.actions.state")
local _actions = require("telescope._extensions.persisted.actions")
local _finders = require("telescope._extensions.persisted.finders")
local telescope_opts = {}
local function search_sessions(opts)
local config = require("persisted.config").options
opts = vim.tbl_extend("force", telescope_opts, opts or {})
pickers
.new(opts, {
prompt_title = "Sessions",
sorter = conf.generic_sorter(opts),
finder = _finders.session_finder(require("persisted").list()),
attach_mappings = function(prompt_bufnr, map)
local refresh_sessions = function()
local picker = action_state.get_current_picker(prompt_bufnr)
picker:refresh(_finders.session_finder(require("persisted").list()), {
-- INFO: Account for users who are still using the old API
reset_prompt = config.telescope.reset_prompt or config.telescope.reset_prompt_after_deletion,
})
end
_actions.change_branch:enhance({ post = refresh_sessions })
_actions.copy_session:enhance({ post = refresh_sessions })
_actions.delete_session:enhance({ post = refresh_sessions })
local change_session_branch = function()
return _actions.change_branch(config)
end
local copy_session = function()
return _actions.copy_session(config)
end
map("i", config.telescope.mappings.change_branch, change_session_branch)
map("i", config.telescope.mappings.copy_session, copy_session)
map("i", config.telescope.mappings.delete_session, _actions.delete_session)
actions.select_default:replace(function()
local session = action_state.get_selected_entry()
actions.close(prompt_bufnr)
_actions.load_session(session, config)
end)
return true
end,
})
:find()
end
return require("telescope").register_extension({
setup = function(topts)
vim.api.nvim_set_hl(0, "TelescopePersistedIsCurrent", { link = "TelescopeResultsOperator" })
vim.api.nvim_set_hl(0, "TelescopePersistedDir", { link = "Directory" })
vim.api.nvim_set_hl(0, "TelescopePersistedBranch", { link = "TelescopeResultsIdentifier" })
telescope_opts = topts
end,
exports = {
persisted = search_sessions,
},
})

View File

@ -0,0 +1,80 @@
local actions_state = require("telescope.actions.state")
local transform_mod = require("telescope.actions.mt").transform_mod
local utils = require("persisted.utils")
local M = {}
---Get the selected session from Telescope
---@return table
local get_selected_session = function()
return actions_state.get_selected_entry()
end
---Load the selected session
---@param session table
---@param config table
---@return nil
M.load_session = function(session, config)
vim.api.nvim_exec_autocmds("User", { pattern = "PersistedTelescopeLoadPre", data = session })
vim.schedule(function()
utils.load_session(session.file_path, config.silent)
end)
vim.api.nvim_exec_autocmds("User", { pattern = "PersistedTelescopeLoadPost", data = session })
end
---Delete the selected session from disk
--@return nil
M.delete_session = function()
local session = get_selected_session()
local path = session.file_path
if vim.fn.confirm("Delete [" .. session.name .. "]?", "&Yes\n&No") == 1 then
vim.fn.delete(vim.fn.expand(path))
end
end
---Change the branch of an existing session
---@param config table
---@return nil
M.change_branch = function(config)
local session = get_selected_session()
local path = session.file_path
local branch = vim.fn.input("Branch name: ")
if vim.fn.confirm("Add/update branch to [" .. branch .. "]?", "&Yes\n&No") == 1 then
local ext = path:match("^.+(%..+)$")
-- Check for existing branch in the filename
local branch_separator = config.branch_separator or "@@"
local pattern = "(.*)" .. branch_separator .. ".+" .. ext .. "$"
local base = path:match(pattern) or path:sub(1, #path - #ext)
-- Replace or add the new branch name
local new_path = ""
if branch == "" then
new_path = base .. ext
else
new_path = base .. branch_separator .. branch .. ext
end
os.rename(path, new_path)
end
end
---Copy an existing session
---@return nil
M.copy_session = function(config)
local session = get_selected_session()
local old_name = session.file_path:gsub(config.save_dir, "")
local new_name = vim.fn.input("New session name: ", old_name)
if vim.fn.confirm("Rename session from [" .. old_name .. "] to [" .. new_name .. "]?", "&Yes\n&No") == 1 then
os.execute("cp " .. session.file_path .. " " .. config.save_dir .. new_name)
end
end
return transform_mod(M)

View File

@ -0,0 +1,56 @@
local config = require("persisted.config")
local finders = require("telescope.finders")
local M = {}
local no_icons = {
branch = "",
dir = "",
selected = "",
}
M.session_finder = function(sessions)
local icons = vim.tbl_extend("force", no_icons, config.options.telescope.icons or {})
local custom_displayer = function(session)
local final_str = ""
local hls = {}
local function append(str, hl)
local hl_start = #final_str
final_str = final_str .. str
if hl then
table.insert(hls, { { hl_start, #final_str }, hl })
end
end
-- is current session
append(session.file_path == vim.v.this_session and (icons.selected .. " ") or " ", "TelescopePersistedIsCurrent")
-- session path
append(icons.dir, "TelescopePersistedDir")
append(session.dir_path)
-- branch
if session.branch then
append(" " .. icons.branch .. session.branch, "TelescopePersistedBranch")
end
return final_str, hls
end
return finders.new_table({
results = sessions,
entry_maker = function(session)
session.ordinal = session.name
session.display = custom_displayer
session.name = session.name
session.branch = session.branch
session.file_path = session.file_path
return session
end,
})
end
return M

View File

@ -0,0 +1,34 @@
if vim.g.loaded_persisted then
return
end
local persisted = require("persisted")
-- Create the user commands
vim.cmd([[command! SessionStart :lua require("persisted").start()]])
vim.cmd([[command! SessionStop :lua require("persisted").stop()]])
vim.cmd([[command! SessionSave :lua require("persisted").save({ force = true })]])
vim.cmd([[command! SessionLoad :lua require("persisted").load()]])
vim.cmd([[command! SessionLoadLast :lua require("persisted").load({ last = true })]])
vim.cmd([[command! -nargs=1 SessionLoadFromFile :lua require("persisted").load({ session = <f-args> })]])
vim.cmd([[command! SessionDelete :lua require("persisted").delete()]])
vim.cmd([[command! SessionToggle :lua require("persisted").toggle()]])
-- Create the autocmds
local group = vim.api.nvim_create_augroup("Persisted", {})
vim.api.nvim_create_autocmd({ "VimEnter" }, {
group = group,
nested = true,
callback = persisted.autoload,
})
vim.api.nvim_create_autocmd({ "VimLeavePre" }, {
group = group,
nested = true,
callback = function()
persisted.save()
vim.cmd('sleep 10m')
end
})
vim.g.loaded_persisted = true

View File

@ -0,0 +1,3 @@
indent_type = "Spaces"
indent_width = 2
column_width = 120

View File

@ -0,0 +1,22 @@
local session_dir = vim.loop.cwd() .. "/tests/dummy_data/"
require("persisted").setup({
save_dir = session_dir,
autoload = true,
autosave = true,
allowed_dirs = { vim.loop.cwd() },
})
describe("Autoloading", function()
it("autoloads a file with allowed_dirs config option present", function()
local co = coroutine.running()
vim.defer_fn(function()
coroutine.resume(co)
local content = vim.fn.getline(1, "$")
assert.equals("If you're reading this, I guess auto-loading works", content[1])
end, 1000)
coroutine.yield()
end)
end)

View File

@ -0,0 +1,47 @@
describe("Autoloading", function()
it("autoloads a file", function()
local co = coroutine.running()
vim.defer_fn(function()
coroutine.resume(co)
end, 2000)
local session_dir = vim.fn.getcwd() .. "/tests/dummy_data/"
require("persisted").setup({
save_dir = session_dir,
autoload = true,
autosave = true,
})
coroutine.yield()
local content = vim.fn.getline(1, "$")
assert.equals("If you're reading this, I guess auto-loading works", content[1])
end)
it("autoloads the a child directory of ignored_dirs exact", function()
local co = coroutine.running()
vim.defer_fn(function()
coroutine.resume(co)
end, 2000)
local fp_sep = vim.loop.os_uname().sysname:lower():match("windows") and "\\" or "/" -- \ for windows, mac and linux both use \
local session_dir = vim.fn.getcwd() .. "/test/dummy_data/"
require("persisted").setup({
save_dir = session_dir,
autoload = true,
autosave = true,
ignored_dirs = {
-- Setting the parent of our current to an ignored directory
{
string.format("%s%s..%s", vim.fn.getcwd(), fp_sep, fp_sep),
exact = true,
},
},
})
coroutine.yield()
local content = vim.fn.getline(1, "$")
assert.equals("If you're reading this, I guess auto-loading works", content[1])
end)
end)

View File

@ -0,0 +1,20 @@
local session_dir = vim.fn.getcwd() .. "/tests/dummy_data/"
require("persisted").setup({
save_dir = session_dir,
autoload = true,
ignored_dirs = { vim.fn.getcwd() },
})
describe("Autoloading", function()
it("is stopped if an ignored dir is present", function()
local co = coroutine.running()
vim.defer_fn(function()
coroutine.resume(co)
end, 1000)
coroutine.yield()
local content = vim.fn.getline(1, "$")
assert.equals(content[1], "")
end)
end)

View File

@ -0,0 +1,77 @@
local session_dir = vim.fn.getcwd() .. "/tests/default_data/"
require("persisted").setup({
save_dir = session_dir,
})
describe("With default settings:", function()
after_each(function()
-- vim.fn.system("rm -rf " .. e(session_dir))
end)
it("it saves a session", function()
-- Check no file exists
assert.equals(vim.fn.system("ls tests/default_data | wc -l"):gsub("%s+", ""), "0")
-- Edit a buffer
vim.cmd(":e tests/stubs/test.txt")
vim.cmd(":w")
-- Save the session
require("persisted").save()
-- Check that the session is written to disk
assert.equals(vim.g.persisting, true)
assert.equals("1", vim.fn.system("ls tests/default_data | wc -l"):gsub("%s+", ""))
end)
it("it loads a session", function()
-- Load a session
require("persisted").load()
-- Read the buffers contents
local content = vim.fn.getline(1, "$")
assert.equals("This is a test file", content[1])
assert.equals(vim.g.persisting, true)
end)
it("it stops a session", function()
require("persisted").stop()
assert.equals(vim.g.persisting, false)
end)
it("it starts a session", function()
require("persisted").start()
assert.equals(vim.g.persisting, true)
end)
it("it lists sessions", function()
local sessions = require("persisted").list()
local path = require("plenary.path"):new(sessions[1].file_path)
assert.equals(path:is_path(), true)
end)
end)
local async = require("plenary.async.tests")
local util = require("plenary.async.util")
async.describe("With default settings:", function()
async.it("it deletes a session", function()
require("persisted").delete()
util.scheduler()
assert.equals("0", vim.fn.system("ls tests/default_data | wc -l"):gsub("%s+", ""))
end)
end)
describe("Utilities", function()
it("can make derive the session name", function()
local session = "%home%username%projects%front@@user%fix-analytics-export-null-values.vim"
local data = require("persisted.utils").make_session_data(session)
assert.equals("home/username/projects/front@@user/fix-analytics-export-null-values", data.name)
end)
end)

View File

@ -0,0 +1,61 @@
pcall(vim.fn.system, "rm -rf tests/dummy_data")
local session_dir = vim.fn.getcwd() .. "/tests/dummy_data/"
-- follow_cwd = true
require("persisted").setup({
save_dir = session_dir,
follow_cwd = true,
})
describe("Follow cwd change", function()
it("creates two sessions with change in cwd", function()
vim.cmd(":e tests/stubs/test_autoload.txt")
vim.cmd(":w")
require("persisted").save()
vim.cmd(":cd tests/stubs")
vim.cmd(":w")
require("persisted").save()
vim.cmd(":bdelete")
end)
it("ensures both sessions were created", function()
local pattern = "/"
local name1 = vim.fn.getcwd():gsub(pattern, "%%") .. require("persisted").get_branch() .. ".vim"
vim.cmd(":cd ../..")
local name2 = vim.fn.getcwd():gsub(pattern, "%%") .. require("persisted").get_branch() .. ".vim"
local sessions = vim.fn.readdir(session_dir)
assert.equals(sessions[1], name1)
assert.equals(sessions[2], name2)
end)
end)
-- follow_cwd = false
pcall(vim.fn.system, "rm -rf tests/dummy_data")
require("persisted").setup({
save_dir = session_dir,
follow_cwd = false,
})
describe("Don't follow cwd change", function()
it("creates two sessions with change in cwd", function()
vim.cmd(":e tests/stubs/test_autoload.txt")
vim.cmd(":w")
require("persisted").save()
require("persisted").load()
vim.cmd(":cd tests/stubs")
vim.cmd(":w")
require("persisted").save()
vim.cmd(":bdelete")
end)
it("ensures only one session was created", function()
local pattern = "/"
vim.cmd(":cd ../..")
local name = vim.fn.getcwd():gsub(pattern, "%%") .. require("persisted").get_branch() .. ".vim"
local sessions = vim.fn.readdir(session_dir)
assert.equals(#sessions, 1)
assert.equals(name, sessions[1])
end)
end)

View File

@ -0,0 +1,33 @@
pcall(vim.fn.system, "rm -rf tests/git_branch_data")
local session_dir = vim.fn.getcwd() .. "/tests/git_branch_data/"
require("persisted").setup({
save_dir = session_dir,
use_git_branch = true,
})
describe("Git Branching", function()
it("creates a session", function()
vim.fn.system("mkdir tests/git_branch_data")
vim.fn.system("cd tests/git_branch_data && git init")
assert.equals(vim.fn.system("ls tests/git_branch_data | wc -l"):gsub("%s+", ""), "0")
vim.cmd(":e tests/stubs/test_git_branching.txt")
vim.cmd(":w tests/git_branch_data/test_git_branching.txt")
require("persisted").save()
assert.equals(vim.fn.system("ls tests/git_branch_data | wc -l"):gsub("%s+", ""), "2")
end)
it("ensures the session has the branch name in", function()
-- Workout what the name should be
local pattern = "/"
local name = vim.fn.getcwd():gsub(pattern, "%%") .. require("persisted").get_branch() .. ".vim"
local session = vim.fn.glob(session_dir .. "*.vim", true, true)[1]
session:gsub(session_dir .. "/", "")
assert.equals(session, vim.fn.getcwd() .. "/tests/git_branch_data/" .. name)
-- assert.equals(sessions[1]:gsub(pattern, "%%"), name)
end)
end)

View File

@ -0,0 +1,10 @@
set rtp+=.
set rtp+=./misc/plenary
runtime! plugin/plenary.vim
command Setup PlenaryBustedDirectory tests/setup {minimal_init = 'tests/minimal.vim'}
command TestAutoloading PlenaryBustedDirectory tests/autoload {minimal_init = 'tests/minimal.vim'}
command TestGitBranching PlenaryBustedDirectory tests/git_branching {minimal_init = 'tests/minimal.vim'}
command TestFollowCwd PlenaryBustedDirectory tests/follow_cwd/follow_cwd.lua {minimal_init = 'tests/minimal.vim'}
command TestDefaults PlenaryBustedFile tests/default_settings_spec.lua
command TearDown PlenaryBustedFile tests/teardown/clean_up_dirs.lua

View File

@ -0,0 +1,17 @@
pcall(vim.fn.system, "rm -rf tests/dummy_data")
local session_dir = vim.fn.getcwd() .. "/tests/dummy_data/"
require("persisted").setup({
save_dir = session_dir,
autoload = true,
autosave = true,
})
describe("As part of the setup", function()
it("creates a session to autoload from", function()
vim.cmd(":e tests/stubs/test_autoload.txt")
vim.cmd(":w")
require("persisted").save()
end)
end)

View File

@ -0,0 +1 @@
This is a test file

View File

@ -0,0 +1 @@
If you're reading this, I guess auto-loading works

View File

@ -0,0 +1 @@
Git branching is awesome

View File

@ -0,0 +1,7 @@
describe("As part of the teardown", function()
it("dummy data is deleted", function()
pcall(vim.fn.system, "rm -rf tests/dummy_data")
pcall(vim.fn.system, "rm -rf tests/git_branch_data")
pcall(vim.fn.system, "rm -rf tests/default_data")
end)
end)