1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
|
*luasnip.txt* For NVIM v0.8.0 Last change: 2024 April 16
==============================================================================
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. Config-Options |luasnip-config-options|
30. Troubleshooting |luasnip-troubleshooting|
- Adding Snippets |luasnip-troubleshooting-adding-snippets|
31. 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 reffered 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 initialliy 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 occurences 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 occurences 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 occurences 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. Occurences 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 occurences.
- `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 arbritary 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 conjuction 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 additionaly 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 preceeding 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
preceeding 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 cusutom `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 preceeding 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 the arg-table with the
extend table (accepts `nil`).
This extend behaviour is adaptable to accomodate `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 necessiates 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 colletion (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", {
patter = "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.
`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()`.
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"`.
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. 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.
- `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 bet set to the 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`).
- `store_selection_keys`: Mapping for populating `TM_SELECTED_TEXT` and related
variables (not set by default). If you want to set this mapping yourself, map
`ls.select_keys` (not a function, actually a string/key-combination) as a rhs.
- `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).
==============================================================================
30. 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.
==============================================================================
31. 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:
|