Path: blob/main/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/brackets.ts
3296 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 { escapeRegExpCharacters } from '../../../../../base/common/strings.js';5import { ResolvedLanguageConfiguration } from '../../../languages/languageConfigurationRegistry.js';6import { BracketKind } from '../../../languages/supports/languageBracketsConfiguration.js';7import { BracketAstNode } from './ast.js';8import { toLength } from './length.js';9import { DenseKeyProvider, identityKeyProvider, SmallImmutableSet } from './smallImmutableSet.js';10import { OpeningBracketId, Token, TokenKind } from './tokenizer.js';1112export class BracketTokens {13static createFromLanguage(configuration: ResolvedLanguageConfiguration, denseKeyProvider: DenseKeyProvider<string>): BracketTokens {14function getId(bracketInfo: BracketKind): OpeningBracketId {15return denseKeyProvider.getKey(`${bracketInfo.languageId}:::${bracketInfo.bracketText}`);16}1718const map = new Map<string, Token>();19for (const openingBracket of configuration.bracketsNew.openingBrackets) {20const length = toLength(0, openingBracket.bracketText.length);21const openingTextId = getId(openingBracket);22const bracketIds = SmallImmutableSet.getEmpty().add(openingTextId, identityKeyProvider);23map.set(openingBracket.bracketText, new Token(24length,25TokenKind.OpeningBracket,26openingTextId,27bracketIds,28BracketAstNode.create(length, openingBracket, bracketIds)29));30}3132for (const closingBracket of configuration.bracketsNew.closingBrackets) {33const length = toLength(0, closingBracket.bracketText.length);34let bracketIds = SmallImmutableSet.getEmpty();35const closingBrackets = closingBracket.getOpeningBrackets();36for (const bracket of closingBrackets) {37bracketIds = bracketIds.add(getId(bracket), identityKeyProvider);38}39map.set(closingBracket.bracketText, new Token(40length,41TokenKind.ClosingBracket,42getId(closingBrackets[0]),43bracketIds,44BracketAstNode.create(length, closingBracket, bracketIds)45));46}4748return new BracketTokens(map);49}5051private hasRegExp = false;52private _regExpGlobal: RegExp | null = null;5354constructor(55private readonly map: Map<string, Token>56) { }5758getRegExpStr(): string | null {59if (this.isEmpty) {60return null;61} else {62const keys = [...this.map.keys()];63keys.sort();64keys.reverse();65return keys.map(k => prepareBracketForRegExp(k)).join('|');66}67}6869/**70* Returns null if there is no such regexp (because there are no brackets).71*/72get regExpGlobal(): RegExp | null {73if (!this.hasRegExp) {74const regExpStr = this.getRegExpStr();75this._regExpGlobal = regExpStr ? new RegExp(regExpStr, 'gi') : null;76this.hasRegExp = true;77}78return this._regExpGlobal;79}8081getToken(value: string): Token | undefined {82return this.map.get(value.toLowerCase());83}8485findClosingTokenText(openingBracketIds: SmallImmutableSet<OpeningBracketId>): string | undefined {86for (const [closingText, info] of this.map) {87if (info.kind === TokenKind.ClosingBracket && info.bracketIds.intersects(openingBracketIds)) {88return closingText;89}90}91return undefined;92}9394get isEmpty(): boolean {95return this.map.size === 0;96}97}9899function prepareBracketForRegExp(str: string): string {100let escaped = escapeRegExpCharacters(str);101// These bracket pair delimiters start or end with letters102// see https://github.com/microsoft/vscode/issues/132162 https://github.com/microsoft/vscode/issues/150440103if (/^[\w ]+/.test(str)) {104escaped = `\\b${escaped}`;105}106if (/[\w ]+$/.test(str)) {107escaped = `${escaped}\\b`;108}109return escaped;110}111112export class LanguageAgnosticBracketTokens {113private readonly languageIdToBracketTokens = new Map<string, BracketTokens>();114115constructor(116private readonly denseKeyProvider: DenseKeyProvider<string>,117private readonly getLanguageConfiguration: (languageId: string) => ResolvedLanguageConfiguration,118) {119}120121public didLanguageChange(languageId: string): boolean {122// Report a change whenever the language configuration updates.123return this.languageIdToBracketTokens.has(languageId);124}125126getSingleLanguageBracketTokens(languageId: string): BracketTokens {127let singleLanguageBracketTokens = this.languageIdToBracketTokens.get(languageId);128if (!singleLanguageBracketTokens) {129singleLanguageBracketTokens = BracketTokens.createFromLanguage(this.getLanguageConfiguration(languageId), this.denseKeyProvider);130this.languageIdToBracketTokens.set(languageId, singleLanguageBracketTokens);131}132return singleLanguageBracketTokens;133}134135getToken(value: string, languageId: string): Token | undefined {136const singleLanguageBracketTokens = this.getSingleLanguageBracketTokens(languageId);137return singleLanguageBracketTokens.getToken(value);138}139}140141142