javadoc.mp4
- Tabstops
- Text-Transformations using Lua functions
- Conditional Expansion
- Defining nested Snippets
- Filetype-specific Snippets
- Choices
- Dynamic Snippet creation
- Regex-Trigger
- Autotriggered Snippets
- Fast
- Parse LSP-Style Snippets either directly in lua, as a vscode package or a snipmate snippet collection.
- Expand LSP-Snippets with nvim-compe (or its' successor, nvim-cmp (requires cmp_luasnip))
- Snippet history (jump back into older snippets)
- Resolve filetype at the cursor using Treesitter
- Snippets that make use of the entire functionality of this plugin have to be defined in Lua (but 95% of snippets can be written in lsp-syntax).
- The LSP-parser does not support Regex-Transformations.
Neovim >= 0.5 (extmarks)
Ie. With vim-plug
in vimscript
" press <Tab> to expand or jump in a snippet. These can also be mapped separately
" via <Plug>luasnip-expand-snippet and <Plug>luasnip-jump-next.
imap <silent><expr> <Tab> luasnip#expand_or_jumpable() ? '<Plug>luasnip-expand-or-jump' : '<Tab>'
" -1 for jumping backwards.
inoremap <silent> <S-Tab> <cmd>lua require'luasnip'.jump(-1)<Cr>
snoremap <silent> <Tab> <cmd>lua require('luasnip').jump(1)<Cr>
snoremap <silent> <S-Tab> <cmd>lua require('luasnip').jump(-1)<Cr>
" For changing choices in choiceNodes (not strictly necessary for a basic setup).
imap <silent><expr> <C-E> luasnip#choice_active() ? '<Plug>luasnip-next-choice' : '<C-E>'
smap <silent><expr> <C-E> luasnip#choice_active() ? '<Plug>luasnip-next-choice' : '<C-E>'
or in lua (includes supertab-like functionality with nvim-cmp)
local function prequire(...)
local status, lib = pcall(require, ...)
if (status) then return lib end
return nil
end
local luasnip = prequire('luasnip')
local cmp = prequire("cmp")
local t = function(str)
return vim.api.nvim_replace_termcodes(str, true, true, true)
end
local check_back_space = function()
local col = vim.fn.col('.') - 1
if col == 0 or vim.fn.getline('.'):sub(col, col):match('%s') then
return true
else
return false
end
end
_G.tab_complete = function()
if cmp and cmp.visible() then
cmp.select_next_item()
elseif luasnip and luasnip.expand_or_jumpable() then
return t("<Plug>luasnip-expand-or-jump")
elseif check_back_space() then
return t "<Tab>"
else
cmp.complete()
end
return ""
end
_G.s_tab_complete = function()
if cmp and cmp.visible() then
cmp.select_prev_item()
elseif luasnip and luasnip.jumpable(-1) then
return t("<Plug>luasnip-jump-prev")
else
return t "<S-Tab>"
end
return ""
end
vim.api.nvim_set_keymap("i", "<Tab>", "v:lua.tab_complete()", {expr = true})
vim.api.nvim_set_keymap("s", "<Tab>", "v:lua.tab_complete()", {expr = true})
vim.api.nvim_set_keymap("i", "<S-Tab>", "v:lua.s_tab_complete()", {expr = true})
vim.api.nvim_set_keymap("s", "<S-Tab>", "v:lua.s_tab_complete()", {expr = true})
vim.api.nvim_set_keymap("i", "<C-E>", "<Plug>luasnip-next-choice", {})
vim.api.nvim_set_keymap("s", "<C-E>", "<Plug>luasnip-next-choice", {})
or in lua with nvim-compe
local function prequire(...)
local status, lib = pcall(require, ...)
if (status) then return lib end
return nil
end
local luasnip = prequire('luasnip')
local t = function(str)
return vim.api.nvim_replace_termcodes(str, true, true, true)
end
local check_back_space = function()
local col = vim.fn.col('.') - 1
if col == 0 or vim.fn.getline('.'):sub(col, col):match('%s') then
return true
else
return false
end
end
_G.tab_complete = function()
if vim.fn.pumvisible() == 1 then
return t "<C-n>"
elseif luasnip and luasnip.expand_or_jumpable() then
return t("<Plug>luasnip-expand-or-jump")
elseif check_back_space() then
return t "<Tab>"
else
return vim.fn['compe#complete']()
end
return ""
end
_G.s_tab_complete = function()
if vim.fn.pumvisible() == 1 then
return t "<C-p>"
elseif luasnip and luasnip.jumpable(-1) then
return t("<Plug>luasnip-jump-prev")
else
return t "<S-Tab>"
end
return ""
end
vim.api.nvim_set_keymap("i", "<Tab>", "v:lua.tab_complete()", {expr = true})
vim.api.nvim_set_keymap("s", "<Tab>", "v:lua.tab_complete()", {expr = true})
vim.api.nvim_set_keymap("i", "<S-Tab>", "v:lua.s_tab_complete()", {expr = true})
vim.api.nvim_set_keymap("s", "<S-Tab>", "v:lua.s_tab_complete()", {expr = true})
vim.api.nvim_set_keymap("i", "<C-E>", "<Plug>luasnip-next-choice", {})
vim.api.nvim_set_keymap("s", "<C-E>", "<Plug>luasnip-next-choice", {})
For nvim-cmp, it is also possible to follow the example recommendation from the nvim-cmp wiki.
-
Vscode-like: To use existing vs-code style snippets from a plugin (eg. rafamadriz/friendly-snippets) simply install the plugin and then add
require("luasnip.loaders.from_vscode").lazy_load()
somewhere in your nvim-config. LuaSnip will then load the snippets contained in the plugin on startup. You can also easily load your own custom vscode style snippets by passing the path to the custom snippet-directory to the load function:
-- load snippets from path/of/your/nvim/config/my-cool-snippets require("luasnip.loaders.from_vscode").lazy_load({ paths = { "./my-cool-snippets" } })
For more info on the vscode-loader, check the examples or documentation.
-
Snipmate-like: Very similar to Vscode-packages: install a plugin that provides snippets and call the
load
-function:require("luasnip.loaders.from_snipmate").lazy_load()
The snipmate format is very simple, so adding custom snippets only requires a few steps:
- add a directory beside your
init.vim
(or any other place that is in yourruntimepath
) namedsnippets
. - inside that directory, create files named
<filetype>.snippets
and add snippets for the given filetype in it (for inspiration, check honza/vim-snippets).# comment snippet <trigger> <description> <snippet-body> snippet if C-style if if ($1) $0
- add a directory beside your
-
Lua: Add the snippets by calling
require("luasnip").add_snippets(filetype, snippets)
. An example for this can be found here.
This can also be done much better (one snippet-file per filetype+command for editing the current filetype+reload on edit) than in the example by using the loader for lua There's also a repository collecting snippets for various languages, molleweide/LuaSnip-snippets.nvim
Check DOC.md
(or :help luasnip
) for a short overview and in-depth explanations of the different nodes and available API.
I highly recommend looking into (or better yet, :luafile
ing) Examples/snippets.lua
before writing snippets in lua.
The Wiki contains some pretty useful extensions to Luasnip.
history
: If true, Snippets that were exited can still be jumped back into. As Snippets are not removed when their text is deleted, they have to be removed manually viaLuasnipUnlinkCurrent
ifdelete_check_events
is not enabled (set to eg.'TextChanged'
).update_events
: Choose which events trigger an update of the active nodes' dependents. Default is just'InsertLeave'
,'TextChanged,TextChangedI'
would update on every change.region_check_events
: Events on which to leave the current snippet if the cursor is outside its' 'region'. Disabled by default,'CursorMoved'
,'CursorHold'
or'InsertEnter'
seem reasonable.delete_check_events
: When to check if the current snippet was deleted, and if so, remove it from the history. Off by default,'TextChanged'
(perhaps'InsertLeave'
, to react to changes done in Insert mode) should work just fine (alternatively, this can also be mapped using<Plug>luasnip-delete-check
).store_selection_keys
: Mapping for populatingTM_SELECTED_TEXT
and related variables (not set by default).enable_autosnippets
: Autosnippets are disabled by default to minimize performance penalty if unused. Set totrue
to enable.ext_opts
: Additional options passed to extmarks. Can be used to add passive/active highlight on a per-node-basis (more info in DOC.md)parser_nested_assembler
: Override the default behaviour of inserting achoiceNode
containing the nested snippet and an emptyinsertNode
for nested placeholders ("${1: ${2: this is nested}}"
). For an example (behaviour more similar to vscode), check hereft_func
: Source of possible filetypes for snippets. Defaults to a function, which returnsvim.split(vim.bo.filetype, ".", true)
, but check filetype_functions for other optionssnip_env
: The global environment will be extended with this table in some places, eg. in files loaded by the lua-loader.
Settingsnip_env
to{ some_global = "a value" }
will add the global variablesome_global
while evaluating these files. If you mind the (probably) large number of generated warnings, consider adding the keys set here to the globals recognized by lua-language-server or add---@diagnostic disable: undefined-global
somewhere in the affected files.
Inspired by vsnip.vim