Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/brackets.ts
3296 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 { escapeRegExpCharacters } from '../../../../../base/common/strings.js';
6
import { ResolvedLanguageConfiguration } from '../../../languages/languageConfigurationRegistry.js';
7
import { BracketKind } from '../../../languages/supports/languageBracketsConfiguration.js';
8
import { BracketAstNode } from './ast.js';
9
import { toLength } from './length.js';
10
import { DenseKeyProvider, identityKeyProvider, SmallImmutableSet } from './smallImmutableSet.js';
11
import { OpeningBracketId, Token, TokenKind } from './tokenizer.js';
12
13
export class BracketTokens {
14
static createFromLanguage(configuration: ResolvedLanguageConfiguration, denseKeyProvider: DenseKeyProvider<string>): BracketTokens {
15
function getId(bracketInfo: BracketKind): OpeningBracketId {
16
return denseKeyProvider.getKey(`${bracketInfo.languageId}:::${bracketInfo.bracketText}`);
17
}
18
19
const map = new Map<string, Token>();
20
for (const openingBracket of configuration.bracketsNew.openingBrackets) {
21
const length = toLength(0, openingBracket.bracketText.length);
22
const openingTextId = getId(openingBracket);
23
const bracketIds = SmallImmutableSet.getEmpty().add(openingTextId, identityKeyProvider);
24
map.set(openingBracket.bracketText, new Token(
25
length,
26
TokenKind.OpeningBracket,
27
openingTextId,
28
bracketIds,
29
BracketAstNode.create(length, openingBracket, bracketIds)
30
));
31
}
32
33
for (const closingBracket of configuration.bracketsNew.closingBrackets) {
34
const length = toLength(0, closingBracket.bracketText.length);
35
let bracketIds = SmallImmutableSet.getEmpty();
36
const closingBrackets = closingBracket.getOpeningBrackets();
37
for (const bracket of closingBrackets) {
38
bracketIds = bracketIds.add(getId(bracket), identityKeyProvider);
39
}
40
map.set(closingBracket.bracketText, new Token(
41
length,
42
TokenKind.ClosingBracket,
43
getId(closingBrackets[0]),
44
bracketIds,
45
BracketAstNode.create(length, closingBracket, bracketIds)
46
));
47
}
48
49
return new BracketTokens(map);
50
}
51
52
private hasRegExp = false;
53
private _regExpGlobal: RegExp | null = null;
54
55
constructor(
56
private readonly map: Map<string, Token>
57
) { }
58
59
getRegExpStr(): string | null {
60
if (this.isEmpty) {
61
return null;
62
} else {
63
const keys = [...this.map.keys()];
64
keys.sort();
65
keys.reverse();
66
return keys.map(k => prepareBracketForRegExp(k)).join('|');
67
}
68
}
69
70
/**
71
* Returns null if there is no such regexp (because there are no brackets).
72
*/
73
get regExpGlobal(): RegExp | null {
74
if (!this.hasRegExp) {
75
const regExpStr = this.getRegExpStr();
76
this._regExpGlobal = regExpStr ? new RegExp(regExpStr, 'gi') : null;
77
this.hasRegExp = true;
78
}
79
return this._regExpGlobal;
80
}
81
82
getToken(value: string): Token | undefined {
83
return this.map.get(value.toLowerCase());
84
}
85
86
findClosingTokenText(openingBracketIds: SmallImmutableSet<OpeningBracketId>): string | undefined {
87
for (const [closingText, info] of this.map) {
88
if (info.kind === TokenKind.ClosingBracket && info.bracketIds.intersects(openingBracketIds)) {
89
return closingText;
90
}
91
}
92
return undefined;
93
}
94
95
get isEmpty(): boolean {
96
return this.map.size === 0;
97
}
98
}
99
100
function prepareBracketForRegExp(str: string): string {
101
let escaped = escapeRegExpCharacters(str);
102
// These bracket pair delimiters start or end with letters
103
// see https://github.com/microsoft/vscode/issues/132162 https://github.com/microsoft/vscode/issues/150440
104
if (/^[\w ]+/.test(str)) {
105
escaped = `\\b${escaped}`;
106
}
107
if (/[\w ]+$/.test(str)) {
108
escaped = `${escaped}\\b`;
109
}
110
return escaped;
111
}
112
113
export class LanguageAgnosticBracketTokens {
114
private readonly languageIdToBracketTokens = new Map<string, BracketTokens>();
115
116
constructor(
117
private readonly denseKeyProvider: DenseKeyProvider<string>,
118
private readonly getLanguageConfiguration: (languageId: string) => ResolvedLanguageConfiguration,
119
) {
120
}
121
122
public didLanguageChange(languageId: string): boolean {
123
// Report a change whenever the language configuration updates.
124
return this.languageIdToBracketTokens.has(languageId);
125
}
126
127
getSingleLanguageBracketTokens(languageId: string): BracketTokens {
128
let singleLanguageBracketTokens = this.languageIdToBracketTokens.get(languageId);
129
if (!singleLanguageBracketTokens) {
130
singleLanguageBracketTokens = BracketTokens.createFromLanguage(this.getLanguageConfiguration(languageId), this.denseKeyProvider);
131
this.languageIdToBracketTokens.set(languageId, singleLanguageBracketTokens);
132
}
133
return singleLanguageBracketTokens;
134
}
135
136
getToken(value: string, languageId: string): Token | undefined {
137
const singleLanguageBracketTokens = this.getSingleLanguageBracketTokens(languageId);
138
return singleLanguageBracketTokens.getToken(value);
139
}
140
}
141
142