mirror of
https://github.com/L3MON4D3/LuaSnip
synced 2024-09-16 21:54:03 +02:00
3742 lines
155 KiB
Text
3742 lines
155 KiB
Text
*luasnip.txt* For NVIM v0.8.0 Last change: 2024 September 12
|
||
|
||
==============================================================================
|
||
Table of Contents *luasnip-table-of-contents*
|
||
|
||
1. Basics |luasnip-basics|
|
||
- Jump-Index |luasnip-basics-jump-index|
|
||
- Adding Snippets |luasnip-basics-adding-snippets|
|
||
- Snippet Insertion |luasnip-basics-snippet-insertion|
|
||
2. Node |luasnip-node|
|
||
- Api |luasnip-node-api|
|
||
3. Snippets |luasnip-snippets|
|
||
- Data |luasnip-snippets-data|
|
||
4. TextNode |luasnip-textnode|
|
||
5. InsertNode |luasnip-insertnode|
|
||
6. FunctionNode |luasnip-functionnode|
|
||
7. Node Reference |luasnip-node-reference|
|
||
8. ChoiceNode |luasnip-choicenode|
|
||
9. SnippetNode |luasnip-snippetnode|
|
||
10. IndentSnippetNode |luasnip-indentsnippetnode|
|
||
11. DynamicNode |luasnip-dynamicnode|
|
||
12. RestoreNode |luasnip-restorenode|
|
||
13. Key Indexer |luasnip-key-indexer|
|
||
14. Absolute Indexer |luasnip-absolute-indexer|
|
||
15. MultiSnippet |luasnip-multisnippet|
|
||
16. Extras |luasnip-extras|
|
||
- Lambda |luasnip-extras-lambda|
|
||
- Match |luasnip-extras-match|
|
||
- Repeat |luasnip-extras-repeat|
|
||
- Partial |luasnip-extras-partial|
|
||
- Nonempty |luasnip-extras-nonempty|
|
||
- Dynamic Lambda |luasnip-extras-dynamic-lambda|
|
||
- FMT |luasnip-extras-fmt|
|
||
- Conditions |luasnip-extras-conditions|
|
||
- On The Fly-Snippets |luasnip-extras-on-the-fly-snippets|
|
||
- select_choice |luasnip-extras-select_choice|
|
||
- Filetype-Functions |luasnip-extras-filetype-functions|
|
||
- Postfix-Snippet |luasnip-extras-postfix-snippet|
|
||
- Treesitter-Postfix-Snippet |luasnip-extras-treesitter-postfix-snippet|
|
||
- Snippet List |luasnip-extras-snippet-list|
|
||
- Snippet Location |luasnip-extras-snippet-location|
|
||
17. Extend Decorator |luasnip-extend-decorator|
|
||
18. LSP-Snippets |luasnip-lsp-snippets|
|
||
- Snipmate Parser |luasnip-lsp-snippets-snipmate-parser|
|
||
- Transformations |luasnip-lsp-snippets-transformations|
|
||
19. Variables |luasnip-variables|
|
||
- Environment Namespaces |luasnip-variables-environment-namespaces|
|
||
- LSP-Variables |luasnip-variables-lsp-variables|
|
||
20. Loaders |luasnip-loaders|
|
||
- Snippet-specific filetypes |luasnip-loaders-snippet-specific-filetypes|
|
||
- VS-Code |luasnip-loaders-vs-code|
|
||
- SNIPMATE |luasnip-loaders-snipmate|
|
||
- Lua |luasnip-loaders-lua|
|
||
- edit_snippets |luasnip-loaders-edit_snippets|
|
||
21. SnippetProxy |luasnip-snippetproxy|
|
||
22. ext_opts |luasnip-ext_opts|
|
||
23. Docstrings |luasnip-docstrings|
|
||
24. Docstring-Cache |luasnip-docstring-cache|
|
||
25. Events |luasnip-events|
|
||
26. Cleanup |luasnip-cleanup|
|
||
27. Logging |luasnip-logging|
|
||
28. Source |luasnip-source|
|
||
29. Selection |luasnip-selection|
|
||
30. Config-Options |luasnip-config-options|
|
||
31. Troubleshooting |luasnip-troubleshooting|
|
||
- Adding Snippets |luasnip-troubleshooting-adding-snippets|
|
||
32. API |luasnip-api|
|
||
>
|
||
__ ____
|
||
/\ \ /\ _`\ __
|
||
\ \ \ __ __ __ \ \,\L\_\ ___ /\_\ _____
|
||
\ \ \ __/\ \/\ \ /'__`\\/_\__ \ /' _ `\/\ \/\ '__`\
|
||
\ \ \L\ \ \ \_\ \/\ \L\.\_/\ \L\ \/\ \/\ \ \ \ \ \L\ \
|
||
\ \____/\ \____/\ \__/.\_\ `\____\ \_\ \_\ \_\ \ ,__/
|
||
\/___/ \/___/ \/__/\/_/\/_____/\/_/\/_/\/_/\ \ \/
|
||
\ \_\
|
||
\/_/
|
||
<
|
||
|
||
LuaSnip is a snippet engine written entirely in Lua. It has some great features
|
||
like inserting text (`luasnip-function-node`) or nodes (`luasnip-dynamic-node`)
|
||
based on user input, parsing LSP syntax and switching nodes
|
||
(`luasnip-choice-node`). For basic setup like mappings and installing, check
|
||
the README.
|
||
|
||
All code snippets in this help assume the following:
|
||
|
||
>lua
|
||
local ls = require("luasnip")
|
||
local s = ls.snippet
|
||
local sn = ls.snippet_node
|
||
local isn = ls.indent_snippet_node
|
||
local t = ls.text_node
|
||
local i = ls.insert_node
|
||
local f = ls.function_node
|
||
local c = ls.choice_node
|
||
local d = ls.dynamic_node
|
||
local r = ls.restore_node
|
||
local events = require("luasnip.util.events")
|
||
local ai = require("luasnip.nodes.absolute_indexer")
|
||
local extras = require("luasnip.extras")
|
||
local l = extras.lambda
|
||
local rep = extras.rep
|
||
local p = extras.partial
|
||
local m = extras.match
|
||
local n = extras.nonempty
|
||
local dl = extras.dynamic_lambda
|
||
local fmt = require("luasnip.extras.fmt").fmt
|
||
local fmta = require("luasnip.extras.fmt").fmta
|
||
local conds = require("luasnip.extras.expand_conditions")
|
||
local postfix = require("luasnip.extras.postfix").postfix
|
||
local types = require("luasnip.util.types")
|
||
local parse = require("luasnip.util.parser").parse_snippet
|
||
local ms = ls.multi_snippet
|
||
local k = require("luasnip.nodes.key_indexer").new_key
|
||
<
|
||
|
||
As noted in the |luasnip-loaders-lua|-section:
|
||
|
||
|
||
By default, the names from `luasnip.config.snip_env`
|
||
<https://github.com/L3MON4D3/LuaSnip/blob/master/lua/luasnip/config.lua#L22-L48>
|
||
will be used, but it’s possible to customize them by setting `snip_env` in
|
||
`setup`.
|
||
Furthermore, note that while this document assumes you have defined `ls` to be
|
||
`require("luasnip")`, it is **not** provided in the default set of variables.
|
||
|
||
|
||
==============================================================================
|
||
1. Basics *luasnip-basics*
|
||
|
||
In LuaSnip, snippets are made up of `nodes`. These can contain either
|
||
|
||
- static text (`textNode`)
|
||
- text that can be edited (`insertNode`)
|
||
- text that can be generated from the contents of other nodes (`functionNode`)
|
||
- other nodes
|
||
- `choiceNode`: allows choosing between two nodes (which might contain more
|
||
nodes)
|
||
- `restoreNode`: store and restore input to nodes
|
||
- or nodes that can be generated based on input (`dynamicNode`).
|
||
|
||
Snippets are always created using the `s(trigger:string,
|
||
nodes:table)`-function. It is explained in more detail in |luasnip-snippets|,
|
||
but the gist is that it creates a snippet that contains the nodes specified in
|
||
`nodes`, which will be inserted into a buffer if the text before the cursor
|
||
matches `trigger` when `ls.expand` is called.
|
||
|
||
|
||
JUMP-INDEX *luasnip-basics-jump-index*
|
||
|
||
Nodes that can be jumped to (`insertNode`, `choiceNode`, `dynamicNode`,
|
||
`restoreNode`, `snippetNode`) all require a "jump-index" so luasnip knows the
|
||
order in which these nodes are supposed to be visited ("jumped to").
|
||
|
||
>lua
|
||
s("trig", {
|
||
i(1), t"text", i(2), t"text again", i(3)
|
||
})
|
||
<
|
||
|
||
These indices don’t "run" through the entire snippet, like they do in
|
||
textmate-snippets (`"$1 ${2: $3 $4}"`), they restart at 1 in each nested
|
||
snippetNode:
|
||
|
||
>lua
|
||
s("trig", {
|
||
i(1), t" ", sn(2, {
|
||
t" ", i(1), t" ", i(2)
|
||
})
|
||
})
|
||
<
|
||
|
||
(roughly equivalent to the given textmate-snippet).
|
||
|
||
|
||
ADDING SNIPPETS *luasnip-basics-adding-snippets*
|
||
|
||
The snippets for a given filetype have to be added to luasnip via
|
||
`ls.add_snippets(filetype, snippets)`. Snippets that should be accessible
|
||
globally (in all filetypes) have to be added to the special filetype `all`.
|
||
|
||
>lua
|
||
ls.add_snippets("all", {
|
||
s("ternary", {
|
||
-- equivalent to "${1:cond} ? ${2:then} : ${3:else}"
|
||
i(1, "cond"), t(" ? "), i(2, "then"), t(" : "), i(3, "else")
|
||
})
|
||
})
|
||
<
|
||
|
||
It is possible to make snippets from one filetype available to another using
|
||
`ls.filetype_extend`, more info on that in the section |luasnip-api|.
|
||
|
||
|
||
SNIPPET INSERTION *luasnip-basics-snippet-insertion*
|
||
|
||
When a new snippet is expanded, it can be connected with the snippets that have
|
||
already been expanded in the buffer in various ways. First of all, Luasnip
|
||
distinguishes between root-snippets and child-snippets. The latter are nested
|
||
inside other snippets, so when jumping through a snippet, one may also traverse
|
||
the child-snippets expanded inside it, more or less as if the child just
|
||
contains more nodes of the parent. Root-snippets are of course characterised by
|
||
not being child-snippets. When expanding a new snippet, it becomes a child of
|
||
the snippet whose region it is expanded inside, and a root if it is not inside
|
||
any snippet’s region. If it is inside another snippet, the specific node it
|
||
is inside is determined, and the snippet then nested inside that node.
|
||
|
||
- If that node is interactive (for example, an `insertNode`), the new snippet
|
||
will be traversed when the node is visited, as long as the
|
||
configuration-option `link_children` is enabled. If it is not enabled, it is
|
||
possible to jump from the snippet to the node, but not the other way around.
|
||
- If that node is not interactive, the snippet will be linked to the currently
|
||
active node, also such that it will not be jumped to again once it is left.
|
||
This is to prevent jumping large distances across the buffer as much as
|
||
possible. There may still be one large jump from the snippet back to the
|
||
current node it is nested inside, but that seems hard to avoid.
|
||
Thus, one should design snippets such that the regions where other snippets
|
||
may be expanded are inside `insertNodes`.
|
||
|
||
If the snippet is not a child, but a root, it can be linked up with the roots
|
||
immediately adjacent to it by enabling `link_roots` in `setup`. Since by
|
||
default only one root is remembered, one should also set `keep_roots` if
|
||
`link_roots` is enabled. The two are separate options, since roots that are not
|
||
linked can still be reached by `ls.activate_node()`. This setup (remember
|
||
roots, but don’t jump to them) is useful for a super-tab like mapping
|
||
(`<Tab>` and jump on the same key), where one would like to still enter
|
||
previous roots. Since there would almost always be more jumps if the roots are
|
||
linked, regular `<Tab>` would not work almost all the time, and thus
|
||
`link_roots` has to stay disabled.
|
||
|
||
|
||
==============================================================================
|
||
2. Node *luasnip-node*
|
||
|
||
Every node accepts, as its last parameter, an optional table of arguments.
|
||
There are some common ones (which are listed here), and some that only apply to
|
||
some nodes (`user_args` for function/dynamicNode). These `opts` are only
|
||
mentioned if they accept options that are not common to all nodes.
|
||
|
||
Common opts:
|
||
|
||
- `node_ext_opts` and `merge_node_ext_opts`: Control `ext_opts` (most likely
|
||
highlighting) of the node. Described in detail in |luasnip-ext_opts|
|
||
- `key`: The node can be referred to by this key. Useful for either |luasnip-key-indexer| or for finding the node at runtime (See
|
||
|luasnip-snippets-api|), for example inside a `dynamicNode`. The keys
|
||
do not have to be unique across the entire lifetime of the snippet, but at any
|
||
point in time, the snippet may contain each key only once. This means it is
|
||
fine to return a keyed node from a `dynamicNode`, because even if it will be
|
||
generated multiple times, those will not be valid at the same time.
|
||
- `node_callbacks`: Define event-callbacks for this node (see
|
||
|luasnip-events|).
|
||
Accepts a table that maps an event, e.g. `events.enter` to the callback
|
||
(essentially the same as `callbacks` passed to `s`, only that there is no
|
||
first mapping from jump-index to the table of callbacks).
|
||
|
||
|
||
API *luasnip-node-api*
|
||
|
||
- `get_jump_index()`: this method returns the jump-index of a node. If a node
|
||
doesn’t have a jump-index, this method returns `nil` instead.
|
||
- `get_buf_position(opts) -> {from_position, to_position}`:
|
||
Determines the range of the buffer occupied by this node. `from`- and
|
||
`to_position` are `row,column`-tuples, `0,0`-indexed (first line is 0, first
|
||
column is 0) and end-inclusive (see |api-indexing|, this is extmarks
|
||
indexing).
|
||
- `opts`: `table|nil`, options, valid keys are:
|
||
- `raw`: `bool`, default `true`. This can be used to switch between
|
||
byte-columns (`raw=true`) and visual columns (`raw=false`). This makes a
|
||
difference if the line contains characters represented by multiple bytes
|
||
in UTF, for example `ÿ`.
|
||
|
||
|
||
==============================================================================
|
||
3. Snippets *luasnip-snippets*
|
||
|
||
The most direct way to define snippets is `s`:
|
||
|
||
>lua
|
||
s({trig="trigger"}, {})
|
||
<
|
||
|
||
(This snippet is useless beyond serving as a minimal example)
|
||
|
||
`s(context, nodes, opts) -> snippet`
|
||
|
||
- `context`: Either table or a string. Passing a string is equivalent to passing
|
||
>lua
|
||
{
|
||
trig = context
|
||
}
|
||
<
|
||
The following keys are valid:
|
||
- `trig`: string, the trigger of the snippet. If the text in front of (to the
|
||
left of) the cursor when `ls.expand()` is called matches it, the snippet will
|
||
be expanded. By default, "matches" means the text in front of the cursor
|
||
matches the trigger exactly, this behaviour can be modified through
|
||
`trigEngine`
|
||
- `name`: string, can be used by e.g. `nvim-compe` to identify the snippet.
|
||
- `desc` (or `dscr`): string, description of the snippet, -separated or table for
|
||
multiple lines.
|
||
- `wordTrig`: boolean, if true, the snippet is only expanded if the word
|
||
(`[%w_]+`) before the cursor matches the trigger entirely. True by default.
|
||
- `regTrig`: boolean, whether the trigger should be interpreted as a lua pattern.
|
||
False by default. Consider setting `trigEngine` to `"pattern"` instead, it is
|
||
more expressive, and in line with other settings.
|
||
- `trigEngine`: (function|string), determines how `trig` is interpreted, and what
|
||
it means for it to "match" the text in front of the cursor. This behaviour can
|
||
be completely customized by passing a function, but the predefined ones, which
|
||
are accessible by passing their identifier, should suffice in most cases:
|
||
- `"plain"`: the default-behaviour, the trigger has to match the text before
|
||
the cursor exactly.
|
||
- `"pattern"`: the trigger is interpreted as a lua-pattern, and is a match if
|
||
`trig .. "$"` matches the line up to the cursor. Capture-groups will be
|
||
accessible as `snippet.captures`.
|
||
- `"ecma"`: the trigger is interpreted as an ECMAscript-regex, and is a
|
||
match if `trig .. "$"` matches the line up to the cursor. Capture-groups
|
||
will be accessible as `snippet.captures`.
|
||
This `trigEngine` requires `jsregexp` (see
|
||
|luasnip-lsp-snippets-transformations|) to be installed, if it
|
||
is not, this engine will behave like `"plain"`.
|
||
- `"vim"`: the trigger is interpreted as a vim-regex, and is a match if
|
||
`trig .. "$"` matches the line up to the cursor. As with the other
|
||
regex/pattern-engines, captures will be available as `snippet.captures`,
|
||
but there is one caveat: the matching is done using `matchlist`, so for
|
||
now empty-string submatches will be interpreted as unmatched, and the
|
||
corresponding `snippet.capture[i]` will be `nil` (this will most likely
|
||
change, don’t rely on this behavior).
|
||
Besides these predefined engines, it is also possible to create new ones:
|
||
Instead of a string, pass a function which satisfies `trigEngine(trigger, opts)
|
||
-> (matcher(line_to_cursor, trigger) -> whole_match, captures)` (ie. the
|
||
function receives `trig` and `trigEngineOpts` can, for example, precompile a
|
||
regex, and then returns a function responsible for determining whether the
|
||
current cursor-position (represented by the line up to the cursor) matches the
|
||
trigger (it is passed again here so engines which don’t do any
|
||
trigger-specific work (like compilation) can just return a static `matcher`),
|
||
and what the capture-groups are). The `lua`-engine, for example, can be
|
||
implemented like this:
|
||
>lua
|
||
local function matcher(line_to_cursor, trigger)
|
||
-- look for match which ends at the cursor.
|
||
-- put all results into a list, there might be many capture-groups.
|
||
local find_res = { line_to_cursor:find(trigger .. "$") }
|
||
|
||
if #find_res > 0 then
|
||
-- if there is a match, determine matching string, and the
|
||
-- capture-groups.
|
||
local captures = {}
|
||
-- find_res[1] is `from`, find_res[2] is `to` (which we already know
|
||
-- anyway).
|
||
local from = find_res[1]
|
||
local match = line_to_cursor:sub(from, #line_to_cursor)
|
||
-- collect capture-groups.
|
||
for i = 3, #find_res do
|
||
captures[i - 2] = find_res[i]
|
||
end
|
||
return match, captures
|
||
else
|
||
return nil
|
||
end
|
||
end
|
||
|
||
local function engine(trigger)
|
||
-- don't do any special work here, can't precompile lua-pattern.
|
||
return matcher
|
||
end
|
||
<
|
||
The predefined engines are defined in `trig_engines.lua`
|
||
<https://github.com/L3MON4D3/LuaSnip/blob/master/lua/luasnip/nodes/util/trig_engines.lua>,
|
||
read it for more examples.
|
||
- `trigEngineOpts`: `table<string, any>`, options for the used trigEngine. The
|
||
valid options are:
|
||
- `max_len`: number, upper bound on the length of the trigger.
|
||
If this is set, the `line_to_cursor` will be truncated (from the cursor of
|
||
course) to `max_len` characters before performing the match.
|
||
This is implemented because feeding long `line_to_cursor` into eg. the
|
||
pattern-trigEngine will hurt performance quite a bit (see issue
|
||
Luasnip#1103).
|
||
This option is implemented for all `trigEngines`.
|
||
- `docstring`: string, textual representation of the snippet, specified like
|
||
`desc`. Overrides docstrings loaded from json.
|
||
- `docTrig`: string, used as `line_to_cursor` during docstring-generation. This
|
||
might be relevant if the snippet relies on specific values in the
|
||
capture-groups (for example, numbers, which won’t work with the default
|
||
`$CAPTURESN` used during docstring-generation)
|
||
- `hidden`: boolean, hint for completion-engines. If set, the snippet should not
|
||
show up when querying snippets.
|
||
- `priority`: positive number, Priority of the snippet, 1000 by default. Snippets
|
||
with high priority will be matched to a trigger before those with a lower one.
|
||
The priority for multiple snippets can also be set in `add_snippets`.
|
||
- `snippetType`: string, should be either `snippet` or `autosnippet` (ATTENTION:
|
||
singular form is used), decides whether this snippet has to be triggered by
|
||
`ls.expand()` or whether is triggered automatically (don’t forget to set
|
||
`ls.config.setup({ enable_autosnippets = true })` if you want to use this
|
||
feature). If unset it depends on how the snippet is added of which type the
|
||
snippet will be.
|
||
- `resolveExpandParams`: `fn(snippet, line_to_cursor, matched_trigger, captures)
|
||
-> table|nil`, where
|
||
- `snippet`: `Snippet`, the expanding snippet object
|
||
- `line_to_cursor`: `string`, the line up to the cursor.
|
||
- `matched_trigger`: `string`, the fully matched trigger (can be retrieved
|
||
from `line_to_cursor`, but we already have that info here :D)
|
||
- `captures`: `captures` as returned by `trigEngine`.
|
||
This function will be evaluated in `Snippet:matches()` to decide whether the
|
||
snippet can be expanded or not. Returns a table if the snippet can be expanded,
|
||
`nil` if can not. The returned table can contain any of these fields:
|
||
- `trigger`: `string`, the fully matched trigger.
|
||
- `captures`: `table`, this list could update the capture-groups from
|
||
parameter in snippet expansion.
|
||
Both `trigger` and `captures` can override the values returned via
|
||
`trigEngine`.
|
||
- `clear_region`: `{ "from": {<row>, <column>}, "to": {<row>, <column>} }`,
|
||
both (0, 0)-indexed, the region where text has to be cleared before
|
||
inserting the snippet.
|
||
- `env_override`: `map string->(string[]|string)`, override or extend
|
||
the snippet’s environment (`snip.env`)
|
||
If any of these is `nil`, the default is used (`trigger` and `captures` as
|
||
returned by `trigEngine`, `clear_region` such that exactly the trigger is
|
||
deleted, no overridden environment-variables).
|
||
A good example for the usage of `resolveExpandParams` can be found in the
|
||
implementation of `postfix`
|
||
<https://github.com/L3MON4D3/LuaSnip/blob/master/lua/luasnip/extras/postfix.lua>.
|
||
- `condition`: `fn(line_to_cursor, matched_trigger, captures) -> bool`, where
|
||
- `line_to_cursor`: `string`, the line up to the cursor.
|
||
- `matched_trigger`: `string`, the fully matched trigger (can be retrieved
|
||
from `line_to_cursor`, but we already have that info here :D)
|
||
- `captures`: if the trigger is pattern, this list contains the
|
||
capture-groups. Again, could be computed from `line_to_cursor`, but we
|
||
already did so.
|
||
- `show_condition`: `f(line_to_cursor) -> bool`.
|
||
- `line_to_cursor`: `string`, the line up to the cursor.
|
||
This function is (should be) evaluated by completion engines, indicating
|
||
whether the snippet should be included in current completion candidates.
|
||
Defaults to a function returning `true`. This is different from `condition`
|
||
because `condition` is evaluated by LuaSnip on snippet expansion (and thus has
|
||
access to the matched trigger and captures), while `show_condition` is (should
|
||
be) evaluated by the completion engines when scanning for available snippet
|
||
candidates.
|
||
- `filetype`: `string`, the filetype of the snippet. This overrides the filetype
|
||
the snippet is added (via `add_snippet`) as.
|
||
- `nodes`: A single node or a list of nodes. The nodes that make up the snippet.
|
||
- `opts`: A table with the following valid keys:
|
||
- `callbacks`: Contains functions that are called upon entering/leaving a node of
|
||
this snippet. For example: to print text upon entering the _second_ node of a
|
||
snippet, `callbacks` should be set as follows:
|
||
>lua
|
||
{
|
||
-- position of the node, not the jump-index!!
|
||
-- s("trig", {t"first node", t"second node", i(1, "third node")}).
|
||
[2] = {
|
||
[events.enter] = function(node, _event_args) print("2!") end
|
||
}
|
||
}
|
||
<
|
||
To register a callback for the snippets’ own events, the key `[-1]` may be
|
||
used. More info on events in |luasnip-events|
|
||
- `child_ext_opts`, `merge_child_ext_opts`: Control `ext_opts` applied to the
|
||
children of this snippet. More info on those in the |luasnip-ext_opts|-section.
|
||
|
||
The `opts`-table, as described here, can also be passed to e.g. `snippetNode`
|
||
and `indentSnippetNode`. It is also possible to set `condition` and
|
||
`show_condition` (described in the documentation of the `context`-table) from
|
||
`opts`. They should, however, not be set from both.
|
||
|
||
|
||
DATA *luasnip-snippets-data*
|
||
|
||
Snippets contain some interesting tables during runtime:
|
||
|
||
- `snippet.env`: Contains variables used in the LSP-protocol, for example
|
||
`TM_CURRENT_LINE` or `TM_FILENAME`. It’s possible to add customized variables
|
||
here too, check |luasnip-variables-environment-namespaces|
|
||
- `snippet.captures`: If the snippet was triggered by a pattern (`regTrig`), and
|
||
the pattern contained capture-groups, they can be retrieved here.
|
||
- `snippet.trigger`: The string that triggered this snippet. Again, only
|
||
interesting if the snippet was triggered through `regTrig`, for getting the
|
||
full match.
|
||
|
||
These variables/tables primarily come in handy in `dynamic/functionNodes`,
|
||
where the snippet can be accessed through the immediate parent
|
||
(`parent.snippet`), which is passed to the function. (in most cases `parent ==
|
||
parent.snippet`, but the `parent` of the dynamicNode is not always the
|
||
surrounding snippet, it could be a `snippetNode`).
|
||
|
||
## Api
|
||
|
||
- `invalidate()`: call this method to effectively remove the snippet. The
|
||
snippet will no longer be able to expand via `expand` or `expand_auto`. It
|
||
will also be hidden from lists (at least if the plugin creating the list
|
||
respects the `hidden`-key), but it might be necessary to call
|
||
`ls.refresh_notify(ft)` after invalidating snippets.
|
||
- `get_keyed_node(key)`: Returns the currently visible node associated with
|
||
`key`.
|
||
|
||
|
||
==============================================================================
|
||
4. TextNode *luasnip-textnode*
|
||
|
||
The most simple kind of node; just text.
|
||
|
||
>lua
|
||
s("trigger", { t("Wow! Text!") })
|
||
<
|
||
|
||
This snippet expands to
|
||
|
||
>
|
||
Wow! Text!⎵
|
||
<
|
||
|
||
where ⎵ is the cursor.
|
||
|
||
Multiline strings can be defined by passing a table of lines rather than a
|
||
string:
|
||
|
||
>lua
|
||
s("trigger", {
|
||
t({"Wow! Text!", "And another line."})
|
||
})
|
||
<
|
||
|
||
`t(text, node_opts)`:
|
||
|
||
- `text`: `string` or `string[]`
|
||
- `node_opts`: `table`, see |luasnip-node|
|
||
|
||
|
||
==============================================================================
|
||
5. InsertNode *luasnip-insertnode*
|
||
|
||
These Nodes contain editable text and can be jumped to- and from (e.g.
|
||
traditional placeholders and tabstops, like `$1` in textmate-snippets).
|
||
|
||
The functionality is best demonstrated with an example:
|
||
|
||
>lua
|
||
s("trigger", {
|
||
t({"After expanding, the cursor is here ->"}), i(1),
|
||
t({"", "After jumping forward once, cursor is here ->"}), i(2),
|
||
t({"", "After jumping once more, the snippet is exited there ->"}), i(0),
|
||
})
|
||
<
|
||
|
||
The Insert Nodes are visited in order `1,2,3,..,n,0`. (The jump-index 0 also
|
||
_has_ to belong to an `insertNode`!) So the order of InsertNode-jumps is as
|
||
follows:
|
||
|
||
1. After expansion, the cursor is at InsertNode 1,
|
||
2. after jumping forward once at InsertNode 2,
|
||
3. and after jumping forward again at InsertNode 0.
|
||
|
||
If no 0-th InsertNode is found in a snippet, one is automatically inserted
|
||
after all other nodes.
|
||
|
||
The jump-order doesn’t have to follow the "textual" order of the nodes:
|
||
|
||
>lua
|
||
s("trigger", {
|
||
t({"After jumping forward once, cursor is here ->"}), i(2),
|
||
t({"", "After expanding, the cursor is here ->"}), i(1),
|
||
t({"", "After jumping once more, the snippet is exited there ->"}), i(0),
|
||
})
|
||
<
|
||
|
||
The above snippet will behave as follows:
|
||
|
||
1. After expansion, we will be at InsertNode 1.
|
||
2. After jumping forward, we will be at InsertNode 2.
|
||
3. After jumping forward again, we will be at InsertNode 0.
|
||
|
||
An **important** (because here Luasnip differs from other snippet engines)
|
||
detail is that the jump-indices restart at 1 in nested snippets:
|
||
|
||
>lua
|
||
s("trigger", {
|
||
i(1, "First jump"),
|
||
t(" :: "),
|
||
sn(2, {
|
||
i(1, "Second jump"),
|
||
t" : ",
|
||
i(2, "Third jump")
|
||
})
|
||
})
|
||
<
|
||
|
||
as opposed to e.g. the textmate syntax, where tabstops are snippet-global:
|
||
|
||
>snippet
|
||
${1:First jump} :: ${2: ${3:Third jump} : ${4:Fourth jump}}
|
||
<
|
||
|
||
(this is not exactly the same snippet of course, but as close as possible) (the
|
||
restart-rule only applies when defining snippets in lua, the above
|
||
textmate-snippet will expand correctly when parsed).
|
||
|
||
`i(jump_index, text, node_opts)`
|
||
|
||
- `jump_index`: `number`, this determines when this node will be jumped to (see
|
||
|luasnip-basics-jump-index|).
|
||
- `text`: `string|string[]`, a single string for just one line, a list with >1
|
||
entries for multiple lines.
|
||
This text will be SELECTed when the `insertNode` is jumped into.
|
||
- `node_opts`: `table`, described in |luasnip-node|
|
||
|
||
If the `jump_index` is `0`, replacing its’ `text` will leave it outside the
|
||
`insertNode` (for reasons, check out Luasnip#110).
|
||
|
||
|
||
==============================================================================
|
||
6. FunctionNode *luasnip-functionnode*
|
||
|
||
Function Nodes insert text based on the content of other nodes using a
|
||
user-defined function:
|
||
|
||
>lua
|
||
local function fn(
|
||
args, -- text from i(2) in this example i.e. { { "456" } }
|
||
parent, -- parent snippet or parent node
|
||
user_args -- user_args from opts.user_args
|
||
)
|
||
return '[' .. args[1][1] .. user_args .. ']'
|
||
end
|
||
|
||
s("trig", {
|
||
i(1), t '<-i(1) ',
|
||
f(fn, -- callback (args, parent, user_args) -> string
|
||
{2}, -- node indice(s) whose text is passed to fn, i.e. i(2)
|
||
{ user_args = { "user_args_value" }} -- opts
|
||
),
|
||
t ' i(2)->', i(2), t '<-i(2) i(0)->', i(0)
|
||
})
|
||
<
|
||
|
||
`f(fn, argnode_references, node_opts)`: - `fn`: `function(argnode_text, parent,
|
||
user_args1,...,user_argsn) -> text` - `argnode_text`: `string[][]`, the text
|
||
currently contained in the argnodes (e.g. `{{line1}, {line1, line2}}`). The
|
||
snippet indent will be removed from all lines following the first.
|
||
|
||
- `parent`: The immediate parent of the `functionNode`. It is included here as it
|
||
allows easy access to some information that could be useful in functionNodes
|
||
(see |luasnip-snippets-data| for some examples). Many snippets access the
|
||
surrounding snippet just as `parent`, but if the `functionNode` is nested
|
||
within a `snippetNode`, the immediate parent is a `snippetNode`, not the
|
||
surrounding snippet (only the surrounding snippet contains data like `env` or
|
||
`captures`).
|
||
- `user_args`: The `user_args` passed in `opts`. Note that there may be multiple
|
||
user_args (e.g. `user_args1, ..., user_argsn`).
|
||
|
||
`fn` shall return a string, which will be inserted as is, or a table of strings
|
||
for multiline strings, where all lines following the first will be prefixed
|
||
with the snippets’ indentation.
|
||
|
||
- `argnode_references`: `node_reference[]|node_refernce|nil`. Either no, a
|
||
single, or multiple |luasnip-node-reference|s. Changing any of these will
|
||
trigger a re-evaluation of `fn`, and insertion of the updated text. If no node
|
||
reference is passed, the `functionNode` is evaluated once upon expansion.
|
||
- `node_opts`: `table`, see |luasnip-node|. One additional key is supported:
|
||
- `user_args`: `any[]`, these will be passed to `fn` as `user_arg1`-`user_argn`.
|
||
These make it easier to reuse similar functions, for example a functionNode
|
||
that wraps some text in different delimiters (`()`, `[]`, …).
|
||
>lua
|
||
local function reused_func(_,_, user_arg1)
|
||
return user_arg1
|
||
end
|
||
|
||
s("trig", {
|
||
f(reused_func, {}, {
|
||
user_args = {"text"}
|
||
}),
|
||
f(reused_func, {}, {
|
||
user_args = {"different text"}
|
||
}),
|
||
})
|
||
<
|
||
|
||
**Examples**:
|
||
|
||
- Use captures from the regex trigger using a functionNode:
|
||
>lua
|
||
s({trig = "b(%d)", regTrig = true},
|
||
f(function(args, snip) return
|
||
"Captured Text: " .. snip.captures[1] .. "." end, {})
|
||
)
|
||
<
|
||
- `argnodes_text` during function evaluation:
|
||
>lua
|
||
s("trig", {
|
||
i(1, "text_of_first"),
|
||
i(2, {"first_line_of_second", "second_line_of_second"}),
|
||
f(function(args, snip)
|
||
--here
|
||
-- order is 2,1, not 1,2!!
|
||
end, {2, 1} )})
|
||
<
|
||
At `--here`, `args` would look as follows (provided no text was changed after
|
||
expansion):
|
||
>lua
|
||
args = {
|
||
{"first_line_of_second", "second_line_of_second"},
|
||
{"text_of_first"}
|
||
}
|
||
<
|
||
- |luasnip-absolute-indexer|:
|
||
>lua
|
||
s("trig", {
|
||
i(1, "text_of_first"),
|
||
i(2, {"first_line_of_second", "second_line_of_second"}),
|
||
f(function(args, snip)
|
||
-- just concat first lines of both.
|
||
return args[1][1] .. args[2][1]
|
||
end, {ai[2], ai[1]} )})
|
||
<
|
||
|
||
If the function only performs simple operations on text, consider using the
|
||
`lambda` from `luasnip.extras` (See |luasnip-extras-lambda|)
|
||
|
||
|
||
==============================================================================
|
||
7. Node Reference *luasnip-node-reference*
|
||
|
||
Node references are used to refer to other nodes in various parts of
|
||
luasnip’s API. For example, argnodes in functionNode, dynamicNode or lambda
|
||
are node references. These references can be either of:
|
||
|
||
- `number`: the jump-index of the node.
|
||
This will be resolved relative to the parent of the node this is passed to.
|
||
(So, only nodes with the same parent can be referenced. This is very easy to
|
||
grasp, but also limiting)
|
||
- `key_indexer`: the key of the node, if it is present. This will come in
|
||
handy if the node that is being referred to is not in the same
|
||
snippet/snippetNode as the one the node reference is passed to.
|
||
Also, it is the proper way to refer to a non-interactive node (a
|
||
functionNode, for example)
|
||
- `absolute_indexer`: the absolute position of the node. Just like
|
||
`key_indexer`, it allows addressing non-sibling nodes, but is a bit more
|
||
awkward to handle since a path from root to node has to be determined,
|
||
whereas `key_indexer` just needs the key to match.
|
||
Due to this, `key_indexer` should be generally preferred.
|
||
(More information in |luasnip-absolute-indexer|).
|
||
- `node`: just the node. Usage of this is discouraged since it can lead to
|
||
subtle errors (for example, if the node passed here is captured in a closure
|
||
and therefore not copied with the remaining tables in the snippet; there’s a
|
||
big comment about just this in commit 8bfbd61).
|
||
|
||
|
||
==============================================================================
|
||
8. ChoiceNode *luasnip-choicenode*
|
||
|
||
ChoiceNodes allow choosing between multiple nodes.
|
||
|
||
>lua
|
||
s("trig", c(1, {
|
||
t("Ugh boring, a text node"),
|
||
i(nil, "At least I can edit something now..."),
|
||
f(function(args) return "Still only counts as text!!" end, {})
|
||
}))
|
||
<
|
||
|
||
`c(jump_index, choices, node_opts)`
|
||
|
||
- `jump_index`: `number`, since choiceNodes can be jumped to, they need a
|
||
jump-index (Info in |luasnip-basics-jump-index|).
|
||
- `choices`: `node[]|node`, the choices. The first will be initially active.
|
||
A list of nodes will be turned into a `snippetNode`.
|
||
- `node_opts`: `table`. `choiceNode` supports the keys common to all nodes
|
||
described in |luasnip-node|, and one additional key:
|
||
- `restore_cursor`: `false` by default. If it is set, and the node that was being
|
||
edited also appears in the switched to choice (can be the case if a
|
||
`restoreNode` is present in both choice) the cursor is restored relative to
|
||
that node. The default is `false` as enabling might lead to decreased
|
||
performance. It’s possible to override the default by wrapping the
|
||
`choiceNode` constructor in another function that sets `opts.restore_cursor` to
|
||
`true` and then using that to construct `choiceNode`s:
|
||
>lua
|
||
local function restore_cursor_choice(pos, choices, opts)
|
||
if opts then
|
||
opts.restore_cursor = true
|
||
else
|
||
opts = {restore_cursor = true}
|
||
end
|
||
return c(pos, choices, opts)
|
||
end
|
||
<
|
||
|
||
Jumpable nodes that normally expect an index as their first parameter don’t
|
||
need one inside a choiceNode; their jump-index is the same as the
|
||
choiceNodes’.
|
||
|
||
As it is only possible (for now) to change choices from within the choiceNode,
|
||
make sure that all of the choices have some place for the cursor to stop at!
|
||
|
||
This means that in `sn(nil, {...nodes...})` `nodes` has to contain e.g. an
|
||
`i(1)`, otherwise luasnip will just "jump through" the nodes, making it
|
||
impossible to change the choice.
|
||
|
||
>lua
|
||
c(1, {
|
||
t"some text", -- textNodes are just stopped at.
|
||
i(nil, "some text"), -- likewise.
|
||
sn(nil, {t"some text"}) -- this will not work!
|
||
sn(nil, {i(1), t"some text"}) -- this will.
|
||
})
|
||
<
|
||
|
||
The active choice for a choiceNode can be changed by either calling one of
|
||
`ls.change_choice(1)` (forwards) or `ls.change_choice(-1)` (backwards), or by
|
||
calling `ls.set_choice(choice_indx)`.
|
||
|
||
One way to easily interact with choiceNodes is binding `change_choice(1/-1)` to
|
||
keys:
|
||
|
||
>lua
|
||
-- set keybinds for both INSERT and VISUAL.
|
||
vim.api.nvim_set_keymap("i", "<C-n>", "<Plug>luasnip-next-choice", {})
|
||
vim.api.nvim_set_keymap("s", "<C-n>", "<Plug>luasnip-next-choice", {})
|
||
vim.api.nvim_set_keymap("i", "<C-p>", "<Plug>luasnip-prev-choice", {})
|
||
vim.api.nvim_set_keymap("s", "<C-p>", "<Plug>luasnip-prev-choice", {})
|
||
<
|
||
|
||
Apart from this, there is also a picker (see |luasnip-select_choice| where no
|
||
cycling is necessary and any choice can be selected right away, via
|
||
`vim.ui.select`.
|
||
|
||
|
||
==============================================================================
|
||
9. SnippetNode *luasnip-snippetnode*
|
||
|
||
SnippetNodes directly insert their contents into the surrounding snippet. This
|
||
is useful for `choiceNode`s, which only accept one child, or `dynamicNode`s,
|
||
where nodes are created at runtime and inserted as a `snippetNode`.
|
||
|
||
Their syntax is similar to `s`, however, where snippets require a table
|
||
specifying when to expand, `snippetNode`s, similar to `insertNode`s, expect a
|
||
jump-index.
|
||
|
||
>lua
|
||
s("trig", sn(1, {
|
||
t("basically just text "),
|
||
i(1, "And an insertNode.")
|
||
}))
|
||
<
|
||
|
||
`sn(jump_index, nodes, node_opts)`
|
||
|
||
- `jump_index`: `number`, the usual |luasnip-jump-index|.
|
||
- `nodes`: `node[]|node`, just like for `s`.
|
||
Note that `snippetNode`s don’t accept an `i(0)`, so the jump-indices of the nodes
|
||
inside them have to be in `1,2,...,n`.
|
||
- `node_opts`: `table`: again, the keys common to all nodes (documented in
|
||
|luasnip-node|) are supported, but also
|
||
- `callbacks`,
|
||
- `child_ext_opts` and
|
||
- `merge_child_ext_opts`,
|
||
which are further explained in |luasnip-snippets|.
|
||
|
||
|
||
==============================================================================
|
||
10. IndentSnippetNode *luasnip-indentsnippetnode*
|
||
|
||
By default, all nodes are indented at least as deep as the trigger. With these
|
||
nodes it’s possible to override that behaviour:
|
||
|
||
>lua
|
||
s("isn", {
|
||
isn(1, {
|
||
t({"This is indented as deep as the trigger",
|
||
"and this is at the beginning of the next line"})
|
||
}, "")
|
||
})
|
||
<
|
||
|
||
(Note the empty string passed to isn).
|
||
|
||
Indent is only applied after linebreaks, so it’s not possible to remove
|
||
indent on the line where the snippet was triggered using `ISN` (That is
|
||
possible via regex triggers where the entire line before the trigger is
|
||
matched).
|
||
|
||
Another nice use case for `ISN` is inserting text, e.g. `//` or some other
|
||
comment string before the nodes of the snippet:
|
||
|
||
>lua
|
||
s("isn2", {
|
||
isn(1, t({"//This is", "A multiline", "comment"}), "$PARENT_INDENT//")
|
||
})
|
||
<
|
||
|
||
Here the `//` before `This is` is important, once again, because indent is only
|
||
applied after linebreaks.
|
||
|
||
To enable such usage, `$PARENT_INDENT` in the indentstring is replaced by the
|
||
parent’s indent.
|
||
|
||
`isn(jump_index, nodes, indentstring, node_opts)`
|
||
|
||
All of these parameters except `indentstring` are exactly the same as in
|
||
|luasnip-snippetnode|.
|
||
|
||
- `indentstring`: `string`, will be used to indent the nodes inside this
|
||
`snippetNode`.
|
||
All occurrences of `"$PARENT_INDENT"` are replaced with the actual indent of
|
||
the parent.
|
||
|
||
|
||
==============================================================================
|
||
11. DynamicNode *luasnip-dynamicnode*
|
||
|
||
Very similar to functionNode, but returns a snippetNode instead of just text,
|
||
which makes them very powerful as parts of the snippet can be changed based on
|
||
user input.
|
||
|
||
`d(jump_index, function, node-references, opts)`:
|
||
|
||
- `jump_index`: `number`, just like all jumpable nodes, its’ position in the
|
||
jump-list (|luasnip-basics-jump-index|).
|
||
- `function`: `fn(args, parent, old_state, user_args) -> snippetNode` This
|
||
function is called when the argnodes’ text changes. It should generate and
|
||
return (wrapped inside a `snippetNode`) nodes, which will be inserted at the
|
||
dynamicNode’s place. `args`, `parent` and `user_args` are also explained in
|
||
|luasnip-functionnode|
|
||
- `args`: `table of text` (`{{"node1line1", "node1line2"}, {"node2line1"}}`)
|
||
from nodes the `dynamicNode` depends on.
|
||
- `parent`: the immediate parent of the `dynamicNode`.
|
||
- `old_state`: a user-defined table. This table may contain anything; its
|
||
intended usage is to preserve information from the previously generated
|
||
`snippetNode`. If the `dynamicNode` depends on other nodes, it may be
|
||
reconstructed, which means all user input (text inserted in `insertNodes`,
|
||
changed choices) to the previous `dynamicNode` is lost.
|
||
The `old_state` table must be stored in `snippetNode` returned by
|
||
the function (`snippetNode.old_state`).
|
||
The second example below illustrates the usage of `old_state`.
|
||
- `user_args`: passed through from `dynamicNode`-opts; may have more than one
|
||
argument.
|
||
- `node_references`: `node_reference[]|node_references|nil`,
|
||
|luasnip-node-references| to the nodes the dynamicNode depends on: if any of
|
||
these trigger an update (for example, if the text inside them changes), the
|
||
`dynamicNode`s’ function will be executed, and the result inserted at the
|
||
`dynamicNode`s place. (`dynamicNode` behaves exactly the same as `functionNode`
|
||
in this regard).
|
||
- `opts`: In addition to the common |luasnip-node|-keys, there is, again,
|
||
- `user_args`, which is described in |luasnip-functionnode|.
|
||
|
||
**Examples**:
|
||
|
||
This `dynamicNode` inserts an `insertNode` which copies the text inside the
|
||
first `insertNode`.
|
||
|
||
>lua
|
||
s("trig", {
|
||
t"text: ", i(1), t{"", "copy: "},
|
||
d(2, function(args)
|
||
-- the returned snippetNode doesn't need a position; it's inserted
|
||
-- "inside" the dynamicNode.
|
||
return sn(nil, {
|
||
-- jump-indices are local to each snippetNode, so restart at 1.
|
||
i(1, args[1])
|
||
})
|
||
end,
|
||
{1})
|
||
})
|
||
<
|
||
|
||
This snippet makes use of `old_state` to count the number of updates.
|
||
|
||
To store/restore values generated by the `dynamicNode` or entered into
|
||
`insert/choiceNode`, consider using the shortly-introduced `restoreNode`
|
||
instead of `old_state`.
|
||
|
||
>lua
|
||
local function count(_, _, old_state)
|
||
old_state = old_state or {
|
||
updates = 0
|
||
}
|
||
|
||
old_state.updates = old_state.updates + 1
|
||
|
||
local snip = sn(nil, {
|
||
t(tostring(old_state.updates))
|
||
})
|
||
|
||
snip.old_state = old_state
|
||
return snip
|
||
end
|
||
|
||
ls.add_snippets("all",
|
||
s("trig", {
|
||
i(1, "change to update"),
|
||
d(2, count, {1})
|
||
})
|
||
)
|
||
<
|
||
|
||
As with `functionNode`, `user_args` can be used to reuse similar `dynamicNode`-
|
||
functions.
|
||
|
||
|
||
==============================================================================
|
||
12. RestoreNode *luasnip-restorenode*
|
||
|
||
This node can store and restore a snippetNode as is. This includes changed
|
||
choices and changed text. Its’ usage is best demonstrated by an example:
|
||
|
||
>lua
|
||
s("paren_change", {
|
||
c(1, {
|
||
sn(nil, { t("("), r(1, "user_text"), t(")") }),
|
||
sn(nil, { t("["), r(1, "user_text"), t("]") }),
|
||
sn(nil, { t("{"), r(1, "user_text"), t("}") }),
|
||
}),
|
||
}, {
|
||
stored = {
|
||
-- key passed to restoreNodes.
|
||
["user_text"] = i(1, "default_text")
|
||
}
|
||
})
|
||
<
|
||
|
||
Here the text entered into `user_text` is preserved upon changing choice.
|
||
|
||
`r(jump_index, key, nodes, node_opts)`:
|
||
|
||
- `jump_index`, when to jump to this node.
|
||
- `key`, `string`: `restoreNode`s with the same key share their content.
|
||
- `nodes`, `node[]|node`: the content of the `restoreNode`.
|
||
Can either be a single node, or a table of nodes (both of which will be
|
||
wrapped inside a `snippetNode`, except if the single node already is a
|
||
`snippetNode`).
|
||
The content for a given key may be defined multiple times, but if the
|
||
contents differ, it’s undefined which will actually be used.
|
||
If a key’s content is defined in a `dynamicNode`, it will not be initially
|
||
used for `restoreNodes` outside that `dynamicNode`. A way around this
|
||
limitation is defining the content in the `restoreNode` outside the
|
||
`dynamicNode`.
|
||
|
||
The content for a key may also be defined in the `opts`-parameter of the
|
||
snippet-constructor, as seen in the example above. The `stored`-table accepts
|
||
the same values as the `nodes`-parameter passed to `r`. If no content is
|
||
defined for a key, it defaults to the empty `insertNode`.
|
||
|
||
An important-to-know limitation of `restoreNode` is that, for a given key, only
|
||
one may be visible at a time. See this issue
|
||
<https://github.com/L3MON4D3/LuaSnip/issues/234> for details.
|
||
|
||
The `restoreNode` is especially useful for storing input across updates of a
|
||
`dynamicNode`. Consider this:
|
||
|
||
>lua
|
||
local function simple_restore(args, _)
|
||
return sn(nil, {i(1, args[1]), i(2, "user_text")})
|
||
end
|
||
|
||
s("rest", {
|
||
i(1, "preset"), t{"",""},
|
||
d(2, simple_restore, 1)
|
||
})
|
||
<
|
||
|
||
Every time the `i(1)` in the outer snippet is changed, the text inside the
|
||
`dynamicNode` is reset to `"user_text"`. This can be prevented by using a
|
||
`restoreNode`:
|
||
|
||
>lua
|
||
local function simple_restore(args, _)
|
||
return sn(nil, {i(1, args[1]), r(2, "dyn", i(nil, "user_text"))})
|
||
end
|
||
|
||
s("rest", {
|
||
i(1, "preset"), t{"",""},
|
||
d(2, simple_restore, 1)
|
||
})
|
||
<
|
||
|
||
Now the entered text is stored.
|
||
|
||
`restoreNode`s indent is not influenced by `indentSnippetNodes` right now. If
|
||
that really bothers you feel free to open an issue.
|
||
|
||
|
||
==============================================================================
|
||
13. Key Indexer *luasnip-key-indexer*
|
||
|
||
A very flexible way of referencing nodes (|luasnip-node-reference|). While the
|
||
straightforward way of addressing nodes via their |luasnip-jump-index| suffices
|
||
in most cases, a `dynamic/functionNode` can only depend on nodes in the same
|
||
snippet(Node), its siblings (since the index is interpreted as relative to
|
||
their parent). Accessing a node with a different parent is thus not possible.
|
||
Secondly, and less relevant, only nodes that actually have a jump-index can be
|
||
referred to (a `functionNode`, for example, cannot be depended on). Both of
|
||
these restrictions are lifted with `key_indexer`: It allows addressing nodes by
|
||
their key, which can be set when the node is constructed, and is wholly
|
||
independent of the nodes’ position in the snippet, thus enabling descriptive
|
||
labeling.
|
||
|
||
The following snippets demonstrate the issue and the solution by using
|
||
`key_indexer`:
|
||
|
||
First, the addressed problem of referring to nodes outside the `functionNode`s
|
||
parent:
|
||
|
||
>lua
|
||
s("trig", {
|
||
i(1), c(2, {
|
||
sn(nil, {
|
||
t"cannot access the argnode :(",
|
||
f(function(args)
|
||
return args[1]
|
||
end, {???}) -- can't refer to i(1), since it isn't a sibling of `f`.
|
||
}),
|
||
t"sample_text"
|
||
})
|
||
})
|
||
<
|
||
|
||
And the solution: first give the node we want to refer to a key, and then pass
|
||
the same to the `functionNode`.
|
||
|
||
>lua
|
||
s("trig", {
|
||
i(1, "", {key = "i1-key"}), c(2, {
|
||
sn(nil, { i(1),
|
||
t"can access the argnode :)",
|
||
f(function(args)
|
||
return args[1]
|
||
end, k("i1-key") )
|
||
}),
|
||
t"sample_text"
|
||
})
|
||
})
|
||
<
|
||
|
||
|
||
==============================================================================
|
||
14. Absolute Indexer *luasnip-absolute-indexer*
|
||
|
||
`absolute_indexer` allows accessing nodes by their unique jump-index path from
|
||
the snippet-root. This makes it almost as powerful as |luasnip-key-indexer|,
|
||
but again removes the possibility of referring to non-jumpable nodes and makes
|
||
it all a bit more error-prone since the jump-index paths are hard to follow,
|
||
and (unfortunately) have to be a bit verbose (see the long example of
|
||
`absolute_indexer`-positions below). Consider just using |luasnip-key-indexer|
|
||
instead.
|
||
|
||
(The solution-snippet from |luasnip-key-indexer|, but using `ai` instead.)
|
||
|
||
>lua
|
||
s("trig", {
|
||
i(1), c(2, {
|
||
sn(nil, { i(1),
|
||
t"can access the argnode :)",
|
||
f(function(args)
|
||
return args[1]
|
||
end, ai(1) )
|
||
}),
|
||
t"sample_text"
|
||
})
|
||
})
|
||
<
|
||
|
||
There are some quirks in addressing nodes:
|
||
|
||
>lua
|
||
s("trig", {
|
||
i(2), -- ai[2]: indices based on jump-index, not position.
|
||
sn(1, { -- ai[1]
|
||
i(1), -- ai[1][1]
|
||
t"lel", -- not addressable.
|
||
i(2) -- ai[1][2]
|
||
}),
|
||
c(3, { -- ai[3]
|
||
i(nil), -- ai[3][1]
|
||
t"lel", -- ai[3][2]: choices are always addressable.
|
||
}),
|
||
d(4, function() -- ai[4]
|
||
return sn(nil, { -- ai[4][0]
|
||
i(1), -- ai[4][0][1]
|
||
})
|
||
end, {}),
|
||
r(5, "restore_key", -- ai[5]
|
||
i(1) -- ai[5][0][1]: restoreNodes always store snippetNodes.
|
||
),
|
||
r(6, "restore_key_2", -- ai[6]
|
||
sn(nil, { -- ai[6][0]
|
||
i(1) -- ai[6][0][1]
|
||
})
|
||
)
|
||
})
|
||
<
|
||
|
||
Note specifically that the index of a dynamicNode differs from that of the
|
||
generated snippetNode, and that restoreNodes (internally) always store a
|
||
snippetNode, so even if the restoreNode only contains one node, that node has
|
||
to be accessed as `ai[restoreNodeIndx][0][1]`.
|
||
|
||
`absolute_indexer`s’ can be constructed in different ways:
|
||
|
||
- `ai[1][2][3]`
|
||
- `ai(1, 2, 3)`
|
||
- `ai{1, 2, 3}`
|
||
|
||
are all the same node.
|
||
|
||
|
||
==============================================================================
|
||
15. MultiSnippet *luasnip-multisnippet*
|
||
|
||
There are situations where it might be comfortable to access a snippet in
|
||
different ways. For example, one might want to enable auto-triggering in
|
||
regions where the snippets usage is common, while leaving it manual-only in
|
||
others. This is where `ms` should be used: A single snippet can be associated
|
||
with multiple `context`s (the `context`-table determines the conditions under
|
||
which a snippet may be triggered). This has the advantage (compared with just
|
||
registering copies) that all `context`s are backed by a single snippet, and not
|
||
multiple, and it’s (at least should be :D) more comfortable to use.
|
||
|
||
`ms(contexts, nodes, opts) -> addable`:
|
||
|
||
- `contexts`: table containing list of `contexts`, and some keywords.
|
||
`context` are described in |luasnip-snippets|, here they may also be tables
|
||
or strings.
|
||
So far, there is only one valid keyword:
|
||
- `common`: Accepts yet another context.
|
||
The options in `common` are applied to (but don’t override) the other
|
||
contexts specified in `contexts`.
|
||
- `nodes`: List of nodes, exactly like in |luasnip-snippets|.
|
||
- `opts`: Table, options for this function:
|
||
- `common_opts`: The snippet-options (see also |luasnip-snippets|) applied
|
||
to the snippet generated from `nodes`.
|
||
|
||
The returned object is an `addable`, something which can be passed to
|
||
`add_snippets`, or returned from the lua-loader.
|
||
|
||
**Examples**:
|
||
|
||
>lua
|
||
ls.add_snippets("all", {
|
||
ms({"a", "b"}, {t"a or b"})
|
||
})
|
||
<
|
||
|
||
>lua
|
||
ls.add_snippets("all", {
|
||
ms({
|
||
common = {snippetType = "autosnippet"},
|
||
"a",
|
||
"b"
|
||
}, {
|
||
t"a or b (but autotriggered!!)"
|
||
})
|
||
})
|
||
<
|
||
|
||
>lua
|
||
ls.add_snippets("all", {
|
||
ms({
|
||
common = {snippetType = "autosnippet"},
|
||
{trig = "a", snippetType = "snippet"},
|
||
"b",
|
||
{trig = "c", condition = function(line_to_cursor)
|
||
return line_to_cursor == ""
|
||
end}
|
||
}, {
|
||
t"a or b (but autotriggered!!)"
|
||
})
|
||
})
|
||
<
|
||
|
||
|
||
==============================================================================
|
||
16. Extras *luasnip-extras*
|
||
|
||
|
||
LAMBDA *luasnip-extras-lambda*
|
||
|
||
A shortcut for `functionNode`s that only do very basic string manipulation.
|
||
|
||
`l(lambda, argnodes)`:
|
||
|
||
- `lambda`: An object created by applying string-operations to `l._n`, objects
|
||
representing the `n`th argnode.
|
||
For example:
|
||
- `l._1:gsub("a", "e")` replaces all occurrences of "a" in the text of the
|
||
first argnode with "e", or
|
||
- `l._1 .. l._2` concatenates text of the first and second argnode.
|
||
If an argnode contains multiple lines of text, they are concatenated with
|
||
`"\n"` prior to any operation.
|
||
- `argnodes`, a |luasnip-node-reference|, just like in function- and
|
||
dynamicNode.
|
||
|
||
There are many examples for `lambda` in `Examples/snippets.lua`
|
||
|
||
|
||
MATCH *luasnip-extras-match*
|
||
|
||
`match` can insert text based on a predicate (again, a shorthand for
|
||
`functionNode`).
|
||
|
||
`match(argnodes, condition, then, else)`:
|
||
|
||
- `argnode`: A single |luasnip-node-reference|. May not be nil, or
|
||
a table.
|
||
- `condition` may be either of
|
||
- `string`: interpreted as a lua pattern. Matched on the `\n`-joined (in case
|
||
it’s multiline) text of the first argnode (`args[1]:match(condition)`).
|
||
- `function`: `fn(args, snip) -> bool`: takes the same parameters as the
|
||
`functionNode`-function, any value other than nil or false is interpreted
|
||
as a match.
|
||
- `lambda`: `l._n` is the `\n`-joined text of the nth argnode.
|
||
Useful if string manipulations have to be performed before the string is matched.
|
||
Should end with `match`, but any other truthy result will be interpreted
|
||
as matching.
|
||
- `then` is inserted if the condition matches,
|
||
- `else` if it does not.
|
||
|
||
Both `then` and `else` can be either text, lambda or function (with the same
|
||
parameters as specified above). `then`’s default-value depends on the
|
||
`condition`:
|
||
|
||
- `pattern`: Simply the return value from the `match`, e.g. the entire match,
|
||
or, if there were capture groups, the first capture group.
|
||
- `function`: the return value of the function if it is either a string, or a
|
||
table (if there is no `then`, the function cannot return a table containing
|
||
something other than strings).
|
||
- `lambda`: Simply the first value returned by the lambda.
|
||
|
||
Examples:
|
||
|
||
- `match(n, "^ABC$", "A")`.
|
||
- `match(n, lambda._1:match(lambda._1:reverse()), "PALINDROME")`
|
||
>lua
|
||
s("trig", {
|
||
i(1), t":",
|
||
i(2), t"::",
|
||
m({1, 2}, l._1:match("^"..l._2.."$"), l._1:gsub("a", "e"))
|
||
})
|
||
<
|
||
- >lua
|
||
s("extras1", {
|
||
i(1), t { "", "" }, m(1, "^ABC$", "A")
|
||
})
|
||
<
|
||
Inserts "A" if the node with jump-index `n` matches "ABC" exactly, nothing
|
||
otherwise.
|
||
- >lua
|
||
s("extras2", {
|
||
i(1, "INPUT"), t { "", "" }, m(1, l._1:match(l._1:reverse()), "PALINDROME")
|
||
})
|
||
<
|
||
Inserts `"PALINDROME"` if i(1) contains a palindrome.
|
||
- >lua
|
||
s("extras3", {
|
||
i(1), t { "", "" }, i(2), t { "", "" },
|
||
m({ 1, 2 }, l._1:match("^" .. l._2 .. "$"), l._1:gsub("a", "e"))
|
||
})
|
||
<
|
||
This inserts the text of the node with jump-index 1, with all occurrences of
|
||
`a` replaced with `e`, if the second insertNode matches the first exactly.
|
||
|
||
|
||
REPEAT *luasnip-extras-repeat*
|
||
|
||
Inserts the text of the passed node.
|
||
|
||
`rep(node_reference)` - `node_reference`, a single |luasnip-node-reference|.
|
||
|
||
>lua
|
||
s("extras4", { i(1), t { "", "" }, extras.rep(1) })
|
||
<
|
||
|
||
|
||
PARTIAL *luasnip-extras-partial*
|
||
|
||
Evaluates a function on expand and inserts its value.
|
||
|
||
`partial(fn, params...)` - `fn`: any function - `params`: varargs, any, will be
|
||
passed to `fn`.
|
||
|
||
For example `partial(os.date, "%Y")` inserts the current year on expansion.
|
||
|
||
>lua
|
||
s("extras5", { extras.partial(os.date, "%Y") })
|
||
<
|
||
|
||
|
||
NONEMPTY *luasnip-extras-nonempty*
|
||
|
||
Inserts text if the referenced node doesn’t contain any text.
|
||
|
||
`nonempty(node_reference, not_empty, empty)`:
|
||
|
||
- `node_reference`, a single |luasnip-node-reference|.
|
||
- `not_empty`, `string`: inserted if the node is not empty.
|
||
- `empty`, `string`: inserted if the node is empty.
|
||
|
||
>lua
|
||
s("extras6", { i(1, ""), t { "", "" }, extras.nonempty(1, "not empty!", "empty!") })
|
||
<
|
||
|
||
|
||
DYNAMIC LAMBDA *luasnip-extras-dynamic-lambda*
|
||
|
||
Pretty much the same as lambda, but it inserts the resulting text as an
|
||
insertNode, and, as such, it can be quickly overridden.
|
||
|
||
`dynamic_lambda(jump_indx, lambda, node_references)` - `jump_indx`, as usual,
|
||
the jump-indx.
|
||
|
||
The remaining arguments carry over from lambda.
|
||
|
||
>lua
|
||
s("extras7", { i(1), t { "", "" }, extras.dynamic_lambda(2, l._1 .. l._1, 1) })
|
||
<
|
||
|
||
|
||
FMT *luasnip-extras-fmt*
|
||
|
||
Authoring snippets can be quite clunky, especially since every second node is
|
||
probably a `textNode`, inserting a small number of characters between two more
|
||
complicated nodes.
|
||
|
||
`fmt` can be used to define snippets in a much more readable way. This is
|
||
achieved by borrowing (as the name implies) from `format`-functionality (our
|
||
syntax is very similar to python’s
|
||
<https://docs.python.org/3/library/stdtypes.html#str.format>).
|
||
|
||
`fmt` accepts a string and a table of nodes. Each occurrence of a delimiter
|
||
pair in the string is replaced by one node from the table, while text outside
|
||
the delimiters is turned into textNodes.
|
||
|
||
Simple example:
|
||
|
||
>lua
|
||
ls.add_snippets("all", {
|
||
-- important! fmt does not return a snippet, it returns a table of nodes.
|
||
s("example1", fmt("just an {iNode1}", {
|
||
iNode1 = i(1, "example")
|
||
})),
|
||
s("example2", fmt([[
|
||
if {} then
|
||
{}
|
||
end
|
||
]], {
|
||
-- i(1) is at nodes[1], i(2) at nodes[2].
|
||
i(1, "not now"), i(2, "when")
|
||
})),
|
||
s("example3", fmt([[
|
||
if <> then
|
||
<>
|
||
end
|
||
]], {
|
||
-- i(1) is at nodes[1], i(2) at nodes[2].
|
||
i(1, "not now"), i(2, "when")
|
||
}, {
|
||
delimiters = "<>"
|
||
})),
|
||
s("example4", fmt([[
|
||
repeat {a} with the same key {a}
|
||
]], {
|
||
a = i(1, "this will be repeat")
|
||
}, {
|
||
repeat_duplicates = true
|
||
}))
|
||
})
|
||
<
|
||
|
||
One important detail here is that the position of the delimiters does not, in
|
||
any way, correspond to the jump-index of the nodes!
|
||
|
||
`fmt(format:string, nodes:table of nodes, opts:table|nil) -> table of nodes`
|
||
|
||
- `format`: a string. Occurrences of `{<somekey>}` ( `{}` are customizable; more
|
||
on that later) are replaced with `content[<somekey>]` (which should be a
|
||
node), while surrounding text becomes `textNode`s.
|
||
To escape a delimiter, repeat it (`"{{"`).
|
||
If no key is given (`{}`) are numbered automatically:
|
||
`"{} ? {} : {}"` becomes `"{1} ? {2} : {3}"`, while
|
||
`"{} ? {3} : {}"` becomes `"{1} ? {3} : {4}"` (the count restarts at each
|
||
numbered placeholder).
|
||
If a key appears more than once in `format`, the node in
|
||
`content[<duplicate_key>]` is inserted for the first, and copies of it for
|
||
subsequent occurrences.
|
||
- `nodes`: just a table of nodes.
|
||
- `opts`: optional arguments:
|
||
- `delimiters`: string, two characters. Change `{}` to some other pair, e.g.
|
||
`"<>"`.
|
||
- `strict`: Warn about unused nodes (default true).
|
||
- `trim_empty`: remove empty (`"%s*"`) first and last line in `format`. Useful
|
||
when passing multiline strings via `[[]]` (default true).
|
||
- `dedent`: remove indent common to all lines in `format`. Again, makes
|
||
passing multiline-strings a bit nicer (default true).
|
||
- `repeat_duplicates`: repeat nodes when a key is reused instead of copying
|
||
the node if it has a jump-index, refer to |luasnip-basics-jump-index| to
|
||
know which nodes have a jump-index (default false).
|
||
|
||
There is also `require("luasnip.extras.fmt").fmta`. This only differs from
|
||
`fmt` by using angle brackets (`<>`) as the default delimiter.
|
||
|
||
|
||
CONDITIONS *luasnip-extras-conditions*
|
||
|
||
This module (`luasnip.extras.condition`) contains functions that can be passed
|
||
to a snippet’s `condition` or `show_condition`. These are grouped accordingly
|
||
into `luasnip.extras.conditions.expand` and `luasnip.extras.conditions.show`:
|
||
|
||
**expand**:
|
||
|
||
- `line_begin`: only expand if the cursor is at the beginning of the line.
|
||
|
||
**show**:
|
||
|
||
- `line_end`: only expand at the end of the line.
|
||
- `has_selected_text`: only expand if there’s selected text stored after pressing
|
||
`store_selection_keys`.
|
||
|
||
Additionally, `expand` contains all conditions provided by `show`.
|
||
|
||
|
||
CONDITION OBJECTS ~
|
||
|
||
`luasnip.extras.conditions` also contains condition objects. These can, just
|
||
like functions, be passed to `condition` or `show_condition`, but can also be
|
||
combined with each other into logical expressions:
|
||
|
||
- `-c1 -> not c1`
|
||
- `c1 * c2 -> c1 and c2`
|
||
- `c1 + c2 -> c1 or c2`
|
||
- `c1 - c2 -> c1 and not c2`: This is similar to set differences:
|
||
`A \ B = {a in A | a not in B}`. This makes `-(a + b) = -a - b` an identity
|
||
representing de Morgan’s law: `not (a or b) = not a and not b`. However,
|
||
since boolean algebra lacks an additive inverse, `a + (-b) = a - b` does not
|
||
hold. Thus, this is NOT the same as `c1 + (-c2)`.
|
||
- `c1 ^ c2 -> c1 xor(!=) c2`
|
||
- `c1 % c2 -> c1 xnor(==) c2`: This decision may seem weird, considering how
|
||
there is an overload for the `==`-operator. Unfortunately, it’s not possible
|
||
to use this for our purposes (some info
|
||
here <https://github.com/L3MON4D3/LuaSnip/pull/612#issuecomment-1264487743>),
|
||
so we decided to make use of a more obscure symbol (which will hopefully avoid
|
||
false assumptions about its meaning).
|
||
|
||
This makes logical combinations of conditions very readable. Compare
|
||
|
||
>lua
|
||
condition = conditions.expand.line_end + conditions.expand.line_begin
|
||
<
|
||
|
||
with the more verbose
|
||
|
||
>lua
|
||
condition = function(...) return conditions.expand.line_end(...) or conditions.expand.line_begin(...) end
|
||
<
|
||
|
||
The conditions provided in `show` and `expand` are already condition objects.
|
||
To create new ones, use
|
||
`require("luasnip.extras.conditions").make_condition(condition_fn)`
|
||
|
||
|
||
ON THE FLY-SNIPPETS *luasnip-extras-on-the-fly-snippets*
|
||
|
||
Sometimes it’s desirable to create snippets tailored for exactly the current
|
||
situation. For example inserting repetitive, but just slightly different
|
||
invocations of some function, or supplying data in some schema.
|
||
|
||
On-the-fly snippets enable exactly this use case: they can be quickly created
|
||
and expanded with as little disruption as possible.
|
||
|
||
Since they should mainly fast to write and don’t necessarily need all bells
|
||
and whistles, they don’t make use of lsp/textmate-syntax, but a more
|
||
simplistic one:
|
||
|
||
- `$anytext` denotes a placeholder (`insertNode`) with text "anytext". The text
|
||
also serves as a unique key: if there are multiple placeholders with the same
|
||
key, only the first will be editable, the others will just mirror it.
|
||
- … That’s it. `$` can be escaped by preceding it with a second `$`, all other
|
||
symbols will be interpreted literally.
|
||
|
||
There is currently only one way to expand on-the-fly snippets:
|
||
`require('luasnip.extras.otf').on_the_fly("<some-register>")` will interpret
|
||
whatever text is in the register `<some-register>` as a snippet, and expand it
|
||
immediately. The idea behind this mechanism is that it enables a very immediate
|
||
way of supplying and retrieving (expanding) the snippet: write the snippet-body
|
||
into the buffer, cut/yank it into some register, and call
|
||
`on_the_fly("<register>")` to expand the snippet.
|
||
|
||
Here’s one set of example keybindings:
|
||
|
||
>vim
|
||
" in the first call: passing the register is optional since `on_the_fly`
|
||
" defaults to the unnamed register, which will always contain the previously cut
|
||
" text.
|
||
vnoremap <c-f> "ec<cmd>lua require('luasnip.extras.otf').on_the_fly("e")<cr>
|
||
inoremap <c-f> <cmd>lua require('luasnip.extras.otf').on_the_fly("e")<cr>
|
||
<
|
||
|
||
Obviously, `<c-f>` is arbitrary and can be changed to any other key combo.
|
||
Another interesting application is allowing multiple on-the-fly snippets at the
|
||
same time by retrieving snippets from multiple registers:
|
||
|
||
>vim
|
||
" For register a
|
||
vnoremap <c-f>a "ac<cmd>lua require('luasnip.extras.otf').on_the_fly()<cr>
|
||
inoremap <c-f>a <cmd>lua require('luasnip.extras.otf').on_the_fly("a")<cr>
|
||
|
||
|
||
" For register b
|
||
vnoremap <c-f>a "bc<cmd>:lua require('luasnip.extras.otf').on_the_fly()<cr>
|
||
inoremap <c-f>b <cmd>lua require('luasnip.extras.otf').on_the_fly("b")<cr>
|
||
<
|
||
|
||
|
||
SELECT_CHOICE *luasnip-extras-select_choice*
|
||
|
||
It’s possible to leverage `vim.ui.select` for selecting a choice directly,
|
||
without cycling through the available choices. All that is needed for this is
|
||
calling `require("luasnip.extras.select_choice")`, most likely via some
|
||
keybind, e.g.
|
||
|
||
>vim
|
||
inoremap <c-u> <cmd>lua require("luasnip.extras.select_choice")()<cr>
|
||
<
|
||
|
||
while inside a choiceNode. The `opts.kind` hint for `vim.ui.select` will be set
|
||
to `luasnip`.
|
||
|
||
|
||
FILETYPE-FUNCTIONS *luasnip-extras-filetype-functions*
|
||
|
||
Contains some utility functions that can be passed to the `ft_func` or
|
||
`load_ft_func`-settings.
|
||
|
||
- `from_filetype`: the default for `ft_func`. Simply returns the filetype(s) of
|
||
the buffer.
|
||
- `from_cursor_pos`: uses treesitter to determine the filetype at the cursor.
|
||
With that, it’s possible to expand snippets in injected regions, as long as
|
||
the treesitter parser supports them. If this is used in conjunction with
|
||
`lazy_load`, extra care must be taken that all the filetypes that can be
|
||
expanded in a given buffer are also returned by `load_ft_func` (otherwise their
|
||
snippets may not be loaded). This can easily be achieved with `extend_load_ft`.
|
||
- `extend_load_ft`: `fn(extend_ft:map) -> fn` A simple solution to the problem
|
||
described above is loading more filetypes than just that of the target buffer
|
||
when `lazy_load`ing. This can be done ergonomically via `extend_load_ft`:
|
||
calling it with a table where the keys are filetypes, and the values are the
|
||
filetypes that should be loaded additionally returns a function that can be
|
||
passed to `load_ft_func` and takes care of extending the filetypes properly.
|
||
>lua
|
||
ls.setup({
|
||
load_ft_func =
|
||
-- Also load both lua and json when a markdown-file is opened,
|
||
-- javascript for html.
|
||
-- Other filetypes just load themselves.
|
||
require("luasnip.extras.filetype_functions").extend_load_ft({
|
||
markdown = {"lua", "json"},
|
||
html = {"javascript"}
|
||
})
|
||
})
|
||
<
|
||
|
||
|
||
POSTFIX-SNIPPET *luasnip-extras-postfix-snippet*
|
||
|
||
Postfix snippets, famously used in rust analyzer
|
||
<https://rust-analyzer.github.io/> and various IDEs, are a type of snippet
|
||
which alters text before the snippet’s trigger. While these can be
|
||
implemented using regTrig snippets, this helper makes the process easier in
|
||
most cases.
|
||
|
||
The simplest example, which surrounds the text preceding the `.br` with
|
||
brackets `[]`, looks like:
|
||
|
||
>lua
|
||
postfix(".br", {
|
||
f(function(_, parent)
|
||
return "[" .. parent.snippet.env.POSTFIX_MATCH .. "]"
|
||
end, {}),
|
||
})
|
||
<
|
||
|
||
and is triggered with `xxx.br` and expands to `[xxx]`.
|
||
|
||
Note the `parent.snippet.env.POSTFIX_MATCH` in the function node. This is
|
||
additional field generated by the postfix snippet. This field is generated by
|
||
extracting the text matched (using a configurable matching string, see below)
|
||
from before the trigger. In the case above, the field would equal `"xxx"`. This
|
||
is also usable within dynamic nodes.
|
||
|
||
This field can also be used within lambdas and dynamic nodes.
|
||
|
||
>lua
|
||
postfix(".br", {
|
||
l("[" .. l.POSTFIX_MATCH .. "]"),
|
||
})
|
||
<
|
||
|
||
>lua
|
||
postfix(".brd", {
|
||
d(1, function (_, parent)
|
||
return sn(nil, {t("[" .. parent.env.POSTFIX_MATCH .. "]")})
|
||
end)
|
||
})
|
||
<
|
||
|
||
The arguments to `postfix` are identical to the arguments to `s` but with a few
|
||
extra options.
|
||
|
||
The first argument can be either a string or a table. If it is a string, that
|
||
string will act as the trigger, and if it is a table it has the same valid keys
|
||
as the table in the same position for `s` except:
|
||
|
||
- `wordTrig`: This key will be ignored if passed in, as it must always be
|
||
false for postfix snippets.
|
||
- `match_pattern`: The pattern that the line before the trigger is matched
|
||
against. The default match pattern is `"[%w%.%_%-]+$"`. Note the `$`. This
|
||
matches since only the line _up until_ the beginning of the trigger is
|
||
matched against the pattern, which makes the character immediately
|
||
preceding the trigger match as the end of the string.
|
||
|
||
Some other match strings, including the default, are available from the postfix
|
||
module. `require("luasnip.extras.postfix).matches`:
|
||
|
||
- `default`: `[%w%.%_%-%"%']+$`
|
||
- `line`: `^.+$`
|
||
|
||
The second argument is identical to the second argument for `s`, that is, a
|
||
table of nodes.
|
||
|
||
The optional third argument is the same as the third (`opts`) argument to the
|
||
`s` function, but with one difference:
|
||
|
||
The postfix snippet works using a callback on the pre_expand event of the
|
||
snippet. If you pass a callback on the pre_expand event (structure example
|
||
below) it will get run after the builtin callback.
|
||
|
||
>lua
|
||
{
|
||
callbacks = {
|
||
[-1] = {
|
||
[events.pre_expand] = function(snippet, event_args)
|
||
-- function body to match before the dot
|
||
-- goes here
|
||
end
|
||
}
|
||
}
|
||
}
|
||
<
|
||
|
||
|
||
TREESITTER-POSTFIX-SNIPPET *luasnip-extras-treesitter-postfix-snippet*
|
||
|
||
Instead of triggering a postfix-snippet when some pattern matches in front of
|
||
the trigger, it might be useful to match if some specific treesitter-nodes
|
||
surround/are in front of the trigger. While this functionality can also be
|
||
implemented by a custom `resolveExpandParams`, this helper simplifies the
|
||
common cases.
|
||
|
||
This matching of treesitter-nodes can be done either
|
||
|
||
- by providing a query and the name of the capture that should be in front of
|
||
the trigger (in most cases, the complete match, but requiring specific nodes
|
||
before/after the matched node may be useful as well), or
|
||
- by providing a function that manually walks the node-tree, and returns the
|
||
node in front of the trigger on success (for increased flexibility).
|
||
|
||
A simple example, which surrounds the previous node’s text preceding the
|
||
`.mv` with `std::move()` in cpp files, looks like:
|
||
|
||
>lua
|
||
local treesitter_postfix = require("luasnip.extras.treesitter_postfix").treesitter_postfix
|
||
|
||
treesitter_postfix({
|
||
trig = ".mv",
|
||
matchTSNode = {
|
||
query = [[
|
||
[
|
||
(call_expression)
|
||
(identifier)
|
||
(template_function)
|
||
(subscript_expression)
|
||
(field_expression)
|
||
(user_defined_literal)
|
||
] @prefix
|
||
]]
|
||
query_lang = "cpp"
|
||
},
|
||
},{
|
||
f(function(_, parent)
|
||
local node_content = table.concat(parent.snippet.env.LS_TSMATCH, '\n')
|
||
local replaced_content = ("std::move(%s)"):format(node_content)
|
||
return vim.split(ret_str, "\n", { trimempty = false })
|
||
end)
|
||
})
|
||
<
|
||
|
||
`LS_TSMATCH` is the treesitter-postfix equivalent to `POSTFIX_MATCH`, and is
|
||
populated with the match (in this case the text of a treesitter-node) in front
|
||
of the trigger.
|
||
|
||
The arguments to `treesitter_postfix` are identical to the arguments to `s` but
|
||
with a few extra options.
|
||
|
||
The first argument has to be a table, which defines at least `trig` and
|
||
`matchTSNode`. All keys from the regular `s` may be set here (except for
|
||
`wordTrig`, which will be ignored), and additionally the following:
|
||
|
||
- `reparseBuffer`, `string?`: Sometimes the trigger may interfere with
|
||
treesitter recognizing queries correctly. With this option, the trigger may
|
||
either be removed from the live-buffer (`"live"`), from a copy of the buffer
|
||
(`"copy"`), or not at all (`nil`).
|
||
- `matchTSNode`: How to determine whether there is a matching node in front of
|
||
the cursor. There are two options:
|
||
- `fun(parser: LuaSnip.extra.TSParser, pos: { [1]: number, [2]: number }): LuaSnip.extra.NamedTSMatch?, TSNode? Manually determine whether there is a matching node that ends just before`pos`(the beginning of the trigger). Return`nil,nil`if there is no match, otherwise first return a table mapping names to nodes (the text, position and type of these will be provided via`snip.env`), and second the node that is the matched node.
|
||
- `LuaSnip.extra.MatchTSNodeOpts`, which represents a query and provides all
|
||
captures of the matched pattern in `NamedTSMatch`. It contains the following
|
||
options:
|
||
- `query`, `string`: The query, in textual form.
|
||
- `query_name`, `string`: The name of the runtime-query to be used (passed
|
||
to `query.get()`), defaults to `"luasnip"` (so one could create a
|
||
file which only contains queries used by luasnip, like
|
||
`$CONFDIR/queries/<lang>/luasnip.scm`, which might make sense to define
|
||
general concepts independent of a single snippet).
|
||
`query` and `query_name` are mutually exclusive, only one of both shall be
|
||
defined.
|
||
- `query_lang`, `string`: The language of the query. This is the only
|
||
required parameter to this function, since there’s no sufficiently
|
||
straightforward way to determine the language of the query for us.
|
||
Consider using `extend_override` to define a `ts_postfix`-function that
|
||
automatically fills in the language for the filetype of the snippet-file.
|
||
- `match_captures`, `string|string[]`: The capture(s) to use for determining
|
||
the actual prefix (so the node that should be immediately in front of the
|
||
trigger). This defaults to just `"prefix"`.
|
||
- `select`, `string?|fun(): LuaSnip.extra.MatchSelector`: Since there may be
|
||
multiple matching captures in front of the cursor, there has to be some
|
||
way to select the node that will actually be used.
|
||
If this is a string, it has to be one of "any", "shortest", or "longest",
|
||
which mean that any, the shortest, or the longest match is used.
|
||
If it is a function, it must return a table with two fields, `record` and
|
||
`retrieve`. `record` is called with a TSMatch and a potential node for the
|
||
ts-match, and may return `true` to abort the selection-procedure.
|
||
`retrieve` must return either a TSMatch-TSNode-tuple (which is used as the
|
||
match) or `nil`, to signify that there is no match.
|
||
`lua/luasnip/extras/_treesitter.lua` contains the table
|
||
`builtin_tsnode_selectors`, which contains the implementations for
|
||
any/shortest/longest, which can be used as examples for more complicated
|
||
custom-selectors.
|
||
|
||
The text of the matched node can be accessed as `snip.env.LS_TSMATCH`. The text
|
||
of the nodes returned as `NamedTSMatch` can be accessed as
|
||
`snip.env.LS_TSCAPTURE_<node-name-in-caps>`, and their range and type as
|
||
`snip.env.LS_TSDATA.<node-name-NOT-in-caps>.range/type` (where range is a tuple
|
||
of row-col-tuples, both 0-indexed).
|
||
|
||
For a query like
|
||
|
||
>scm
|
||
(function_declaration
|
||
name: (identifier) @fname
|
||
parameters: (parameters) @params
|
||
body: (block) @body
|
||
) @prefix
|
||
<
|
||
|
||
matched against
|
||
|
||
>lua
|
||
function add(a, b)
|
||
return a + b
|
||
end
|
||
<
|
||
|
||
`snip.env` would contain:
|
||
|
||
- `LS_TSMATCH`: `{ "function add(a, b)", "\treturn a + b", "end" }`
|
||
- `LS_TSDATA`:
|
||
>lua
|
||
{
|
||
body = {
|
||
range = { { 1, 1 }, { 1, 13 } },
|
||
type = "block"
|
||
},
|
||
fname = {
|
||
range = { { 0, 9 }, { 0, 12 } },
|
||
type = "identifier"
|
||
},
|
||
params = {
|
||
range = { { 0, 12 }, { 0, 18 } },
|
||
type = "parameters"
|
||
},
|
||
prefix = {
|
||
range = { { 0, 0 }, { 2, 3 } },
|
||
type = "function_declaration"
|
||
}
|
||
}
|
||
<
|
||
- `LS_TSCAPTURE_FNAME`: `{ "add" }`
|
||
- `LS_TSCAPTURE_PARAMS`: `{ "(a, b)" }`
|
||
- `LS_TSCAPTURE_BODY`: `{ "return a + b" }`
|
||
- `LS_TSCAPTURE_PREFIX`: `{ "function add(a, b)", "\treturn a + b", "end" }`
|
||
|
||
(note that all variables containing text of nodes are string-arrays, one entry
|
||
for each line)
|
||
|
||
There is one important caveat when accessing `LS_TSDATA` in
|
||
function/dynamicNodes: It won’t contain the values as specified here while
|
||
generating docstrings (in fact, it won’t even be a table). Since docstrings
|
||
have to be generated without any runtime-information, we just have to provide
|
||
dummy-data in `env`, which will be some kind of string related to the name of
|
||
the env-variable. Since the structure of `LS_TSDATA` obviously does not fit
|
||
that model, we can’t really handle it in a nice way (at least yet). So, for
|
||
now, best include a check like `local static_evaluation = type(env.LS_TSDATA)
|
||
== "string"`, and behave accordingly if `static_evaluation` is true (for
|
||
example, return some value tailored for displaying it in a docstring).
|
||
|
||
One more example, which actually uses a few captures:
|
||
|
||
>lua
|
||
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 module `luasnip.extras.treesitter_postfix` contains a few functions that
|
||
may be useful for creating more efficient ts-postfix-snippets. Nested in
|
||
`builtin.tsnode_matcher` are:
|
||
|
||
- `fun find_topmost_types(types: string[]): MatchTSNodeFunc`: Generates
|
||
a `LuaSnip.extra.MatchTSNodeFunc` which returns the last parent whose type
|
||
is in `types`.
|
||
- `fun find_first_types(types: string[]): MatchTSNodeFunc`: Similar to
|
||
`find_topmost_types`, only this one matches the first parent whose type is in
|
||
types.
|
||
- `find_nth_parent(n: number): MatchTSNodeFunc`: Simply matches the `n`-th
|
||
parent of the innermost node infront of the trigger.
|
||
|
||
With `find_topmost_types`, the first example can be implemented more
|
||
efficiently (without needing a whole query):
|
||
|
||
>lua
|
||
local postfix_builtin = require("luasnip.extras.treesitter_postfix").builtin
|
||
|
||
ls.add_snippets("all", {
|
||
ts_post({
|
||
matchTSNode = postfix_builtin.tsnode_matcher.find_topmost_types({
|
||
"call_expression",
|
||
"identifier",
|
||
"template_function",
|
||
"subscript_expression",
|
||
"field_expression",
|
||
"user_defined_literal"
|
||
}),
|
||
trig = ".mv"
|
||
}, {
|
||
l(l_str.format("std::move(%s)", l.LS_TSMATCH))
|
||
})
|
||
}, {key = "asdf"})
|
||
<
|
||
|
||
|
||
SNIPPET LIST *luasnip-extras-snippet-list*
|
||
|
||
>lua
|
||
local sl = require("luasnip.extras.snippet_list")
|
||
<
|
||
|
||
Makes an `open` function available to use to open currently available snippets
|
||
in a different buffer/window/tab.
|
||
|
||
`sl.open(opts:table|nil)`
|
||
|
||
- `opts`: optional arguments:
|
||
- `snip_info`: `snip_info(snippet) -> table representation of snippet`
|
||
- `printer`: `printer(snippets:table) -> any`
|
||
- `display`: `display(snippets:any)`
|
||
|
||
Benefits include: syntax highlighting, searching, and customizability.
|
||
|
||
Simple Example:
|
||
|
||
>lua
|
||
sl.open()
|
||
<
|
||
|
||
Customization Examples:
|
||
|
||
>lua
|
||
-- making our own snip_info
|
||
local function snip_info(snippet)
|
||
return { name = snippet.name }
|
||
end
|
||
|
||
-- using it
|
||
sl.open({snip_info = snip_info})
|
||
<
|
||
|
||
>lua
|
||
-- making our own printer
|
||
local function printer(snippets)
|
||
local res = ""
|
||
|
||
for ft, snips in pairs(snippets) do
|
||
res = res .. ft .. "\n"
|
||
for _, snip in pairs(snips) do
|
||
res = res .. " " .. "Name: " .. snip.name .. "\n"
|
||
res = res .. " " .. "Desc: " .. snip.description[1] .. "\n"
|
||
res = res .. " " .. "Trigger: " .. snip.trigger .. "\n"
|
||
res = res .. " ----" .. "\n"
|
||
end
|
||
end
|
||
|
||
return res
|
||
end
|
||
|
||
|
||
-- using it
|
||
sl.open({printer = printer})
|
||
<
|
||
|
||
>lua
|
||
-- making our own display
|
||
local function display(printer_result)
|
||
-- right vertical split
|
||
vim.cmd("botright vnew")
|
||
|
||
-- get buf and win handle
|
||
local buf = vim.api.nvim_get_current_buf()
|
||
local win = vim.api.nvim_get_current_win()
|
||
|
||
-- setting window and buffer options
|
||
vim.api.nvim_win_set_option(win, "foldmethod", "manual")
|
||
vim.api.nvim_buf_set_option(buf, "filetype", "javascript")
|
||
|
||
vim.api.nvim_buf_set_option(buf, "buftype", "nofile")
|
||
vim.api.nvim_buf_set_option(buf, "bufhidden", "wipe")
|
||
vim.api.nvim_buf_set_option(buf, "buflisted", false)
|
||
|
||
vim.api.nvim_buf_set_name(buf, "Custom Display buf " .. buf)
|
||
|
||
-- dump snippets
|
||
local replacement = vim.split(printer_result)
|
||
vim.api.nvim_buf_set_lines(buf, 0, 0, false, replacement)
|
||
end
|
||
|
||
-- using it
|
||
sl.open({display = display})
|
||
<
|
||
|
||
There is a **caveat** with implementing your own printer and/or display
|
||
function. The **default** behavior for the printer function is to return a
|
||
string representation of the snippets. The display function uses the results
|
||
from the printer function, therefore by **default** the display function is
|
||
expecting that result to be a string.
|
||
|
||
However, this doesn’t have to be the case. For example, you can implement
|
||
your own printer function that returns a table representation of the snippets
|
||
**but** you would have to then implement your own display function or some
|
||
other function in order to return the result as a string.
|
||
|
||
An `options` table, which has some core functionality that can be used to
|
||
customize 'common' settings, is provided.
|
||
|
||
- `sl.options`: options table:
|
||
- `display`: `display(opts:table|nil) -> function(printer_result:string)`
|
||
|
||
You can see from the example above that making a custom display is a fairly
|
||
involved process. What if you just wanted to change a buffer option like the
|
||
name or just the filetype? This is where `sl.options.display` comes in. It
|
||
allows you to customize buffer and window options while keeping the default
|
||
behavior.
|
||
|
||
`sl.options.display(opts:table|nil) -> function(printer_result:string)`
|
||
|
||
- `opts`: optional arguments:
|
||
- `win_opts`: `table which has a {window_option = value} form`
|
||
- `buf_opts`: `table which has a {buffer_option = value} form`
|
||
- `get_name`: `get_name(buf) -> string`
|
||
|
||
Let’s recreate the custom display example above:
|
||
|
||
>lua
|
||
-- keeping the default display behavior but modifying window/buffer
|
||
local modified_default_display = sl.options.display({
|
||
buf_opts = {filetype = "javascript"},
|
||
win_opts = {foldmethod = "manual"},
|
||
get_name = function(buf) return "Custom Display buf " .. buf end
|
||
})
|
||
|
||
-- using it
|
||
sl.open({display = modified_default_display})
|
||
<
|
||
|
||
|
||
SNIPPET LOCATION *luasnip-extras-snippet-location*
|
||
|
||
This module can consume a snippets |luasnip-source|, more specifically, jump to
|
||
the location referred by it. This is primarily implemented for snippet which
|
||
got their source from one of the loaders, but might also work for snippets
|
||
where the source was set manually.
|
||
|
||
`require("luasnip.extras.snip_location")`:
|
||
|
||
- `snip_location.jump_to_snippet(snip, opts)`
|
||
Jump to the definition of `snip`.
|
||
- `snip`: a snippet with attached source-data.
|
||
- `opts`: `nil|table`, optional arguments, valid keys are:
|
||
- `hl_duration_ms`: `number`, duration for which the definition should be highlighted,
|
||
in milliseconds. 0 disables the highlight.
|
||
- `edit_fn`: `function(file)`, this function will be called with the file
|
||
the snippet is located in, and is responsible for jumping to it.
|
||
We assume that after it has returned, the current buffer contains `file`.
|
||
- `snip_location.jump_to_active_snippet(opts)`
|
||
Jump to definition of active snippet.
|
||
- `opts`: `nil|table`, accepts the same keys as the `opts`-parameter of
|
||
`jump_to_snippet`.
|
||
|
||
|
||
==============================================================================
|
||
17. Extend Decorator *luasnip-extend-decorator*
|
||
|
||
Most of luasnip’s functions have some arguments to control their behaviour.
|
||
Examples include `s`, where `wordTrig`, `regTrig`, … can be set in the first
|
||
argument to the function, or `fmt`, where the delimiter can be set in the third
|
||
argument. This is all good and well, but if these functions are often used with
|
||
non-default settings, it can become cumbersome to always explicitly set them.
|
||
|
||
This is where the `extend_decorator` comes in: it can be used to create
|
||
decorated functions which always extend the arguments passed directly with
|
||
other previously defined ones.
|
||
|
||
An example:
|
||
|
||
>lua
|
||
local fmt = require("luasnip.extras.fmt").fmt
|
||
|
||
fmt("{}", {i(1)}) -- -> list of nodes, containing just the i(1).
|
||
|
||
-- when authoring snippets for some filetype where `{` and `}` are common, they
|
||
-- would always have to be escaped in the format-string. It might be preferable
|
||
-- to use other delimiters, like `<` and `>`.
|
||
|
||
fmt("<>", {i(1)}, {delimiters = "<>"}) -- -> same as above.
|
||
|
||
-- but it's quite annoying to always pass the `{delimiters = "<>"}`.
|
||
|
||
-- with extend_decorator:
|
||
local fmt_angle = ls.extend_decorator.apply(fmt, {delimiters = "<>"})
|
||
fmt_angle("<>", {i(1)}) -- -> same as above.
|
||
|
||
-- the same also works with other functions provided by luasnip, for example all
|
||
-- node/snippet-constructors and `parse_snippet`.
|
||
<
|
||
|
||
`extend_decorator.apply(fn, ...)` requires that `fn` is previously registered
|
||
via `extend_decorator.register`. (This is not limited to LuaSnip’s functions;
|
||
although, for usage outside of LuaSnip, best copy the source file:
|
||
`/lua/luasnip/util/extend_decorator.lua`).
|
||
|
||
`register(fn, ...)`:
|
||
|
||
- `fn`: the function.
|
||
- `...`: any number of tables. Each specifies how to extend an argument of `fn`.
|
||
The tables accept:
|
||
- arg_indx, `number` (required): the position of the parameter to override.
|
||
- extend, `fn(arg, extend_value) -> effective_arg` (optional): this function
|
||
is used to extend the args passed to the decorated function.
|
||
It defaults to a function which just extends the arg-table with the
|
||
extend table (accepts `nil`).
|
||
This extend behaviour is adaptable to accommodate `s`, where the first
|
||
argument may be string or table.
|
||
|
||
`apply(fn, ...) -> decorated_fn`:
|
||
|
||
- `fn`: the function to decorate.
|
||
- `...`: The values to extend with. These should match the descriptions passed
|
||
in `register` (the argument first passed to `register` will be extended with
|
||
the first value passed here).
|
||
|
||
One more example for registering a new function:
|
||
|
||
>lua
|
||
local function somefn(arg1, arg2, opts1, opts2)
|
||
-- not important
|
||
end
|
||
|
||
-- note the reversed arg_indx!!
|
||
extend_decorator.register(somefn, {arg_indx=4}, {arg_indx=3})
|
||
local extended = extend_decorator.apply(somefn,
|
||
{key = "opts2 is extended with this"},
|
||
{key = "and opts1 with this"})
|
||
extended(...)
|
||
<
|
||
|
||
|
||
==============================================================================
|
||
18. LSP-Snippets *luasnip-lsp-snippets*
|
||
|
||
LuaSnip is capable of parsing LSP-style snippets using
|
||
`ls.parser.parse_snippet(context, snippet_string, opts)`:
|
||
|
||
>lua
|
||
ls.parser.parse_snippet({trig = "lsp"}, "$1 is ${2|hard,easy,challenging|}")
|
||
<
|
||
|
||
`context` can be: - `string|table`: treated like the first argument to `ls.s`,
|
||
`parse_snippet` returns a snippet. - `number`: `parse_snippet` returns a
|
||
snippetNode, with the position `context`. - `nil`: `parse_snippet` returns a
|
||
flat table of nodes. This can be used like `fmt`.
|
||
|
||
Nested placeholders(`"${1:this is ${2:nested}}"`) will be turned into
|
||
choiceNodes with: - the given snippet(`"this is ${1:nested}"`) and - an empty
|
||
insertNode
|
||
|
||
This behaviour can be modified by changing `parser_nested_assembler` in
|
||
`ls.setup()`.
|
||
|
||
LuaSnip will also modify some snippets that it is incapable of representing
|
||
accurately:
|
||
|
||
- if the `$0` is a placeholder with something other than just text inside
|
||
- if the `$0` is a choice
|
||
- if the `$0` is not an immediate child of the snippet (it could be inside a
|
||
placeholder: `"${1: $0 }"`)
|
||
|
||
To remedy those incompatibilities, the invalid `$0` will be replaced with a
|
||
tabstop/placeholder/choice which will be visited just before the new `$0`. This
|
||
new `$0` will be inserted at the (textually) earliest valid position behind the
|
||
invalid `$0`.
|
||
|
||
`opts` can contain the following keys: - `trim_empty`: boolean, remove empty
|
||
lines from the snippet. Default true. - `dedent`: boolean, remove common indent
|
||
from the snippet’s lines. Default true.
|
||
|
||
Both `trim_empty` and `dedent` will be disabled for snippets parsed via
|
||
`ls.lsp_expand`: it might prevent correct expansion of snippets sent by lsp.
|
||
|
||
|
||
SNIPMATE PARSER *luasnip-lsp-snippets-snipmate-parser*
|
||
|
||
It is furthermore possible to parse SnipMate snippets (this includes support
|
||
for vimscript-evaluation!!)
|
||
|
||
SnipMate snippets need to be parsed with a different function,
|
||
`ls.parser.parse_snipmate`:
|
||
|
||
>lua
|
||
ls.parser.parse_snipmate("year", "The year is `strftime('%Y')`")
|
||
<
|
||
|
||
`parse_snipmate` accepts the same arguments as `parse_snippet`, only the
|
||
snippet body is parsed differently.
|
||
|
||
|
||
TRANSFORMATIONS *luasnip-lsp-snippets-transformations*
|
||
|
||
To apply Variable/Placeholder-transformations
|
||
<https://code.visualstudio.com/docs/editor/userdefinedsnippets#_variable-transforms>,
|
||
luasnip needs to apply ECMAScript regexes. This is implemented by relying on
|
||
`jsregexp` <https://github.com/kmarius/jsregexp>.
|
||
|
||
The easiest (but potentially error-prone) way to install it is by calling `make
|
||
install_jsregexp` in the repo root.
|
||
|
||
This process can be automated by `packer.nvim`:
|
||
|
||
>lua
|
||
use { "L3MON4D3/LuaSnip", run = "make install_jsregexp" }
|
||
<
|
||
|
||
If this fails, first open an issue :P, and then try installing the
|
||
`jsregexp`-luarock. This is also possible via `packer.nvim`, although actual
|
||
usage may require a small workaround, see here
|
||
<https://github.com/wbthomason/packer.nvim/issues/593> or here
|
||
<https://github.com/wbthomason/packer.nvim/issues/358>.
|
||
|
||
Alternatively, `jsregexp` can be cloned locally, `make`d, and the resulting
|
||
`jsregexp.so` placed in some place where nvim can find it (probably
|
||
`~/.config/nvim/lua/`).
|
||
|
||
If `jsregexp` is not available, transformations are replaced by a simple copy.
|
||
|
||
|
||
==============================================================================
|
||
19. Variables *luasnip-variables*
|
||
|
||
All `TM_something`-variables are supported with two additions: `LS_SELECT_RAW`
|
||
and `LS_SELECT_DEDENT`. These were introduced because `TM_SELECTED_TEXT` is
|
||
designed to be compatible with VSCode’s behavior, which can be
|
||
counterintuitive when the snippet can be expanded at places other than the
|
||
point where selection started (or when doing transformations on selected text).
|
||
Besides those we also provide `LS_TRIGGER` which contains the trigger of the
|
||
snippet, and `LS_CAPTURE_n` (where n is a positive integer) that contains the
|
||
n-th capture when using a regex with capture groups as `trig` in the snippet
|
||
definition.
|
||
|
||
All variables can be used outside of lsp-parsed snippets as their values are
|
||
stored in a snippets’ `snip.env`-table:
|
||
|
||
>lua
|
||
s("selected_text", f(function(args, snip)
|
||
local res, env = {}, snip.env
|
||
table.insert(res, "Selected Text (current line is " .. env.TM_LINE_NUMBER .. "):")
|
||
for _, ele in ipairs(env.LS_SELECT_RAW) do table.insert(res, ele) end
|
||
return res
|
||
end, {}))
|
||
<
|
||
|
||
To use any `*SELECT*` variable, the `store_selection_keys` must be set via
|
||
`require("luasnip").config.setup({store_selection_keys="<Tab>"})`. In this
|
||
case, hitting `<Tab>` while in visual mode will populate the `*SELECT*`-vars
|
||
for the next snippet and then clear them.
|
||
|
||
|
||
ENVIRONMENT NAMESPACES *luasnip-variables-environment-namespaces*
|
||
|
||
You can also add your own variables by using the `ls.env_namespace(name, opts)`
|
||
where:
|
||
|
||
- `name`: `string` the names the namespace, can’t contain the character “_”
|
||
- `opts` is a table containing (in every case `EnvVal` is the same as `string|list[string]`:
|
||
- `vars`: `(fn(name:string)->EnvVal) | map[string, EnvVal]`
|
||
Is a function that receives a string and returns a value for the var with that name
|
||
or a table from var name to a value
|
||
(in this case, if the value is a function it will be executed lazily once per snippet expansion).
|
||
- `init`: `fn(info: table)->map[string, EnvVal]` Returns
|
||
a table of variables that will set to the environment of the snippet on expansion,
|
||
use this for vars that have to be calculated in that moment or that depend on each other.
|
||
The `info` table argument contains `pos` (0-based position of the cursor on expansion),
|
||
the `trigger` of the snippet and the `captures` list.
|
||
- `eager`: `list[string]` names of variables that will be taken from `vars` and appended eagerly (like those in init)
|
||
- `multiline_vars`: `(fn(name:string)->bool)|map[sting, bool]|bool|string[]` Says if certain vars are a table or just a string,
|
||
can be a function that get’s the name of the var and returns true if the var is a key,
|
||
a list of vars that are tables or a boolean for the full namespace, it’s false by default. Refer to
|
||
issue#510 <https://github.com/L3MON4D3/LuaSnip/issues/510#issuecomment-1209333698> for more information.
|
||
|
||
The four fields of `opts` are optional but you need to provide either `init` or
|
||
`vars`, and `eager` can’t be without `vars`. Also, you can’t use namespaces
|
||
that override default vars.
|
||
|
||
A simple example to make it more clear:
|
||
|
||
>lua
|
||
local function random_lang()
|
||
return ({"LUA", "VIML", "VIML9"})[math.floor(math.random()/2 + 1.5)]
|
||
end
|
||
|
||
ls.env_namespace("MY", {vars={ NAME="LuaSnip", LANG=random_lang }})
|
||
|
||
-- then you can use $MY_NAME and $MY_LANG in your snippets
|
||
|
||
ls.env_namespace("SYS", {vars=os.getenv, eager={"HOME"}})
|
||
|
||
-- then you can use $SYS_HOME which was eagerly initialized but also $SYS_USER (or any other system environment var) in your snippets
|
||
|
||
lsp.env_namespace("POS", {init=function(info) return {VAL=vim.inspect(info.pos)} end})
|
||
|
||
-- then you can use $POS_VAL in your snippets
|
||
|
||
s("custom_env", d(1, function(args, parent)
|
||
local env = parent.snippet.env
|
||
return sn(nil, t {
|
||
"NAME: " .. env.MY_NAME,
|
||
"LANG: " .. env.MY_LANG,
|
||
"HOME: " .. env.SYS_HOME,
|
||
"USER: " .. env.SYS_USER,
|
||
"VAL: " .. env.POS_VAL
|
||
})
|
||
end, {}))
|
||
<
|
||
|
||
|
||
LSP-VARIABLES *luasnip-variables-lsp-variables*
|
||
|
||
All variables, even ones added via `env_namespace`, can be accessed in LSP
|
||
snippets as `$VAR_NAME`.
|
||
|
||
The lsp-spec states:
|
||
|
||
------------------------------------------------------------------------------
|
||
With `$name` or `${name:default}` you can insert the value of a variable. When
|
||
a variable isn’t set, its default or the empty string is inserted. When a
|
||
variable is unknown (that is, its name isn’t defined) the name of the
|
||
variable is inserted and it is transformed into a placeholder.
|
||
|
||
------------------------------------------------------------------------------
|
||
The above necessitates a differentiation between `unknown` and `unset`
|
||
variables:
|
||
|
||
For LuaSnip, a variable `VARNAME` is `unknown` when `env.VARNAME` returns `nil`
|
||
and `unset` if it returns an empty string.
|
||
|
||
Consider this when adding env-variables which might be used in LSP snippets.
|
||
|
||
|
||
==============================================================================
|
||
20. Loaders *luasnip-loaders*
|
||
|
||
Luasnip is capable of loading snippets from different formats, including both
|
||
the well-established VSCode and SnipMate format, as well as plain Lua files for
|
||
snippets written in Lua.
|
||
|
||
All loaders (except the vscode-standalone-loader) share a similar interface:
|
||
`require("luasnip.loaders.from_{vscode,snipmate,lua}").{lazy_,}load(opts:table|nil)`
|
||
|
||
where `opts` can contain the following keys:
|
||
|
||
- `paths`: List of paths to load. Can be a table, or a single
|
||
comma-separated string.
|
||
The paths may begin with `~/` or `./` to indicate that the path is
|
||
relative to your `$HOME` or to the directory where your `$MYVIMRC` resides
|
||
(useful to add your snippets).
|
||
If not set, `runtimepath` is searched for
|
||
directories that contain snippets. This procedure differs slightly for
|
||
each loader:
|
||
- `lua`: the snippet-library has to be in a directory named
|
||
`"luasnippets"`.
|
||
- `snipmate`: similar to lua, but the directory has to be `"snippets"`.
|
||
- `vscode`: any directory in `runtimepath` that contains a
|
||
`package.json` contributing snippets.
|
||
- `lazy_paths`: behaves essentially like `paths`, with two exceptions: if it is
|
||
`nil`, it does not default to `runtimepath`, and the paths listed here do not
|
||
need to exist, and will be loaded on creation.
|
||
LuaSnip will do its best to determine the path that this should resolve to,
|
||
but since the resolving we do is not very sophisticated it may produce
|
||
incorrect paths. Definitely check the log if snippets are not loaded as
|
||
expected.
|
||
- `exclude`: List of languages to exclude, empty by default.
|
||
- `include`: List of languages to include, includes everything by default.
|
||
- `{override,default}_priority`: These keys are passed straight to the
|
||
`add_snippets`-calls (documented in |luasnip-api|) and can therefore change the
|
||
priority of snippets loaded from some collection (or, in combination with
|
||
`{in,ex}clude`, only some of its snippets).
|
||
- `fs_event_providers`: `table<string, boolean>?`, specifies which mechanisms
|
||
should be used to watch files for updates/creation.
|
||
If `autocmd` is set to `true`, a `BufWritePost`-hook watches files of this
|
||
collection, if `libuv` is set, the file-watcher-api exposed by libuv is used
|
||
to watch for updates.
|
||
Use `libuv` if you want snippets to update from other neovim-instances, and
|
||
`autocmd` if the collection resides on a filesystem where the libuv-watchers
|
||
may not work correctly. Or, of course, just enable both :D
|
||
By default, only `autocmd` is enabled.
|
||
|
||
While `load` will immediately load the snippets, `lazy_load` will defer loading
|
||
until the snippets are actually needed (whenever a new buffer is created, or
|
||
the filetype is changed luasnip actually loads `lazy_load`ed snippets for the
|
||
filetypes associated with this buffer. This association can be changed by
|
||
customizing `load_ft_func` in `setup`: the option takes a function that, passed
|
||
a `bufnr`, returns the filetypes that should be loaded (`fn(bufnr) -> filetypes
|
||
(string[])`)).
|
||
|
||
All of the loaders support reloading, so simply editing any file contributing
|
||
snippets will reload its snippets (according to `fs_event_providers` in the
|
||
instance where the file was edited, or in other instances as well).
|
||
|
||
As an alternative (or addition) to automatic reloading, luasnip can also
|
||
process manual updates to files: Call
|
||
`require("luasnip.loaders").reload_file(path)` to reload the file at `path`.
|
||
This may be useful when the collection is controlled by some other plugin, or
|
||
when enabling the other reload-mechanisms is for some reason undesirable
|
||
(performance? minimalism?).
|
||
|
||
For easy editing of these files, LuaSnip provides a `vim.ui.select`-based
|
||
dialog (|luasnip-loaders-edit_snippets|) where first the filetype, and then the
|
||
file can be selected.
|
||
|
||
|
||
SNIPPET-SPECIFIC FILETYPES *luasnip-loaders-snippet-specific-filetypes*
|
||
|
||
Some loaders (vscode,lua) support giving snippets generated in some file their
|
||
own filetype (vscode via `scope`, lua via the underlying `filetype`-option for
|
||
snippets). These snippet-specific filetypes are not considered when determining
|
||
which files to `lazy_load` for some filetype, this is exclusively determined by
|
||
the `language` associated with a file in vscodes’ `package.json`, and the
|
||
file/directory-name in lua.
|
||
|
||
- This can be resolved relatively easily in vscode, where the `language`
|
||
advertised in `package.json` can just be a superset of the `scope`s in the file.
|
||
- Another simplistic solution is to set the language to `all` (in lua, it might
|
||
make sense to create a directory `luasnippets/all/*.lua` to group these files
|
||
together).
|
||
- Another approach is to modify `load_ft_func` to load a custom filetype if the
|
||
snippets should be activated, and store the snippets in a file for that
|
||
filetype. This can be used to group snippets by e.g. framework, and load them
|
||
once a file belonging to such a framework is edited.
|
||
|
||
**Example**: `react.lua`
|
||
|
||
>lua
|
||
return {
|
||
s({filetype = "css", trig = ...}, ...),
|
||
s({filetype = "html", trig = ...}, ...),
|
||
s({filetype = "js", trig = ...}, ...),
|
||
}
|
||
<
|
||
|
||
`luasnip_config.lua`
|
||
|
||
>lua
|
||
load_ft_func = function(bufnr)
|
||
if "<bufnr-in-react-framework>" then
|
||
-- will load `react.lua` for this buffer
|
||
return {"react"}
|
||
else
|
||
return require("luasnip.extras.filetype_functions").from_filetype_load
|
||
end
|
||
end
|
||
<
|
||
|
||
See the |luasnip-troubleshooting-adding-snippets-loaders| section if one is
|
||
having issues adding snippets via loaders.
|
||
|
||
|
||
VS-CODE *luasnip-loaders-vs-code*
|
||
|
||
As a reference on the structure of these snippet libraries, see
|
||
`friendly-snippets` <https://github.com/rafamadriz/friendly-snippets>.
|
||
|
||
We support a small extension: snippets can contain LuaSnip-specific options in
|
||
the `luasnip`-table:
|
||
|
||
>json
|
||
"example1": {
|
||
"prefix": "options",
|
||
"body": [
|
||
"whoa! :O"
|
||
],
|
||
"luasnip": {
|
||
"priority": 2000,
|
||
"autotrigger": true,
|
||
"wordTrig": false
|
||
}
|
||
}
|
||
<
|
||
|
||
Files with the extension `jsonc` will be parsed as `jsonc`, json with comments
|
||
<https://code.visualstudio.com/docs/languages/json#_json-with-comments>, while
|
||
`*.json` are parsed with a regular `json` parser, where comments are
|
||
disallowed. (the json-parser is a bit faster, so don’t default to `jsonc` if
|
||
it’s not necessary).
|
||
|
||
**Example**:
|
||
|
||
`~/.config/nvim/my_snippets/package.json`:
|
||
|
||
>json
|
||
{
|
||
"name": "example-snippets",
|
||
"contributes": {
|
||
"snippets": [
|
||
{
|
||
"language": [
|
||
"all"
|
||
],
|
||
"path": "./snippets/all.json"
|
||
},
|
||
{
|
||
"language": [
|
||
"lua"
|
||
],
|
||
"path": "./lua.json"
|
||
}
|
||
]
|
||
}
|
||
}
|
||
<
|
||
|
||
`~/.config/nvim/my_snippets/snippets/all.json`:
|
||
|
||
>json
|
||
{
|
||
"snip1": {
|
||
"prefix": "all1",
|
||
"body": [
|
||
"expands? jumps? $1 $2 !"
|
||
]
|
||
},
|
||
"snip2": {
|
||
"prefix": "all2",
|
||
"body": [
|
||
"multi $1",
|
||
"line $2",
|
||
"snippet$0"
|
||
]
|
||
}
|
||
}
|
||
<
|
||
|
||
`~/.config/nvim/my_snippets/lua.json`:
|
||
|
||
>json
|
||
{
|
||
"snip1": {
|
||
"prefix": "lua",
|
||
"body": [
|
||
"lualualua"
|
||
]
|
||
}
|
||
}
|
||
<
|
||
|
||
This collection can be loaded with any of
|
||
|
||
>lua
|
||
-- don't pass any arguments, luasnip will find the collection because it is
|
||
-- (probably) in rtp.
|
||
require("luasnip.loaders.from_vscode").lazy_load()
|
||
-- specify the full path...
|
||
require("luasnip.loaders.from_vscode").lazy_load({paths = "~/.config/nvim/my_snippets"})
|
||
-- or relative to the directory of $MYVIMRC
|
||
require("luasnip.loaders.from_vscode").load({paths = "./my_snippets"})
|
||
<
|
||
|
||
|
||
STANDALONE ~
|
||
|
||
Beside snippet-libraries provided by packages, vscode also supports another
|
||
format which can be used for project-local snippets, or user-defined snippets,
|
||
`.code-snippets`.
|
||
|
||
The layout of these files is almost identical to that of the package-provided
|
||
snippets, but there is one additional field supported in the
|
||
snippet-definitions, `scope`, with which the filetype of the snippet can be
|
||
set. If `scope` is not set, the snippet will be added to the global filetype
|
||
(`all`).
|
||
|
||
`require("luasnip.loaders.from_vscode").load_standalone(opts)`
|
||
|
||
- `opts`: `table`, can contain the following keys:
|
||
- `path`: `string`, Path to the `*.code-snippets`-file that should be loaded.
|
||
Just like the paths in `load`, this one can begin with a `"~/"` to be
|
||
relative to `$HOME`, and a `"./"` to be relative to the
|
||
neovim-config-directory.
|
||
- `{override,default}_priority`: These keys are passed straight to the
|
||
`add_snippets`-calls (documented in |luasnip-api|) and can be used to change
|
||
the priority of the loaded snippets.
|
||
- `lazy`: `boolean`, if it is set, the file does not have to exist when
|
||
`load_standalone` is called, and it will be loaded on creation.
|
||
`false` by default.
|
||
|
||
**Example**: `a.code-snippets`:
|
||
|
||
>jsonc
|
||
{
|
||
// a comment, since `.code-snippets` may contain jsonc.
|
||
"c/cpp-snippet": {
|
||
"prefix": [
|
||
"trigger1",
|
||
"trigger2"
|
||
],
|
||
"body": [
|
||
"this is $1",
|
||
"my snippet $2"
|
||
],
|
||
"description": "A description of the snippet.",
|
||
"scope": "c,cpp"
|
||
},
|
||
"python-snippet": {
|
||
"prefix": "trig",
|
||
"body": [
|
||
"this is $1",
|
||
"a different snippet $2"
|
||
],
|
||
"description": "Another snippet-description.",
|
||
"scope": "python"
|
||
},
|
||
"global snippet": {
|
||
"prefix": "trigg",
|
||
"body": [
|
||
"this is $1",
|
||
"the last snippet $2"
|
||
],
|
||
"description": "One last snippet-description.",
|
||
}
|
||
}
|
||
<
|
||
|
||
This file can be loaded by calling
|
||
|
||
>lua
|
||
require("luasnip.loaders.from_vscode").load_standalone({path = "a.code-snippets"})
|
||
<
|
||
|
||
|
||
SNIPMATE *luasnip-loaders-snipmate*
|
||
|
||
Luasnip does not support the full snipmate format: Only `./{ft}.snippets` and
|
||
`./{ft}/*.snippets` will be loaded. See honza/vim-snippets
|
||
<https://github.com/honza/vim-snippets> for lots of examples.
|
||
|
||
Like VSCode, the SnipMate format is also extended to make use of some of
|
||
LuaSnip’s more advanced capabilities:
|
||
|
||
>snippets
|
||
priority 2000
|
||
autosnippet options
|
||
whoa :O
|
||
<
|
||
|
||
**Example**:
|
||
|
||
`~/.config/nvim/snippets/c.snippets`:
|
||
|
||
>snippets
|
||
# this is a comment
|
||
snippet c c-snippet
|
||
c!
|
||
<
|
||
|
||
`~/.config/nvim/snippets/cpp.snippets`:
|
||
|
||
>snippets
|
||
extends c
|
||
|
||
snippet cpp cpp-snippet
|
||
cpp!
|
||
<
|
||
|
||
This can, again, be loaded with any of
|
||
|
||
>lua
|
||
require("luasnip.loaders.from_snipmate").load()
|
||
-- specify the full path...
|
||
require("luasnip.loaders.from_snipmate").lazy_load({paths = "~/.config/nvim/snippets"})
|
||
-- or relative to the directory of $MYVIMRC
|
||
require("luasnip.loaders.from_snipmate").lazy_load({paths = "./snippets"})
|
||
<
|
||
|
||
Stuff to watch out for:
|
||
|
||
- Using both `extends <ft2>` in `<ft1>.snippets` and
|
||
`ls.filetype_extend("<ft1>", {"<ft2>"})` leads to duplicate snippets.
|
||
- `${VISUAL}` will be replaced by `$TM_SELECTED_TEXT` to make the snippets
|
||
compatible with LuaSnip
|
||
- We do not implement eval using ` (backtick). This may be implemented in the
|
||
future.
|
||
|
||
|
||
LUA *luasnip-loaders-lua*
|
||
|
||
Instead of adding all snippets via `add_snippets`, it’s possible to store
|
||
them in separate files and load all of those. The file-structure here is
|
||
exactly the supported snipmate-structure, e.g. `<ft>.lua` or `<ft>/*.lua` to
|
||
add snippets for the filetype `<ft>`.
|
||
|
||
There are two ways to add snippets:
|
||
|
||
- the files may return two lists of snippets, the snippets in the first are all
|
||
added as regular snippets, while the snippets in the second will be added as
|
||
autosnippets (both are the defaults, if a snippet defines a different
|
||
`snippetType`, that will have preference)
|
||
- snippets can also be appended to the global (only for these files - they are
|
||
not visible anywhere else) tables `ls_file_snippets` and
|
||
`ls_file_autosnippets`. This can be combined with a custom `snip_env` to define
|
||
and add snippets with one function call:
|
||
>lua
|
||
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.
|
||
...
|
||
},
|
||
...
|
||
})
|
||
<
|
||
This is more flexible than the previous approach since the snippets don’t
|
||
have to be collected; they just have to be defined using the above `s` and
|
||
`parse`.
|
||
|
||
As defining all of the snippet constructors (`s`, `c`, `t`, …) in every file
|
||
is rather cumbersome, LuaSnip will bring some globals into scope for executing
|
||
these files. By default, the names from `luasnip.config.snip_env`
|
||
<https://github.com/L3MON4D3/LuaSnip/blob/master/lua/luasnip/config.lua#L22-L48>
|
||
will be used, but it’s possible to customize them by setting `snip_env` in
|
||
`setup`.
|
||
|
||
**Example**:
|
||
|
||
`~/snippets/all.lua`:
|
||
|
||
>lua
|
||
return {
|
||
s("trig", t("loaded!!"))
|
||
}
|
||
<
|
||
|
||
`~/snippets/c.lua`:
|
||
|
||
>lua
|
||
return {
|
||
s("ctrig", t("also loaded!!"))
|
||
}, {
|
||
s("autotrig", t("autotriggered, if enabled"))
|
||
}
|
||
<
|
||
|
||
Load via
|
||
|
||
>lua
|
||
require("luasnip.loaders.from_lua").load({paths = "~/snippets"})
|
||
<
|
||
|
||
|
||
RELOADING WHEN EDITING REQUIRE’D FILES ~
|
||
|
||
While the lua-snippet-files will be reloaded on edit, this does not
|
||
automatically happen if a file the snippet-file depends on (eg. via `require`)
|
||
is changed. Since this still may still be desirable, there are two functions
|
||
exposed when a file is loaded by the lua-loader: `ls_tracked_dofile` and
|
||
`ls_tracked_dopackage`. They perform like `dofile` and (almost like) `require`,
|
||
but both register the loaded file internally as a dependency of the
|
||
snippet-file, so it can be reloaded when the loaded file is edited. As stated,
|
||
`ls_tracked_dofile` behaves exactly like `dofile`, but does the dependency-work
|
||
as well. `ls_tracked_dopackage` mimics `require` in that it does not take a
|
||
path, but a module-name like `"luasnip.loaders.from_lua"`, and then searches
|
||
the `runtimepath/lua`-directories, and path and cpath for the module. Unlike
|
||
`require`, the file will not be cached, since that would complicate the
|
||
reload-on-edit-behaviour.
|
||
|
||
|
||
EDIT_SNIPPETS *luasnip-loaders-edit_snippets*
|
||
|
||
To easily edit snippets for the current session, the files loaded by any loader
|
||
can be quickly edited via
|
||
`require("luasnip.loaders").edit_snippet_files(opts:table|nil)`
|
||
|
||
When called, it will open a `vim.ui.select`-dialog to select first a filetype,
|
||
and then (if there are multiple) the associated file to edit.
|
||
|
||
`opts` contains four settings:
|
||
|
||
- `ft_filter`: `fn(filetype:string) -> bool` Optionally filter initially listed
|
||
filetypes. `true` -> filetype will be listed, `false` -> not listed. Accepts
|
||
all filetypes by default.
|
||
- `format`: `fn(file:string, source_name:string) -> string|nil` `file` is simply
|
||
the path to the file, `source_name` is one of `"lua"`, `"snipmate"` or
|
||
`"vscode"`. If a string is returned, it is used as the title of the item, `nil`
|
||
on the other hand will filter out this item. The default simply replaces some
|
||
long strings (packer-path and config-path) in `file` with shorter, symbolic
|
||
names (`"$PLUGINS"`, `"$CONFIG"`), but this can be extended to
|
||
- filter files from some specific source/path
|
||
- more aggressively shorten paths using symbolic names, e.g.
|
||
`"$FRIENDLY_SNIPPETS"`. Example: hide the `*.lua` snippet files, and shorten
|
||
the path with `$LuaSnip`:
|
||
>lua
|
||
require "luasnip.loaders" .edit_snippet_files {
|
||
format = function(file, source_name)
|
||
if source_name == "lua" then return nil
|
||
else return file:gsub("/root/.config/nvim/luasnippets", "$LuaSnip")
|
||
end
|
||
end
|
||
}
|
||
<
|
||
- `edit`: `fn(file:string)` This function is supposed to open the file for
|
||
editing. The default is a simple `vim.cmd("edit " .. file)` (replace the
|
||
current buffer), but one could open the file in a split, a tab, or a floating
|
||
window, for example.
|
||
- `extend`: `fn(ft:string, ft_paths:string[]) -> (string,string)[]` This function
|
||
can be used to create additional choices for the file-selection.
|
||
- `ft`: The filetype snippet-files are queried for.
|
||
- `ft_paths`: list of paths to the known snippet files.
|
||
The function should return a list of `(string,string)`-tuples. The first of
|
||
each pair is the label that will appear in the selection-prompt, and the second
|
||
is the path that will be passed to the `edit()` function if that item was
|
||
selected.
|
||
This can be used to create a new snippet file for the current filetype:
|
||
>lua
|
||
require("luasnip.loaders").edit_snippet_files {
|
||
extend = function(ft, paths)
|
||
if #paths == 0 then
|
||
return {
|
||
{ "$CONFIG/" .. ft .. ".snippets",
|
||
string.format("%s/%s.snippets", <PERSONAL_SNIPPETS_FOLDER>, ft) }
|
||
}
|
||
end
|
||
|
||
return {}
|
||
end
|
||
}
|
||
<
|
||
|
||
One comfortable way to call this function is registering it as a command:
|
||
|
||
>vim
|
||
command! LuaSnipEdit :lua require("luasnip.loaders").edit_snippet_files()
|
||
<
|
||
|
||
|
||
==============================================================================
|
||
21. SnippetProxy *luasnip-snippetproxy*
|
||
|
||
`SnippetProxy` is used internally to alleviate the upfront cost of loading
|
||
snippets from e.g. a SnipMate library or a VSCode package. This is achieved by
|
||
only parsing the snippet on expansion, not immediately after reading it from
|
||
some file. `SnippetProxy` may also be used from Lua directly to get the same
|
||
benefits:
|
||
|
||
This will parse the snippet on startup:
|
||
|
||
>lua
|
||
ls.parser.parse_snippet("trig", "a snippet $1!")
|
||
<
|
||
|
||
while this will parse the snippet upon expansion:
|
||
|
||
>lua
|
||
local sp = require("luasnip.nodes.snippetProxy")
|
||
sp("trig", "a snippet $1")
|
||
<
|
||
|
||
`sp(context, body, opts) -> snippetProxy`
|
||
|
||
- `context`: exactly the same as the first argument passed to `ls.s`.
|
||
- `body`: the snippet body.
|
||
- `opts`: accepts the same `opts` as `ls.s`, with some additions:
|
||
- `parse_fn`: the function for parsing the snippet. Defaults to
|
||
`ls.parser.parse_snippet` (the parser for LSP snippets), an alternative is
|
||
the parser for SnipMate snippets (`ls.parser.parse_snipmate`).
|
||
|
||
|
||
==============================================================================
|
||
22. ext_opts *luasnip-ext_opts*
|
||
|
||
`ext_opts` can be used to set the `opts` (see `nvim_buf_set_extmark`) of the
|
||
extmarks used for marking node positions, either globally, per snippet or per
|
||
node. This means that they allow highlighting the text inside of nodes, or
|
||
adding virtual text to the line the node begins on.
|
||
|
||
This is an example for the `node_ext_opts` used to set `ext_opts` of single
|
||
nodes:
|
||
|
||
>lua
|
||
local ext_opts = {
|
||
-- these ext_opts are applied when the node is active (e.g. it has been
|
||
-- jumped into, and not out yet).
|
||
active =
|
||
-- this is the table actually passed to `nvim_buf_set_extmark`.
|
||
{
|
||
-- highlight the text inside the node red.
|
||
hl_group = "GruvboxRed"
|
||
},
|
||
-- these ext_opts are applied when the node is not active, but
|
||
-- the snippet still is.
|
||
passive = {
|
||
-- add virtual text on the line of the node, behind all text.
|
||
virt_text = {{"virtual text!!", "GruvboxBlue"}}
|
||
},
|
||
-- visited or unvisited are applied when a node was/was not jumped into.
|
||
visited = {
|
||
hl_group = "GruvboxBlue"
|
||
},
|
||
unvisited = {
|
||
hl_group = "GruvboxGreen"
|
||
},
|
||
-- and these are applied when both the node and the snippet are inactive.
|
||
snippet_passive = {}
|
||
}
|
||
|
||
s("trig", {
|
||
i(1, "text1", {
|
||
node_ext_opts = ext_opts
|
||
}),
|
||
i(2, "text2", {
|
||
node_ext_opts = ext_opts
|
||
})
|
||
})
|
||
<
|
||
|
||
In the above example, the text inside the insertNodes is higlighted in green if
|
||
they were not yet visited, in blue once they were, and red while they are. The
|
||
virtual text "virtual text!!" is visible as long as the snippet is active.
|
||
|
||
To make defining `ext_opts` less verbose, more specific states inherit from
|
||
less specific ones:
|
||
|
||
- `passive` inherits from `snippet_passive`
|
||
- `visited` and `unvisited` from `passive`
|
||
- `active` from `visited`
|
||
|
||
To disable a key from a less specific state, it has to be explicitly set to its
|
||
default, e.g. to disable highlighting inherited from `passive` when the node
|
||
is `active`, `hl_group` should be set to `None`.
|
||
|
||
------------------------------------------------------------------------------
|
||
As stated earlier, these `ext_opts` can also be applied globally or for an
|
||
entire snippet. For this, it’s necessary to specify which kind of node a
|
||
given set of `ext_opts` should be applied to:
|
||
|
||
>lua
|
||
local types = require("luasnip.util.types")
|
||
|
||
ls.setup({
|
||
ext_opts = {
|
||
[types.insertNode] = {
|
||
active = {...},
|
||
visited = {...},
|
||
passive = {...},
|
||
snippet_passive = {...}
|
||
},
|
||
[types.choiceNode] = {
|
||
active = {...},
|
||
unvisited = {...}
|
||
},
|
||
[types.snippet] = {
|
||
passive = {...}
|
||
}
|
||
}
|
||
})
|
||
<
|
||
|
||
The above applies the given `ext_opts` to all nodes of these types, in all
|
||
snippets.
|
||
|
||
>lua
|
||
local types = require("luasnip.util.types")
|
||
|
||
s("trig", { i(1, "text1"), i(2, "text2") }, {
|
||
child_ext_opts = {
|
||
[types.insertNode] = {
|
||
passive = {
|
||
hl_group = "GruvboxAqua"
|
||
}
|
||
}
|
||
}
|
||
})
|
||
<
|
||
|
||
However, the `ext_opts` here are only applied to the `insertNodes` inside this
|
||
snippet.
|
||
|
||
------------------------------------------------------------------------------
|
||
By default, the `ext_opts` actually used for a node are created by extending
|
||
the `node_ext_opts` with the `effective_child_ext_opts[node.type]` of the
|
||
parent, which are in turn the parent’s `child_ext_opts` extended with the
|
||
global `ext_opts` (those set `ls.setup`).
|
||
|
||
It’s possible to prevent both of these merges by passing
|
||
`merge_node/child_ext_opts=false` to the snippet/node-opts:
|
||
|
||
>lua
|
||
ls.setup({
|
||
ext_opts = {
|
||
[types.insertNode] = {
|
||
active = {...}
|
||
}
|
||
}
|
||
})
|
||
|
||
s("trig", {
|
||
i(1, "text1", {
|
||
node_ext_opts = {
|
||
active = {...}
|
||
},
|
||
merge_node_ext_opts = false
|
||
}),
|
||
i(2, "text2")
|
||
}, {
|
||
child_ext_opts = {
|
||
[types.insertNode] = {
|
||
passive = {...}
|
||
}
|
||
},
|
||
merge_child_ext_opts = false
|
||
})
|
||
<
|
||
|
||
------------------------------------------------------------------------------
|
||
The `hl_group` of the global `ext_opts` can also be set via standard highlight
|
||
groups:
|
||
|
||
>lua
|
||
vim.cmd("hi link LuasnipInsertNodePassive GruvboxRed")
|
||
vim.cmd("hi link LuasnipSnippetPassive GruvboxBlue")
|
||
|
||
-- needs to be called for resolving the effective ext_opts.
|
||
ls.setup({})
|
||
<
|
||
|
||
The names for the used highlight groups are
|
||
`"Luasnip<node>{Passive,Active,SnippetPassive}"`, where `<node>` can be any
|
||
kind of node in PascalCase (or "Snippet").
|
||
|
||
------------------------------------------------------------------------------
|
||
One problem that might arise when nested nodes are highlighted is that the
|
||
highlight of inner nodes should be visible, e.g. above that of nodes they are
|
||
nested inside.
|
||
|
||
This can be controlled using the `priority`-key in `ext_opts`. In
|
||
`nvim_buf_set_extmark`, that value is an absolute value, but here it is
|
||
relative to some base-priority, which is increased for each nesting level of
|
||
snippet(Nodes)s.
|
||
|
||
Both the initial base-priority and its’ increase and can be controlled using
|
||
`ext_base_prio` and `ext_prio_increase`:
|
||
|
||
>lua
|
||
ls.setup({
|
||
ext_opts = {
|
||
[types.insertNode] = {
|
||
active = {
|
||
hl_group = "GruvboxBlue",
|
||
-- the priorities should be \in [0, ext_prio_increase).
|
||
priority = 1
|
||
}
|
||
},
|
||
[types.choiceNode] = {
|
||
active = {
|
||
hl_group = "GruvboxRed"
|
||
-- priority defaults to 0
|
||
}
|
||
}
|
||
}
|
||
ext_base_prio = 200,
|
||
ext_prio_increase = 2
|
||
})
|
||
<
|
||
|
||
Here the highlight of an insertNode nested directly inside a choiceNode is
|
||
always visible on top of it.
|
||
|
||
|
||
==============================================================================
|
||
23. Docstrings *luasnip-docstrings*
|
||
|
||
Snippet docstrings can be queried using `snippet:get_docstring()`. The function
|
||
evaluates the snippet as if it was expanded regularly, which can be problematic
|
||
if e.g. a dynamicNode in the snippet relies on inputs other than the argument
|
||
nodes. `snip.env` and `snip.captures` are populated with the names of the
|
||
queried variable and the index of the capture respectively
|
||
(`snip.env.TM_SELECTED_TEXT` -> `'$TM_SELECTED_TEXT'`, `snip.captures[1]` ->
|
||
`'$CAPTURES1'`). Although this leads to more expressive docstrings, it can
|
||
cause errors in functions that e.g. rely on a capture being a number:
|
||
|
||
>lua
|
||
s({trig = "(%d)", regTrig = true}, {
|
||
f(function(args, snip)
|
||
return string.rep("repeatme ", tonumber(snip.captures[1]))
|
||
end, {})
|
||
})
|
||
<
|
||
|
||
This snippet works fine because `snippet.captures[1]` is always a number.
|
||
During docstring generation, however, `snippet.captures[1]` is `'$CAPTURES1'`,
|
||
which will cause an error in the functionNode. Issues with `snippet.captures`
|
||
can be prevented by specifying `docTrig` during snippet-definition:
|
||
|
||
>lua
|
||
s({trig = "(%d)", regTrig = true, docTrig = "3"}, {
|
||
f(function(args, snip)
|
||
return string.rep("repeatme ", tonumber(snip.captures[1]))
|
||
end, {})
|
||
})
|
||
<
|
||
|
||
`snippet.captures` and `snippet.trigger` will be populated as if actually
|
||
triggered with `3`.
|
||
|
||
Other issues will have to be handled manually by checking the contents of e.g.
|
||
`snip.env` or predefining the docstring for the snippet:
|
||
|
||
>lua
|
||
s({trig = "(%d)", regTrig = true, docstring = "repeatmerepeatmerepeatme"}, {
|
||
f(function(args, snip)
|
||
return string.rep("repeatme ", tonumber(snip.captures[1]))
|
||
end, {})
|
||
})
|
||
<
|
||
|
||
Refer to #515 <https://github.com/L3MON4D3/LuaSnip/pull/515> for a better
|
||
example to understand `docTrig` and `docstring`.
|
||
|
||
|
||
==============================================================================
|
||
24. Docstring-Cache *luasnip-docstring-cache*
|
||
|
||
Although generation of docstrings is pretty fast, it’s preferable to not redo
|
||
it as long as the snippets haven’t changed. Using
|
||
`ls.store_snippet_docstrings(snippets)` and its counterpart
|
||
`ls.load_snippet_docstrings(snippets)`, they may be serialized from or
|
||
deserialized into the snippets. Both functions accept a table structsured like
|
||
this: `{ft1={snippets}, ft2={snippets}}`. Such a table containing all snippets
|
||
can be obtained via `ls.get_snippets()`. `load` should be called before any of
|
||
the `loader`-functions as snippets loaded from VSCode style packages already
|
||
have their `docstring` set (`docstrings` wouldn’t be overwritten, but
|
||
there’d be unnecessary calls).
|
||
|
||
The cache is located at `stdpath("cache")/luasnip/docstrings.json` (probably
|
||
`~/.cache/nvim/luasnip/docstrings.json`).
|
||
|
||
|
||
==============================================================================
|
||
25. Events *luasnip-events*
|
||
|
||
Events can be used to react to some action inside snippets. These callbacks can
|
||
be defined per snippet (`callbacks`-key in snippet constructor), per-node by
|
||
passing them as `node_callbacks` in `node_opts`, or globally (autocommand).
|
||
|
||
`callbacks`: `fn(node[, event_args]) -> event_res` All callbacks receive the
|
||
`node` associated with the event and event-specific optional arguments,
|
||
`event_args`. `event_res` is only used in one event, `pre_expand`, where some
|
||
properties of the snippet can be changed. If multiple callbacks return
|
||
`event_res`, we only guarantee that one of them will be effective, not all of
|
||
them.
|
||
|
||
`autocommand`: Luasnip uses `User`-events. Autocommands for these can be
|
||
registered using
|
||
|
||
>vim
|
||
au User SomeUserEvent echom "SomeUserEvent was triggered"
|
||
<
|
||
|
||
or
|
||
|
||
>lua
|
||
vim.api.nvim_create_autocommand("User", {
|
||
pattern = "SomeUserEvent",
|
||
command = "echom SomeUserEvent was triggered"
|
||
})
|
||
<
|
||
|
||
The node and `event_args` can be accessed through `require("luasnip").session`:
|
||
|
||
- `node`: `session.event_node`
|
||
- `event_args`: `session.event_args`
|
||
|
||
**Events**:
|
||
|
||
- `enter/leave`: Called when a node is entered/left (for example when jumping
|
||
around in a snippet).
|
||
`User-event`: `"Luasnip<Node>{Enter,Leave}"`, with `<Node>` in
|
||
PascalCase, e.g. `InsertNode` or `DynamicNode`.
|
||
`event_args`: none
|
||
- `change_choice`: When the active choice in a choiceNode is changed.
|
||
`User-event`: `"LuasnipChangeChoice"`
|
||
`event_args`: none
|
||
- `pre_expand`: Called before a snippet is expanded. Modifying text is allowed,
|
||
the expand-position will be adjusted so the snippet expands at the same
|
||
position relative to existing text.
|
||
`User-event`: `"LuasnipPreExpand"`
|
||
`event_args`:
|
||
- `expand_pos`: `{<row>, <column>}`, position at which the snippet will be
|
||
expanded. `<row>` and `<column>` are both 0-indexed.
|
||
- `expand_pos_mark_id`: `number`, the id of the extmark luasnip uses to track
|
||
`expand_pos`. This may be moved around freely.
|
||
`event_res`:
|
||
- `env_override`: `map string->(string[]|string)`, override or extend the
|
||
snippet’s environment (`snip.env`).
|
||
|
||
A pretty useless, beyond serving as an example here, application of these would
|
||
be printing e.g. the node’s text after entering:
|
||
|
||
>lua
|
||
vim.api.nvim_create_autocmd("User", {
|
||
pattern = "LuasnipInsertNodeEnter",
|
||
callback = function()
|
||
local node = require("luasnip").session.event_node
|
||
print(table.concat(node:get_text(), "\n"))
|
||
end
|
||
})
|
||
<
|
||
|
||
or some information about expansions:
|
||
|
||
>lua
|
||
vim.api.nvim_create_autocmd("User", {
|
||
pattern = "LuasnipPreExpand",
|
||
callback = function()
|
||
-- get event-parameters from `session`.
|
||
local snippet = require("luasnip").session.event_node
|
||
local expand_position =
|
||
require("luasnip").session.event_args.expand_pos
|
||
|
||
print(string.format("expanding snippet %s at %s:%s",
|
||
table.concat(snippet:get_docstring(), "\n"),
|
||
expand_position[1],
|
||
expand_position[2]
|
||
))
|
||
end
|
||
})
|
||
<
|
||
|
||
|
||
==============================================================================
|
||
26. Cleanup *luasnip-cleanup*
|
||
|
||
The function ls.cleanup() triggers the `LuasnipCleanup` user event, that you
|
||
can listen to do some kind of cleaning in your own snippets; by default it will
|
||
empty the snippets table and the caches of the lazy_load.
|
||
|
||
|
||
==============================================================================
|
||
27. Logging *luasnip-logging*
|
||
|
||
Luasnip uses logging to report unexpected program states, and information on
|
||
what’s going on in general. If something does not work as expected, taking a
|
||
look at the log (and potentially increasing the loglevel) might give some good
|
||
hints towards what is going wrong.
|
||
|
||
The log is stored in `<vim.fn.stdpath("log")>/luasnip.log`
|
||
(`<vim.fn.stdpath("cache")>/luasnip.log` for Neovim versions where
|
||
`stdpath("log")` does not exist), and can be opened by calling `ls.log.open()`.
|
||
You can get the log path through `ls.log.log_location()`. The loglevel
|
||
(granularity of reported events) can be adjusted by calling
|
||
`ls.log.set_loglevel("error"|"warn"|"info"|"debug")`. `"debug"` has the highest
|
||
granularity, `"error"` the lowest, the default is `"warn"`. You can also adjust
|
||
the datetime formatting through the `ls.log.time_fmt` variable. By default, it
|
||
uses the `'%X'` formatting, which results in the full time (hour, minutes and
|
||
seconds) being shown.
|
||
|
||
Once this log grows too large (10MiB, currently not adjustable), it will be
|
||
renamed to `luasnip.log.old`, and a new, empty log created in its place. If
|
||
there already exists a `luasnip.log.old`, it will be deleted.
|
||
|
||
`ls.log.ping()` can be used to verify the log is working correctly: it will
|
||
print a short message to the log.
|
||
|
||
|
||
==============================================================================
|
||
28. Source *luasnip-source*
|
||
|
||
It is possible to attach, to a snippet, information about its source. This can
|
||
be done either by the various loaders (if it is enabled in `ls.setup`
|
||
(|luasnip-config-options|, `loaders_store_source`)), or manually. The attached
|
||
data can be used by |luasnip-extras-snippet-location| to jump to the definition
|
||
of a snippet.
|
||
|
||
It is also possible to get/set the source of a snippet via API:
|
||
|
||
`ls.snippet_source`:
|
||
|
||
- `get(snippet) -> source_data`:
|
||
Retrieve the source-data of `snippet`. `source_data` always contains the key
|
||
`file`, the file in which the snippet was defined, and may additionally
|
||
contain `line` or `line_end`, the first and last line of the definition.
|
||
- `set(snippet, source)`:
|
||
Set the source of a snippet.
|
||
- `snippet`: a snippet which was added via `ls.add_snippets`.
|
||
- `source`: a `source`-object, obtained from either `from_debuginfo` or
|
||
`from_location`.
|
||
- `from_location(file, opts) -> source`:
|
||
- `file`: `string`, The path to the file in which the snippet is defined.
|
||
- `opts`: `table|nil`, optional parameters for the source.
|
||
- `line`: `number`, the first line of the definition. 1-indexed.
|
||
- `line_end`: `number`, the final line of the definition. 1-indexed.
|
||
- `from_debuginfo(debuginfo) -> source`:
|
||
Generates source from the table returned by `debug.getinfo` (from now on
|
||
referred to as `debuginfo`). `debuginfo` has to be of a frame of a function
|
||
which is backed by a file, and has to contain this information, ie. has to be
|
||
generated by `debug.get_info(*, "Sl")` (at least `"Sl"`, it may also contain
|
||
more info).
|
||
|
||
|
||
==============================================================================
|
||
29. Selection *luasnip-selection*
|
||
|
||
Many snippets use the `$TM_SELECTED_TEXT` or (for LuaSnip, preferably
|
||
`LS_SELECT_RAW` or `LS_SELECT_DEDENT`) variable, which has to be populated by
|
||
selecting and then yanking (and usually also cutting) text from the buffer
|
||
before expanding.
|
||
|
||
By default, this is disabled (as to not pollute keybindings which may be used
|
||
for something else), so one has to * either set `cut_selection_keys` in `setup`
|
||
(see |luasnip-config-options|). * or map `ls.cut_keys` as the rhs of a mapping
|
||
* or manually configure the keybinding. For this, create a new keybinding that
|
||
1. `<Esc>`es to NORMAL (to populate the `<` and `>`-markers) 2. calls
|
||
`luasnip.pre_yank(<namedreg>)` 3. yanks text to some named register
|
||
`<namedreg>` 4. calls `luasnip.post_yank(<namedreg>)` Take care that the
|
||
yanking actually takes place between the two calls. One way to ensure this is
|
||
to call the two functions via `<cmd>lua ...<Cr>`: `lua vim.keymap.set("v",
|
||
"<Tab>", [[<Esc><cmd>lua
|
||
require("luasnip.util.select").pre_yank("z")<Cr>gv"zs<cmd>lua
|
||
require('luasnip.util.select').post_yank("z")<Cr>]])` The reason for this
|
||
specific order is to allow us to take a snapshot of registers (in the
|
||
pre-callback), and then restore them (in the post-callback) (so that we may get
|
||
the visual selection directly from the register, which seems to be the most
|
||
foolproof way of doing this).
|
||
|
||
|
||
==============================================================================
|
||
30. Config-Options *luasnip-config-options*
|
||
|
||
These are the settings you can provide to `luasnip.setup()`:
|
||
|
||
- `keep_roots`: Whether snippet-roots should be linked. See
|
||
|luasnip-basics-snippet-insertion| for more context.
|
||
- `link_roots`: Whether snippet-roots should be linked. See
|
||
|luasnip-basics-snippet-insertion| for more context.
|
||
- `exit_roots`: Whether snippet-roots should exit at reaching at their last node,
|
||
`$0`. This setting is only valid for root snippets, not child snippets. This
|
||
setting may avoid unexpected behavior by disallowing to jump earlier (finished)
|
||
snippets. Check |luasnip-basics-snippet-insertion| for more information on
|
||
snippet-roots.
|
||
- `link_children`: Whether children should be linked. See
|
||
|luasnip-basics-snippet-insertion| for more context.
|
||
- `history` (deprecated): if not nil, `keep_roots`, `link_roots`, and
|
||
`link_children` will be set to the value of `history`, and `exit_roots` will
|
||
set to inverse value of `history`. This is just to ensure
|
||
backwards-compatibility.
|
||
- `update_events`: Choose which events trigger an update of the active nodes’
|
||
dependents. Default is just `'InsertLeave'`, `'TextChanged,TextChangedI'` would
|
||
update on every change. These, like all other `*_events` are passed to
|
||
`nvim_create_autocmd` as `events`, so they can be wrapped in a table, like
|
||
>lua
|
||
ls.setup({
|
||
update_events = {"TextChanged", "TextChangedI"}
|
||
})
|
||
<
|
||
- `region_check_events`: Events on which to leave the current snippet-root 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`).
|
||
- `cut_selection_keys`: Mapping for populating `TM_SELECTED_TEXT` and related
|
||
variables (not set by default). See |luasnip-selection| for more infos.
|
||
- `store_selection_keys` (deprecated): same as `cut_selection_keys`
|
||
- `enable_autosnippets`: Autosnippets are disabled by default to minimize
|
||
performance penalty if unused. Set to `true` 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 a
|
||
`choiceNode` containing the nested snippet and an empty `insertNode` for nested
|
||
placeholders (`"${1: ${2: this is nested}}"`). For an example (behaviour more
|
||
similar to vscode), check here
|
||
<https://github.com/L3MON4D3/LuaSnip/wiki/Nice-Configs#imitate-vscodes-behaviour-for-nested-placeholders>
|
||
- `ft_func`: Source of possible filetypes for snippets. Defaults to a function,
|
||
which returns `vim.split(vim.bo.filetype, ".", true)`, but check
|
||
filetype_functions <lua/luasnip/extras/filetype_functions.lua> or the
|
||
|luasnip-extras-filetype-functions|-section for more options.
|
||
- `load_ft_func`: Function to determine which filetypes belong to a given buffer
|
||
(used for `lazy_loading`). `fn(bufnr) -> filetypes (string[])`. Again, there
|
||
are some examples in filetype_functions
|
||
<lua/luasnip/extras/filetype_functions.lua>.
|
||
- `snip_env`: The best way to author snippets in lua involves the lua-loader (see
|
||
|luasnip-loaders-lua|). Unfortunately, this requires that snippets are defined
|
||
in separate files, which means that common definitions like `s`, `i`, `sn`,
|
||
`t`, `fmt`, … have to be repeated in each of them, and that adding more
|
||
customized functions to ease writing snippets also requires some setup.
|
||
`snip_env` can be used to insert variables into exactly the places where
|
||
lua-snippets are defined (for now only the file loaded by the lua-loader).
|
||
Setting `snip_env` to `{ some_global = "a value" }` will add (amongst the
|
||
defaults stated at the beginning of this documentation) the global variable
|
||
`some_global` while evaluating these files. There are special keys which, when
|
||
set in `snip_env` change the behaviour of this option, and are not passed
|
||
through to the lua-files:
|
||
- `__snip_env_behaviour`, string: either `"set"` or `"extend"` (default
|
||
`"extend"`)
|
||
If this is `"extend"`, the variables defined in `snip_env` will complement (and
|
||
override) the defaults. If this is not desired, `"set"` will not include the
|
||
defaults, but only the variables set here.
|
||
One side-effect of this is that analysis-tools (most likely
|
||
`lua-language-server`) for lua will generate diagnostics for the usage of
|
||
undefined symbols. If you mind the (probably) large number of generated
|
||
warnings, consider adding the undefined globals to the globals recognized by
|
||
`lua-language-server` or add `---@diagnostic disable: undefined-global`
|
||
somewhere in the affected files.
|
||
- `loaders_store_source`, boolean, whether loaders should store the source of the
|
||
loaded snippets. Enabling this means that the definition of any snippet can be
|
||
jumped to via |luasnip-extras-snippet-location|, but also entails slightly
|
||
increased memory consumption (and load-time, but it’s not really noticeable).
|
||
|
||
|
||
==============================================================================
|
||
31. Troubleshooting *luasnip-troubleshooting*
|
||
|
||
|
||
ADDING SNIPPETS *luasnip-troubleshooting-adding-snippets*
|
||
|
||
### Loaders
|
||
|
||
- **Filetypes**. LuaSnip uses `all` as the global filetype. As most snippet
|
||
collections don’t explicitly target LuaSnip, they may not provide global
|
||
snippets for this filetype, but another, like `_` (`honza/vim-snippets`). In
|
||
these cases, it’s necessary to extend LuaSnip’s global filetype with the
|
||
collection’s global filetype:
|
||
>lua
|
||
ls.filetype_extend("all", { "_" })
|
||
<
|
||
In general, if some snippets don’t show up when loading a collection, a good
|
||
first step is checking the filetype LuaSnip is actually looking into (print
|
||
them for the current buffer via `:lua
|
||
print(vim.inspect(require("luasnip").get_snippet_filetypes()))`), against the
|
||
one the missing snippet is provided for (in the collection). If there is indeed
|
||
a mismatch, `filetype_extend` can be used to also search the collection’s
|
||
filetype:
|
||
>lua
|
||
ls.filetype_extend("<luasnip-filetype>", { "<collection-filetype>" })
|
||
<
|
||
- **Non-default ft_func loading**. As we only load `lazy_load`ed snippets on some
|
||
events, `lazy_load` will probably not play nice when a non-default `ft_func` is
|
||
used: if it depends on e.g. the cursor position, only the filetypes for the
|
||
cursor position when the `lazy_load` events are triggered will be loaded. Check
|
||
|luasnip-extras-filetype-function|’s `extend_load_ft` for a solution.
|
||
|
||
|
||
GENERAL ~
|
||
|
||
- **Snippets sharing triggers**. If multiple snippets could be triggered at the
|
||
current buffer-position, the snippet that was defined first in one’s
|
||
configuration will be expanded first. As a small, real-world LaTeX math
|
||
example, given the following two snippets with triggers `.ov` and `ov`:
|
||
>lua
|
||
postfix( -- Insert over-line command to text via post-fix
|
||
{ trig = ".ov", snippetType = "autosnippet" },
|
||
{
|
||
f(function(_, parent)
|
||
return "\\overline{" .. parent.snippet.env.POSTFIX_MATCH .. "}"
|
||
end, {}),
|
||
}
|
||
),
|
||
s( -- Insert over-line command
|
||
{ trig = "ov", snippetType="autosnippet" },
|
||
fmt(
|
||
[[\overline{<>}]],
|
||
{ i(1) },
|
||
{ delimiters = "<>" }
|
||
)
|
||
),
|
||
<
|
||
If one types `x` followed by `.ov`, the postfix snippet expands producing
|
||
`\overline{x}`. However, if the `postfix` snippet above is defined _after_ the
|
||
normal snippet `s`, then the same key press sequence produces `x.\overline{}`.
|
||
This behaviour can be overridden by explicitly providing a priority to such
|
||
snippets. For example, in the above code, if the `postfix` snippet was defined
|
||
after the normal snippet `s`, then adding `priority=1001` to the `postfix`
|
||
snippet will cause it to expand as if it were defined before the normal snippet
|
||
`s`. Snippet `priority` is discussed in the Snippets section
|
||
<https://github.com/L3MON4D3/LuaSnip/blob/master/DOC.md#snippets> of the
|
||
documentation.
|
||
|
||
|
||
==============================================================================
|
||
32. API *luasnip-api*
|
||
|
||
`require("luasnip")`:
|
||
|
||
- `add_snippets(ft:string or nil, snippets:list or table, opts:table or nil)`:
|
||
Makes `snippets` (list of snippets) available in `ft`. If `ft` is `nil`,
|
||
`snippets` should be a table containing lists of snippets, the keys are
|
||
corresponding filetypes. `opts` may contain the following keys:
|
||
- `type`: type of `snippets`, `"snippets"` or `"autosnippets"` (ATTENTION:
|
||
plural form used here). This serves as default value for the `snippetType`
|
||
key of each snippet added by this call see |luasnip-snippets|.
|
||
- `key`: Key that identifies snippets added via this call.
|
||
If `add_snippets` is called with a key that was already used, the snippets
|
||
from that previous call will be removed.
|
||
This can be used to reload snippets: pass an unique key to each
|
||
`add_snippets` and just redo the `add_snippets`-call when the snippets have
|
||
changed.
|
||
- `override_priority`: set priority for all snippets.
|
||
- `default_priority`: set priority only for snippets without snippet priority.
|
||
- `clean_invalidated(opts: table or nil) -> bool`: clean invalidated snippets
|
||
from internal snippet storage. Invalidated snippets are still stored; it might
|
||
be useful to actually remove them as they still have to be iterated during
|
||
expansion.
|
||
`opts` may contain:
|
||
- `inv_limit`: how many invalidated snippets are allowed. If the number of
|
||
invalid snippets doesn’t exceed this threshold, they are not yet cleaned up.
|
||
A small number of invalidated snippets (<100) probably doesn’t affect runtime
|
||
at all, whereas recreating the internal snippet storage might.
|
||
- `get_id_snippet(id)`: returns snippet corresponding to id.
|
||
- `in_snippet()`: returns true if the cursor is inside the current snippet.
|
||
- `jumpable(direction)`: returns true if the current node has a next(`direction`
|
||
= 1) or previous(`direction` = -1), e.g. whether it’s possible to jump
|
||
forward or backward to another node.
|
||
- `jump(direction)`: returns true if the jump was successful.
|
||
- `expandable()`: true if a snippet can be expanded at the current cursor
|
||
position.
|
||
- `expand(opts)`: expands the snippet at(before) the cursor. `opts` may contain:
|
||
- `jump_into_func` passed through to `ls.snip_expand`, check its’ doc for a
|
||
description.
|
||
- `expand_or_jumpable()`: returns `expandable() or jumpable(1)` (exists only
|
||
because commonly, one key is used to both jump forward and expand).
|
||
- `expand_or_locally_jumpable()`: same as `expand_or_jumpable()` except jumpable
|
||
is ignored if the cursor is not inside the current snippet.
|
||
- `locally_jumpable(direction)`: same as `jumpable()` except it is ignored if the
|
||
cursor is not inside the current snippet.
|
||
- `expand_or_jump()`: returns true if jump/expand was succesful.
|
||
- `expand_auto()`: expands the autosnippets before the cursor (not necessary to
|
||
call manually, will be called via autocmd if `enable_autosnippets` is set in
|
||
the config).
|
||
- `snip_expand(snip, opts)`: expand `snip` at the current cursor position. `opts`
|
||
may contain the following keys:
|
||
- `clear_region`: A region of text to clear after expanding (but before jumping
|
||
into) snip. It has to be at this point (and therefore passed to this function)
|
||
as clearing before expansion will populate `TM_CURRENT_LINE` and
|
||
`TM_CURRENT_WORD` with wrong values (they would miss the snippet trigger) and
|
||
clearing after expansion may move the text currently under the cursor and have
|
||
it end up not at the `i(1)`, but a `#trigger` chars to its right. The actual
|
||
values used for clearing are `from` and `to`, both (0,0)-indexed
|
||
byte-positions. If the variables don’t have to be populated with the correct
|
||
values, it’s safe to remove the text manually.
|
||
- `expand_params`: table, override `trigger`, `captures` or environment of the
|
||
snippet. This is useful for manually expanding snippets where the trigger
|
||
passed via `trig` is not the text triggering the snippet, or those which expect
|
||
`captures` (basically, snippets with a non-plaintext `trigEngine`).
|
||
One example: ```lua snip_expand(snip, { trigger = "override_trigger", captures
|
||
= {"first capture", "second capture"}, env_override = { this_key = "some
|
||
value", other_key = {"multiple", "lines"}, TM_FILENAME =
|
||
"some_other_filename.lua" } })
|
||
- `pos`: position (`{line, col}`), (0,0)-indexed (in bytes, as returned by
|
||
`nvim_win_get_cursor()`), where the snippet should be expanded. The snippet
|
||
will be put between `(line,col-1)` and `(line,col)`. The snippet will be
|
||
expanded at the current cursor if pos is nil.
|
||
- `jump_into_func`: fn(snippet) -> node: Callback responsible for jumping into
|
||
the snippet. The returned node is set as the new active node, ie. it is the
|
||
origin of the next jump. The default is basically this: `lua function(snip) --
|
||
jump_into set the placeholder of the snippet, 1 -- to jump forwards. return
|
||
snip:jump_into(1)` while this can be used to only insert the snippet: `lua
|
||
function(snip) return snip.insert_nodes[0] end`
|
||
- `indent`: bool?, defaults to `true`. Whether LuaSnip will try to add additional
|
||
indents to fit current indent level in snippet expanding. This option is useful
|
||
when some LSP server already take indents into consideration. In such cases,
|
||
LuaSnip should not try to add additional indents. If you are using `nvim-cmp`,
|
||
sample config:
|
||
>lua
|
||
require("cmp").setup {
|
||
snippet = {
|
||
expand = function(args)
|
||
local indent_nodes = true
|
||
if vim.api.nvim_get_option_value("filetype", { buf = 0 }) == "dart" then
|
||
indent_nodes = false
|
||
end
|
||
require("luasnip").lsp_expand(args.body, {
|
||
indent = indent_nodes,
|
||
})
|
||
end,
|
||
},
|
||
}
|
||
<
|
||
`opts` and any of its parameters may be nil.
|
||
- `get_active_snip()`: returns the currently active snippet (not node!).
|
||
- `choice_active()`: true if inside a choiceNode.
|
||
- `change_choice(direction)`: changes the choice in the innermost currently
|
||
active choiceNode forward (`direction` = 1) or backward (`direction` = -1).
|
||
- `unlink_current()`: removes the current snippet from the jumplist (useful if
|
||
luasnip fails to automatically detect e.g. deletion of a snippet) and sets the
|
||
current node behind the snippet, or, if not possible, before it.
|
||
- `lsp_expand(snip_string, opts)`: expands the LSP snippet defined via
|
||
`snip_string` at the cursor. `opts` can have the same options as `opts` in
|
||
`snip_expand`.
|
||
- `active_update_dependents()`: update all function/dynamicNodes that have the
|
||
current node as an argnode (will actually only update them if the text in any
|
||
of the argnodes changed).
|
||
- `available(snip_info)`: returns a table of all snippets defined for the current
|
||
filetypes(s) (`{ft1={snip1, snip2}, ft2={snip3, snip4}}`). The structure of the
|
||
snippet is defined by `snip_info` which is a function (`snip_info(snip)`) that
|
||
takes in a snippet (`snip`), finds the desired information on it, and returns
|
||
it. `snip_info` is an optional argument as a default has already been defined.
|
||
You can use it for more granular control over the table of snippets that is
|
||
returned.
|
||
- `exit_out_of_region(node)`: checks whether the cursor is still within the range
|
||
of the root-snippet `node` belongs to. If yes, no change occurs; if no, the
|
||
root-snippet is exited and its `$0` will be the new active node. If a jump
|
||
causes an error (happens mostly because the text of a snippet was deleted), the
|
||
snippet is removed from the jumplist and the current node set to the
|
||
end/beginning of the next/previous snippet.
|
||
- `store_snippet_docstrings(snippet_table)`: Stores the docstrings of all
|
||
snippets in `snippet_table` to a file
|
||
(`stdpath("cache")/luasnip/docstrings.json`). Calling
|
||
`store_snippet_docstrings(snippet_table)` after adding/modifying snippets and
|
||
`load_snippet_docstrings(snippet_table)` on startup after all snippets have
|
||
been added to `snippet_table` is a way to avoide regenerating the (unchanged)
|
||
docstrings on each startup. (Depending on when the docstrings are required and
|
||
how luasnip is loaded, it may be more sensible to let them load lazily,
|
||
e.g. just before they are required). `snippet_table` should be laid out just
|
||
like `luasnip.snippets` (it will most likely always _be_ `luasnip.snippets`).
|
||
- `load_snippet_docstrings(snippet_table)`: Load docstrings for all snippets in
|
||
`snippet_table` from `stdpath("cache")/luasnip/docstrings.json`. The docstrings
|
||
are stored and restored via trigger, meaning if two snippets for one filetype
|
||
have the same (very unlikely to happen in actual usage), bugs could occur.
|
||
`snippet_table` should be laid out as described in `store_snippet_docstrings`.
|
||
- `unlink_current_if_deleted()`: Checks if the current snippet was deleted; if
|
||
so, it is removed from the jumplist. This is not 100% reliable as LuaSnip only
|
||
sees the extmarks and their beginning/end may not be on the same position, even
|
||
if all the text between them was deleted.
|
||
- `filetype_extend(filetype:string, extend_filetypes:table of string)`: Tells
|
||
luasnip that for a buffer with `ft=filetype`, snippets from `extend_filetypes`
|
||
should be searched as well. `extend_filetypes` is a lua-array (`{ft1, ft2,
|
||
ft3}`). `luasnip.filetype_extend("lua", {"c", "cpp"})` would search and expand
|
||
c and cpp snippets for lua files.
|
||
- `filetype_set(filetype:string, replace_filetypes:table of string)`: Similar to
|
||
`filetype_extend`, but where _append_ appended filetypes, _set_ sets them:
|
||
`filetype_set("lua", {"c"})` causes only c snippets to be expanded in lua
|
||
files; lua snippets aren’t even searched.
|
||
- `cleanup()`: clears all snippets. Not useful for regular usage, only when
|
||
authoring and testing snippets.
|
||
- `refresh_notify(ft:string)`: Triggers an autocmd that other plugins can hook
|
||
into to perform various cleanup for the refreshed filetype. Useful for
|
||
signaling that new snippets were added for the filetype `ft`.
|
||
- `set_choice(indx:number)`: Changes to the `indx`th choice. If no `choiceNode`
|
||
is active, an error is thrown. If the active `choiceNode` doesn’t have an
|
||
`indx`th choice, an error is thrown.
|
||
- `get_current_choices() -> string[]`: Returns a list of multiline-strings
|
||
(themselves lists, even if they have only one line), the `i`th string
|
||
corresponding to the `i`th choice of the currently active `choiceNode`. If no
|
||
`choiceNode` is active, an error is thrown.
|
||
- `setup_snip_env()`: Adds the variables defined (during `setup`) in `snip_env`
|
||
to the callers environment.
|
||
- `get_snip_env()`: Returns `snip_env`.
|
||
- `jump_destination(direction)`: Returns the node the next jump in `direction`
|
||
(either -1 or 1, for backwards, forwards respectively) leads to, or `nil` if
|
||
the destination could not be determined (most likely because there is no node
|
||
that can be jumped to in the given direction, or there is no active node).
|
||
- `activate_node(opts)`: Activate a node in any snippet. `opts` contains the
|
||
following options:
|
||
- `pos`, `{[1]: row, [2]: byte-column}?`: The position at which a node should
|
||
be activated. Defaults to the position of the cursor.
|
||
- `strict`, `bool?`: If set, throw an error if the node under the cursor can’t
|
||
be jumped into. If not set, fall back to any node of the snippet and enter
|
||
that instead.
|
||
- `select`, `bool?`: Whether the text inside the node should be selected.
|
||
Defaults to true.
|
||
|
||
Not covered in this section are the various node-constructors exposed by the
|
||
module, their usage is shown either previously in this file or in
|
||
`Examples/snippets.lua` (in the repo).
|
||
|
||
Generated by panvimdoc <https://github.com/kdheepak/panvimdoc>
|
||
|
||
vim:tw=78:ts=8:noet:ft=help:norl:
|