Path: blob/main/src/vs/platform/contextkey/test/common/scanner.test.ts
5248 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/4import assert from 'assert';5import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';6import { Scanner, Token, TokenType } from '../../common/scanner.js';78suite('Context Key Scanner', () => {910ensureNoDisposablesAreLeakedInTestSuite();1112function tokenTypeToStr(token: Token) {13switch (token.type) {14case TokenType.LParen:15return '(';16case TokenType.RParen:17return ')';18case TokenType.Neg:19return '!';20case TokenType.Eq:21return token.isTripleEq ? '===' : '==';22case TokenType.NotEq:23return token.isTripleEq ? '!==' : '!=';24case TokenType.Lt:25return '<';26case TokenType.LtEq:27return '<=';28case TokenType.Gt:29return '>';30case TokenType.GtEq:31return '>=';32case TokenType.RegexOp:33return '=~';34case TokenType.RegexStr:35return 'RegexStr';36case TokenType.True:37return 'true';38case TokenType.False:39return 'false';40case TokenType.In:41return 'in';42case TokenType.Not:43return 'not';44case TokenType.And:45return '&&';46case TokenType.Or:47return '||';48case TokenType.Str:49return 'Str';50case TokenType.QuotedStr:51return 'QuotedStr';52case TokenType.Error:53return 'ErrorToken';54case TokenType.EOF:55return 'EOF';56}5758}5960function scan(input: string) {61return (new Scanner()).reset(input).scan().map((token: Token) => {62return 'lexeme' in token63? {64type: tokenTypeToStr(token),65offset: token.offset,66lexeme: token.lexeme67} : {68type: tokenTypeToStr(token),69offset: token.offset70};71});72}7374suite('scanning various cases of context keys', () => {7576test('foo.bar<C-shift+2>', () => {77const input = 'foo.bar<C-shift+2>';78assert.deepStrictEqual(scan(input), ([{ type: 'Str', lexeme: 'foo.bar<C-shift+2>', offset: 0 }, { type: 'EOF', offset: 18 }]));79});8081test('!foo', () => {82const input = '!foo';83assert.deepStrictEqual(scan(input), ([{ type: '!', offset: 0 }, { type: 'Str', lexeme: 'foo', offset: 1 }, { type: 'EOF', offset: 4 }]));84});8586test('foo === bar', () => {87const input = 'foo === bar';88assert.deepStrictEqual(scan(input), ([{ type: 'Str', offset: 0, lexeme: 'foo' }, { type: '===', offset: 4 }, { type: 'Str', offset: 8, lexeme: 'bar' }, { type: 'EOF', offset: 11 }]));89});9091test('foo !== bar', () => {92const input = 'foo !== bar';93assert.deepStrictEqual(scan(input), ([{ type: 'Str', offset: 0, lexeme: 'foo' }, { type: '!==', offset: 5 }, { type: 'Str', offset: 9, lexeme: 'bar' }, { type: 'EOF', offset: 12 }]));94});9596test('!(foo && bar)', () => {97const input = '!(foo && bar)';98assert.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 }]));99});100101test('=~ ', () => {102const input = '=~ ';103assert.deepStrictEqual(scan(input), ([{ type: '=~', offset: 0 }, { type: 'EOF', offset: 3 }]));104});105106test('foo =~ /bar/', () => {107const input = 'foo =~ /bar/';108assert.deepStrictEqual(scan(input), ([{ type: 'Str', lexeme: 'foo', offset: 0 }, { type: '=~', offset: 4 }, { type: 'RegexStr', lexeme: '/bar/', offset: 7 }, { type: 'EOF', offset: 12 }]));109});110111test('foo =~ /zee/i', () => {112const input = 'foo =~ /zee/i';113assert.deepStrictEqual(scan(input), ([{ type: 'Str', lexeme: 'foo', offset: 0 }, { type: '=~', offset: 4 }, { type: 'RegexStr', lexeme: '/zee/i', offset: 7 }, { type: 'EOF', offset: 13 }]));114});115116117test('foo =~ /zee/gm', () => {118const input = 'foo =~ /zee/gm';119assert.deepStrictEqual(scan(input), ([{ type: 'Str', lexeme: 'foo', offset: 0 }, { type: '=~', offset: 4 }, { type: 'RegexStr', lexeme: '/zee/gm', offset: 7 }, { type: 'EOF', offset: 14 }]));120});121122test('foo in barrr ', () => {123const input = 'foo in barrr ';124assert.deepStrictEqual(scan(input), ([{ type: 'Str', lexeme: 'foo', offset: 0 }, { type: 'in', offset: 4 }, { type: 'Str', lexeme: 'barrr', offset: 7 }, { type: 'EOF', offset: 14 }]));125});126127test(`resource =~ //FileCabinet/(SuiteScripts|Templates/(E-mail%20Templates|Marketing%20Templates)|Web%20Site%20Hosting%20Files)(/.*)*$/`, () => {128const input = `resource =~ //FileCabinet/(SuiteScripts|Templates/(E-mail%20Templates|Marketing%20Templates)|Web%20Site%20Hosting%20Files)(/.*)*$/`;129assert.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 }]));130});131132test('editorLangId in testely.supportedLangIds && resourceFilename =~ /^.+(.test.(\w+))$/gm', () => {133const input = 'editorLangId in testely.supportedLangIds && resourceFilename =~ /^.+(.test.(\w+))$/gm';134assert.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 }]));135});136137test('!(foo && bar) && baz', () => {138const input = '!(foo && bar) && baz';139assert.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 }]));140});141142test('foo.bar:zed==completed - equality with no space', () => {143const input = 'foo.bar:zed==completed';144assert.deepStrictEqual(scan(input), ([{ type: 'Str', lexeme: 'foo.bar:zed', offset: 0 }, { type: '==', offset: 11 }, { type: 'Str', lexeme: 'completed', offset: 13 }, { type: 'EOF', offset: 22 }]));145});146147test('a && b || c', () => {148const input = 'a && b || c';149assert.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 }]));150});151152test('fooBar && baz.jar && fee.bee<K-loo+1>', () => {153const input = 'fooBar && baz.jar && fee.bee<K-loo+1>';154assert.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 }]));155});156157test('foo.barBaz<C-r> < 2', () => {158const input = 'foo.barBaz<C-r> < 2';159assert.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 }]));160});161162test('foo.bar >= -1', () => {163const input = 'foo.bar >= -1';164assert.deepStrictEqual(scan(input), ([{ type: 'Str', lexeme: 'foo.bar', offset: 0 }, { type: '>=', offset: 8 }, { type: 'Str', lexeme: '-1', offset: 11 }, { type: 'EOF', offset: 13 }]));165});166167test('foo.bar <= -1', () => {168const input = 'foo.bar <= -1';169assert.deepStrictEqual(scan(input), ([{ type: 'Str', lexeme: 'foo.bar', offset: 0 }, { type: '<=', offset: 8 }, { type: 'Str', lexeme: '-1', offset: 11 }, { type: 'EOF', offset: 13 }]));170});171172test(`resource =~ /\\/Objects\\/.+\\.xml$/`, () => {173const input = `resource =~ /\\/Objects\\/.+\\.xml$/`;174assert.deepStrictEqual(scan(input), ([{ type: 'Str', lexeme: 'resource', offset: 0 }, { type: '=~', offset: 9 }, { type: 'RegexStr', lexeme: '/\\/Objects\\/.+\\.xml$/', offset: 12 }, { type: 'EOF', offset: 33 }]));175});176177test('view == vsc-packages-activitybar-foldersĀ && vsc-packages-folders-loaded', () => {178const input = `view == vsc-packages-activitybar-foldersĀ && vsc-packages-folders-loaded`;179assert.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 }]));180});181182test(`sfdx:project_opened && resource =~ /.*\\/functions\\/.*\\/[^\\/]+(\\/[^\\/]+\.(ts|js|java|json|toml))?$/ && resourceFilename != package.json && resourceFilename != package-lock.json && resourceFilename != tsconfig.json`, () => {183const input = `sfdx:project_opened && resource =~ /.*\\/functions\\/.*\\/[^\\/]+(\\/[^\\/]+\.(ts|js|java|json|toml))?$/ && resourceFilename != package.json && resourceFilename != package-lock.json && resourceFilename != tsconfig.json`;184assert.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 }]));185});186187test(`view =~ '/(servers)/' && viewItem =~ '/^(Starting|Started|Debugging|Stopping|Stopped)/'`, () => {188const input = `view =~ '/(servers)/' && viewItem =~ '/^(Starting|Started|Debugging|Stopping|Stopped)/'`;189assert.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 }]));190});191192test(`resourcePath =~ /\.md(\.yml|\.txt)*$/gim`, () => {193const input = `resourcePath =~ /\.md(\.yml|\.txt)*$/gim`;194assert.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 }]));195});196});197198test(`foo === bar'`, () => {199const input = `foo === bar'`;200assert.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 }]));201});202203suite('handling lexical errors', () => {204205test(`foo === '`, () => {206const input = `foo === '`;207assert.deepStrictEqual(scan(input), ([{ type: 'Str', offset: 0, lexeme: 'foo' }, { type: '===', offset: 4 }, { type: 'ErrorToken', offset: 8, lexeme: `'` }, { type: 'EOF', offset: 9 }]));208});209210test(`foo && 'bar - unterminated single quote`, () => {211const input = `foo && 'bar`;212assert.deepStrictEqual(scan(input), ([{ type: 'Str', lexeme: 'foo', offset: 0 }, { type: '&&', offset: 4 }, { type: 'ErrorToken', offset: 7, lexeme: `'bar` }, { type: 'EOF', offset: 11 }]));213});214215test('vim<c-r> == 1 && vim<2 <= 3', () => {216const input = 'vim<c-r> == 1 && vim<2 <= 3';217assert.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 }]));218});219220test('vim<c-r>==1 && vim<2<=3', () => {221const input = 'vim<c-r>==1 && vim<2<=3';222assert.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 }]));223});224225test(`foo|bar`, () => {226const input = `foo|bar`;227assert.deepStrictEqual(scan(input), ([{ type: 'Str', offset: 0, lexeme: 'foo' }, { type: 'ErrorToken', offset: 3, lexeme: '|' }, { type: 'Str', offset: 4, lexeme: 'bar' }, { type: 'EOF', offset: 7 }]));228});229230test(`resource =~ //foo/(barr|door/(Foo-Bar%20Templates|Soo%20Looo)|Web%20Site%Jjj%20Llll)(/.*)*$/`, () => {231const input = `resource =~ //foo/(barr|door/(Foo-Bar%20Templates|Soo%20Looo)|Web%20Site%Jjj%20Llll)(/.*)*$/`;232assert.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 }]));233});234235test(`/((/foo/(?!bar)(.*)/)|((/src/).*/)).*$/`, () => {236const input = `/((/foo/(?!bar)(.*)/)|((/src/).*/)).*$/`;237assert.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 }]));238});239240test(`resourcePath =~ //foo/barr// || resourcePath =~ //view/(jarrr|doooor|bees)/(web|templates)// && resourceExtname in foo.Bar`, () => {241const input = `resourcePath =~ //foo/barr// || resourcePath =~ //view/(jarrr|doooor|bees)/(web|templates)// && resourceExtname in foo.Bar`;242assert.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 }]));243});244245test(`foo =~ /file:\// || bar`, () => {246const input = JSON.parse('"foo =~ /file:\// || bar"');247assert.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 }]));248});249});250});251252253