@@ -31,6 +31,7 @@ local d = ls.dynamic_node
3131local r = ls .restore_node
3232local events = require (" luasnip.util.events" )
3333local ai = require (" luasnip.nodes.absolute_indexer" )
34+ local opt = require (" luasnip.nodes.optional_arg" )
3435local extras = require (" luasnip.extras" )
3536local l = extras .lambda
3637local 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",
10241029As with ` functionNode ` , ` user_args ` can be used to reuse similar ` dynamicNode ` -
10251030functions.
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
10291098This 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
11261209A 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
0 commit comments