Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/platform/contextkey/test/common/scanner.test.ts
5248 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
import assert from 'assert';
6
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';
7
import { Scanner, Token, TokenType } from '../../common/scanner.js';
8
9
suite('Context Key Scanner', () => {
10
11
ensureNoDisposablesAreLeakedInTestSuite();
12
13
function tokenTypeToStr(token: Token) {
14
switch (token.type) {
15
case TokenType.LParen:
16
return '(';
17
case TokenType.RParen:
18
return ')';
19
case TokenType.Neg:
20
return '!';
21
case TokenType.Eq:
22
return token.isTripleEq ? '===' : '==';
23
case TokenType.NotEq:
24
return token.isTripleEq ? '!==' : '!=';
25
case TokenType.Lt:
26
return '<';
27
case TokenType.LtEq:
28
return '<=';
29
case TokenType.Gt:
30
return '>';
31
case TokenType.GtEq:
32
return '>=';
33
case TokenType.RegexOp:
34
return '=~';
35
case TokenType.RegexStr:
36
return 'RegexStr';
37
case TokenType.True:
38
return 'true';
39
case TokenType.False:
40
return 'false';
41
case TokenType.In:
42
return 'in';
43
case TokenType.Not:
44
return 'not';
45
case TokenType.And:
46
return '&&';
47
case TokenType.Or:
48
return '||';
49
case TokenType.Str:
50
return 'Str';
51
case TokenType.QuotedStr:
52
return 'QuotedStr';
53
case TokenType.Error:
54
return 'ErrorToken';
55
case TokenType.EOF:
56
return 'EOF';
57
}
58
59
}
60
61
function scan(input: string) {
62
return (new Scanner()).reset(input).scan().map((token: Token) => {
63
return 'lexeme' in token
64
? {
65
type: tokenTypeToStr(token),
66
offset: token.offset,
67
lexeme: token.lexeme
68
} : {
69
type: tokenTypeToStr(token),
70
offset: token.offset
71
};
72
});
73
}
74
75
suite('scanning various cases of context keys', () => {
76
77
test('foo.bar<C-shift+2>', () => {
78
const input = 'foo.bar<C-shift+2>';
79
assert.deepStrictEqual(scan(input), ([{ type: 'Str', lexeme: 'foo.bar<C-shift+2>', offset: 0 }, { type: 'EOF', offset: 18 }]));
80
});
81
82
test('!foo', () => {
83
const input = '!foo';
84
assert.deepStrictEqual(scan(input), ([{ type: '!', offset: 0 }, { type: 'Str', lexeme: 'foo', offset: 1 }, { type: 'EOF', offset: 4 }]));
85
});
86
87
test('foo === bar', () => {
88
const input = 'foo === bar';
89
assert.deepStrictEqual(scan(input), ([{ type: 'Str', offset: 0, lexeme: 'foo' }, { type: '===', offset: 4 }, { type: 'Str', offset: 8, lexeme: 'bar' }, { type: 'EOF', offset: 11 }]));
90
});
91
92
test('foo !== bar', () => {
93
const input = 'foo !== bar';
94
assert.deepStrictEqual(scan(input), ([{ type: 'Str', offset: 0, lexeme: 'foo' }, { type: '!==', offset: 5 }, { type: 'Str', offset: 9, lexeme: 'bar' }, { type: 'EOF', offset: 12 }]));
95
});
96
97
test('!(foo && bar)', () => {
98
const input = '!(foo && bar)';
99
assert.deepStrictEqual(scan(input), ([{ type: '!', offset: 0 }, { type: '(', offset: 1 }, { type: 'Str', lexeme: 'foo', offset: 2 }, { type: '&&', offset: 6 }, { type: 'Str', lexeme: 'bar', offset: 9 }, { type: ')', offset: 12 }, { type: 'EOF', offset: 13 }]));
100
});
101
102
test('=~ ', () => {
103
const input = '=~ ';
104
assert.deepStrictEqual(scan(input), ([{ type: '=~', offset: 0 }, { type: 'EOF', offset: 3 }]));
105
});
106
107
test('foo =~ /bar/', () => {
108
const input = 'foo =~ /bar/';
109
assert.deepStrictEqual(scan(input), ([{ type: 'Str', lexeme: 'foo', offset: 0 }, { type: '=~', offset: 4 }, { type: 'RegexStr', lexeme: '/bar/', offset: 7 }, { type: 'EOF', offset: 12 }]));
110
});
111
112
test('foo =~ /zee/i', () => {
113
const input = 'foo =~ /zee/i';
114
assert.deepStrictEqual(scan(input), ([{ type: 'Str', lexeme: 'foo', offset: 0 }, { type: '=~', offset: 4 }, { type: 'RegexStr', lexeme: '/zee/i', offset: 7 }, { type: 'EOF', offset: 13 }]));
115
});
116
117
118
test('foo =~ /zee/gm', () => {
119
const input = 'foo =~ /zee/gm';
120
assert.deepStrictEqual(scan(input), ([{ type: 'Str', lexeme: 'foo', offset: 0 }, { type: '=~', offset: 4 }, { type: 'RegexStr', lexeme: '/zee/gm', offset: 7 }, { type: 'EOF', offset: 14 }]));
121
});
122
123
test('foo in barrr ', () => {
124
const input = 'foo in barrr ';
125
assert.deepStrictEqual(scan(input), ([{ type: 'Str', lexeme: 'foo', offset: 0 }, { type: 'in', offset: 4 }, { type: 'Str', lexeme: 'barrr', offset: 7 }, { type: 'EOF', offset: 14 }]));
126
});
127
128
test(`resource =~ //FileCabinet/(SuiteScripts|Templates/(E-mail%20Templates|Marketing%20Templates)|Web%20Site%20Hosting%20Files)(/.*)*$/`, () => {
129
const input = `resource =~ //FileCabinet/(SuiteScripts|Templates/(E-mail%20Templates|Marketing%20Templates)|Web%20Site%20Hosting%20Files)(/.*)*$/`;
130
assert.deepStrictEqual(scan(input), ([{ type: 'Str', offset: 0, lexeme: 'resource' }, { type: '=~', offset: 9 }, { type: 'RegexStr', offset: 12, lexeme: '//' }, { type: 'Str', offset: 14, lexeme: 'FileCabinet/' }, { type: '(', offset: 26 }, { type: 'Str', offset: 27, lexeme: 'SuiteScripts' }, { type: 'ErrorToken', offset: 39, lexeme: '|' }, { type: 'Str', offset: 40, lexeme: 'Templates/' }, { type: '(', offset: 50 }, { type: 'Str', offset: 51, lexeme: 'E-mail%20Templates' }, { type: 'ErrorToken', offset: 69, lexeme: '|' }, { type: 'Str', offset: 70, lexeme: 'Marketing%20Templates' }, { type: ')', offset: 91 }, { type: 'ErrorToken', offset: 92, lexeme: '|' }, { type: 'Str', offset: 93, lexeme: 'Web%20Site%20Hosting%20Files' }, { type: ')', offset: 121 }, { type: '(', offset: 122 }, { type: 'RegexStr', offset: 123, lexeme: '/.*)*$/' }, { type: 'EOF', offset: 130 }]));
131
});
132
133
test('editorLangId in testely.supportedLangIds && resourceFilename =~ /^.+(.test.(\w+))$/gm', () => {
134
const input = 'editorLangId in testely.supportedLangIds && resourceFilename =~ /^.+(.test.(\w+))$/gm';
135
assert.deepStrictEqual(scan(input), ([{ type: 'Str', lexeme: 'editorLangId', offset: 0 }, { type: 'in', offset: 13 }, { type: 'Str', lexeme: 'testely.supportedLangIds', offset: 16 }, { type: '&&', offset: 41 }, { type: 'Str', lexeme: 'resourceFilename', offset: 44 }, { type: '=~', offset: 61 }, { type: 'RegexStr', lexeme: '/^.+(.test.(w+))$/gm', offset: 64 }, { type: 'EOF', offset: 84 }]));
136
});
137
138
test('!(foo && bar) && baz', () => {
139
const input = '!(foo && bar) && baz';
140
assert.deepStrictEqual(scan(input), ([{ type: '!', offset: 0 }, { type: '(', offset: 1 }, { type: 'Str', lexeme: 'foo', offset: 2 }, { type: '&&', offset: 6 }, { type: 'Str', lexeme: 'bar', offset: 9 }, { type: ')', offset: 12 }, { type: '&&', offset: 14 }, { type: 'Str', lexeme: 'baz', offset: 17 }, { type: 'EOF', offset: 20 }]));
141
});
142
143
test('foo.bar:zed==completed - equality with no space', () => {
144
const input = 'foo.bar:zed==completed';
145
assert.deepStrictEqual(scan(input), ([{ type: 'Str', lexeme: 'foo.bar:zed', offset: 0 }, { type: '==', offset: 11 }, { type: 'Str', lexeme: 'completed', offset: 13 }, { type: 'EOF', offset: 22 }]));
146
});
147
148
test('a && b || c', () => {
149
const input = 'a && b || c';
150
assert.deepStrictEqual(scan(input), ([{ type: 'Str', lexeme: 'a', offset: 0 }, { type: '&&', offset: 2 }, { type: 'Str', lexeme: 'b', offset: 5 }, { type: '||', offset: 7 }, { type: 'Str', lexeme: 'c', offset: 10 }, { type: 'EOF', offset: 11 }]));
151
});
152
153
test('fooBar && baz.jar && fee.bee<K-loo+1>', () => {
154
const input = 'fooBar && baz.jar && fee.bee<K-loo+1>';
155
assert.deepStrictEqual(scan(input), ([{ type: 'Str', lexeme: 'fooBar', offset: 0 }, { type: '&&', offset: 7 }, { type: 'Str', lexeme: 'baz.jar', offset: 10 }, { type: '&&', offset: 18 }, { type: 'Str', lexeme: 'fee.bee<K-loo+1>', offset: 21 }, { type: 'EOF', offset: 37 }]));
156
});
157
158
test('foo.barBaz<C-r> < 2', () => {
159
const input = 'foo.barBaz<C-r> < 2';
160
assert.deepStrictEqual(scan(input), ([{ type: 'Str', lexeme: 'foo.barBaz<C-r>', offset: 0 }, { type: '<', offset: 16 }, { type: 'Str', lexeme: '2', offset: 18 }, { type: 'EOF', offset: 19 }]));
161
});
162
163
test('foo.bar >= -1', () => {
164
const input = 'foo.bar >= -1';
165
assert.deepStrictEqual(scan(input), ([{ type: 'Str', lexeme: 'foo.bar', offset: 0 }, { type: '>=', offset: 8 }, { type: 'Str', lexeme: '-1', offset: 11 }, { type: 'EOF', offset: 13 }]));
166
});
167
168
test('foo.bar <= -1', () => {
169
const input = 'foo.bar <= -1';
170
assert.deepStrictEqual(scan(input), ([{ type: 'Str', lexeme: 'foo.bar', offset: 0 }, { type: '<=', offset: 8 }, { type: 'Str', lexeme: '-1', offset: 11 }, { type: 'EOF', offset: 13 }]));
171
});
172
173
test(`resource =~ /\\/Objects\\/.+\\.xml$/`, () => {
174
const input = `resource =~ /\\/Objects\\/.+\\.xml$/`;
175
assert.deepStrictEqual(scan(input), ([{ type: 'Str', lexeme: 'resource', offset: 0 }, { type: '=~', offset: 9 }, { type: 'RegexStr', lexeme: '/\\/Objects\\/.+\\.xml$/', offset: 12 }, { type: 'EOF', offset: 33 }]));
176
});
177
178
test('view == vsc-packages-activitybar-foldersĀ && vsc-packages-folders-loaded', () => {
179
const input = `view == vsc-packages-activitybar-foldersĀ && vsc-packages-folders-loaded`;
180
assert.deepStrictEqual(scan(input), ([{ type: 'Str', lexeme: 'view', offset: 0 }, { type: '==', offset: 5 }, { type: 'Str', lexeme: 'vsc-packages-activitybar-folders', offset: 8 }, { type: '&&', offset: 41 }, { type: 'Str', lexeme: 'vsc-packages-folders-loaded', offset: 44 }, { type: 'EOF', offset: 71 }]));
181
});
182
183
test(`sfdx:project_opened && resource =~ /.*\\/functions\\/.*\\/[^\\/]+(\\/[^\\/]+\.(ts|js|java|json|toml))?$/ && resourceFilename != package.json && resourceFilename != package-lock.json && resourceFilename != tsconfig.json`, () => {
184
const input = `sfdx:project_opened && resource =~ /.*\\/functions\\/.*\\/[^\\/]+(\\/[^\\/]+\.(ts|js|java|json|toml))?$/ && resourceFilename != package.json && resourceFilename != package-lock.json && resourceFilename != tsconfig.json`;
185
assert.deepStrictEqual(scan(input), ([{ type: 'Str', lexeme: 'sfdx:project_opened', offset: 0 }, { type: '&&', offset: 20 }, { type: 'Str', lexeme: 'resource', offset: 23 }, { type: '=~', offset: 32 }, { type: 'RegexStr', lexeme: '/.*\\/functions\\/.*\\/[^\\/]+(\\/[^\\/]+.(ts|js|java|json|toml))?$/', offset: 35 }, { type: '&&', offset: 98 }, { type: 'Str', lexeme: 'resourceFilename', offset: 101 }, { type: '!=', offset: 118 }, { type: 'Str', lexeme: 'package.json', offset: 121 }, { type: '&&', offset: 134 }, { type: 'Str', lexeme: 'resourceFilename', offset: 137 }, { type: '!=', offset: 154 }, { type: 'Str', lexeme: 'package-lock.json', offset: 157 }, { type: '&&', offset: 175 }, { type: 'Str', lexeme: 'resourceFilename', offset: 178 }, { type: '!=', offset: 195 }, { type: 'Str', lexeme: 'tsconfig.json', offset: 198 }, { type: 'EOF', offset: 211 }]));
186
});
187
188
test(`view =~ '/(servers)/' && viewItem =~ '/^(Starting|Started|Debugging|Stopping|Stopped)/'`, () => {
189
const input = `view =~ '/(servers)/' && viewItem =~ '/^(Starting|Started|Debugging|Stopping|Stopped)/'`;
190
assert.deepStrictEqual(scan(input), ([{ type: 'Str', lexeme: 'view', offset: 0 }, { type: '=~', offset: 5 }, { type: 'QuotedStr', lexeme: '/(servers)/', offset: 9 }, { type: '&&', offset: 22 }, { type: 'Str', lexeme: 'viewItem', offset: 25 }, { type: '=~', offset: 34 }, { type: 'QuotedStr', lexeme: '/^(Starting|Started|Debugging|Stopping|Stopped)/', offset: 38 }, { type: 'EOF', offset: 87 }]));
191
});
192
193
test(`resourcePath =~ /\.md(\.yml|\.txt)*$/gim`, () => {
194
const input = `resourcePath =~ /\.md(\.yml|\.txt)*$/gim`;
195
assert.deepStrictEqual(scan(input), ([{ type: 'Str', offset: 0, lexeme: 'resourcePath' }, { type: '=~', offset: 13 }, { type: 'RegexStr', offset: 16, lexeme: '/.md(.yml|.txt)*$/gim' }, { type: 'EOF', offset: 37 }]));
196
});
197
});
198
199
test(`foo === bar'`, () => {
200
const input = `foo === bar'`;
201
assert.deepStrictEqual(scan(input), ([{ type: 'Str', offset: 0, lexeme: 'foo' }, { type: '===', offset: 4 }, { type: 'Str', offset: 8, lexeme: 'bar' }, { type: 'ErrorToken', offset: 11, lexeme: `'` }, { type: 'EOF', offset: 12 }]));
202
});
203
204
suite('handling lexical errors', () => {
205
206
test(`foo === '`, () => {
207
const input = `foo === '`;
208
assert.deepStrictEqual(scan(input), ([{ type: 'Str', offset: 0, lexeme: 'foo' }, { type: '===', offset: 4 }, { type: 'ErrorToken', offset: 8, lexeme: `'` }, { type: 'EOF', offset: 9 }]));
209
});
210
211
test(`foo && 'bar - unterminated single quote`, () => {
212
const input = `foo && 'bar`;
213
assert.deepStrictEqual(scan(input), ([{ type: 'Str', lexeme: 'foo', offset: 0 }, { type: '&&', offset: 4 }, { type: 'ErrorToken', offset: 7, lexeme: `'bar` }, { type: 'EOF', offset: 11 }]));
214
});
215
216
test('vim<c-r> == 1 && vim<2 <= 3', () => {
217
const input = 'vim<c-r> == 1 && vim<2 <= 3';
218
assert.deepStrictEqual(scan(input), ([{ type: 'Str', lexeme: 'vim<c-r>', offset: 0 }, { type: '==', offset: 9 }, { type: 'Str', lexeme: '1', offset: 12 }, { type: '&&', offset: 14 }, { type: 'Str', lexeme: 'vim<2', offset: 17 }, { type: '<=', offset: 23 }, { type: 'Str', lexeme: '3', offset: 26 }, { type: 'EOF', offset: 27 }]));
219
});
220
221
test('vim<c-r>==1 && vim<2<=3', () => {
222
const input = 'vim<c-r>==1 && vim<2<=3';
223
assert.deepStrictEqual(scan(input), ([{ type: 'Str', offset: 0, lexeme: 'vim<c-r>' }, { type: '==', offset: 8 }, { type: 'Str', offset: 10, lexeme: '1' }, { type: '&&', offset: 12 }, { type: 'Str', offset: 15, lexeme: 'vim<2<' }, { type: 'ErrorToken', offset: 21, lexeme: '=' }, { type: 'Str', offset: 22, lexeme: '3' }, { type: 'EOF', offset: 23 }]));
224
});
225
226
test(`foo|bar`, () => {
227
const input = `foo|bar`;
228
assert.deepStrictEqual(scan(input), ([{ type: 'Str', offset: 0, lexeme: 'foo' }, { type: 'ErrorToken', offset: 3, lexeme: '|' }, { type: 'Str', offset: 4, lexeme: 'bar' }, { type: 'EOF', offset: 7 }]));
229
});
230
231
test(`resource =~ //foo/(barr|door/(Foo-Bar%20Templates|Soo%20Looo)|Web%20Site%Jjj%20Llll)(/.*)*$/`, () => {
232
const input = `resource =~ //foo/(barr|door/(Foo-Bar%20Templates|Soo%20Looo)|Web%20Site%Jjj%20Llll)(/.*)*$/`;
233
assert.deepStrictEqual(scan(input), ([{ type: 'Str', offset: 0, lexeme: 'resource' }, { type: '=~', offset: 9 }, { type: 'RegexStr', offset: 12, lexeme: '//' }, { type: 'Str', offset: 14, lexeme: 'foo/' }, { type: '(', offset: 18 }, { type: 'Str', offset: 19, lexeme: 'barr' }, { type: 'ErrorToken', offset: 23, lexeme: '|' }, { type: 'Str', offset: 24, lexeme: 'door/' }, { type: '(', offset: 29 }, { type: 'Str', offset: 30, lexeme: 'Foo-Bar%20Templates' }, { type: 'ErrorToken', offset: 49, lexeme: '|' }, { type: 'Str', offset: 50, lexeme: 'Soo%20Looo' }, { type: ')', offset: 60 }, { type: 'ErrorToken', offset: 61, lexeme: '|' }, { type: 'Str', offset: 62, lexeme: 'Web%20Site%Jjj%20Llll' }, { type: ')', offset: 83 }, { type: '(', offset: 84 }, { type: 'RegexStr', offset: 85, lexeme: '/.*)*$/' }, { type: 'EOF', offset: 92 }]));
234
});
235
236
test(`/((/foo/(?!bar)(.*)/)|((/src/).*/)).*$/`, () => {
237
const input = `/((/foo/(?!bar)(.*)/)|((/src/).*/)).*$/`;
238
assert.deepStrictEqual(scan(input), ([{ type: 'RegexStr', offset: 0, lexeme: '/((/' }, { type: 'Str', offset: 4, lexeme: 'foo/' }, { type: '(', offset: 8 }, { type: 'Str', offset: 9, lexeme: '?' }, { type: '!', offset: 10 }, { type: 'Str', offset: 11, lexeme: 'bar' }, { type: ')', offset: 14 }, { type: '(', offset: 15 }, { type: 'Str', offset: 16, lexeme: '.*' }, { type: ')', offset: 18 }, { type: 'RegexStr', offset: 19, lexeme: '/)|((/s' }, { type: 'Str', offset: 26, lexeme: 'rc/' }, { type: ')', offset: 29 }, { type: 'Str', offset: 30, lexeme: '.*/' }, { type: ')', offset: 33 }, { type: ')', offset: 34 }, { type: 'Str', offset: 35, lexeme: '.*$/' }, { type: 'EOF', offset: 39 }]));
239
});
240
241
test(`resourcePath =~ //foo/barr// || resourcePath =~ //view/(jarrr|doooor|bees)/(web|templates)// && resourceExtname in foo.Bar`, () => {
242
const input = `resourcePath =~ //foo/barr// || resourcePath =~ //view/(jarrr|doooor|bees)/(web|templates)// && resourceExtname in foo.Bar`;
243
assert.deepStrictEqual(scan(input), ([{ type: 'Str', offset: 0, lexeme: 'resourcePath' }, { type: '=~', offset: 13 }, { type: 'RegexStr', offset: 16, lexeme: '//' }, { type: 'Str', offset: 18, lexeme: 'foo/barr//' }, { type: '||', offset: 29 }, { type: 'Str', offset: 32, lexeme: 'resourcePath' }, { type: '=~', offset: 45 }, { type: 'RegexStr', offset: 48, lexeme: '//' }, { type: 'Str', offset: 50, lexeme: 'view/' }, { type: '(', offset: 55 }, { type: 'Str', offset: 56, lexeme: 'jarrr' }, { type: 'ErrorToken', offset: 61, lexeme: '|' }, { type: 'Str', offset: 62, lexeme: 'doooor' }, { type: 'ErrorToken', offset: 68, lexeme: '|' }, { type: 'Str', offset: 69, lexeme: 'bees' }, { type: ')', offset: 73 }, { type: 'RegexStr', offset: 74, lexeme: '/(web|templates)/' }, { type: 'ErrorToken', offset: 91, lexeme: '/ && resourceExtname in foo.Bar' }, { type: 'EOF', offset: 122 }]));
244
});
245
246
test(`foo =~ /file:\// || bar`, () => {
247
const input = JSON.parse('"foo =~ /file:\// || bar"');
248
assert.deepStrictEqual(scan(input), ([{ type: 'Str', offset: 0, lexeme: 'foo' }, { type: '=~', offset: 4 }, { type: 'RegexStr', offset: 7, lexeme: '/file:/' }, { type: 'ErrorToken', offset: 14, lexeme: '/ || bar' }, { type: 'EOF', offset: 22 }]));
249
});
250
});
251
});
252
253