Automagical editing and creation of snippets.
add-new-snippet.mp4
edit-existing-snippet.mp4
- Add new snippets, edit snippets, or delete snippets on the fly.
- Syntax highlighting while you edit the snippet. Includes highlighting of
tabstops and placeholders such as
$0,${2:foobar}, or$CLIPBOARD - Automagical conversion from buffer text to JSON string.
- Intuitive UI for editing the snippet, dynamically adapting the number of prefixes.
- Automatic hot-reloading of any changes, so you do not have to restart nvim for changes to take effect.
- Optional JSON-formatting and sorting of the snippet file. (Useful when version-controlling your snippet collection.)
- Snippet/file selection via
telescope,snacks, orvim.ui.select. - Automatic bootstrapping of the snippet folder or new snippet files if needed.
- Prettifies snippet files for clean diffs in version control.
- Supports only VSCode-style snippets.
Tip
You can use snippet-converter.nvim to convert your snippets to the VS Code format.
- The VSCode snippet format is the closest thing to a standard regarding snippets. It is used by friendly-snippets and supported by most snippet engine plugins for nvim.
- However, VS Code snippets are stored as JSON, which are a pain to modify manually. This plugin alleviates that pain by automagically writing the JSON for you.
- nvim 0.12+
- Snippets saved in the VS Code-style snippet format.
- Recommended:
- One of the following pickers:
- Without one of them, the plugin falls back to
vim.ui.select, which works but lacks search and snippet previews.
- A snippet engine that can load VS Code-style snippets, such as:
- Optional: Treesitter parsers for the languages you want syntax highlighting for.
-- lazy.nvim
{
"chrisgrieser/nvim-scissors",
dependencies = "folke/snacks.nvim", -- either snacks, fzf-lua, telescope
-- dependencies = "ibhagwan/fzf-lua",
-- dependencies = "nvim-telescope/telescope.nvim",
opts = {
snippetDir = "path/to/your/snippetFolder",
}
},
-- packer
use {
"chrisgrieser/nvim-scissors",
dependencies = "folke/snacks.nvim", -- either snacks, fzf-lua, telescope
-- dependencies = "ibhagwan/fzf-lua",
-- dependencies = "nvim-telescope/telescope.nvim",
config = function()
require("scissors").setup ({
snippetDir = "path/to/your/snippetFolder",
})
end,
}In addition, your snippet engine must point to the same snippet folder as
nvim-scissors:
Tip
vim.fn.stdpath("config") returns the path to your nvim config.
require("luasnip.loaders.from_vscode").lazy_load {
paths = { "path/to/your/snippetFolder" },
}mini.snippets preferred snippet location is any snippets/ directory in the
runtimepath. For manually maintained snippets the best location is the user
config directory, which requires the following nvim-scissors setup:
require("scissors").setup({
snippetDir = vim.fn.stdpath("config") .. "/snippets",
})The mini.snippets setup requires explicit definition of loaders. Following its
quickstart
guide should be enough to make it respect snippets from 'snippets/' directory
inside user config. Note: nvim-scissors works only with VS-Code-style
snippet files (not Lua files or JSON arrays), and requires a
package.json for the VS Code format.
require("blink.cmp").setup {
sources = {
providers = {
snippets = {
opts = {
search_paths = { "path/to/your/snippetFolder" },
},
}
}
}
}It is recommended to use the latest release of blink.cmp for hot-reloading to
work.
require("lspconfig").basics_ls.setup({
settings = {
snippet = {
enable = true,
sources = { "path/to/your/snippetFolder" }
},
}
})require("nvim-snippets").setup {
search_paths = { "path/to/your/snippetFolder" },
}vim.g.vsnip_snippet_dir = "path/to/your/snippetFolder"
-- OR
vim.g.vsnip_snippet_dirs = { "path/to/your/snippetFolder" }require("yasp").setup {
paths = {
vim.fn.stdpath("config") .. "/snippets/package.json",
},
descs = { "user snippets" },
}The plugin provides two Lua functions, .addNewSnippet() and .editSnippet():
vim.keymap.set(
"n",
"<leader>se",
function() require("scissors").editSnippet() end,
{ desc = "Snippet: Edit" }
)
-- when used in visual mode, prefills the selection as snippet body
vim.keymap.set(
{ "n", "x" },
"<leader>sa",
function() require("scissors").addNewSnippet() end,
{ desc = "Snippet: Add" }
)You can also use :ScissorsAddNewSnippet and :ScissorsEditSnippet if you
prefer ex commands.
The :ScissorsAddSnippet ex command also accepts a range to prefill the snippet
body (for example :'<,'> ScissorsAddNewSnippet or :3 ScissorsAddNewSnippet).
The popup is just one window, so you can move between the prefix area and the
body with j and k or any other movement command. ("Prefix" is how trigger
words are referred to in the VS Code format.)
Use showHelp (default keymap: ?) to show a notification containing all
keymaps.
The popup intelligently adapts to changes in the prefix area: Each line represents one prefix, and creating or removing lines in that area thus changes the number of prefixes.
The .setup() call is optional.
-- default settings
require("scissors").setup {
snippetDir = vim.fn.stdpath("config") .. "/snippets",
editSnippetPopup = {
height = 0.4, -- relative to the window, between 0-1
width = 0.6,
border = vim.o.winborder,
keymaps = {
cancel = "q",
saveChanges = "<CR>", -- alternatively, can also use `:w`
goBackToSearch = "<BS>",
deleteSnippet = "<C-BS>",
duplicateSnippet = "<C-d>",
openInFile = "<C-o>",
insertNextPlaceholder = "<C-p>", -- insert & normal mode
showHelp = "?",
},
},
snippetSelection = {
picker = "auto", ---@type "auto"|"fzf-lua"|"telescope"|"snacks"|"vim.ui.select"
--- @module 'fzf-lua'
fzfLua = {
-- same format as fzf_opts in `:h fzf-lua-customization`
fzf_opts = {},
-- suppress warnings from fzf-lua.
-- This is true by default, since commonly-used fzf-lua presets
-- create warnings due to border settings.
silent = true,
-- same format as winopts in `:h fzf-lua-customization`
winopts = {
preview = {
hidden = false,
},
},
},
telescope = {
-- By default, the query only searches snippet prefixes. Set this to
-- `true` to also search the body of the snippets.
alsoSearchSnippetBody = false,
-- accepts the common telescope picker config
opts = {
layout_strategy = "horizontal",
layout_config = {
horizontal = { width = 0.9 },
preview_width = 0.6,
},
},
},
-- `snacks` picker configurable via snacks config,
-- see https://github.com/folke/snacks.nvim/blob/main/docs/picker.md
},
jsonFormatOpts = { -- formatting of snippet files, passed to `:h vim.json.encode()`
sort_keys = true,
indent = " ",
},
backdrop = {
enabled = true,
blend = 50, -- between 0-100
},
icons = {
scissors = "",
},
}This plugin requires that you have a valid VS Code snippet folder. In addition
to saving the snippets in the required JSON format, there must also be a
package.json file at the root of the snippet folder, specifying which files
should be used for which languages.
Example file structure inside the snippetDir:
.
├── package.json
├── python.json
├── project-specific
│ └── nvim-lua.json
├── javascript.json
└── allFiletypes.jsonExample package.json:
{
"contributes": {
"snippets": [
{
"language": "python",
"path": "./python.json"
},
{
"language": "lua",
"path": "./project-specific/nvim-lua.json"
},
{
"language": ["javascript", "typescript"],
"path": "./javascript.json"
},
{
"language": "all",
"path": "./allFiletypes.json"
}
]
},
"name": "my-snippets"
}Note
The special filetype all enables the snippets globally, regardless of
filetype.
Example snippet file (here: nvim-lua.json):
{
"autocmd (Filetype)": {
"body": [
"vim.api.nvim_create_autocmd(\"FileType\", {",
"\tpattern = \"${1:ft}\",",
"\tcallback = function()",
"\t\t$0",
"\tend,",
"})"
],
"prefix": "autocmd (Filetype)"
},
"file exists": {
"body": "local fileExists = vim.uv.fs_stat(\"${1:filepath}\") ~= nil",
"prefix": "file exists"
},
}For details, read the official VS Code snippet documentation:
Tabstops
are denoted by $1, $2, $3, etc., with $0 being the last tabstop. They
support placeholders such as ${1:foobar}.
Note
Due to the use of $ in the snippet syntax, any literal $ needs to be
escaped as \$.
Furthermore, there are various variables you can use, such as $TM_FILENAME or
$LINE_COMMENT.
See here for a full list of variables.
Even though the snippets from the
friendly-snippets repository
are written in the VS Code-style format, editing them directly is not supported.
The reason being that any changes made would be overwritten as soon as the
friendly-snippets repository is updated (which happens fairly regularly).
Unfortunately, there is little nvim-scissors can do about that.
What you can do, however, is to copy individual snippets files from the
friendly-snippets repository into your own snippet folder, and edit them
there.
nvim-scissors only allows you to edit the snippet prefix and snippet body, to
keep the UI as simple as possible. For the few cases where you need to edit a
snippet's title or description, you can use the openInFile keymap and edit
them directly in the snippet file.
This plugin writes JSON files via vim.encode.json(), with the default options
writing them in a prettified manner for clean diffs.
Previous versions of nvim-scissors required a dependency like jq since
vim.encode.json() was not able to deterministically prettify snippet files
yet.
With Luasnip, this is an opt-in feature, enabled via:
require("luasnip").setup {
store_selection_keys = "<Tab>",
}In your VS Code-style snippet, use the token $TM_SELECTED_TEXT at the location
where you want the selection to be inserted. (It's roughly the equivalent of
LS_SELECT_RAW in the Luasnip syntax.)
Then, in visual mode, press the key from store_selection_keys. The selection
disappears, and you are put in insert mode. The next snippet you now trigger
is going to have $TM_SELECTED_TEXT replaced with your selection.
While the VS Code snippet format does not support auto-triggered snippets,
LuaSnip allows you to specify auto-triggering in the VS Code-style JSON files
by
adding the luasnip key.
nvim-scissors does not touch any keys other than prefix and body in the
JSON files, so any additions like the luasnip key are preserved.
Tip
You can use the openInFile keymap to directory open JSON file at the
snippet's location to make edits there easier.
In my day job, I am a sociologist studying the social mechanisms underlying the digital economy. For my PhD project, I investigate the governance of the app economy and how software ecosystems manage the tension between innovation and compatibility. If you are interested in this subject, feel free to get in touch.
If you find this project helpful, you can support me via 🩷 GitHub Sponsors.