forked from microsoft/vscode-textmate
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.ts
268 lines (230 loc) · 8.65 KB
/
main.ts
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
/*---------------------------------------------------------
* Copyright (C) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------*/
import { SyncRegistry } from './registry';
import * as grammarReader from './grammarReader';
import { Theme } from './theme';
import { StackElement as StackElementImpl, ScopeDependencyProcessor } from './grammar';
import { IRawGrammar, IOnigLib } from './types';
export * from './types';
/**
* A single theme setting.
*/
export interface IRawThemeSetting {
readonly name?: string;
readonly scope?: string | string[];
readonly settings: {
readonly fontStyle?: string;
readonly foreground?: string;
readonly background?: string;
};
}
/**
* A TextMate theme.
*/
export interface IRawTheme {
readonly name?: string;
readonly settings: IRawThemeSetting[];
}
/**
* A registry helper that can locate grammar file paths given scope names.
*/
export interface RegistryOptions {
onigLib: Promise<IOnigLib>;
theme?: IRawTheme;
colorMap?: string[];
loadGrammar(scopeName: string): Promise<IRawGrammar | undefined | null>;
getInjections?(scopeName: string): string[] | undefined;
}
/**
* A map from scope name to a language id. Please do not use language id 0.
*/
export interface IEmbeddedLanguagesMap {
[scopeName: string]: number;
}
/**
* A map from selectors to token types.
*/
export interface ITokenTypeMap {
[selector: string]: StandardTokenType;
}
export const enum StandardTokenType {
Other = 0,
Comment = 1,
String = 2,
RegEx = 4
}
export interface IGrammarConfiguration {
embeddedLanguages?: IEmbeddedLanguagesMap;
tokenTypes?: ITokenTypeMap;
}
/**
* The registry that will hold all grammars.
*/
export class Registry {
private readonly _options: RegistryOptions;
private readonly _syncRegistry: SyncRegistry;
private readonly _ensureGrammarCache: Map<string, Promise<void>>;
constructor(options: RegistryOptions) {
this._options = options;
this._syncRegistry = new SyncRegistry(Theme.createFromRawTheme(options.theme, options.colorMap), options.onigLib);
this._ensureGrammarCache = new Map<string, Promise<void>>();
}
public dispose(): void {
this._syncRegistry.dispose();
}
/**
* Change the theme. Once called, no previous `ruleStack` should be used anymore.
*/
public setTheme(theme: IRawTheme, colorMap?: string[]): void {
this._syncRegistry.setTheme(Theme.createFromRawTheme(theme, colorMap));
}
/**
* Returns a lookup array for color ids.
*/
public getColorMap(): string[] {
return this._syncRegistry.getColorMap();
}
/**
* Load the grammar for `scopeName` and all referenced included grammars asynchronously.
* Please do not use language id 0.
*/
public loadGrammarWithEmbeddedLanguages(initialScopeName: string, initialLanguage: number, embeddedLanguages: IEmbeddedLanguagesMap): Promise<IGrammar | null> {
return this.loadGrammarWithConfiguration(initialScopeName, initialLanguage, { embeddedLanguages });
}
/**
* Load the grammar for `scopeName` and all referenced included grammars asynchronously.
* Please do not use language id 0.
*/
public loadGrammarWithConfiguration(initialScopeName: string, initialLanguage: number, configuration: IGrammarConfiguration): Promise<IGrammar | null> {
return this._loadGrammar(initialScopeName, initialLanguage, configuration.embeddedLanguages, configuration.tokenTypes);
}
/**
* Load the grammar for `scopeName` and all referenced included grammars asynchronously.
*/
public loadGrammar(initialScopeName: string): Promise<IGrammar | null> {
return this._loadGrammar(initialScopeName, 0, null, null);
}
private async _doLoadSingleGrammar(scopeName: string): Promise<void> {
const grammar = await this._options.loadGrammar(scopeName);
if (grammar) {
const injections = (typeof this._options.getInjections === 'function' ? this._options.getInjections(scopeName) : undefined);
this._syncRegistry.addGrammar(grammar, injections);
}
}
private async _loadSingleGrammar(scopeName: string): Promise<void> {
if (!this._ensureGrammarCache.has(scopeName)) {
this._ensureGrammarCache.set(scopeName, this._doLoadSingleGrammar(scopeName));
}
return this._ensureGrammarCache.get(scopeName);
}
private async _loadGrammar(initialScopeName: string, initialLanguage: number, embeddedLanguages: IEmbeddedLanguagesMap | null | undefined, tokenTypes: ITokenTypeMap | null | undefined): Promise<IGrammar | null> {
const dependencyProcessor = new ScopeDependencyProcessor(this._syncRegistry, initialScopeName);
while (dependencyProcessor.Q.length > 0) {
await Promise.all(dependencyProcessor.Q.map(request => this._loadSingleGrammar(request.scopeName)));
dependencyProcessor.processQueue();
}
return this.grammarForScopeName(initialScopeName, initialLanguage, embeddedLanguages, tokenTypes);
}
/**
* Adds a rawGrammar.
*/
public async addGrammar(rawGrammar: IRawGrammar, injections: string[] = [], initialLanguage: number = 0, embeddedLanguages: IEmbeddedLanguagesMap | null = null): Promise<IGrammar> {
this._syncRegistry.addGrammar(rawGrammar, injections);
return (await this.grammarForScopeName(rawGrammar.scopeName, initialLanguage, embeddedLanguages))!;
}
/**
* Get the grammar for `scopeName`. The grammar must first be created via `loadGrammar` or `addGrammar`.
*/
public grammarForScopeName(scopeName: string, initialLanguage: number = 0, embeddedLanguages: IEmbeddedLanguagesMap | null = null, tokenTypes: ITokenTypeMap | null = null): Promise<IGrammar | null> {
return this._syncRegistry.grammarForScopeName(scopeName, initialLanguage, embeddedLanguages, tokenTypes);
}
}
/**
* A grammar
*/
export interface IGrammar {
/**
* Tokenize `lineText` using previous line state `prevState`.
*/
tokenizeLine(lineText: string, prevState: StackElement | null): ITokenizeLineResult;
/**
* Tokenize `lineText` using previous line state `prevState`.
* The result contains the tokens in binary format, resolved with the following information:
* - language
* - token type (regex, string, comment, other)
* - font style
* - foreground color
* - background color
* e.g. for getting the languageId: `(metadata & MetadataConsts.LANGUAGEID_MASK) >>> MetadataConsts.LANGUAGEID_OFFSET`
*/
tokenizeLine2(lineText: string, prevState: StackElement | null): ITokenizeLineResult2;
}
export interface ITokenizeLineResult {
readonly tokens: IToken[];
/**
* The `prevState` to be passed on to the next line tokenization.
*/
readonly ruleStack: StackElement;
}
/**
* Helpers to manage the "collapsed" metadata of an entire StackElement stack.
* The following assumptions have been made:
* - languageId < 256 => needs 8 bits
* - unique color count < 512 => needs 9 bits
*
* The binary format is:
* - -------------------------------------------
* 3322 2222 2222 1111 1111 1100 0000 0000
* 1098 7654 3210 9876 5432 1098 7654 3210
* - -------------------------------------------
* xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
* bbbb bbbb bfff ffff ffFF FTTT LLLL LLLL
* - -------------------------------------------
* - L = LanguageId (8 bits)
* - T = StandardTokenType (3 bits)
* - F = FontStyle (3 bits)
* - f = foreground color (9 bits)
* - b = background color (9 bits)
*/
export const enum MetadataConsts {
LANGUAGEID_MASK = 0b00000000000000000000000011111111,
TOKEN_TYPE_MASK = 0b00000000000000000000011100000000,
FONT_STYLE_MASK = 0b00000000000000000011100000000000,
FOREGROUND_MASK = 0b00000000011111111100000000000000,
BACKGROUND_MASK = 0b11111111100000000000000000000000,
LANGUAGEID_OFFSET = 0,
TOKEN_TYPE_OFFSET = 8,
FONT_STYLE_OFFSET = 11,
FOREGROUND_OFFSET = 14,
BACKGROUND_OFFSET = 23
}
export interface ITokenizeLineResult2 {
/**
* The tokens in binary format. Each token occupies two array indices. For token i:
* - at offset 2*i => startIndex
* - at offset 2*i + 1 => metadata
*
*/
readonly tokens: Uint32Array;
/**
* The `prevState` to be passed on to the next line tokenization.
*/
readonly ruleStack: StackElement;
}
export interface IToken {
startIndex: number;
readonly endIndex: number;
readonly scopes: string[];
}
/**
* **IMPORTANT** - Immutable!
*/
export interface StackElement {
_stackElementBrand: void;
readonly depth: number;
clone(): StackElement;
equals(other: StackElement): boolean;
}
export const INITIAL: StackElement = StackElementImpl.NULL;
export const parseRawGrammar: (content: string, filePath?: string) => IRawGrammar = grammarReader.parseRawGrammar;