Skip to content
This repository was archived by the owner on Dec 15, 2022. It is now read-only.

Commit 95a050a

Browse files
committed
moreee
1 parent 25cb10d commit 95a050a

File tree

2 files changed

+106
-15
lines changed

2 files changed

+106
-15
lines changed

lib/snippet-body.pegjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ formatWithElse = '{' n:integer ':' '-'? elseContent:replace { return { backrefer
129129
modifier = '/' modifier:var { return modifier; }
130130

131131
// Regex flags. Validation is performed when the regex itself is also known.
132-
flags = f:[a-z]* { return f; }
132+
flags = f:[a-z]* { return f.join(""); }
133133

134134
// A tab stop that offers a choice between several fixed values. These values are plain text only.
135135
// This feature is not implemented, but the syntax is parsed to reserve it for future use.
@@ -156,7 +156,7 @@ text = t:([^$\\}])+ { return t.join("") }
156156
tabStopText = text
157157

158158
// None-special text inside a choice. $, {, }, etc. are all regular text in this context.
159-
choiceText = b:(t:[^,|\\]+ { return t.join(""); } / '\\' c:[,|\\] { return c; } / '\\' c:. { return '\\' + c; } )* { return b.join(""); }
159+
choiceText = b:(t:[^,|\\]+ { return t.join(""); } / '\\' c:[,|\\] { return c; } / '\\' c:. { return '\\' + c; } )+ { return b.join(""); }
160160

161161
// None-special text inside a replace (substitution part of transformation). Same as normal text, but `/` is special (the end of the regex-like pattern)
162162
replaceText = t:[^$\\}/]+ { return t.join(""); }

spec/body-parser-spec.js

Lines changed: 104 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
const BodyParser = require('../lib/snippet-body-parser');
1+
const SnippetParser = require('../lib/snippet-body-parser');
22

33
describe("Snippet Body Parser", () => {
44
function expectMatch(input, tree) {
5-
expect(BodyParser.parse(input)).toEqual(tree);
5+
expect(SnippetParser.parse(input)).toEqual(tree);
66
}
77

88
describe("tab stops", () => {
@@ -20,6 +20,14 @@ describe("Snippet Body Parser", () => {
2020
]);
2121
});
2222

23+
it("only allows non-negative integer stop numbers", () => {
24+
expectMatch("$99999", [{ index: 99999, content: [] }]);
25+
expectMatch("$-1", ["$-1"]);
26+
expectMatch("${-1}", ["${-1}"]);
27+
expectMatch("$1.5", [{ index: 1, content: [] }, ".5"]);
28+
expectMatch("${1.5}", ["${1.5}"]);
29+
});
30+
2331
describe("with placeholders", () => {
2432
it("allows placeholders to be arbitrary", () => {
2533
expectMatch("${1:${2}$foo${3|a,b|}}", [
@@ -54,6 +62,20 @@ describe("Snippet Body Parser", () => {
5462
]);
5563
});
5664

65+
it("applies flags to the find regex", () => {
66+
expectMatch("${1/foo/bar/gimsuy}", [
67+
{
68+
index: 1,
69+
transformation: {
70+
find: /foo/gimsuy,
71+
replace: [
72+
"bar"
73+
]
74+
}
75+
}
76+
]);
77+
});
78+
5779
it("does not parse invalid regex as transformations", () => {
5880
expectMatch("${1/foo/bar/a}", ["${1/foo/bar/a}"]); // invalid flag
5981
expectMatch("${1/fo)o$1/$bar/}", [
@@ -85,20 +107,90 @@ describe("Snippet Body Parser", () => {
85107
"\\ar $\\{baz}",
86108
]);
87109
});
110+
111+
describe("naming", () => {
112+
it("only allows ASCII letters, numbers, and underscores in names", () => {
113+
expectMatch("$abc_123-not", [{ variable: "abc_123" }, "-not"]);
114+
});
115+
116+
it("allows names to start with underscores", () => {
117+
expectMatch("$__properties", [{ variable: "__properties" }]);
118+
});
119+
120+
it("doesn't allow names to start with a number", () => {
121+
expectMatch("$1foo", [{ index: 1, content: [] }, "foo"]);
122+
});
123+
});
124+
125+
describe("with placeholders", () => {
126+
it("allows placeholders to be arbitrary", () => {
127+
expectMatch("${foo:${2}$bar${3|a,b|}}", [
128+
{
129+
variable: "foo",
130+
content: [
131+
{ index: 2, content: [] },
132+
{ variable: "bar" },
133+
{ index: 3, choices: ["a", "b"] },
134+
]
135+
}
136+
]);
137+
});
138+
139+
it("allows escaping '}' in placeholders", () => {
140+
expectMatch("${foo:\\}}", [{ variable: "foo", content: ["}"] }]);
141+
});
142+
});
143+
144+
describe("with transformations", () => {
145+
it("parses simple transformations", () => {
146+
expectMatch("${var/foo/bar/}", [
147+
{
148+
variable: "var",
149+
transformation: {
150+
find: /foo/,
151+
replace: [
152+
"bar"
153+
]
154+
}
155+
}
156+
]);
157+
});
158+
});
88159
});
89160

90-
it("parses snippets with no special behaviour as plain text", () => {
91-
const plainSnippets = [
92-
"foo $ bar",
93-
"$% $ 1 ${/upcase} \n ${|world|} ${3foo}",
94-
];
161+
describe("choices", () => {
162+
it("parses choices", () => {
163+
expectMatch("${1|a,b,c|}", [{ index: 1, choices: ["a", "b", "c"] }]);
164+
});
165+
166+
it("skips empty choices", () => {
167+
expectMatch("${1||}", ["${1||}"]);
168+
});
169+
170+
it("skips escaped choices", () => {
171+
expectMatch("\\${1|a|}", ["${1|a|}"]);
172+
});
95173

96-
for (const plain of plainSnippets) {
97-
expectMatch(plain, [plain]);
98-
}
174+
it("treats choice items as plain text", () => {
175+
expectMatch("${1|$2,$foo|}", [{ index: 1, choices: ["$2", "$foo"] }]);
176+
});
177+
178+
it("only allows ',' and '|' to be escaped in choice text", () => {
179+
expectMatch("${1|a,b\\,c,d\\|},e\\$f|}", [
180+
{
181+
index: 1,
182+
choices: [
183+
"a",
184+
"b,c",
185+
"d|}",
186+
"e\\$f"
187+
]
188+
}
189+
]);
190+
});
99191
});
100192

101-
describe("only escapes a select few characters", () => {
193+
describe("escaped characters", () => {
102194
const escapeTest = "\\$ \\\\ \\} \\% \\* \\, \\| \\{ \\n \\r \\:";
103195

104196
const escapeResolveTop = "$ \\ } \\% \\* \\, \\| \\{ \\n \\r \\:";
@@ -178,8 +270,7 @@ describe("Snippet Body Parser", () => {
178270
});
179271

180272
it("parses a snippet with transformations", () => {
181-
const bodyTree = BodyParser.parse("<${1:p}>$0</${1/f/F/}>");
182-
expect(bodyTree).toEqual([
273+
expectMatch("<${1:p}>$0</${1/f/F/}>", [
183274
'<',
184275
{ index: 1, content: ['p'] },
185276
'>',

0 commit comments

Comments
 (0)