11const SnippetParser = require ( '../lib/snippet-body-parser' ) ;
22
33describe ( "Snippet Body Parser" , ( ) => {
4+ // Helper for testing a snippet parse tree. The `input`
5+ // is the snippet string, the `tree` is the expected
6+ // parse tree.
47 function expectMatch ( input , tree ) {
58 expect ( SnippetParser . parse ( input ) ) . toEqual ( tree ) ;
69 }
@@ -30,6 +33,10 @@ describe("Snippet Body Parser", () => {
3033 } ) ;
3134
3235 describe ( "with placeholders" , ( ) => {
36+ it ( "allows placeholders to be empty" , ( ) => {
37+ expectMatch ( "${1:}" , [ { index : 1 , content : [ ] } ] ) ;
38+ } ) ;
39+
3340 it ( "allows placeholders to be arbitrary" , ( ) => {
3441 expectMatch ( "${1:${2}$foo${3|a,b|}}" , [
3542 {
@@ -43,6 +50,37 @@ describe("Snippet Body Parser", () => {
4350 ] ) ;
4451 } ) ;
4552
53+ it ( "even lets placeholders contain placeholders" , ( ) => {
54+ expectMatch ( "${1:${2:${3:levels}}}" , [
55+ {
56+ index : 1 ,
57+ content : [
58+ {
59+ index : 2 ,
60+ content : [
61+ {
62+ index : 3 ,
63+ content : [
64+ "levels"
65+ ]
66+ }
67+ ]
68+ }
69+ ]
70+ }
71+ ] ) ;
72+ } ) ;
73+
74+ it ( "ends the placeholder at an unmatched '}'" , ( ) => {
75+ expectMatch ( "${1:}}" , [
76+ {
77+ index : 1 ,
78+ content : [ ]
79+ } ,
80+ "}"
81+ ] ) ;
82+ } ) ;
83+
4684 it ( "allows escaping '}' in placeholders" , ( ) => {
4785 expectMatch ( "${1:\\}}" , [ { index : 1 , content : [ "}" ] } ] ) ;
4886 } ) ;
@@ -110,6 +148,8 @@ describe("Snippet Body Parser", () => {
110148 } ) ;
111149 } ) ;
112150
151+ // The placeholder implementation is expected to be the same as for tab stops, so
152+ // see the tab stop placeholder section for more thorough tests
113153 describe ( "with placeholders" , ( ) => {
114154 it ( "allows placeholders to be arbitrary" , ( ) => {
115155 expectMatch ( "${foo:${2}$bar${3|a,b|}}" , [
@@ -208,6 +248,153 @@ describe("Snippet Body Parser", () => {
208248 "/}"
209249 ] ) ;
210250 } ) ;
251+
252+ it ( "allows and preserves all escapes in regex strings" , ( ) => {
253+ expectMatch ( "${1/foo\\/\\$\\:\\n\\r/baz/}" , [
254+ {
255+ index : 1 ,
256+ transformation : {
257+ find : / f o o \/ \$ \: \n \r / ,
258+ replace : [
259+ "baz"
260+ ]
261+ }
262+ }
263+ ] ) ;
264+ } ) ;
265+
266+ describe ( "When parsing the replace section" , ( ) => {
267+ // Helper for testing the relacement part of
268+ // transformations, which are relatively deep in
269+ // the tree and have a lot of behaviour to cover
270+ // NOTE: Only use when the replace section is expected to
271+ // be valid, or else you will be testing against the
272+ // boilerplate (which is not a good idea)
273+ function expectReplaceMatch ( replace , tree ) {
274+ expectMatch ( `\${1/foo/${ replace } /}` , [
275+ {
276+ index : 1 ,
277+ transformation : {
278+ find : / f o o / ,
279+ replace : tree ,
280+ }
281+ }
282+ ] ) ;
283+ }
284+
285+ it ( "allows '$' and '}' as plain text if not part of a format" , ( ) => {
286+ expectReplaceMatch ( "$}" , [ "$}" ] ) ;
287+ } ) ;
288+
289+ it ( "allows inline 'escaped modifiers'" , ( ) => {
290+ expectReplaceMatch ( "foo\\E\\l\\L\\u\\Ubar" , [
291+ "foo" ,
292+ { modifier : "E" } ,
293+ { modifier : "l" } ,
294+ { modifier : "L" } ,
295+ { modifier : "u" } ,
296+ { modifier : "U" } ,
297+ "bar"
298+ ] ) ;
299+ } ) ;
300+
301+ it ( "allows '$', '\\', and '/' to be escaped" , ( ) => {
302+ expectReplaceMatch ( "\\$1 \\\\ \\/" , [
303+ "$1 \\ /"
304+ ] ) ;
305+ } ) ;
306+
307+ describe ( "When parsing formats" , ( ) => {
308+ it ( "parses simple formats" , ( ) => {
309+ expectReplaceMatch ( "$1${2}" , [
310+ { backreference : 1 } ,
311+ { backreference : 2 }
312+ ] ) ;
313+ } ) ;
314+
315+ it ( "parses formats with modifiers" , ( ) => {
316+ expectReplaceMatch ( "${1:/upcase}" , [
317+ {
318+ backreference : 1 ,
319+ modifier : "upcase" ,
320+ }
321+ ] ) ;
322+ } ) ;
323+
324+ it ( "parses formats with an if branch" , ( ) => {
325+ expectReplaceMatch ( "${1:+foo$2$bar}" , [
326+ {
327+ backreference : 1 ,
328+ ifContent : [
329+ "foo" ,
330+ { backreference : 2 , } ,
331+ "$bar" // no variables inside a replace / format
332+ ]
333+ }
334+ ] ) ;
335+ } ) ;
336+
337+ it ( "parses formats with if and else branches" , ( ) => {
338+ expectReplaceMatch ( "${1:?foo\\:stillIf:bar\\}stillElse}" , [
339+ {
340+ backreference : 1 ,
341+ ifContent : [
342+ "foo:stillIf"
343+ ] ,
344+ elseContent : [
345+ "bar}stillElse"
346+ ]
347+ }
348+ ] ) ;
349+ } ) ;
350+
351+ it ( "parses formats with an else branch" , ( ) => {
352+ expectReplaceMatch ( "${1:-foo}" , [
353+ {
354+ backreference : 1 ,
355+ elseContent : [
356+ "foo"
357+ ]
358+ }
359+ ] ) ;
360+ } ) ;
361+
362+ it ( "parses formats with the old else branch syntax" , ( ) => {
363+ expectReplaceMatch ( "${1:foo}" , [
364+ {
365+ backreference : 1 ,
366+ elseContent : [
367+ "foo"
368+ ]
369+ }
370+ ] ) ;
371+ } ) ;
372+
373+ it ( "allows nested replacements inside of formats" , ( ) => {
374+ expectReplaceMatch ( "${1:+${2:-${3:?a lot of:layers}}}" , [
375+ {
376+ backreference : 1 ,
377+ ifContent : [
378+ {
379+ backreference : 2 ,
380+ elseContent : [
381+ {
382+ backreference : 3 ,
383+ ifContent : [
384+ "a lot of"
385+ ] ,
386+ elseContent : [
387+ "layers"
388+ ]
389+ }
390+ ]
391+ }
392+ ]
393+ }
394+ ] ) ;
395+ } ) ;
396+ } ) ;
397+ } ) ;
211398 } ) ;
212399
213400 describe ( "When parsing escaped characters" , ( ) => {
0 commit comments