languageConfigurationExtensionPoint.ts ×19

Frontier kind: Code frontier

unlabeled · c_096048ba9400

7 tests · 45401 LOC · 266 files · introduces 0 tests · 775 LOC · 6 files

Introduces — evidence that enters the hierarchy at this concept

Code
48 ranges775 lines · 6 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
4913 ranges45401 lines · 266 files · Browse complete extent
All tests (intent)
7 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

6 files ranked by introduced lines: 775 introduced LOC across 48 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/workbench/contrib/codeEditor/common/languageConfigurationExtensionPoint.ts 559 introduced LOC · 19 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- languageConfigurationExtensionPoint.ts
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)) {
86 );
87 }
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,
115 }));
116 }
118 > private async _loadConfigurationsForMode(languageId: string): Promise<void> {
119 const configurationFiles = this._languageService.getConfigurationFiles(languageId);
120 const configurationHash = hash(configurationFiles.map(uri => uri.toString()));
130 }
131 }
133 > private async _readConfigFile(configFileLocation: URI): Promise<ILanguageConfiguration> {
134 try {
135 const contents = await this._extensionResourceLoaderService.readExtensionResource(configFileLocation);
149 }
150 }
152 > private static _extractValidCommentRule(languageId: string, configuration: ILanguageConfiguration): CommentRule | undefined {
153 > const source = configuration.comments;
154 > if (typeof source === 'undefined') {
155 return undefined;
156 }
189 }
190 return result;
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 }
213 }
214 return result;
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 }
259 }
260 return result;
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 }
299 }
300 return result;
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 }
322 }
323 return result;
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 }
395
396 return result;
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 }
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, '');
450 }
451 }
452 > if (types.isObject(value)) { languageConfigurationExtensionPoint.ts
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') { languageConfigurationExtensionPoint.ts
458 console.warn(`[${languageId}]: language configuration: expected \`${confPath}.flags\` to be a string.`);
459 return undefined;
460 }
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 }
468 console.warn(`[${languageId}]: language configuration: expected \`${confPath}\` to be a string or an object.`);
469 return undefined;
471 >
472 > private static _mapIndentationRules(languageId: string, indentationRules: IIndentationRules): IndentationRule | undefined {
473 const increaseIndentPattern = this._parseRegex(languageId, `indentationRules.increaseIndentPattern`, indentationRules.increaseIndentPattern);
474 if (!increaseIndentPattern) {
494 return result;
495 }
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', 'object'],
751 > description: nls.localize('schema.folding.markers.start', 'The RegExp pattern for the start marker. The regexp must start with \'^\'.'),
752 > properties: {
753 > pattern: {
754 > type: 'string',
755 > description: nls.localize('schema.folding.markers.start.pattern', 'The RegExp pattern for the start marker.'),
756 > default: '',
757 > },
758 > flags: {
759 > type: 'string',
760 > description: nls.localize('schema.folding.markers.start.flags', 'The RegExp flags for the start marker.'),
761 > default: '',
762 > pattern: '^([gimuy]+)$',
763 > patternErrorMessage: nls.localize('schema.folding.markers.start.errorMessage', 'Must match the pattern `/^([gimuy]+)$/`.')
764 > }
765 > }
766 > },
767 > end: {
768 > type: ['string', 'object'],
769 > description: nls.localize('schema.folding.markers.end', 'The RegExp pattern for the end marker. The regexp must start with \'^\'.'),
770 > properties: {
771 > pattern: {
772 > type: 'string',
773 > description: nls.localize('schema.folding.markers.end.pattern', 'The RegExp pattern for the end marker.'),
774 > default: '',
775 > },
776 > flags: {
777 > type: 'string',
778 > description: nls.localize('schema.folding.markers.end.flags', 'The RegExp flags for the end marker.'),
779 > default: '',
780 > pattern: '^([gimuy]+)$',
781 > patternErrorMessage: nls.localize('schema.folding.markers.end.errorMessage', 'Must match the pattern `/^([gimuy]+)$/`.')
782 > }
783 > }
784 > },
785 > }
786 > }
787 > }
788 > },
789 > onEnterRules: {
790 > type: 'array',
791 > description: nls.localize('schema.onEnterRules', 'The language\'s rules to be evaluated when pressing Enter.'),
792 > items: {
793 > type: 'object',
794 > description: nls.localize('schema.onEnterRules', 'The language\'s rules to be evaluated when pressing Enter.'),
795 > required: ['beforeText', 'action'],
796 > properties: {
797 > beforeText: {
798 > type: ['string', 'object'],
799 > description: nls.localize('schema.onEnterRules.beforeText', 'This rule will only execute if the text before the cursor matches this regular expression.'),
800 > properties: {
801 > pattern: {
802 > type: 'string',
803 > description: nls.localize('schema.onEnterRules.beforeText.pattern', 'The RegExp pattern for beforeText.'),
804 > default: '',
805 > },
806 > flags: {
807 > type: 'string',
808 > description: nls.localize('schema.onEnterRules.beforeText.flags', 'The RegExp flags for beforeText.'),
809 > default: '',
810 > pattern: '^([gimuy]+)$',
811 > patternErrorMessage: nls.localize('schema.onEnterRules.beforeText.errorMessage', 'Must match the pattern `/^([gimuy]+)$/`.')
812 > }
813 > }
814 > },
815 > afterText: {
816 > type: ['string', 'object'],
817 > description: nls.localize('schema.onEnterRules.afterText', 'This rule will only execute if the text after the cursor matches this regular expression.'),
818 > properties: {
819 > pattern: {
820 > type: 'string',
821 > description: nls.localize('schema.onEnterRules.afterText.pattern', 'The RegExp pattern for afterText.'),
822 > default: '',
823 > },
824 > flags: {
825 > type: 'string',
826 > description: nls.localize('schema.onEnterRules.afterText.flags', 'The RegExp flags for afterText.'),
827 > default: '',
828 > pattern: '^([gimuy]+)$',
829 > patternErrorMessage: nls.localize('schema.onEnterRules.afterText.errorMessage', 'Must match the pattern `/^([gimuy]+)$/`.')
830 > }
831 > }
832 > },
833 > previousLineText: {
834 > type: ['string', 'object'],
835 > description: nls.localize('schema.onEnterRules.previousLineText', 'This rule will only execute if the text above the line matches this regular expression.'),
836 > properties: {
837 > pattern: {
838 > type: 'string',
839 > description: nls.localize('schema.onEnterRules.previousLineText.pattern', 'The RegExp pattern for previousLineText.'),
840 > default: '',
841 > },
842 > flags: {
843 > type: 'string',
844 > description: nls.localize('schema.onEnterRules.previousLineText.flags', 'The RegExp flags for previousLineText.'),
845 > default: '',
846 > pattern: '^([gimuy]+)$',
847 > patternErrorMessage: nls.localize('schema.onEnterRules.previousLineText.errorMessage', 'Must match the pattern `/^([gimuy]+)$/`.')
848 > }
849 > }
850 > },
851 > action: {
852 > type: ['string', 'object'],
853 > description: nls.localize('schema.onEnterRules.action', 'The action to execute.'),
854 > required: ['indent'],
855 > default: { 'indent': 'indent' },
856 > properties: {
857 > indent: {
858 > type: 'string',
859 > description: nls.localize('schema.onEnterRules.action.indent', "Describe what to do with the indentation"),
860 > default: 'indent',
861 > enum: ['none', 'indent', 'indentOutdent', 'outdent'],
862 > markdownEnumDescriptions: [
863 > nls.localize('schema.onEnterRules.action.indent.none', "Insert new line and copy the previous line's indentation."),
864 > nls.localize('schema.onEnterRules.action.indent.indent', "Insert new line and indent once (relative to the previous line's indentation)."),
865 > 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"),
866 > nls.localize('schema.onEnterRules.action.indent.outdent', "Insert new line and outdent once (relative to the previous line's indentation).")
867 > ]
868 > },
869 > appendText: {
870 > type: 'string',
871 > description: nls.localize('schema.onEnterRules.action.appendText', 'Describes text to be appended after the new line and after the indentation.'),
872 > default: '',
873 > },
874 > removeText: {
875 > type: 'number',
876 > description: nls.localize('schema.onEnterRules.action.removeText', 'Describes the number of characters to remove from the new line\'s indentation.'),
877 > default: 0,
878 > }
879 > }
880 > }
881 > }
882 > }
883 > }
884 >
885 > }
886 > };
887 > const schemaRegistry = Registry.as<IJSONContributionRegistry>(Extensions.JSONContribution);
888 > schemaRegistry.registerSchema(schemaId, schema);
src/vs/editor/common/languages/supports/indentationLineProcessor.ts 97 introduced LOC · 14 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- indentationLineProcessor.ts
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 strings from '../../../../base/common/strings.js';
7 > import { Range } from '../../core/range.js';
8 > import { ITextModel } from '../../model.js';
9 > import { ILanguageConfigurationService } from '../languageConfigurationRegistry.js';
10 > import { createScopedLineTokens, ScopedLineTokens } from '../supports.js';
11 > import { IVirtualModel } from '../autoIndent.js';
12 > import { IViewLineTokens, LineTokens } from '../../tokens/lineTokens.js';
13 > import { IndentRulesSupport } from './indentRules.js';
14 > import { StandardTokenType } from '../../encodedTokenAttributes.js';
15 > import { Position } from '../../core/position.js';
16 >
17 > /**
18 > * This class is a wrapper class around {@link IndentRulesSupport}.
19 > * It processes the lines by removing the language configuration brackets from the regex, string and comment tokens.
20 > * It then calls into the {@link IndentRulesSupport} to validate the indentation conditions.
21 > */
22 > export class ProcessedIndentRulesSupport {
23 >
24 > private readonly _indentRulesSupport: IndentRulesSupport;
25 > private readonly _indentationLineProcessor: IndentationLineProcessor;
26 >
27 > constructor(
28 model: IVirtualModel,
29 indentRulesSupport: IndentRulesSupport,
33 this._indentationLineProcessor = new IndentationLineProcessor(model, languageConfigurationService);
34 }
36 > /**
37 > * Apply the new indentation and return whether the indentation level should be increased after the given line number
38 > */
39 > public shouldIncrease(lineNumber: number, newIndentation?: string): boolean {
40 const processedLine = this._indentationLineProcessor.getProcessedLine(lineNumber, newIndentation);
41 return this._indentRulesSupport.shouldIncrease(processedLine);
42 }
44 > /**
45 > * Apply the new indentation and return whether the indentation level should be decreased after the given line number
46 > */
47 > public shouldDecrease(lineNumber: number, newIndentation?: string): boolean {
48 const processedLine = this._indentationLineProcessor.getProcessedLine(lineNumber, newIndentation);
49 return this._indentRulesSupport.shouldDecrease(processedLine);
50 }
52 > /**
53 > * Apply the new indentation and return whether the indentation level should remain unchanged at the given line number
54 > */
55 > public shouldIgnore(lineNumber: number, newIndentation?: string): boolean {
56 const processedLine = this._indentationLineProcessor.getProcessedLine(lineNumber, newIndentation);
57 return this._indentRulesSupport.shouldIgnore(processedLine);
58 }
60 > /**
61 > * Apply the new indentation and return whether the indentation level should increase on the line after the given line number
62 > */
63 > public shouldIndentNextLine(lineNumber: number, newIndentation?: string): boolean {
64 const processedLine = this._indentationLineProcessor.getProcessedLine(lineNumber, newIndentation);
65 return this._indentRulesSupport.shouldIndentNextLine(processedLine);
66 }
68 > }
69 >
70 > /**
71 > * This class fetches the processed text around a range which can be used for indentation evaluation.
72 > * It returns:
73 > * - The processed text before the given range and on the same start line
74 > * - The processed text after the given range and on the same end line
75 > * - The processed text on the previous line
76 > */
77 > export class IndentationContextProcessor {
78 >
79 > private readonly model: ITextModel;
80 > private readonly indentationLineProcessor: IndentationLineProcessor;
81 >
82 > constructor(
83 model: ITextModel,
84 languageConfigurationService: ILanguageConfigurationService
87 this.indentationLineProcessor = new IndentationLineProcessor(model, languageConfigurationService);
88 }
90 > /**
91 > * Returns the processed text, stripped from the language configuration brackets within the string, comment and regex tokens, around the given range
92 > */
93 > getProcessedTokenContextAroundRange(range: Range): {
94 beforeRangeProcessedTokens: IViewLineTokens;
95 afterRangeProcessedTokens: IViewLineTokens;
101 return { beforeRangeProcessedTokens, afterRangeProcessedTokens, previousLineProcessedTokens };
102 }
104 > private _getProcessedTokensBeforeRange(range: Range): IViewLineTokens {
105 this.model.tokenization.forceTokenization(range.startLineNumber);
106 const lineTokens = this.model.tokenization.getLineTokens(range.startLineNumber);
119 return processedTokens;
120 }
122 > private _getProcessedTokensAfterRange(range: Range): IViewLineTokens {
123 const position: Position = range.isEmpty() ? range.getStartPosition() : range.getEndPosition();
124 this.model.tokenization.forceTokenization(position.lineNumber);
132 return processedTokens;
133 }
135 > private _getProcessedPreviousLineTokens(range: Range): IViewLineTokens {
136 const getScopedLineTokensAtEndColumnOfLine = (lineNumber: number): ScopedLineTokens => {
137 this.model.tokenization.forceTokenization(lineNumber);
164 return processedTokens;
165 }
167 >
168 > /**
169 > * This class performs the actual processing of the indentation lines.
170 > * The brackets of the language configuration are removed from the regex, string and comment tokens.
171 > */
172 > class IndentationLineProcessor {
173 >
174 > constructor(
175 private readonly model: IVirtualModel,
176 private readonly languageConfigurationService: ILanguageConfigurationService
177 ) { }
179 > /**
180 > * Get the processed line for the given line number and potentially adjust the indentation level.
181 > * Remove the language configuration brackets from the regex, string and comment tokens.
182 > */
183 > getProcessedLine(lineNumber: number, newIndentation?: string): string {
184 const replaceIndentation = (line: string, newIndentation: string): string => {
185 const currentIndentation = strings.getLeadingWhitespace(line);
196 return processedLine;
197 }
199 > /**
200 > * Process the line with the given tokens, remove the language configuration brackets from the regex, string and comment tokens.
201 > */
202 > getProcessedTokens(tokens: IViewLineTokens): IViewLineTokens {
203
204 const shouldRemoveBracketsFromTokenType = (tokenType: StandardTokenType): boolean => {
224 return processedLineTokens;
225 }
227 >
228 > export function isLanguageDifferentFromLineStart(model: ITextModel, position: Position): boolean {
229 model.tokenization.forceTokenization(position.lineNumber);
230 const lineTokens = model.tokenization.getLineTokens(position.lineNumber);
src/vs/editor/common/commands/shiftCommand.ts 57 introduced LOC · 8 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- shiftCommand.ts
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 { CharCode } from '../../../base/common/charCode.js';
7 > import * as strings from '../../../base/common/strings.js';
8 > import { CursorColumns } from '../core/cursorColumns.js';
9 > import { Range } from '../core/range.js';
10 > import { Selection, SelectionDirection } from '../core/selection.js';
11 > import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from '../editorCommon.js';
12 > import { ITextModel } from '../model.js';
13 > import { EditorAutoIndentStrategy } from '../config/editorOptions.js';
14 > import { getEnterAction } from '../languages/enterAction.js';
15 > import { ILanguageConfigurationService } from '../languages/languageConfigurationRegistry.js';
16 >
17 > export interface IShiftCommandOpts {
18 > isUnshift: boolean;
19 > tabSize: number;
20 > indentSize: number;
21 > insertSpaces: boolean;
22 > useTabStops: boolean;
23 > autoIndent: EditorAutoIndentStrategy;
24 > }
25 >
26 > const repeatCache: { [str: string]: string[] } = Object.create(null);
27 function cachedStringRepeat(str: string, count: number): string {
28 if (count <= 0) {
38 return cache[count];
39 }
41 > export class ShiftCommand implements ICommand {
42 >
43 > public static unshiftIndent(line: string, column: number, tabSize: number, indentSize: number, insertSpaces: boolean): string {
44 > // Determine the visible column where the content starts
45 > const contentStartVisibleColumn = CursorColumns.visibleColumnFromColumn(line, column, tabSize);
46 >
47 > if (insertSpaces) {
48 > const indent = cachedStringRepeat(' ', indentSize);
49 > const desiredTabStop = CursorColumns.prevIndentTabStop(contentStartVisibleColumn, indentSize);
50 > const indentCount = desiredTabStop / indentSize; // will be an integer
51 > return cachedStringRepeat(indent, indentCount);
52 > } else {
53 const indent = '\t';
54 const desiredTabStop = CursorColumns.prevRenderTabStop(contentStartVisibleColumn, tabSize);
56 return cachedStringRepeat(indent, indentCount);
57 }
59 >
60 > public static shiftIndent(line: string, column: number, tabSize: number, indentSize: number, insertSpaces: boolean): string {
61 // Determine the visible column where the content starts
62 const contentStartVisibleColumn = CursorColumns.visibleColumnFromColumn(line, column, tabSize);
74 }
75 }
77 > private readonly _opts: IShiftCommandOpts;
78 > private readonly _selection: Selection;
79 > private _selectionId: string | null;
80 > private _useLastEditRangeForCursorEndPosition: boolean;
81 > private _selectionStartColumnStaysPut: boolean;
82 >
83 > constructor(
84 range: Selection,
85 opts: IShiftCommandOpts,
92 this._selectionStartColumnStaysPut = false;
93 }
95 > private _addEditOperation(builder: IEditOperationBuilder, range: Range, text: string) {
96 if (this._useLastEditRangeForCursorEndPosition) {
97 builder.addTrackedEditOperation(range, text);
100 }
101 }
103 > public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void {
104 const startLine = this._selection.startLineNumber;
105
252 this._selectionId = builder.trackSelection(this._selection);
253 }
255 > public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection {
256 if (this._useLastEditRangeForCursorEndPosition) {
257 const lastOp = helper.getInverseEditOperations()[0];
src/vs/editor/common/commands/trimTrailingWhitespaceCommand.ts 32 introduced LOC · 4 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- trimTrailingWhitespaceCommand.ts
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 strings from '../../../base/common/strings.js';
7 > import { EditOperation, ISingleEditOperation } from '../core/editOperation.js';
8 > import { Position } from '../core/position.js';
9 > import { Range } from '../core/range.js';
10 > import { Selection } from '../core/selection.js';
11 > import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from '../editorCommon.js';
12 > import { StandardTokenType } from '../encodedTokenAttributes.js';
13 > import { ITextModel } from '../model.js';
14 >
15 > export class TrimTrailingWhitespaceCommand implements ICommand {
16 >
17 > private readonly _selection: Selection;
18 > private _selectionId: string | null;
19 > private readonly _cursors: Position[];
20 > private readonly _trimInRegexesAndStrings: boolean;
21 >
22 > constructor(selection: Selection, cursors: Position[], trimInRegexesAndStrings: boolean) {
23 this._selection = selection;
24 this._cursors = cursors;
26 this._trimInRegexesAndStrings = trimInRegexesAndStrings;
27 }
29 > public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void {
30 const ops = trimTrailingWhitespace(model, this._cursors, this._trimInRegexesAndStrings);
31 for (let i = 0, len = ops.length; i < len; i++) {
37 this._selectionId = builder.trackSelection(this._selection);
38 }
40 > public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection {
41 return helper.getTrackedSelection(this._selectionId!);
42 }
44 >
45 > /**
46 > * Generate commands for trimming trailing whitespace on a model and ignore lines on which cursors are sitting.
47 > */
48 > export function trimTrailingWhitespace(model: ITextModel, cursors: Position[], trimInRegexesAndStrings: boolean): ISingleEditOperation[] {
49 // Sort cursors ascending
50 cursors.sort((a, b) => {
src/vs/editor/contrib/indentation/common/indentation.ts 17 introduced LOC · 2 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- indentation.ts
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 strings from '../../../../base/common/strings.js';
7 > import { ShiftCommand } from '../../../common/commands/shiftCommand.js';
8 > import { EditOperation, ISingleEditOperation } from '../../../common/core/editOperation.js';
9 > import { normalizeIndentation } from '../../../common/core/misc/indentation.js';
10 > import { Selection } from '../../../common/core/selection.js';
11 > import { StandardTokenType } from '../../../common/encodedTokenAttributes.js';
12 > import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js';
13 > import { ProcessedIndentRulesSupport } from '../../../common/languages/supports/indentationLineProcessor.js';
14 > import { ITextModel } from '../../../common/model.js';
15 >
16 > export function getReindentEditOperations(model: ITextModel, languageConfigurationService: ILanguageConfigurationService, startLineNumber: number, endLineNumber: number): ISingleEditOperation[] {
17 if (model.getLineCount() === 1 && model.getLineMaxColumn(1) === 1) {
18 // Model is empty
106 return indentEdits;
107 }
109 function doesLineStartWithString(model: ITextModel, lineNumber: number): boolean {
110 if (!model.tokenization.isCheapToTokenize(lineNumber)) {
src/vs/editor/common/languages/enterAction.ts 13 introduced LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- enterAction.ts
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 { Range } from '../core/range.js';
7 > import { ITextModel } from '../model.js';
8 > import { IndentAction, CompleteEnterAction } from './languageConfiguration.js';
9 > import { EditorAutoIndentStrategy } from '../config/editorOptions.js';
10 > import { getIndentationAtPosition, ILanguageConfigurationService } from './languageConfigurationRegistry.js';
11 > import { IndentationContextProcessor } from './supports/indentationLineProcessor.js';
12 >
13 > export function getEnterAction(
14 autoIndent: EditorAutoIndentStrategy,
15 model: ITextModel,