Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/codeEditor/common/languageConfigurationExtensionPoint.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
6
import * as nls from '../../../../nls.js';
7
import { ParseError, parse, getNodeType } from '../../../../base/common/json.js';
8
import { IJSONSchema } from '../../../../base/common/jsonSchema.js';
9
import * as types from '../../../../base/common/types.js';
10
import { URI } from '../../../../base/common/uri.js';
11
import { CharacterPair, CommentRule, EnterAction, ExplicitLanguageConfiguration, FoldingMarkers, FoldingRules, IAutoClosingPair, IAutoClosingPairConditional, IndentAction, IndentationRule, OnEnterRule } from '../../../../editor/common/languages/languageConfiguration.js';
12
import { ILanguageConfigurationService } from '../../../../editor/common/languages/languageConfigurationRegistry.js';
13
import { ILanguageService } from '../../../../editor/common/languages/language.js';
14
import { Extensions, IJSONContributionRegistry } from '../../../../platform/jsonschemas/common/jsonContributionRegistry.js';
15
import { Registry } from '../../../../platform/registry/common/platform.js';
16
import { IExtensionService } from '../../../services/extensions/common/extensions.js';
17
import { getParseErrorMessage } from '../../../../base/common/jsonErrorMessages.js';
18
import { IExtensionResourceLoaderService } from '../../../../platform/extensionResourceLoader/common/extensionResourceLoader.js';
19
import { hash } from '../../../../base/common/hash.js';
20
import { Disposable } from '../../../../base/common/lifecycle.js';
21
22
interface IRegExp {
23
pattern: string;
24
flags?: string;
25
}
26
27
interface IIndentationRules {
28
decreaseIndentPattern: string | IRegExp;
29
increaseIndentPattern: string | IRegExp;
30
indentNextLinePattern?: string | IRegExp;
31
unIndentedLinePattern?: string | IRegExp;
32
}
33
34
interface IEnterAction {
35
indent: 'none' | 'indent' | 'indentOutdent' | 'outdent';
36
appendText?: string;
37
removeText?: number;
38
}
39
40
interface IOnEnterRule {
41
beforeText: string | IRegExp;
42
afterText?: string | IRegExp;
43
previousLineText?: string | IRegExp;
44
action: IEnterAction;
45
}
46
47
/**
48
* Serialized form of a language configuration
49
*/
50
export interface ILanguageConfiguration {
51
comments?: CommentRule;
52
brackets?: CharacterPair[];
53
autoClosingPairs?: Array<CharacterPair | IAutoClosingPairConditional>;
54
surroundingPairs?: Array<CharacterPair | IAutoClosingPair>;
55
colorizedBracketPairs?: Array<CharacterPair>;
56
wordPattern?: string | IRegExp;
57
indentationRules?: IIndentationRules;
58
folding?: {
59
offSide?: boolean;
60
markers?: {
61
start?: string | IRegExp;
62
end?: string | IRegExp;
63
};
64
};
65
autoCloseBefore?: string;
66
onEnterRules?: IOnEnterRule[];
67
}
68
69
function isStringArr(something: string[] | null): something is string[] {
70
if (!Array.isArray(something)) {
71
return false;
72
}
73
for (let i = 0, len = something.length; i < len; i++) {
74
if (typeof something[i] !== 'string') {
75
return false;
76
}
77
}
78
return true;
79
80
}
81
82
function isCharacterPair(something: CharacterPair | null): boolean {
83
return (
84
isStringArr(something)
85
&& something.length === 2
86
);
87
}
88
89
export class LanguageConfigurationFileHandler extends Disposable {
90
91
/**
92
* A map from language id to a hash computed from the config files locations.
93
*/
94
private readonly _done = new Map<string, number>();
95
96
constructor(
97
@ILanguageService private readonly _languageService: ILanguageService,
98
@IExtensionResourceLoaderService private readonly _extensionResourceLoaderService: IExtensionResourceLoaderService,
99
@IExtensionService private readonly _extensionService: IExtensionService,
100
@ILanguageConfigurationService private readonly _languageConfigurationService: ILanguageConfigurationService,
101
) {
102
super();
103
104
this._register(this._languageService.onDidRequestBasicLanguageFeatures(async (languageIdentifier) => {
105
// Modes can be instantiated before the extension points have finished registering
106
this._extensionService.whenInstalledExtensionsRegistered().then(() => {
107
this._loadConfigurationsForMode(languageIdentifier);
108
});
109
}));
110
this._register(this._languageService.onDidChange(() => {
111
// reload language configurations as necessary
112
for (const [languageId] of this._done) {
113
this._loadConfigurationsForMode(languageId);
114
}
115
}));
116
}
117
118
private async _loadConfigurationsForMode(languageId: string): Promise<void> {
119
const configurationFiles = this._languageService.getConfigurationFiles(languageId);
120
const configurationHash = hash(configurationFiles.map(uri => uri.toString()));
121
122
if (this._done.get(languageId) === configurationHash) {
123
return;
124
}
125
this._done.set(languageId, configurationHash);
126
127
const configs = await Promise.all(configurationFiles.map(configFile => this._readConfigFile(configFile)));
128
for (const config of configs) {
129
this._handleConfig(languageId, config);
130
}
131
}
132
133
private async _readConfigFile(configFileLocation: URI): Promise<ILanguageConfiguration> {
134
try {
135
const contents = await this._extensionResourceLoaderService.readExtensionResource(configFileLocation);
136
const errors: ParseError[] = [];
137
let configuration = <ILanguageConfiguration>parse(contents, errors);
138
if (errors.length) {
139
console.error(nls.localize('parseErrors', "Errors parsing {0}: {1}", configFileLocation.toString(), errors.map(e => (`[${e.offset}, ${e.length}] ${getParseErrorMessage(e.error)}`)).join('\n')));
140
}
141
if (getNodeType(configuration) !== 'object') {
142
console.error(nls.localize('formatError', "{0}: Invalid format, JSON object expected.", configFileLocation.toString()));
143
configuration = {};
144
}
145
return configuration;
146
} catch (err) {
147
console.error(err);
148
return {};
149
}
150
}
151
152
private static _extractValidCommentRule(languageId: string, configuration: ILanguageConfiguration): CommentRule | undefined {
153
const source = configuration.comments;
154
if (typeof source === 'undefined') {
155
return undefined;
156
}
157
if (!types.isObject(source)) {
158
console.warn(`[${languageId}]: language configuration: expected \`comments\` to be an object.`);
159
return undefined;
160
}
161
162
let result: CommentRule | undefined = undefined;
163
if (typeof source.lineComment !== 'undefined') {
164
if (typeof source.lineComment === 'string') {
165
result = result || {};
166
result.lineComment = source.lineComment;
167
} else if (types.isObject(source.lineComment)) {
168
const lineCommentObj = source.lineComment as any;
169
if (typeof lineCommentObj.comment === 'string') {
170
result = result || {};
171
result.lineComment = {
172
comment: lineCommentObj.comment,
173
noIndent: lineCommentObj.noIndent
174
};
175
} else {
176
console.warn(`[${languageId}]: language configuration: expected \`comments.lineComment.comment\` to be a string.`);
177
}
178
} else {
179
console.warn(`[${languageId}]: language configuration: expected \`comments.lineComment\` to be a string or an object with comment property.`);
180
}
181
}
182
if (typeof source.blockComment !== 'undefined') {
183
if (!isCharacterPair(source.blockComment)) {
184
console.warn(`[${languageId}]: language configuration: expected \`comments.blockComment\` to be an array of two strings.`);
185
} else {
186
result = result || {};
187
result.blockComment = source.blockComment;
188
}
189
}
190
return result;
191
}
192
193
private static _extractValidBrackets(languageId: string, configuration: ILanguageConfiguration): CharacterPair[] | undefined {
194
const source = configuration.brackets;
195
if (typeof source === 'undefined') {
196
return undefined;
197
}
198
if (!Array.isArray(source)) {
199
console.warn(`[${languageId}]: language configuration: expected \`brackets\` to be an array.`);
200
return undefined;
201
}
202
203
let result: CharacterPair[] | undefined = undefined;
204
for (let i = 0, len = source.length; i < len; i++) {
205
const pair = source[i];
206
if (!isCharacterPair(pair)) {
207
console.warn(`[${languageId}]: language configuration: expected \`brackets[${i}]\` to be an array of two strings.`);
208
continue;
209
}
210
211
result = result || [];
212
result.push(pair);
213
}
214
return result;
215
}
216
217
private static _extractValidAutoClosingPairs(languageId: string, configuration: ILanguageConfiguration): IAutoClosingPairConditional[] | undefined {
218
const source = configuration.autoClosingPairs;
219
if (typeof source === 'undefined') {
220
return undefined;
221
}
222
if (!Array.isArray(source)) {
223
console.warn(`[${languageId}]: language configuration: expected \`autoClosingPairs\` to be an array.`);
224
return undefined;
225
}
226
227
let result: IAutoClosingPairConditional[] | undefined = undefined;
228
for (let i = 0, len = source.length; i < len; i++) {
229
const pair = source[i];
230
if (Array.isArray(pair)) {
231
if (!isCharacterPair(pair)) {
232
console.warn(`[${languageId}]: language configuration: expected \`autoClosingPairs[${i}]\` to be an array of two strings or an object.`);
233
continue;
234
}
235
result = result || [];
236
result.push({ open: pair[0], close: pair[1] });
237
} else {
238
if (!types.isObject(pair)) {
239
console.warn(`[${languageId}]: language configuration: expected \`autoClosingPairs[${i}]\` to be an array of two strings or an object.`);
240
continue;
241
}
242
if (typeof pair.open !== 'string') {
243
console.warn(`[${languageId}]: language configuration: expected \`autoClosingPairs[${i}].open\` to be a string.`);
244
continue;
245
}
246
if (typeof pair.close !== 'string') {
247
console.warn(`[${languageId}]: language configuration: expected \`autoClosingPairs[${i}].close\` to be a string.`);
248
continue;
249
}
250
if (typeof pair.notIn !== 'undefined') {
251
if (!isStringArr(pair.notIn)) {
252
console.warn(`[${languageId}]: language configuration: expected \`autoClosingPairs[${i}].notIn\` to be a string array.`);
253
continue;
254
}
255
}
256
result = result || [];
257
result.push({ open: pair.open, close: pair.close, notIn: pair.notIn });
258
}
259
}
260
return result;
261
}
262
263
private static _extractValidSurroundingPairs(languageId: string, configuration: ILanguageConfiguration): IAutoClosingPair[] | undefined {
264
const source = configuration.surroundingPairs;
265
if (typeof source === 'undefined') {
266
return undefined;
267
}
268
if (!Array.isArray(source)) {
269
console.warn(`[${languageId}]: language configuration: expected \`surroundingPairs\` to be an array.`);
270
return undefined;
271
}
272
273
let result: IAutoClosingPair[] | undefined = undefined;
274
for (let i = 0, len = source.length; i < len; i++) {
275
const pair = source[i];
276
if (Array.isArray(pair)) {
277
if (!isCharacterPair(pair)) {
278
console.warn(`[${languageId}]: language configuration: expected \`surroundingPairs[${i}]\` to be an array of two strings or an object.`);
279
continue;
280
}
281
result = result || [];
282
result.push({ open: pair[0], close: pair[1] });
283
} else {
284
if (!types.isObject(pair)) {
285
console.warn(`[${languageId}]: language configuration: expected \`surroundingPairs[${i}]\` to be an array of two strings or an object.`);
286
continue;
287
}
288
if (typeof pair.open !== 'string') {
289
console.warn(`[${languageId}]: language configuration: expected \`surroundingPairs[${i}].open\` to be a string.`);
290
continue;
291
}
292
if (typeof pair.close !== 'string') {
293
console.warn(`[${languageId}]: language configuration: expected \`surroundingPairs[${i}].close\` to be a string.`);
294
continue;
295
}
296
result = result || [];
297
result.push({ open: pair.open, close: pair.close });
298
}
299
}
300
return result;
301
}
302
303
private static _extractValidColorizedBracketPairs(languageId: string, configuration: ILanguageConfiguration): CharacterPair[] | undefined {
304
const source = configuration.colorizedBracketPairs;
305
if (typeof source === 'undefined') {
306
return undefined;
307
}
308
if (!Array.isArray(source)) {
309
console.warn(`[${languageId}]: language configuration: expected \`colorizedBracketPairs\` to be an array.`);
310
return undefined;
311
}
312
313
const result: CharacterPair[] = [];
314
for (let i = 0, len = source.length; i < len; i++) {
315
const pair = source[i];
316
if (!isCharacterPair(pair)) {
317
console.warn(`[${languageId}]: language configuration: expected \`colorizedBracketPairs[${i}]\` to be an array of two strings.`);
318
continue;
319
}
320
result.push([pair[0], pair[1]]);
321
322
}
323
return result;
324
}
325
326
private static _extractValidOnEnterRules(languageId: string, configuration: ILanguageConfiguration): OnEnterRule[] | undefined {
327
const source = configuration.onEnterRules;
328
if (typeof source === 'undefined') {
329
return undefined;
330
}
331
if (!Array.isArray(source)) {
332
console.warn(`[${languageId}]: language configuration: expected \`onEnterRules\` to be an array.`);
333
return undefined;
334
}
335
336
let result: OnEnterRule[] | undefined = undefined;
337
for (let i = 0, len = source.length; i < len; i++) {
338
const onEnterRule = source[i];
339
if (!types.isObject(onEnterRule)) {
340
console.warn(`[${languageId}]: language configuration: expected \`onEnterRules[${i}]\` to be an object.`);
341
continue;
342
}
343
if (!types.isObject(onEnterRule.action)) {
344
console.warn(`[${languageId}]: language configuration: expected \`onEnterRules[${i}].action\` to be an object.`);
345
continue;
346
}
347
let indentAction: IndentAction;
348
if (onEnterRule.action.indent === 'none') {
349
indentAction = IndentAction.None;
350
} else if (onEnterRule.action.indent === 'indent') {
351
indentAction = IndentAction.Indent;
352
} else if (onEnterRule.action.indent === 'indentOutdent') {
353
indentAction = IndentAction.IndentOutdent;
354
} else if (onEnterRule.action.indent === 'outdent') {
355
indentAction = IndentAction.Outdent;
356
} else {
357
console.warn(`[${languageId}]: language configuration: expected \`onEnterRules[${i}].action.indent\` to be 'none', 'indent', 'indentOutdent' or 'outdent'.`);
358
continue;
359
}
360
const action: EnterAction = { indentAction };
361
if (onEnterRule.action.appendText) {
362
if (typeof onEnterRule.action.appendText === 'string') {
363
action.appendText = onEnterRule.action.appendText;
364
} else {
365
console.warn(`[${languageId}]: language configuration: expected \`onEnterRules[${i}].action.appendText\` to be undefined or a string.`);
366
}
367
}
368
if (onEnterRule.action.removeText) {
369
if (typeof onEnterRule.action.removeText === 'number') {
370
action.removeText = onEnterRule.action.removeText;
371
} else {
372
console.warn(`[${languageId}]: language configuration: expected \`onEnterRules[${i}].action.removeText\` to be undefined or a number.`);
373
}
374
}
375
const beforeText = this._parseRegex(languageId, `onEnterRules[${i}].beforeText`, onEnterRule.beforeText);
376
if (!beforeText) {
377
continue;
378
}
379
const resultingOnEnterRule: OnEnterRule = { beforeText, action };
380
if (onEnterRule.afterText) {
381
const afterText = this._parseRegex(languageId, `onEnterRules[${i}].afterText`, onEnterRule.afterText);
382
if (afterText) {
383
resultingOnEnterRule.afterText = afterText;
384
}
385
}
386
if (onEnterRule.previousLineText) {
387
const previousLineText = this._parseRegex(languageId, `onEnterRules[${i}].previousLineText`, onEnterRule.previousLineText);
388
if (previousLineText) {
389
resultingOnEnterRule.previousLineText = previousLineText;
390
}
391
}
392
result = result || [];
393
result.push(resultingOnEnterRule);
394
}
395
396
return result;
397
}
398
399
public static extractValidConfig(languageId: string, configuration: ILanguageConfiguration): ExplicitLanguageConfiguration {
400
401
const comments = this._extractValidCommentRule(languageId, configuration);
402
const brackets = this._extractValidBrackets(languageId, configuration);
403
const autoClosingPairs = this._extractValidAutoClosingPairs(languageId, configuration);
404
const surroundingPairs = this._extractValidSurroundingPairs(languageId, configuration);
405
const colorizedBracketPairs = this._extractValidColorizedBracketPairs(languageId, configuration);
406
const autoCloseBefore = (typeof configuration.autoCloseBefore === 'string' ? configuration.autoCloseBefore : undefined);
407
const wordPattern = (configuration.wordPattern ? this._parseRegex(languageId, `wordPattern`, configuration.wordPattern) : undefined);
408
const indentationRules = (configuration.indentationRules ? this._mapIndentationRules(languageId, configuration.indentationRules) : undefined);
409
let folding: FoldingRules | undefined = undefined;
410
if (configuration.folding) {
411
const rawMarkers = configuration.folding.markers;
412
const startMarker = (rawMarkers && rawMarkers.start ? this._parseRegex(languageId, `folding.markers.start`, rawMarkers.start) : undefined);
413
const endMarker = (rawMarkers && rawMarkers.end ? this._parseRegex(languageId, `folding.markers.end`, rawMarkers.end) : undefined);
414
const markers: FoldingMarkers | undefined = (startMarker && endMarker ? { start: startMarker, end: endMarker } : undefined);
415
folding = {
416
offSide: configuration.folding.offSide,
417
markers
418
};
419
}
420
const onEnterRules = this._extractValidOnEnterRules(languageId, configuration);
421
422
const richEditConfig: ExplicitLanguageConfiguration = {
423
comments,
424
brackets,
425
wordPattern,
426
indentationRules,
427
onEnterRules,
428
autoClosingPairs,
429
surroundingPairs,
430
colorizedBracketPairs,
431
autoCloseBefore,
432
folding,
433
__electricCharacterSupport: undefined,
434
};
435
return richEditConfig;
436
}
437
438
private _handleConfig(languageId: string, configuration: ILanguageConfiguration): void {
439
const richEditConfig = LanguageConfigurationFileHandler.extractValidConfig(languageId, configuration);
440
this._languageConfigurationService.register(languageId, richEditConfig, 50);
441
}
442
443
private static _parseRegex(languageId: string, confPath: string, value: string | IRegExp): RegExp | undefined {
444
if (typeof value === 'string') {
445
try {
446
return new RegExp(value, '');
447
} catch (err) {
448
console.warn(`[${languageId}]: Invalid regular expression in \`${confPath}\`: `, err);
449
return undefined;
450
}
451
}
452
if (types.isObject(value)) {
453
if (typeof value.pattern !== 'string') {
454
console.warn(`[${languageId}]: language configuration: expected \`${confPath}.pattern\` to be a string.`);
455
return undefined;
456
}
457
if (typeof value.flags !== 'undefined' && typeof value.flags !== 'string') {
458
console.warn(`[${languageId}]: language configuration: expected \`${confPath}.flags\` to be a string.`);
459
return undefined;
460
}
461
try {
462
return new RegExp(value.pattern, value.flags);
463
} catch (err) {
464
console.warn(`[${languageId}]: Invalid regular expression in \`${confPath}\`: `, err);
465
return undefined;
466
}
467
}
468
console.warn(`[${languageId}]: language configuration: expected \`${confPath}\` to be a string or an object.`);
469
return undefined;
470
}
471
472
private static _mapIndentationRules(languageId: string, indentationRules: IIndentationRules): IndentationRule | undefined {
473
const increaseIndentPattern = this._parseRegex(languageId, `indentationRules.increaseIndentPattern`, indentationRules.increaseIndentPattern);
474
if (!increaseIndentPattern) {
475
return undefined;
476
}
477
const decreaseIndentPattern = this._parseRegex(languageId, `indentationRules.decreaseIndentPattern`, indentationRules.decreaseIndentPattern);
478
if (!decreaseIndentPattern) {
479
return undefined;
480
}
481
482
const result: IndentationRule = {
483
increaseIndentPattern: increaseIndentPattern,
484
decreaseIndentPattern: decreaseIndentPattern
485
};
486
487
if (indentationRules.indentNextLinePattern) {
488
result.indentNextLinePattern = this._parseRegex(languageId, `indentationRules.indentNextLinePattern`, indentationRules.indentNextLinePattern);
489
}
490
if (indentationRules.unIndentedLinePattern) {
491
result.unIndentedLinePattern = this._parseRegex(languageId, `indentationRules.unIndentedLinePattern`, indentationRules.unIndentedLinePattern);
492
}
493
494
return result;
495
}
496
}
497
498
const schemaId = 'vscode://schemas/language-configuration';
499
const schema: IJSONSchema = {
500
allowComments: true,
501
allowTrailingCommas: true,
502
default: {
503
comments: {
504
blockComment: ['/*', '*/'],
505
lineComment: '//'
506
},
507
brackets: [['(', ')'], ['[', ']'], ['{', '}']],
508
autoClosingPairs: [['(', ')'], ['[', ']'], ['{', '}']],
509
surroundingPairs: [['(', ')'], ['[', ']'], ['{', '}']]
510
},
511
definitions: {
512
openBracket: {
513
type: 'string',
514
description: nls.localize('schema.openBracket', 'The opening bracket character or string sequence.')
515
},
516
closeBracket: {
517
type: 'string',
518
description: nls.localize('schema.closeBracket', 'The closing bracket character or string sequence.')
519
},
520
bracketPair: {
521
type: 'array',
522
items: [{
523
$ref: '#/definitions/openBracket'
524
}, {
525
$ref: '#/definitions/closeBracket'
526
}]
527
}
528
},
529
properties: {
530
comments: {
531
default: {
532
blockComment: ['/*', '*/'],
533
lineComment: { comment: '//', noIndent: false }
534
},
535
description: nls.localize('schema.comments', 'Defines the comment symbols'),
536
type: 'object',
537
properties: {
538
blockComment: {
539
type: 'array',
540
description: nls.localize('schema.blockComments', 'Defines how block comments are marked.'),
541
items: [{
542
type: 'string',
543
description: nls.localize('schema.blockComment.begin', 'The character sequence that starts a block comment.')
544
}, {
545
type: 'string',
546
description: nls.localize('schema.blockComment.end', 'The character sequence that ends a block comment.')
547
}]
548
},
549
lineComment: {
550
type: 'object',
551
description: nls.localize('schema.lineComment.object', 'Configuration for line comments.'),
552
properties: {
553
comment: {
554
type: 'string',
555
description: nls.localize('schema.lineComment.comment', 'The character sequence that starts a line comment.')
556
},
557
noIndent: {
558
type: 'boolean',
559
description: nls.localize('schema.lineComment.noIndent', 'Whether the comment token should not be indented and placed at the first column. Defaults to false.'),
560
default: false
561
}
562
},
563
required: ['comment'],
564
additionalProperties: false
565
}
566
}
567
},
568
brackets: {
569
default: [['(', ')'], ['[', ']'], ['{', '}']],
570
markdownDescription: nls.localize('schema.brackets', 'Defines the bracket symbols that increase or decrease the indentation. When bracket pair colorization is enabled and {0} is not defined, this also defines the bracket pairs that are colorized by their nesting level.', '\`colorizedBracketPairs\`'),
571
type: 'array',
572
items: {
573
$ref: '#/definitions/bracketPair'
574
}
575
},
576
colorizedBracketPairs: {
577
default: [['(', ')'], ['[', ']'], ['{', '}']],
578
markdownDescription: nls.localize('schema.colorizedBracketPairs', 'Defines the bracket pairs that are colorized by their nesting level if bracket pair colorization is enabled. Any brackets included here that are not included in {0} will be automatically included in {0}.', '\`brackets\`'),
579
type: 'array',
580
items: {
581
$ref: '#/definitions/bracketPair'
582
}
583
},
584
autoClosingPairs: {
585
default: [['(', ')'], ['[', ']'], ['{', '}']],
586
description: nls.localize('schema.autoClosingPairs', 'Defines the bracket pairs. When a opening bracket is entered, the closing bracket is inserted automatically.'),
587
type: 'array',
588
items: {
589
oneOf: [{
590
$ref: '#/definitions/bracketPair'
591
}, {
592
type: 'object',
593
properties: {
594
open: {
595
$ref: '#/definitions/openBracket'
596
},
597
close: {
598
$ref: '#/definitions/closeBracket'
599
},
600
notIn: {
601
type: 'array',
602
description: nls.localize('schema.autoClosingPairs.notIn', 'Defines a list of scopes where the auto pairs are disabled.'),
603
items: {
604
enum: ['string', 'comment']
605
}
606
}
607
}
608
}]
609
}
610
},
611
autoCloseBefore: {
612
default: ';:.,=}])> \n\t',
613
description: nls.localize('schema.autoCloseBefore', 'Defines what characters must be after the cursor in order for bracket or quote autoclosing to occur when using the \'languageDefined\' autoclosing setting. This is typically the set of characters which can not start an expression.'),
614
type: 'string',
615
},
616
surroundingPairs: {
617
default: [['(', ')'], ['[', ']'], ['{', '}']],
618
description: nls.localize('schema.surroundingPairs', 'Defines the bracket pairs that can be used to surround a selected string.'),
619
type: 'array',
620
items: {
621
oneOf: [{
622
$ref: '#/definitions/bracketPair'
623
}, {
624
type: 'object',
625
properties: {
626
open: {
627
$ref: '#/definitions/openBracket'
628
},
629
close: {
630
$ref: '#/definitions/closeBracket'
631
}
632
}
633
}]
634
}
635
},
636
wordPattern: {
637
default: '',
638
description: nls.localize('schema.wordPattern', 'Defines what is considered to be a word in the programming language.'),
639
type: ['string', 'object'],
640
properties: {
641
pattern: {
642
type: 'string',
643
description: nls.localize('schema.wordPattern.pattern', 'The RegExp pattern used to match words.'),
644
default: '',
645
},
646
flags: {
647
type: 'string',
648
description: nls.localize('schema.wordPattern.flags', 'The RegExp flags used to match words.'),
649
default: 'g',
650
pattern: '^([gimuy]+)$',
651
patternErrorMessage: nls.localize('schema.wordPattern.flags.errorMessage', 'Must match the pattern `/^([gimuy]+)$/`.')
652
}
653
}
654
},
655
indentationRules: {
656
default: {
657
increaseIndentPattern: '',
658
decreaseIndentPattern: ''
659
},
660
description: nls.localize('schema.indentationRules', 'The language\'s indentation settings.'),
661
type: 'object',
662
properties: {
663
increaseIndentPattern: {
664
type: ['string', 'object'],
665
description: nls.localize('schema.indentationRules.increaseIndentPattern', 'If a line matches this pattern, then all the lines after it should be indented once (until another rule matches).'),
666
properties: {
667
pattern: {
668
type: 'string',
669
description: nls.localize('schema.indentationRules.increaseIndentPattern.pattern', 'The RegExp pattern for increaseIndentPattern.'),
670
default: '',
671
},
672
flags: {
673
type: 'string',
674
description: nls.localize('schema.indentationRules.increaseIndentPattern.flags', 'The RegExp flags for increaseIndentPattern.'),
675
default: '',
676
pattern: '^([gimuy]+)$',
677
patternErrorMessage: nls.localize('schema.indentationRules.increaseIndentPattern.errorMessage', 'Must match the pattern `/^([gimuy]+)$/`.')
678
}
679
}
680
},
681
decreaseIndentPattern: {
682
type: ['string', 'object'],
683
description: nls.localize('schema.indentationRules.decreaseIndentPattern', 'If a line matches this pattern, then all the lines after it should be unindented once (until another rule matches).'),
684
properties: {
685
pattern: {
686
type: 'string',
687
description: nls.localize('schema.indentationRules.decreaseIndentPattern.pattern', 'The RegExp pattern for decreaseIndentPattern.'),
688
default: '',
689
},
690
flags: {
691
type: 'string',
692
description: nls.localize('schema.indentationRules.decreaseIndentPattern.flags', 'The RegExp flags for decreaseIndentPattern.'),
693
default: '',
694
pattern: '^([gimuy]+)$',
695
patternErrorMessage: nls.localize('schema.indentationRules.decreaseIndentPattern.errorMessage', 'Must match the pattern `/^([gimuy]+)$/`.')
696
}
697
}
698
},
699
indentNextLinePattern: {
700
type: ['string', 'object'],
701
description: nls.localize('schema.indentationRules.indentNextLinePattern', 'If a line matches this pattern, then **only the next line** after it should be indented once.'),
702
properties: {
703
pattern: {
704
type: 'string',
705
description: nls.localize('schema.indentationRules.indentNextLinePattern.pattern', 'The RegExp pattern for indentNextLinePattern.'),
706
default: '',
707
},
708
flags: {
709
type: 'string',
710
description: nls.localize('schema.indentationRules.indentNextLinePattern.flags', 'The RegExp flags for indentNextLinePattern.'),
711
default: '',
712
pattern: '^([gimuy]+)$',
713
patternErrorMessage: nls.localize('schema.indentationRules.indentNextLinePattern.errorMessage', 'Must match the pattern `/^([gimuy]+)$/`.')
714
}
715
}
716
},
717
unIndentedLinePattern: {
718
type: ['string', 'object'],
719
description: nls.localize('schema.indentationRules.unIndentedLinePattern', 'If a line matches this pattern, then its indentation should not be changed and it should not be evaluated against the other rules.'),
720
properties: {
721
pattern: {
722
type: 'string',
723
description: nls.localize('schema.indentationRules.unIndentedLinePattern.pattern', 'The RegExp pattern for unIndentedLinePattern.'),
724
default: '',
725
},
726
flags: {
727
type: 'string',
728
description: nls.localize('schema.indentationRules.unIndentedLinePattern.flags', 'The RegExp flags for unIndentedLinePattern.'),
729
default: '',
730
pattern: '^([gimuy]+)$',
731
patternErrorMessage: nls.localize('schema.indentationRules.unIndentedLinePattern.errorMessage', 'Must match the pattern `/^([gimuy]+)$/`.')
732
}
733
}
734
}
735
}
736
},
737
folding: {
738
type: 'object',
739
description: nls.localize('schema.folding', 'The language\'s folding settings.'),
740
properties: {
741
offSide: {
742
type: 'boolean',
743
description: nls.localize('schema.folding.offSide', 'A language adheres to the off-side rule if blocks in that language are expressed by their indentation. If set, empty lines belong to the subsequent block.'),
744
},
745
markers: {
746
type: 'object',
747
description: nls.localize('schema.folding.markers', 'Language specific folding markers such as \'#region\' and \'#endregion\'. The start and end regexes will be tested against the contents of all lines and must be designed efficiently'),
748
properties: {
749
start: {
750
type: 'string',
751
description: nls.localize('schema.folding.markers.start', 'The RegExp pattern for the start marker. The regexp must start with \'^\'.')
752
},
753
end: {
754
type: 'string',
755
description: nls.localize('schema.folding.markers.end', 'The RegExp pattern for the end marker. The regexp must start with \'^\'.')
756
},
757
}
758
}
759
}
760
},
761
onEnterRules: {
762
type: 'array',
763
description: nls.localize('schema.onEnterRules', 'The language\'s rules to be evaluated when pressing Enter.'),
764
items: {
765
type: 'object',
766
description: nls.localize('schema.onEnterRules', 'The language\'s rules to be evaluated when pressing Enter.'),
767
required: ['beforeText', 'action'],
768
properties: {
769
beforeText: {
770
type: ['string', 'object'],
771
description: nls.localize('schema.onEnterRules.beforeText', 'This rule will only execute if the text before the cursor matches this regular expression.'),
772
properties: {
773
pattern: {
774
type: 'string',
775
description: nls.localize('schema.onEnterRules.beforeText.pattern', 'The RegExp pattern for beforeText.'),
776
default: '',
777
},
778
flags: {
779
type: 'string',
780
description: nls.localize('schema.onEnterRules.beforeText.flags', 'The RegExp flags for beforeText.'),
781
default: '',
782
pattern: '^([gimuy]+)$',
783
patternErrorMessage: nls.localize('schema.onEnterRules.beforeText.errorMessage', 'Must match the pattern `/^([gimuy]+)$/`.')
784
}
785
}
786
},
787
afterText: {
788
type: ['string', 'object'],
789
description: nls.localize('schema.onEnterRules.afterText', 'This rule will only execute if the text after the cursor matches this regular expression.'),
790
properties: {
791
pattern: {
792
type: 'string',
793
description: nls.localize('schema.onEnterRules.afterText.pattern', 'The RegExp pattern for afterText.'),
794
default: '',
795
},
796
flags: {
797
type: 'string',
798
description: nls.localize('schema.onEnterRules.afterText.flags', 'The RegExp flags for afterText.'),
799
default: '',
800
pattern: '^([gimuy]+)$',
801
patternErrorMessage: nls.localize('schema.onEnterRules.afterText.errorMessage', 'Must match the pattern `/^([gimuy]+)$/`.')
802
}
803
}
804
},
805
previousLineText: {
806
type: ['string', 'object'],
807
description: nls.localize('schema.onEnterRules.previousLineText', 'This rule will only execute if the text above the line matches this regular expression.'),
808
properties: {
809
pattern: {
810
type: 'string',
811
description: nls.localize('schema.onEnterRules.previousLineText.pattern', 'The RegExp pattern for previousLineText.'),
812
default: '',
813
},
814
flags: {
815
type: 'string',
816
description: nls.localize('schema.onEnterRules.previousLineText.flags', 'The RegExp flags for previousLineText.'),
817
default: '',
818
pattern: '^([gimuy]+)$',
819
patternErrorMessage: nls.localize('schema.onEnterRules.previousLineText.errorMessage', 'Must match the pattern `/^([gimuy]+)$/`.')
820
}
821
}
822
},
823
action: {
824
type: ['string', 'object'],
825
description: nls.localize('schema.onEnterRules.action', 'The action to execute.'),
826
required: ['indent'],
827
default: { 'indent': 'indent' },
828
properties: {
829
indent: {
830
type: 'string',
831
description: nls.localize('schema.onEnterRules.action.indent', "Describe what to do with the indentation"),
832
default: 'indent',
833
enum: ['none', 'indent', 'indentOutdent', 'outdent'],
834
markdownEnumDescriptions: [
835
nls.localize('schema.onEnterRules.action.indent.none', "Insert new line and copy the previous line's indentation."),
836
nls.localize('schema.onEnterRules.action.indent.indent', "Insert new line and indent once (relative to the previous line's indentation)."),
837
nls.localize('schema.onEnterRules.action.indent.indentOutdent', "Insert two new lines:\n - the first one indented which will hold the cursor\n - the second one at the same indentation level"),
838
nls.localize('schema.onEnterRules.action.indent.outdent', "Insert new line and outdent once (relative to the previous line's indentation).")
839
]
840
},
841
appendText: {
842
type: 'string',
843
description: nls.localize('schema.onEnterRules.action.appendText', 'Describes text to be appended after the new line and after the indentation.'),
844
default: '',
845
},
846
removeText: {
847
type: 'number',
848
description: nls.localize('schema.onEnterRules.action.removeText', 'Describes the number of characters to remove from the new line\'s indentation.'),
849
default: 0,
850
}
851
}
852
}
853
}
854
}
855
}
856
857
}
858
};
859
const schemaRegistry = Registry.as<IJSONContributionRegistry>(Extensions.JSONContribution);
860
schemaRegistry.registerSchema(schemaId, schema);
861
862