This commit is contained in:
2025-11-02 19:17:20 +08:00
parent ebf784146e
commit e71b69db5f
2575 changed files with 1242294 additions and 95 deletions

View File

@@ -0,0 +1,467 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
import { Event, PauseableEmitter } from '../../../base/common/event.js';
import { Iterable } from '../../../base/common/iterator.js';
import { Disposable, DisposableStore, MutableDisposable } from '../../../base/common/lifecycle.js';
import { cloneAndChange } from '../../../base/common/objects.js';
import { TernarySearchTree } from '../../../base/common/ternarySearchTree.js';
import { URI } from '../../../base/common/uri.js';
import { localize } from '../../../nls.js';
import { CommandsRegistry } from '../../commands/common/commands.js';
import { IConfigurationService } from '../../configuration/common/configuration.js';
import { IContextKeyService, RawContextKey } from '../common/contextkey.js';
import { InputFocusedContext } from '../common/contextkeys.js';
import { mainWindow } from '../../../base/browser/window.js';
import { addDisposableListener, EventType, getActiveWindow, isEditableElement, onDidRegisterWindow, trackFocus } from '../../../base/browser/dom.js';
const KEYBINDING_CONTEXT_ATTR = 'data-keybinding-context';
export class Context {
constructor(id, parent) {
this._id = id;
this._parent = parent;
this._value = Object.create(null);
this._value['_contextId'] = id;
}
get value() {
return { ...this._value };
}
setValue(key, value) {
// console.log('SET ' + key + ' = ' + value + ' ON ' + this._id);
if (this._value[key] !== value) {
this._value[key] = value;
return true;
}
return false;
}
removeValue(key) {
// console.log('REMOVE ' + key + ' FROM ' + this._id);
if (key in this._value) {
delete this._value[key];
return true;
}
return false;
}
getValue(key) {
const ret = this._value[key];
if (typeof ret === 'undefined' && this._parent) {
return this._parent.getValue(key);
}
return ret;
}
}
class NullContext extends Context {
static { this.INSTANCE = new NullContext(); }
constructor() {
super(-1, null);
}
setValue(key, value) {
return false;
}
removeValue(key) {
return false;
}
getValue(key) {
return undefined;
}
}
class ConfigAwareContextValuesContainer extends Context {
static { this._keyPrefix = 'config.'; }
constructor(id, _configurationService, emitter) {
super(id, null);
this._configurationService = _configurationService;
this._values = TernarySearchTree.forConfigKeys();
this._listener = this._configurationService.onDidChangeConfiguration(event => {
if (event.source === 7 /* ConfigurationTarget.DEFAULT */) {
// new setting, reset everything
const allKeys = Array.from(this._values, ([k]) => k);
this._values.clear();
emitter.fire(new ArrayContextKeyChangeEvent(allKeys));
}
else {
const changedKeys = [];
for (const configKey of event.affectedKeys) {
const contextKey = `config.${configKey}`;
const cachedItems = this._values.findSuperstr(contextKey);
if (cachedItems !== undefined) {
changedKeys.push(...Iterable.map(cachedItems, ([key]) => key));
this._values.deleteSuperstr(contextKey);
}
if (this._values.has(contextKey)) {
changedKeys.push(contextKey);
this._values.delete(contextKey);
}
}
emitter.fire(new ArrayContextKeyChangeEvent(changedKeys));
}
});
}
dispose() {
this._listener.dispose();
}
getValue(key) {
if (key.indexOf(ConfigAwareContextValuesContainer._keyPrefix) !== 0) {
return super.getValue(key);
}
if (this._values.has(key)) {
return this._values.get(key);
}
const configKey = key.substr(ConfigAwareContextValuesContainer._keyPrefix.length);
const configValue = this._configurationService.getValue(configKey);
let value = undefined;
switch (typeof configValue) {
case 'number':
case 'boolean':
case 'string':
value = configValue;
break;
default:
if (Array.isArray(configValue)) {
value = JSON.stringify(configValue);
}
else {
value = configValue;
}
}
this._values.set(key, value);
return value;
}
setValue(key, value) {
return super.setValue(key, value);
}
removeValue(key) {
return super.removeValue(key);
}
}
class ContextKey {
constructor(service, key, defaultValue) {
this._service = service;
this._key = key;
this._defaultValue = defaultValue;
this.reset();
}
set(value) {
this._service.setContext(this._key, value);
}
reset() {
if (typeof this._defaultValue === 'undefined') {
this._service.removeContext(this._key);
}
else {
this._service.setContext(this._key, this._defaultValue);
}
}
get() {
return this._service.getContextKeyValue(this._key);
}
}
class SimpleContextKeyChangeEvent {
constructor(key) {
this.key = key;
}
affectsSome(keys) {
return keys.has(this.key);
}
allKeysContainedIn(keys) {
return this.affectsSome(keys);
}
}
class ArrayContextKeyChangeEvent {
constructor(keys) {
this.keys = keys;
}
affectsSome(keys) {
for (const key of this.keys) {
if (keys.has(key)) {
return true;
}
}
return false;
}
allKeysContainedIn(keys) {
return this.keys.every(key => keys.has(key));
}
}
class CompositeContextKeyChangeEvent {
constructor(events) {
this.events = events;
}
affectsSome(keys) {
for (const e of this.events) {
if (e.affectsSome(keys)) {
return true;
}
}
return false;
}
allKeysContainedIn(keys) {
return this.events.every(evt => evt.allKeysContainedIn(keys));
}
}
function allEventKeysInContext(event, context) {
return event.allKeysContainedIn(new Set(Object.keys(context)));
}
export class AbstractContextKeyService extends Disposable {
get onDidChangeContext() { return this._onDidChangeContext.event; }
constructor(myContextId) {
super();
this._onDidChangeContext = this._register(new PauseableEmitter({ merge: input => new CompositeContextKeyChangeEvent(input) }));
this._isDisposed = false;
this._myContextId = myContextId;
}
createKey(key, defaultValue) {
if (this._isDisposed) {
throw new Error(`AbstractContextKeyService has been disposed`);
}
return new ContextKey(this, key, defaultValue);
}
bufferChangeEvents(callback) {
this._onDidChangeContext.pause();
try {
callback();
}
finally {
this._onDidChangeContext.resume();
}
}
createScoped(domNode) {
if (this._isDisposed) {
throw new Error(`AbstractContextKeyService has been disposed`);
}
return new ScopedContextKeyService(this, domNode);
}
contextMatchesRules(rules) {
if (this._isDisposed) {
throw new Error(`AbstractContextKeyService has been disposed`);
}
const context = this.getContextValuesContainer(this._myContextId);
const result = (rules ? rules.evaluate(context) : true);
// console.group(rules.serialize() + ' -> ' + result);
// rules.keys().forEach(key => { console.log(key, ctx[key]); });
// console.groupEnd();
return result;
}
getContextKeyValue(key) {
if (this._isDisposed) {
return undefined;
}
return this.getContextValuesContainer(this._myContextId).getValue(key);
}
setContext(key, value) {
if (this._isDisposed) {
return;
}
const myContext = this.getContextValuesContainer(this._myContextId);
if (!myContext) {
return;
}
if (myContext.setValue(key, value)) {
this._onDidChangeContext.fire(new SimpleContextKeyChangeEvent(key));
}
}
removeContext(key) {
if (this._isDisposed) {
return;
}
if (this.getContextValuesContainer(this._myContextId).removeValue(key)) {
this._onDidChangeContext.fire(new SimpleContextKeyChangeEvent(key));
}
}
getContext(target) {
if (this._isDisposed) {
return NullContext.INSTANCE;
}
return this.getContextValuesContainer(findContextAttr(target));
}
dispose() {
super.dispose();
this._isDisposed = true;
}
}
let ContextKeyService = class ContextKeyService extends AbstractContextKeyService {
constructor(configurationService) {
super(0);
this._contexts = new Map();
this._lastContextId = 0;
this.inputFocusedContext = InputFocusedContext.bindTo(this);
const myContext = this._register(new ConfigAwareContextValuesContainer(this._myContextId, configurationService, this._onDidChangeContext));
this._contexts.set(this._myContextId, myContext);
// Uncomment this to see the contexts continuously logged
// let lastLoggedValue: string | null = null;
// setInterval(() => {
// let values = Object.keys(this._contexts).map((key) => this._contexts[key]);
// let logValue = values.map(v => JSON.stringify(v._value, null, '\t')).join('\n');
// if (lastLoggedValue !== logValue) {
// lastLoggedValue = logValue;
// console.log(lastLoggedValue);
// }
// }, 2000);
this._register(Event.runAndSubscribe(onDidRegisterWindow, ({ window, disposables }) => {
const onFocusDisposables = disposables.add(new MutableDisposable());
disposables.add(addDisposableListener(window, EventType.FOCUS_IN, () => {
onFocusDisposables.value = new DisposableStore();
this.updateInputContextKeys(window.document, onFocusDisposables.value);
}, true));
}, { window: mainWindow, disposables: this._store }));
}
updateInputContextKeys(ownerDocument, disposables) {
function activeElementIsInput() {
return !!ownerDocument.activeElement && isEditableElement(ownerDocument.activeElement);
}
const isInputFocused = activeElementIsInput();
this.inputFocusedContext.set(isInputFocused);
if (isInputFocused) {
const tracker = disposables.add(trackFocus(ownerDocument.activeElement));
Event.once(tracker.onDidBlur)(() => {
// Ensure we are only updating the context key if we are
// still in the same document that we are tracking. This
// fixes a race condition in multi-window setups where
// the blur event arrives in the inactive window overwriting
// the context key of the active window. This is because
// blur events from the focus tracker are emitted with a
// timeout of 0.
if (getActiveWindow().document === ownerDocument) {
this.inputFocusedContext.set(activeElementIsInput());
}
tracker.dispose();
}, undefined, disposables);
}
}
getContextValuesContainer(contextId) {
if (this._isDisposed) {
return NullContext.INSTANCE;
}
return this._contexts.get(contextId) || NullContext.INSTANCE;
}
createChildContext(parentContextId = this._myContextId) {
if (this._isDisposed) {
throw new Error(`ContextKeyService has been disposed`);
}
const id = (++this._lastContextId);
this._contexts.set(id, new Context(id, this.getContextValuesContainer(parentContextId)));
return id;
}
disposeContext(contextId) {
if (!this._isDisposed) {
this._contexts.delete(contextId);
}
}
};
ContextKeyService = __decorate([
__param(0, IConfigurationService)
], ContextKeyService);
export { ContextKeyService };
class ScopedContextKeyService extends AbstractContextKeyService {
constructor(parent, domNode) {
super(parent.createChildContext());
this._parentChangeListener = this._register(new MutableDisposable());
this._parent = parent;
this._updateParentChangeListener();
this._domNode = domNode;
if (this._domNode.hasAttribute(KEYBINDING_CONTEXT_ATTR)) {
let extraInfo = '';
if (this._domNode.classList) {
extraInfo = Array.from(this._domNode.classList.values()).join(', ');
}
console.error(`Element already has context attribute${extraInfo ? ': ' + extraInfo : ''}`);
}
this._domNode.setAttribute(KEYBINDING_CONTEXT_ATTR, String(this._myContextId));
}
_updateParentChangeListener() {
// Forward parent events to this listener. Parent will change.
this._parentChangeListener.value = this._parent.onDidChangeContext(e => {
const thisContainer = this._parent.getContextValuesContainer(this._myContextId);
const thisContextValues = thisContainer.value;
if (!allEventKeysInContext(e, thisContextValues)) {
this._onDidChangeContext.fire(e);
}
});
}
dispose() {
if (this._isDisposed) {
return;
}
this._parent.disposeContext(this._myContextId);
this._domNode.removeAttribute(KEYBINDING_CONTEXT_ATTR);
super.dispose();
}
getContextValuesContainer(contextId) {
if (this._isDisposed) {
return NullContext.INSTANCE;
}
return this._parent.getContextValuesContainer(contextId);
}
createChildContext(parentContextId = this._myContextId) {
if (this._isDisposed) {
throw new Error(`ScopedContextKeyService has been disposed`);
}
return this._parent.createChildContext(parentContextId);
}
disposeContext(contextId) {
if (this._isDisposed) {
return;
}
this._parent.disposeContext(contextId);
}
}
function findContextAttr(domNode) {
while (domNode) {
if (domNode.hasAttribute(KEYBINDING_CONTEXT_ATTR)) {
const attr = domNode.getAttribute(KEYBINDING_CONTEXT_ATTR);
if (attr) {
return parseInt(attr, 10);
}
return NaN;
}
domNode = domNode.parentElement;
}
return 0;
}
export function setContext(accessor, contextKey, contextValue) {
const contextKeyService = accessor.get(IContextKeyService);
contextKeyService.createKey(String(contextKey), stringifyURIs(contextValue));
}
function stringifyURIs(contextValue) {
return cloneAndChange(contextValue, (obj) => {
if (typeof obj === 'object' && obj.$mid === 1 /* MarshalledId.Uri */) {
return URI.revive(obj).toString();
}
if (obj instanceof URI) {
return obj.toString();
}
return undefined;
});
}
CommandsRegistry.registerCommand('_setContext', setContext);
CommandsRegistry.registerCommand({
id: 'getContextKeyInfo',
handler() {
return [...RawContextKey.all()].sort((a, b) => a.key.localeCompare(b.key));
},
metadata: {
description: localize(1659, "A command that returns information about context keys"),
args: []
}
});
CommandsRegistry.registerCommand('_generateContextKeyInfo', function () {
const result = [];
const seen = new Set();
for (const info of RawContextKey.all()) {
if (!seen.has(info.key)) {
seen.add(info.key);
result.push(info);
}
}
result.sort((a, b) => a.key.localeCompare(b.key));
console.log(JSON.stringify(result, undefined, 2));
});
//# sourceMappingURL=contextKeyService.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,19 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { isIOS, isLinux, isMacintosh, isMobile, isWeb, isWindows } from '../../../base/common/platform.js';
import { localize } from '../../../nls.js';
import { RawContextKey } from './contextkey.js';
export const IsMacContext = new RawContextKey('isMac', isMacintosh, localize(1669, "Whether the operating system is macOS"));
export const IsLinuxContext = new RawContextKey('isLinux', isLinux, localize(1670, "Whether the operating system is Linux"));
export const IsWindowsContext = new RawContextKey('isWindows', isWindows, localize(1671, "Whether the operating system is Windows"));
export const IsWebContext = new RawContextKey('isWeb', isWeb, localize(1672, "Whether the platform is a web browser"));
export const IsMacNativeContext = new RawContextKey('isMacNative', isMacintosh && !isWeb, localize(1673, "Whether the operating system is macOS on a non-browser platform"));
export const IsIOSContext = new RawContextKey('isIOS', isIOS, localize(1674, "Whether the operating system is iOS"));
export const IsMobileContext = new RawContextKey('isMobile', isMobile, localize(1675, "Whether the platform is a mobile web browser"));
export const IsDevelopmentContext = new RawContextKey('isDevelopment', false, true);
export const ProductQualityContext = new RawContextKey('productQualityType', '', localize(1676, "Quality type of VS Code"));
export const InputFocusedContextKey = 'inputFocus';
export const InputFocusedContext = new RawContextKey(InputFocusedContextKey, false, localize(1677, "Whether keyboard focus is inside an input box"));
//# sourceMappingURL=contextkeys.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["vs/platform/contextkey/common/contextkeys.ts"],"names":[],"mappings":"AAAA;;;gGAGgG;AAEhG,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAC3G,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,aAAa,CAAU,OAAO,EAAE,WAAW,EAAE,QAAQ,CAAC,IAAO,EAAE,uCAAuC,CAAC,CAAC,CAAC;AACzI,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,aAAa,CAAU,SAAS,EAAE,OAAO,EAAE,QAAQ,CAAC,IAAS,EAAE,uCAAuC,CAAC,CAAC,CAAC;AAC3I,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,aAAa,CAAU,WAAW,EAAE,SAAS,EAAE,QAAQ,CAAC,IAAW,EAAE,yCAAyC,CAAC,CAAC,CAAC;AAErJ,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,aAAa,CAAU,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,IAAO,EAAE,uCAAuC,CAAC,CAAC,CAAC;AACnI,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,aAAa,CAAU,aAAa,EAAE,WAAW,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAa,EAAE,iEAAiE,CAAC,CAAC,CAAC;AAC/L,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,aAAa,CAAU,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC,IAAO,EAAE,qCAAqC,CAAC,CAAC,CAAC;AACjI,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,aAAa,CAAU,UAAU,EAAE,QAAQ,EAAE,QAAQ,CAAC,IAAU,EAAE,8CAA8C,CAAC,CAAC,CAAC;AAEtJ,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,aAAa,CAAU,eAAe,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;AAC7F,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,aAAa,CAAS,oBAAoB,EAAE,EAAE,EAAE,QAAQ,CAAC,IAAoB,EAAE,yBAAyB,CAAC,CAAC,CAAC;AAEpJ,MAAM,CAAC,MAAM,sBAAsB,GAAG,YAAY,CAAC;AACnD,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,aAAa,CAAU,sBAAsB,EAAE,KAAK,EAAE,QAAQ,CAAC,IAAY,EAAE,+CAA+C,CAAC,CAAC,CAAC","file":"contextkeys.js","sourceRoot":"file:///mnt/vss/_work/1/s/dependencies/vscode/out-editor-src","sourcesContent":["/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\n\nimport { isIOS, isLinux, isMacintosh, isMobile, isWeb, isWindows } from '../../../base/common/platform.js';\nimport { localize } from '../../../nls.js';\nimport { RawContextKey } from './contextkey.js';\n\nexport const IsMacContext = new RawContextKey<boolean>('isMac', isMacintosh, localize('isMac', \"Whether the operating system is macOS\"));\nexport const IsLinuxContext = new RawContextKey<boolean>('isLinux', isLinux, localize('isLinux', \"Whether the operating system is Linux\"));\nexport const IsWindowsContext = new RawContextKey<boolean>('isWindows', isWindows, localize('isWindows', \"Whether the operating system is Windows\"));\n\nexport const IsWebContext = new RawContextKey<boolean>('isWeb', isWeb, localize('isWeb', \"Whether the platform is a web browser\"));\nexport const IsMacNativeContext = new RawContextKey<boolean>('isMacNative', isMacintosh && !isWeb, localize('isMacNative', \"Whether the operating system is macOS on a non-browser platform\"));\nexport const IsIOSContext = new RawContextKey<boolean>('isIOS', isIOS, localize('isIOS', \"Whether the operating system is iOS\"));\nexport const IsMobileContext = new RawContextKey<boolean>('isMobile', isMobile, localize('isMobile', \"Whether the platform is a mobile web browser\"));\n\nexport const IsDevelopmentContext = new RawContextKey<boolean>('isDevelopment', false, true);\nexport const ProductQualityContext = new RawContextKey<string>('productQualityType', '', localize('productQualityType', \"Quality type of VS Code\"));\n\nexport const InputFocusedContextKey = 'inputFocus';\nexport const InputFocusedContext = new RawContextKey<boolean>(InputFocusedContextKey, false, localize('inputFocus', \"Whether keyboard focus is inside an input box\"));\n"]}

View File

@@ -0,0 +1,285 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { illegalState } from '../../../base/common/errors.js';
import { localize } from '../../../nls.js';
function hintDidYouMean(...meant) {
switch (meant.length) {
case 1:
return localize(1678, "Did you mean {0}?", meant[0]);
case 2:
return localize(1679, "Did you mean {0} or {1}?", meant[0], meant[1]);
case 3:
return localize(1680, "Did you mean {0}, {1} or {2}?", meant[0], meant[1], meant[2]);
default: // we just don't expect that many
return undefined;
}
}
const hintDidYouForgetToOpenOrCloseQuote = localize(1681, "Did you forget to open or close the quote?");
const hintDidYouForgetToEscapeSlash = localize(1682, "Did you forget to escape the '/' (slash) character? Put two backslashes before it to escape, e.g., '\\\\/\'.");
/**
* A simple scanner for context keys.
*
* Example:
*
* ```ts
* const scanner = new Scanner().reset('resourceFileName =~ /docker/ && !config.docker.enabled');
* const tokens = [...scanner];
* if (scanner.errorTokens.length > 0) {
* scanner.errorTokens.forEach(err => console.error(`Unexpected token at ${err.offset}: ${err.lexeme}\nHint: ${err.additional}`));
* } else {
* // process tokens
* }
* ```
*/
export class Scanner {
constructor() {
this._input = '';
this._start = 0;
this._current = 0;
this._tokens = [];
this._errors = [];
// u - unicode, y - sticky // TODO@ulugbekna: we accept double quotes as part of the string rather than as a delimiter (to preserve old parser's behavior)
this.stringRe = /[a-zA-Z0-9_<>\-\./\\:\*\?\+\[\]\^,#@;"%\$\p{L}-]+/uy;
}
static getLexeme(token) {
switch (token.type) {
case 0 /* TokenType.LParen */:
return '(';
case 1 /* TokenType.RParen */:
return ')';
case 2 /* TokenType.Neg */:
return '!';
case 3 /* TokenType.Eq */:
return token.isTripleEq ? '===' : '==';
case 4 /* TokenType.NotEq */:
return token.isTripleEq ? '!==' : '!=';
case 5 /* TokenType.Lt */:
return '<';
case 6 /* TokenType.LtEq */:
return '<=';
case 7 /* TokenType.Gt */:
return '>=';
case 8 /* TokenType.GtEq */:
return '>=';
case 9 /* TokenType.RegexOp */:
return '=~';
case 10 /* TokenType.RegexStr */:
return token.lexeme;
case 11 /* TokenType.True */:
return 'true';
case 12 /* TokenType.False */:
return 'false';
case 13 /* TokenType.In */:
return 'in';
case 14 /* TokenType.Not */:
return 'not';
case 15 /* TokenType.And */:
return '&&';
case 16 /* TokenType.Or */:
return '||';
case 17 /* TokenType.Str */:
return token.lexeme;
case 18 /* TokenType.QuotedStr */:
return token.lexeme;
case 19 /* TokenType.Error */:
return token.lexeme;
case 20 /* TokenType.EOF */:
return 'EOF';
default:
throw illegalState(`unhandled token type: ${JSON.stringify(token)}; have you forgotten to add a case?`);
}
}
static { this._regexFlags = new Set(['i', 'g', 's', 'm', 'y', 'u'].map(ch => ch.charCodeAt(0))); }
static { this._keywords = new Map([
['not', 14 /* TokenType.Not */],
['in', 13 /* TokenType.In */],
['false', 12 /* TokenType.False */],
['true', 11 /* TokenType.True */],
]); }
reset(value) {
this._input = value;
this._start = 0;
this._current = 0;
this._tokens = [];
this._errors = [];
return this;
}
scan() {
while (!this._isAtEnd()) {
this._start = this._current;
const ch = this._advance();
switch (ch) {
case 40 /* CharCode.OpenParen */:
this._addToken(0 /* TokenType.LParen */);
break;
case 41 /* CharCode.CloseParen */:
this._addToken(1 /* TokenType.RParen */);
break;
case 33 /* CharCode.ExclamationMark */:
if (this._match(61 /* CharCode.Equals */)) {
const isTripleEq = this._match(61 /* CharCode.Equals */); // eat last `=` if `!==`
this._tokens.push({ type: 4 /* TokenType.NotEq */, offset: this._start, isTripleEq });
}
else {
this._addToken(2 /* TokenType.Neg */);
}
break;
case 39 /* CharCode.SingleQuote */:
this._quotedString();
break;
case 47 /* CharCode.Slash */:
this._regex();
break;
case 61 /* CharCode.Equals */:
if (this._match(61 /* CharCode.Equals */)) { // support `==`
const isTripleEq = this._match(61 /* CharCode.Equals */); // eat last `=` if `===`
this._tokens.push({ type: 3 /* TokenType.Eq */, offset: this._start, isTripleEq });
}
else if (this._match(126 /* CharCode.Tilde */)) {
this._addToken(9 /* TokenType.RegexOp */);
}
else {
this._error(hintDidYouMean('==', '=~'));
}
break;
case 60 /* CharCode.LessThan */:
this._addToken(this._match(61 /* CharCode.Equals */) ? 6 /* TokenType.LtEq */ : 5 /* TokenType.Lt */);
break;
case 62 /* CharCode.GreaterThan */:
this._addToken(this._match(61 /* CharCode.Equals */) ? 8 /* TokenType.GtEq */ : 7 /* TokenType.Gt */);
break;
case 38 /* CharCode.Ampersand */:
if (this._match(38 /* CharCode.Ampersand */)) {
this._addToken(15 /* TokenType.And */);
}
else {
this._error(hintDidYouMean('&&'));
}
break;
case 124 /* CharCode.Pipe */:
if (this._match(124 /* CharCode.Pipe */)) {
this._addToken(16 /* TokenType.Or */);
}
else {
this._error(hintDidYouMean('||'));
}
break;
// TODO@ulugbekna: 1) rewrite using a regex 2) reconsider what characters are considered whitespace, including unicode, nbsp, etc.
case 32 /* CharCode.Space */:
case 13 /* CharCode.CarriageReturn */:
case 9 /* CharCode.Tab */:
case 10 /* CharCode.LineFeed */:
case 160 /* CharCode.NoBreakSpace */: // &nbsp
break;
default:
this._string();
}
}
this._start = this._current;
this._addToken(20 /* TokenType.EOF */);
return Array.from(this._tokens);
}
_match(expected) {
if (this._isAtEnd()) {
return false;
}
if (this._input.charCodeAt(this._current) !== expected) {
return false;
}
this._current++;
return true;
}
_advance() {
return this._input.charCodeAt(this._current++);
}
_peek() {
return this._isAtEnd() ? 0 /* CharCode.Null */ : this._input.charCodeAt(this._current);
}
_addToken(type) {
this._tokens.push({ type, offset: this._start });
}
_error(additional) {
const offset = this._start;
const lexeme = this._input.substring(this._start, this._current);
const errToken = { type: 19 /* TokenType.Error */, offset: this._start, lexeme };
this._errors.push({ offset, lexeme, additionalInfo: additional });
this._tokens.push(errToken);
}
_string() {
this.stringRe.lastIndex = this._start;
const match = this.stringRe.exec(this._input);
if (match) {
this._current = this._start + match[0].length;
const lexeme = this._input.substring(this._start, this._current);
const keyword = Scanner._keywords.get(lexeme);
if (keyword) {
this._addToken(keyword);
}
else {
this._tokens.push({ type: 17 /* TokenType.Str */, lexeme, offset: this._start });
}
}
}
// captures the lexeme without the leading and trailing '
_quotedString() {
while (this._peek() !== 39 /* CharCode.SingleQuote */ && !this._isAtEnd()) { // TODO@ulugbekna: add support for escaping ' ?
this._advance();
}
if (this._isAtEnd()) {
this._error(hintDidYouForgetToOpenOrCloseQuote);
return;
}
// consume the closing '
this._advance();
this._tokens.push({ type: 18 /* TokenType.QuotedStr */, lexeme: this._input.substring(this._start + 1, this._current - 1), offset: this._start + 1 });
}
/*
* Lexing a regex expression: /.../[igsmyu]*
* Based on https://github.com/microsoft/TypeScript/blob/9247ef115e617805983740ba795d7a8164babf89/src/compiler/scanner.ts#L2129-L2181
*
* Note that we want slashes within a regex to be escaped, e.g., /file:\\/\\/\\// should match `file:///`
*/
_regex() {
let p = this._current;
let inEscape = false;
let inCharacterClass = false;
while (true) {
if (p >= this._input.length) {
this._current = p;
this._error(hintDidYouForgetToEscapeSlash);
return;
}
const ch = this._input.charCodeAt(p);
if (inEscape) { // parsing an escape character
inEscape = false;
}
else if (ch === 47 /* CharCode.Slash */ && !inCharacterClass) { // end of regex
p++;
break;
}
else if (ch === 91 /* CharCode.OpenSquareBracket */) {
inCharacterClass = true;
}
else if (ch === 92 /* CharCode.Backslash */) {
inEscape = true;
}
else if (ch === 93 /* CharCode.CloseSquareBracket */) {
inCharacterClass = false;
}
p++;
}
// Consume flags // TODO@ulugbekna: use regex instead
while (p < this._input.length && Scanner._regexFlags.has(this._input.charCodeAt(p))) {
p++;
}
this._current = p;
const lexeme = this._input.substring(this._start, this._current);
this._tokens.push({ type: 10 /* TokenType.RegexStr */, lexeme, offset: this._start });
}
_isAtEnd() {
return this._current >= this._input.length;
}
}
//# sourceMappingURL=scanner.js.map

File diff suppressed because one or more lines are too long