Skip to content

Commit f5bd74d

Browse files
committed
add brief documentation for new features.
1 parent 42fdd36 commit f5bd74d

File tree

3 files changed

+373
-49
lines changed

3 files changed

+373
-49
lines changed

DOC.md

Lines changed: 109 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ local d = ls.dynamic_node
3131
local r = ls.restore_node
3232
local events = require("luasnip.util.events")
3333
local ai = require("luasnip.nodes.absolute_indexer")
34+
local opt = require("luasnip.nodes.optional_arg")
3435
local extras = require("luasnip.extras")
3536
local l = extras.lambda
3637
local rep = extras.rep
@@ -937,8 +938,10 @@ user input.
937938
the dynamicNode's place.
938939
`args`, `parent` and `user_args` are also explained in
939940
[FunctionNode](#functionnode)
940-
- `args`: `table of text` (`{{"node1line1", "node1line2"}, {"node2line1"}}`)
941-
from nodes the `dynamicNode` depends on.
941+
- `args`: `table of text` (`snippetstring_args == false`) or `snippetString`
942+
(otherwise). Represents content of the nodes this dynamicNode depends on.
943+
Their index in the `node_references`-list determines the position in
944+
this list.
942945
- `parent`: the immediate parent of the `dynamicNode`.
943946
- `old_state`: a user-defined table. This table may contain anything; its
944947
intended usage is to preserve information from the previously generated
@@ -957,8 +960,10 @@ user input.
957960
inserted at the `dynamicNode`s place.
958961
(`dynamicNode` behaves exactly the same as `functionNode` in this regard).
959962

960-
- `opts`: In addition to the common [Node](#node)-keys, there is, again,
963+
- `opts`: In addition to the common [Node](#node)-keys, there is:
961964
- `user_args`, which is described in [FunctionNode](#functionnode).
965+
- `snippetstring_args`, boolean: If set, `function` receives a list of
966+
[Snippetstring](#snippetstring) instead of `string[]`. `false` by default.
962967

963968
**Examples**:
964969

@@ -1024,6 +1029,70 @@ ls.add_snippets("all",
10241029
As with `functionNode`, `user_args` can be used to reuse similar `dynamicNode`-
10251030
functions.
10261031

1032+
## Self-dependent DynamicNode
1033+
1034+
While the previous examples only showed dynamicNodes that depend on nodes
1035+
outside of itself, it's possible to update a dynamicNode in response to a change
1036+
to a node within it:
1037+
1038+
```lua
1039+
ls.snip_expand(s("trig", {
1040+
d(1, function(args)
1041+
if not args[1] then
1042+
-- the arg does not exist -> provide a default.
1043+
return sn(nil, {i(1, "asdf", {key = "ins"})})
1044+
else
1045+
-- This branch is only take after the dynamicNode was updated once.
1046+
-- Now we can perform the actual "task" of this dynamicNode:
1047+
-- replacing all occurences of "a" with "e".
1048+
return sn(nil, {i(1, args[1]:gsub("a", "e"), {key = "ins"})})
1049+
end
1050+
end, {opt(k("ins"))}, { snippetstring_args = true })
1051+
}))
1052+
```
1053+
1054+
The example above shows a dynamicNode that will substitute all occurrences of
1055+
"a" within it with an "e". Important for this to work are:
1056+
* Use an [Optional Noderef](#optional-noderef) `opt(k("ins"))` to reference the
1057+
generated insertNode that will eventually update the dynamicNode on changes:
1058+
A dynamicNode does not update when one of its argnodes is missing, so in
1059+
order to get anything out of the node, we need `opt`.
1060+
* Set `snippetstring_args`: This ensures that snippets expanded inside the
1061+
dynamicNode are preserved during the update! While not completely necessary,
1062+
this is a good idea because it can be annoying to lose jump-points
1063+
only because the snippet they belong is inside such a self-dependent
1064+
dynamicNode.
1065+
1066+
When using these self-dependent dynamicNodes it is a really good idea to give
1067+
nodes that can cause an update some unique key, or make sure that they are
1068+
inside of a restoreNode. If this is not done, LuaSnip may not find a node that
1069+
is equivalent to the one that contained the cursor before the update, and thus
1070+
may have to jump into the dynamicNode anew, which may be unexpected.
1071+
1072+
Another danger is in accidentally constructing an infinite loop of updates.
1073+
Right now, LuaSnip is very indiscriminate in updating dynamicNodes, and if given
1074+
a snippet like
1075+
1076+
```lua
1077+
s("srep", {
1078+
d(1, function(args)
1079+
if not args[1] then
1080+
return sn(nil, {i(1, "sdf", {key = "ins"})})
1081+
else
1082+
return sn(nil, {i(1, args[1]:gsub("a", "aa"), {key = "ins"})})
1083+
end
1084+
end, {opt(k("ins"))}, {snippetstring_args = true})
1085+
}),
1086+
```
1087+
1088+
any "a" is replaced with "aa", ad infinitum (or until the process is killed,
1089+
which is likely soon).
1090+
1091+
These infinite loops may also be caused by nested self-dependent dynamicNodes,
1092+
think one that replaces "a" with "e", and another that changes "e" back to "a",
1093+
so it's best to build in some kind of safeguard into the generating function.
1094+
One could for example skip the update once the argnode exceeds a certain length.
1095+
10271096
# RestoreNode
10281097

10291098
This node can store and restore a snippetNode as is. This includes changed
@@ -1121,6 +1190,20 @@ that really bothers you feel free to open an issue.
11211190

11221191
<!-- panvimdoc-ignore-end -->
11231192

1193+
# Snippetstring
1194+
1195+
Snippetstrings can store the content of an insertNode as-is. This includes both
1196+
the regular text, and expanded snippets. The primary purpose of snippetstrings
1197+
is to facilitate easy modifications of text in dynamicNode, while preserving
1198+
snippets and cursor-positions.
1199+
1200+
A snippetstring supports the string-functions `lower`, `upper`, `gsub` and
1201+
`sub`, and the `..`-metamethod. While `lower`, `upper`, and `..` will always
1202+
preserve snippets inside the snippetstring, `sub` will replace partially
1203+
contained snippets with their text, while `gsub` will do so if a replacement
1204+
crosses a node-boundary.
1205+
1206+
11241207
# Key Indexer
11251208

11261209
A very flexible way of referencing nodes ([Node Reference](#node-reference)).
@@ -1178,6 +1261,29 @@ s("trig", {
11781261
<!-- panvimdoc-ignore-end -->
11791262

11801263

1264+
# Optional Noderef
1265+
1266+
`opt` wraps another node-reference and makes the wrapped node not strictly
1267+
required for performing an update. This means, amongs other things, that a
1268+
`dynamicNode`s can be updated by nodes within it:
1269+
1270+
```lua
1271+
ls.snip_expand(s("trig", {
1272+
d(1, function(args)
1273+
if not args[1] then
1274+
-- the arg does not exist -> provide a default.
1275+
return sn(nil, {i(1, "asdf", {key = "ins"})})
1276+
else
1277+
-- This branch is only take after the dynamicNode was updated once.
1278+
-- Now we can perform the actual "task" of this dynamicNode:
1279+
-- replacing all occurences of "a" with "e".
1280+
return sn(nil, {i(1, args[1][1]:gsub("a", "e"), {key = "ins"})})
1281+
end
1282+
end, {opt(k("ins"))})
1283+
}))
1284+
```
1285+
1286+
11811287
# Absolute Indexer
11821288

11831289
`absolute_indexer` allows accessing nodes by their unique jump-index path from

data/DOC-template.md

Lines changed: 109 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ local d = ls.dynamic_node
3131
local r = ls.restore_node
3232
local events = require("luasnip.util.events")
3333
local ai = require("luasnip.nodes.absolute_indexer")
34+
local opt = require("luasnip.nodes.optional_arg")
3435
local extras = require("luasnip.extras")
3536
local l = extras.lambda
3637
local rep = extras.rep
@@ -902,8 +903,10 @@ user input.
902903
the dynamicNode's place.
903904
`args`, `parent` and `user_args` are also explained in
904905
[FunctionNode](#functionnode)
905-
- `args`: `table of text` (`{{"node1line1", "node1line2"}, {"node2line1"}}`)
906-
from nodes the `dynamicNode` depends on.
906+
- `args`: `table of text` (`snippetstring_args == false`) or `snippetString`
907+
(otherwise). Represents content of the nodes this dynamicNode depends on.
908+
Their index in the `node_references`-list determines the position in
909+
this list.
907910
- `parent`: the immediate parent of the `dynamicNode`.
908911
- `old_state`: a user-defined table. This table may contain anything; its
909912
intended usage is to preserve information from the previously generated
@@ -922,8 +925,10 @@ user input.
922925
inserted at the `dynamicNode`s place.
923926
(`dynamicNode` behaves exactly the same as `functionNode` in this regard).
924927

925-
- `opts`: In addition to the common [Node](#node)-keys, there is, again,
928+
- `opts`: In addition to the common [Node](#node)-keys, there is:
926929
- `user_args`, which is described in [FunctionNode](#functionnode).
930+
- `snippetstring_args`, boolean: If set, `function` receives a list of
931+
[Snippetstring](#snippetstring) instead of `string[]`. `false` by default.
927932

928933
**Examples**:
929934

@@ -989,6 +994,70 @@ ls.add_snippets("all",
989994
As with `functionNode`, `user_args` can be used to reuse similar `dynamicNode`-
990995
functions.
991996

997+
## Self-dependent DynamicNode
998+
999+
While the previous examples only showed dynamicNodes that depend on nodes
1000+
outside of itself, it's possible to update a dynamicNode in response to a change
1001+
to a node within it:
1002+
1003+
```lua
1004+
ls.snip_expand(s("trig", {
1005+
d(1, function(args)
1006+
if not args[1] then
1007+
-- the arg does not exist -> provide a default.
1008+
return sn(nil, {i(1, "asdf", {key = "ins"})})
1009+
else
1010+
-- This branch is only take after the dynamicNode was updated once.
1011+
-- Now we can perform the actual "task" of this dynamicNode:
1012+
-- replacing all occurences of "a" with "e".
1013+
return sn(nil, {i(1, args[1]:gsub("a", "e"), {key = "ins"})})
1014+
end
1015+
end, {opt(k("ins"))}, { snippetstring_args = true })
1016+
}))
1017+
```
1018+
1019+
The example above shows a dynamicNode that will substitute all occurrences of
1020+
"a" within it with an "e". Important for this to work are:
1021+
* Use an [Optional Noderef](#optional-noderef) `opt(k("ins"))` to reference the
1022+
generated insertNode that will eventually update the dynamicNode on changes:
1023+
A dynamicNode does not update when one of its argnodes is missing, so in
1024+
order to get anything out of the node, we need `opt`.
1025+
* Set `snippetstring_args`: This ensures that snippets expanded inside the
1026+
dynamicNode are preserved during the update! While not completely necessary,
1027+
this is a good idea because it can be annoying to lose jump-points
1028+
only because the snippet they belong is inside such a self-dependent
1029+
dynamicNode.
1030+
1031+
When using these self-dependent dynamicNodes it is a really good idea to give
1032+
nodes that can cause an update some unique key, or make sure that they are
1033+
inside of a restoreNode. If this is not done, LuaSnip may not find a node that
1034+
is equivalent to the one that contained the cursor before the update, and thus
1035+
may have to jump into the dynamicNode anew, which may be unexpected.
1036+
1037+
Another danger is in accidentally constructing an infinite loop of updates.
1038+
Right now, LuaSnip is very indiscriminate in updating dynamicNodes, and if given
1039+
a snippet like
1040+
1041+
```lua
1042+
s("srep", {
1043+
d(1, function(args)
1044+
if not args[1] then
1045+
return sn(nil, {i(1, "sdf", {key = "ins"})})
1046+
else
1047+
return sn(nil, {i(1, args[1]:gsub("a", "aa"), {key = "ins"})})
1048+
end
1049+
end, {opt(k("ins"))}, {snippetstring_args = true})
1050+
}),
1051+
```
1052+
1053+
any "a" is replaced with "aa", ad infinitum (or until the process is killed,
1054+
which is likely soon).
1055+
1056+
These infinite loops may also be caused by nested self-dependent dynamicNodes,
1057+
think one that replaces "a" with "e", and another that changes "e" back to "a",
1058+
so it's best to build in some kind of safeguard into the generating function.
1059+
One could for example skip the update once the argnode exceeds a certain length.
1060+
9921061
# RestoreNode
9931062

9941063
This node can store and restore a snippetNode as is. This includes changed
@@ -1086,6 +1155,20 @@ that really bothers you feel free to open an issue.
10861155

10871156
<!-- panvimdoc-ignore-end -->
10881157

1158+
# Snippetstring
1159+
1160+
Snippetstrings can store the content of an insertNode as-is. This includes both
1161+
the regular text, and expanded snippets. The primary purpose of snippetstrings
1162+
is to facilitate easy modifications of text in dynamicNode, while preserving
1163+
snippets and cursor-positions.
1164+
1165+
A snippetstring supports the string-functions `lower`, `upper`, `gsub` and
1166+
`sub`, and the `..`-metamethod. While `lower`, `upper`, and `..` will always
1167+
preserve snippets inside the snippetstring, `sub` will replace partially
1168+
contained snippets with their text, while `gsub` will do so if a replacement
1169+
crosses a node-boundary.
1170+
1171+
10891172
# Key Indexer
10901173

10911174
A very flexible way of referencing nodes ([Node Reference](#node-reference)).
@@ -1143,6 +1226,29 @@ s("trig", {
11431226
<!-- panvimdoc-ignore-end -->
11441227

11451228

1229+
# Optional Noderef
1230+
1231+
`opt` wraps another node-reference and makes the wrapped node not strictly
1232+
required for performing an update. This means, amongs other things, that a
1233+
`dynamicNode`s can be updated by nodes within it:
1234+
1235+
```lua
1236+
ls.snip_expand(s("trig", {
1237+
d(1, function(args)
1238+
if not args[1] then
1239+
-- the arg does not exist -> provide a default.
1240+
return sn(nil, {i(1, "asdf", {key = "ins"})})
1241+
else
1242+
-- This branch is only take after the dynamicNode was updated once.
1243+
-- Now we can perform the actual "task" of this dynamicNode:
1244+
-- replacing all occurences of "a" with "e".
1245+
return sn(nil, {i(1, args[1][1]:gsub("a", "e"), {key = "ins"})})
1246+
end
1247+
end, {opt(k("ins"))})
1248+
}))
1249+
```
1250+
1251+
11461252
# Absolute Indexer
11471253

11481254
`absolute_indexer` allows accessing nodes by their unique jump-index path from

0 commit comments

Comments
 (0)