LuaSnip Versions Save

Snippet Engine for Neovim written in Lua.

v2.3.0

1 month ago

2.3.0

Highlights

Easier event-callbacks (#1092)

Before this change, event-callbacks for some node had to be defined in the snippet/snippetNode that contained it. Since that is cumbersome, it's now possible to define them in the node-opts:

s("qwer", {t'  ', i(1, "asdf", {node_callbacks = {[events.enter] = function()
	print("enter!!!")
end}})})

Healthcheck (#1149)

LuaSnip now checks if jsregexp is in working order when :checkhealth is performed. Try it, but keep in mind that jsregexp is fully optional, and not necessarily required.

New Contributors

Full Changelog: https://github.com/L3MON4D3/LuaSnip/compare/v2.2.0...v2.3.0

And, as always, a big Thank You to all new and recurring contributors :heart:

v2.2.0

4 months ago

Highlights

Overhaul of vscode/snipmate/lua-loaders

The implementation of the loaders was greatly refactored, and there are a few new features:

  • We can now reload files when they are updated in another neovim-instance/in some other editor (via libuv). This can be enabled by passing fs_event_providers = {libuv=true} to the load-call, like so
require("luasnip.loaders.from_lua").lazy_load({paths = "./luasnippets", fs_event_providers = {libuv=true}})
  • Files that are created after the load-call will be loaded (which was not the case previously)
  • Similarly to the above, it is also possible to "register" a snippet-collection for loading as soon as it is created. To enable this for some collection, pass it to the lazy_paths-key in load:
require("luasnip.loaders.from_lua").load({lazy_paths = ".luasnippets"})

(this example is especially useful since it will load a snippet-collection in the current directory, which could be a project-specific one)

  • The lua-loader may define dependencies on files, such that the snippet-files that depend on some file will be reloaded when the file is edited. Load a file via ls_tracked_dofile or a package via ls_tracked_dopackageto make use of this.

All of these features are also documented in DOC.md, take a look for more details.

What's Changed

New Contributors

A hearty Thank You! to all contributors :)

Full Changelog: https://github.com/L3MON4D3/LuaSnip/compare/v2.1.1...v2.2.0

v2.1.1

5 months ago

Very minor release, the only changes to the previous release are a few small bugfixes and support for jsregexp 0.0.6 in addition to 0.0.5.

Full Changelog: https://github.com/L3MON4D3/LuaSnip/compare/v2.1.0...v2.1.1

v2.1.0

6 months ago

Deprecations and Breaking Changes

Only a deprecation in this release: the history-option is superseded by the more granular keep_roots, link_roots, and link_children. Take a look at the documentation for more information on the new options.

Highlights

Snippet-insertion (#941)

Before this PR, a newly-expanded snippet was always set up such that after jumping through it, the node that was active during expansion was entered again. This was very easy to implement, but unfortunately can cause annoying issues when the active node is not close to the expanded snippet (jumping across the whole buffer). The improved snippet-insertion prevents issues like this by linking the jumps of newly-expanded snippets not based on active node, but based on buffer-position. There is some information on it here. The following recording shows the new behaviour, for example how snippets are traversed based on buffer-position, and how inserting snippets into a node properly activates it (visible due to orange dots indicating an active choiceNode)

https://github.com/L3MON4D3/LuaSnip/assets/41961280/7dea57aa-17da-4ce4-b0cd-d214d6b30e93

Another nice feature enabled by the datastructures that have to be maintained to allow this behaviour is that given some buffer-position (for example the cursor), we can look for the node that is located there. See this entry in the wiki for an example of this.

Treesitter-postfix (#980)

Another pretty cool feature: where previously postfix-snippets could only capture text matching some pattern, treesitter-postfix-snippets can capture text from treesitter-queries! Since captures from the query are also made available to the snippet (via snippet.env), this may be a quick way to quickly refactor, see for example the following:

ts_post({
    matchTSNode = {
        query = [[
            (function_declaration
              name: (identifier) @fname
              parameters: (parameters) @params
              body: (block) @body
            ) @prefix
        ]],
        query_lang = "lua",
    },
    trig = ".var"
}, fmt([[
    local {} = function{}
        {}
    end
]], {
    l(l.LS_TSCAPTURE_FNAME),
    l(l.LS_TSCAPTURE_PARAMS),
    l(l.LS_TSCAPTURE_BODY),
}))

The documentation has another example, and describes the various options.

Luasnip-Luarock (#1050)

Finally, luasnip is now available as a luarock, which means it can be added by rocks.nvim.

What's Changed

New Contributors

A big Thank You! to all contributors to LuaSnip :heart: :)

Full Changelog: https://github.com/L3MON4D3/LuaSnip/compare/v2.0.0...v2.1.0

v2.0.0

10 months ago

Breaking Changes

Drop support for neovim < 0.7. Since this probably affects very few users, and enables access to some better api, this decision was obvious.

Highlights

Multi-Context snippets

This essentially makes it possible to trigger one snippet in different ways. For example, one may want to trigger a snippet with a short trigger manually, but use a longer one as an autotrigger. Or, expand a snippet automatically in comments, and manually otherwise (or, and maybe more likely, vice versa).

The DOC.md-entry is pretty comprehensive, so I won't repeat it here.

Snippet-Source

If loaders_store_source is set to true in ls.setup, the loaders will attach information about the source of a snippet (like file, row, column of its definition).
We of course also include a consumer for this information: luasnip.extras.snip_location can jump to the source of the current snippet! Combined with auto-reload provided by the loaders, this allows quick edits of the current snippet, for example if it does not behave correctly.

For easy access, create a command for calling the function.

vim.api.nvim_create_user_command("LuaSnipEditS", require("luasnip.extras.snip_location").jump_to_active_snippet, {})

https://github.com/L3MON4D3/LuaSnip/assets/41961280/8b1c3c22-76bb-4f6f-aabe-e1eb1527e3fd

To work correctly for all loaders, this needs treesitter, and parsers for json (jsonc if there are snippet-files in that format) and lua.

More details here and here

Key-indexer

This can be used to easily refer to nodes (for example, if their text should be passed to a dynamic/functionNode) several levels deep in a snippet, simply by a unique identifier. Once again, the documentation has some details+examples.

.code-snippets

Support for the .code-snippets-format used by vscode was also added since the last release. It is useful for project-specific snippets, and a bit more compact than the other formats, since only a single file has to be added, not a directory (and god forbid, not a package.json which has to correctly list all files). More here.

More trigger-types!!

Finally, support for actual regexes as snippet-triggers. Supports ecma and vim-flavoured regex, as well as completely custom behaviour. The documentation (look for the trigEngine-entry in the first list) has some more details.

PR's since last release:

Big Thank You! to all new contributors :)

Full Changelog: https://github.com/L3MON4D3/LuaSnip/compare/v1.2.1...v1.3

v1.2.1

1 year ago

Very minor release: The difference to 1.2.0 is only a fix for an off-by-one-error in the lsp-snippets, which causes a large amount of possible transformations to fail (but hasn't come up until now, so not-so-common?? :shrug:)

See #746 and d191820

Full Changelog: https://github.com/L3MON4D3/LuaSnip/compare/v1.2.0...v1.2.1

v1.2.0

1 year ago

Deprecations & Breaking Changes

None :)

New Features

ls_file_snippets and ls_file_autosnippets

Up until now the snippets loaded by the lua-loader had to be returned in a big list at the end of the files. This prevents defining functions used in those snippets near them (at least if they are defined naively), and generally makes these files less readable.

Now, the tables ls_file_snippets and ls_file_autosnippets exposed (additionally!), and snippets can be added to them and don't have to be returned at the end of the files.

To really make use of this, adding something like

ls.setup({
	snip_env = {
		s = function(...)
			local snip = ls.s(...)
			-- we can't just access the global `ls_file_snippets`, since it will be
			-- resolved in the environment of the scope in which it was defined.
			table.insert(getfenv(2).ls_file_snippets, snip)
		end,
		parse = function(...)
			local snip = ls.parser.parse_snippet(...)
			table.insert(getfenv(2).ls_file_snippets, snip)
		end,
		-- remaining definitions.
		...
	},
	...
})

makes s and parse automatically add the snippets defined with them.

edit_snippet_files: extend-option, by @pianocomposer321

edit_snippet_files can be used to quickly jump to any file contributing snippets to the current file. Before 61238b9e0ef, it was only possible to select an already-existing file, after it, it's possible to add some custom logic to extend the list of found files arbritarily.
A great application of this is the possibility of adding a new file in known snippet-collections:

-- loaded collections.
local snippet_collections = {
	-- lua-snippets
	{
		dir = "/home/simon/.config/nvim/luasnippets",
		extension = "lua"
	},
	-- snipmate-snippets
	-- this would edit snippets provided by vim-snippets.
	{
		dir = "/home/simon/.local/share/nvim/site/pack/packer/start/vim-snippets/snippets/",
		extension = "snippets"
	}
	-- vscode would be much more involved, if you figure something out, showcase
	-- it in a discussion :D
}

require("luasnip.loaders").edit_snippet_files({
	extend = function(ft, files)
		local extend_items = {}

		for _, collection in ipairs(snippet_collections) do
			-- check if a file from the collection is present in the items
			-- already.
			for _, file in ipairs(files) do
				if file:match(collection.dir) then
					-- a file is in personal_dir, no need to create a new file there.
					goto continue
				end
			end

			-- not present, create item to add this file.
			table.insert(extend_items, {
				-- label of the new file.
				"New file in " .. collection.dir,
				-- location of the new file.
				("%s/%s.%s"):format(collection.dir, ft, collection.extension)})

			-- luajit only!!
			:: continue ::
		end

		return extend_items
	end
})

(The doc-entry contains a simpler example, and a proper definition of the option).

Repeat nodes automatically in fmt, by @uyha

This can be used to make snippets created with fmt more readable, by repeating nodes belonging to duplicated keys.

s("fmt", fmt([[
	This {iNode} will be repeated here {iNode}
]], {
	iNode = i(1)
}, {repeat_duplicates = true}))

repeat_duplicates is false by defaults, use extend_decorator to override this default if you prefer this behaviour to failing.

pascalcase and camelcase for lsp-snippets

Up until now, we had not implemented these transformations. As long as jsregexp is installed, these will behave exactly like they do in vscode.

Logging

Luasnip now has some logging in place! This is especially useful to quickly find misconfigurations with the loaders, so definitely give it a shot. Some more details in the doc.

Pretty display of available snippets, by @lyonelz96

Opens a buffer with a pretty display of all available snippets. Very configurable! The doc contains an extensive overview of what is possible with this. So far, there is only one pretty static way to show the available snippets in a buffer, enhancing this to create a tree-like view of all snippets would be a nice extension.

New Contributors

Thanks, all of you :)

Full Changelog: https://github.com/L3MON4D3/LuaSnip/compare/v1.1.0...v1.2.0

v1.1.0

1 year ago

Deprecations

extras.expand_conditions

See here Necessitated by the subsequently introduced condition-objects.

Notable additions

Autotrigger-option for individual snippets (by @atticus-sullivan)

Previously the autotriggered-feature for a snippet could only (somewhat implicitly) be specified when adding snippets (add_snippets(..., {type = "autosnippets"}) or put the snippet into the second table returned by lua-loader-loaded files). This was originally due to a limitation (snippets had to be assigned to different tables ls.snippets or ls.autosnippets, as the OGs will remember :P) which has not existed for a while, and now we finally make use of its removal.

Basically:

ls.add_snippets("all", {
	s({trig = "trig", snippetType = "autosnippet"}, {
		t"autotriggerd"
	})
})

Will add the autotriggered snippet, without passing additional parameters to s. For extra style, one may

local autosnippet = ls.extend_decorator.apply(s, {snippetType = "autosnippet"})

so autotriggered snippets can be easily created with autosnippet.

The old ways of creating autosnippets are still present, and not deprecated, so for adding bigger collections of snippets as autosnippets, they can be used as well.

Condition-objects (by @atticus-sullivan)

Condition-objects!! They are useful for combining multiple conditions into logical expressions:

-- two show-conditions
local function even_line()
	-- omitted
end
local function line_end()
	-- omitted
end

-- without condition-objects:
s(
	-- omitted
{
	show_condition = function(...)
		return even_line(...) and line_end(...)
	end
	-- show-conditions can also be passed to `condition`!
	condition = function(...)
		return even_line(...) and line_end(...)
	end,
})

-- with condition-objects:
local make_condition = require("luasnip.extras.conditions").make_condition
local even_line_obj = make_condition(even_line)
local end_line_obj = make_condition(end_line)

s(
	-- omitted
{
	-- "*" for `and`
	show_condition = even_line_obj * line_end_obj
	condition = even_line_obj * line_end_obj
})

Much more readable!

There are more details in the doc

Big Documentation overhaul (by me!)

The doc is now much more coherent and detailed, and concepts like jump-index (i(1) <- that number) and node-reference (f(somefunction, {1,2}) <- those numbers) are properly explained (and consistenly named!)

Readme (by @ejmastnak)

This is a smaller addition, but important nonetheless: The readme now contains an improved Getting Started-section, and a section for external resources (Videos, blogs, articles, anything really) on luasnip. Feel free to extend it!

Smaller additions

Full Changelog: https://github.com/L3MON4D3/LuaSnip/compare/v1.0.0...v1.1.0

v1.0.0

1 year ago

We're doing versioning now!! :D

The current plan is adhering closely to semver, although just vMajor.Minor.Patch

This means that there's now an easy way to prevent surprise-breaking changes: configure your package manager to stick to some version (or version matching a pattern), and manually update this version once you're comfortable: For packer, this means

use({"L3MON4D3/LuaSnip", tag = "v<CurrentMajor>.*"})

and for vim-plug

Plug 'L3MON4D3/LuaSnip', {'tag': 'v<CurrentMajor>.*'}

The above examples will update when a new minor or patch version becomes available, but not when the major changes (this is my recommendation, it will prevent breaking changes from suddenly messing up your config). Of course, following the minor, or even the patch is also an option.

I recommend watching releases so you're aware of new stuff (all the way at the top watch -> custom -> releases)

That's all, until the next release :wave: (hopefully not 2.0.0 :D)