Welcome to mirror list, hosted at ThFree Co, Russian Federation.

brackets.ts « bracketPairsTree « bracketPairsTextModelPart « model « common « editor « vs « src - github.com/microsoft/vscode.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 2673aeacb66ae70cf24d98189abaad35133823a0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
import { escapeRegExpCharacters } from 'vs/base/common/strings';
import { ResolvedLanguageConfiguration } from 'vs/editor/common/languages/languageConfigurationRegistry';
import { BracketKind } from 'vs/editor/common/languages/supports/languageBracketsConfiguration';
import { BracketAstNode } from './ast';
import { toLength } from './length';
import { DenseKeyProvider, identityKeyProvider, SmallImmutableSet } from './smallImmutableSet';
import { OpeningBracketId, Token, TokenKind } from './tokenizer';

export class BracketTokens {
	static createFromLanguage(configuration: ResolvedLanguageConfiguration, denseKeyProvider: DenseKeyProvider<string>): BracketTokens {
		function getId(bracketInfo: BracketKind): OpeningBracketId {
			return denseKeyProvider.getKey(`${bracketInfo.languageId}:::${bracketInfo.bracketText}`);
		}

		const map = new Map<string, Token>();
		for (const openingBracket of configuration.bracketsNew.openingBrackets) {
			const length = toLength(0, openingBracket.bracketText.length);
			const openingTextId = getId(openingBracket);
			const bracketIds = SmallImmutableSet.getEmpty().add(openingTextId, identityKeyProvider);
			map.set(openingBracket.bracketText, new Token(
				length,
				TokenKind.OpeningBracket,
				openingTextId,
				bracketIds,
				BracketAstNode.create(length, openingBracket, bracketIds)
			));
		}

		for (const closingBracket of configuration.bracketsNew.closingBrackets) {
			const length = toLength(0, closingBracket.bracketText.length);
			let bracketIds = SmallImmutableSet.getEmpty();
			const closingBrackets = closingBracket.getClosedBrackets();
			for (const bracket of closingBrackets) {
				bracketIds = bracketIds.add(getId(bracket), identityKeyProvider);
			}
			map.set(closingBracket.bracketText, new Token(
				length,
				TokenKind.ClosingBracket,
				getId(closingBrackets[0]),
				bracketIds,
				BracketAstNode.create(length, closingBracket, bracketIds)
			));
		}

		return new BracketTokens(map);
	}

	private hasRegExp = false;
	private _regExpGlobal: RegExp | null = null;

	constructor(
		private readonly map: Map<string, Token>
	) { }

	getRegExpStr(): string | null {
		if (this.isEmpty) {
			return null;
		} else {
			const keys = [...this.map.keys()];
			keys.sort();
			keys.reverse();
			return keys.map(k => prepareBracketForRegExp(k)).join('|');
		}
	}

	/**
	 * Returns null if there is no such regexp (because there are no brackets).
	*/
	get regExpGlobal(): RegExp | null {
		if (!this.hasRegExp) {
			const regExpStr = this.getRegExpStr();
			this._regExpGlobal = regExpStr ? new RegExp(regExpStr, 'gi') : null;
			this.hasRegExp = true;
		}
		return this._regExpGlobal;
	}

	getToken(value: string): Token | undefined {
		return this.map.get(value.toLowerCase());
	}

	findClosingTokenText(openingBracketIds: SmallImmutableSet<OpeningBracketId>): string | undefined {
		for (const [closingText, info] of this.map) {
			if (info.kind === TokenKind.ClosingBracket && info.bracketIds.intersects(openingBracketIds)) {
				return closingText;
			}
		}
		return undefined;
	}

	get isEmpty(): boolean {
		return this.map.size === 0;
	}
}

function prepareBracketForRegExp(str: string): string {
	const escaped = escapeRegExpCharacters(str);
	// This bracket pair uses letters like e.g. "begin" - "end" (see https://github.com/microsoft/vscode/issues/132162)
	const needsWordBoundaries = (/^[\w ]+$/.test(str));
	return (needsWordBoundaries ? `\\b${escaped}\\b` : escaped);
}

export class LanguageAgnosticBracketTokens {
	private readonly languageIdToBracketTokens = new Map<string, BracketTokens>();

	constructor(
		private readonly denseKeyProvider: DenseKeyProvider<string>,
		private readonly getLanguageConfiguration: (languageId: string) => ResolvedLanguageConfiguration,
	) {
	}

	public didLanguageChange(languageId: string): boolean {
		// Report a change whenever the language configuration updates.
		return this.languageIdToBracketTokens.has(languageId);
	}

	getSingleLanguageBracketTokens(languageId: string): BracketTokens {
		let singleLanguageBracketTokens = this.languageIdToBracketTokens.get(languageId);
		if (!singleLanguageBracketTokens) {
			singleLanguageBracketTokens = BracketTokens.createFromLanguage(this.getLanguageConfiguration(languageId), this.denseKeyProvider);
			this.languageIdToBracketTokens.set(languageId, singleLanguageBracketTokens);
		}
		return singleLanguageBracketTokens;
	}

	getToken(value: string, languageId: string): Token | undefined {
		const singleLanguageBracketTokens = this.getSingleLanguageBracketTokens(languageId);
		return singleLanguageBracketTokens.getToken(value);
	}
}