preferencesModels.ts ×90

Frontier kind: Code frontier

unlabeled · c_00e3a72c47c1

8 tests · 12442 LOC · 52 files · introduces 0 tests · 441 LOC · 2 files

Introduces — evidence that enters the hierarchy at this concept

Code
96 ranges441 lines · 2 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
1424 ranges12442 lines · 52 files · Browse complete extent
All tests (intent)
8 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.

2 files ranked by introduced lines: 441 introduced LOC across 96 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/workbench/services/preferences/common/preferencesModels.ts 406 introduced LOC · 90 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- preferencesModels.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 { coalesce } from '../../../../base/common/arrays.js';
7 > import { IStringDictionary } from '../../../../base/common/collections.js';
8 > import { Emitter, Event } from '../../../../base/common/event.js';
9 > import { JSONVisitor, visit } from '../../../../base/common/json.js';
10 > import { Disposable, IReference } from '../../../../base/common/lifecycle.js';
11 > import { URI } from '../../../../base/common/uri.js';
12 > import { IRange, Range } from '../../../../editor/common/core/range.js';
13 > import { Selection } from '../../../../editor/common/core/selection.js';
14 > import { ITextModel } from '../../../../editor/common/model.js';
15 > import { ISingleEditOperation } from '../../../../editor/common/core/editOperation.js';
16 > import { ITextEditorModel } from '../../../../editor/common/services/resolverService.js';
17 > import * as nls from '../../../../nls.js';
18 > import { ConfigurationTarget, IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
19 > import { ConfigurationDefaultValueSource, ConfigurationScope, Extensions, IConfigurationNode, IConfigurationRegistry, IRegisteredConfigurationPropertySchema, OVERRIDE_PROPERTY_REGEX } from '../../../../platform/configuration/common/configurationRegistry.js';
20 > import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js';
21 > import { Registry } from '../../../../platform/registry/common/platform.js';
22 > import { EditorModel } from '../../../common/editor/editorModel.js';
23 > import { IFilterMetadata, IFilterResult, IGroupFilter, IKeybindingsEditorModel, ISearchResultGroup, ISetting, ISettingMatch, ISettingMatcher, ISettingsEditorModel, ISettingsGroup, SettingMatchType } from './preferences.js';
24 > import { FOLDER_SCOPES, WORKSPACE_SCOPES } from '../../configuration/common/configuration.js';
25 > import { createValidator } from './preferencesValidation.js';
26 > import { isString } from '../../../../base/common/types.js';
27 >
28 > export const nullRange: IRange = { startLineNumber: -1, startColumn: -1, endLineNumber: -1, endColumn: -1 };
29 function isNullRange(range: IRange): boolean { return range.startLineNumber === -1 && range.startColumn === -1 && range.endLineNumber === -1 && range.endColumn === -1; }
31 > /**
32 > * Strips VS Code's custom `#settingId#` link syntax from a markdown string so the setting key
33 > * remains as inline code (e.g. `` `settingId` ``). Useful for contexts that don't render markdown links.
34 > */
35 > export function fixSettingLinks(text: string): string {
36 return text.replace(/`#([^#`]*)#`/g, (_, settingName) => `\`${settingName}\``);
37 }
39 abstract class AbstractSettingsModel extends EditorModel {
40
41 protected _currentResultGroups = new Map<string, ISearchResultGroup>();
43 > updateResultGroup(id: string, resultGroup: ISearchResultGroup | undefined): IFilterResult | undefined {
44 if (resultGroup) {
45 this._currentResultGroups.set(id, resultGroup);
51 return this.update();
52 }
54 > /**
55 > * Remove duplicates between result groups, preferring results in earlier groups
56 > */
57 > private removeDuplicateResults(): void {
58 const settingKeys = new Set<string>();
59 [...this._currentResultGroups.keys()]
65 });
66 }
68 > filterSettings(filter: string, groupFilter: IGroupFilter, settingMatcher: ISettingMatcher): ISettingMatch[] {
69 const allGroups = this.filterGroups;
70
91 return filterMatches;
92 }
94 > getPreference(key: string): ISetting | undefined {
95 for (const group of this.settingsGroups) {
96 for (const section of group.sections) {
105 return undefined;
106 }
108 > protected collectMetadata(groups: ISearchResultGroup[]): IStringDictionary<IFilterMetadata> | null {
109 const metadata = Object.create(null);
110 let hasMetadata = false;
118 return hasMetadata ? metadata : null;
119 }
121 >
122 > protected get filterGroups(): ISettingsGroup[] {
123 return this.settingsGroups;
124 }
126 > abstract settingsGroups: ISettingsGroup[];
127 >
128 > protected abstract update(): IFilterResult | undefined;
129 > }
130 >
131 > export class SettingsEditorModel extends AbstractSettingsModel implements ISettingsEditorModel {
132 >
133 > private _settingsGroups: ISettingsGroup[] | undefined;
134 > protected settingsModel: ITextModel;
135 >
136 > private readonly _onDidChangeGroups: Emitter<void> = this._register(new Emitter<void>());
137 > readonly onDidChangeGroups: Event<void> = this._onDidChangeGroups.event;
138 >
139 > constructor(reference: IReference<ITextEditorModel>, private _configurationTarget: ConfigurationTarget) {
140 super();
141 this.settingsModel = reference.object.textEditorModel!;
146 }));
147 }
149 > get uri(): URI {
150 return this.settingsModel.uri;
151 }
153 > get configurationTarget(): ConfigurationTarget {
154 return this._configurationTarget;
155 }
157 > get settingsGroups(): ISettingsGroup[] {
158 if (!this._settingsGroups) {
159 this.parse();
161 return this._settingsGroups!;
162 }
164 > get content(): string {
165 return this.settingsModel.getValue();
166 }
168 > protected isSettingsProperty(property: string, previousParents: string[]): boolean {
169 return previousParents.length === 0; // Settings is root
170 }
172 > protected parse(): void {
173 this._settingsGroups = parse(this.settingsModel, (property: string, previousParents: string[]): boolean => this.isSettingsProperty(property, previousParents));
174 }
176 > protected update(): IFilterResult | undefined {
177 const resultGroups = [...this._currentResultGroups.values()];
178 if (!resultGroups.length) {
216 };
217 }
219 >
220 > export class Settings2EditorModel extends AbstractSettingsModel implements ISettingsEditorModel {
221 > private readonly _onDidChangeGroups: Emitter<void> = this._register(new Emitter<void>());
222 > readonly onDidChangeGroups: Event<void> = this._onDidChangeGroups.event;
223 >
224 > private additionalGroups: ISettingsGroup[] = [];
225 > private dirty = false;
226 >
227 > constructor(
228 private _defaultSettings: DefaultSettings,
229 @IConfigurationService configurationService: IConfigurationService,
242 }));
243 }
245 > /** Doesn't include the "Commonly Used" group */
246 > protected override get filterGroups(): ISettingsGroup[] {
247 return this.settingsGroups.slice(1);
248 }
250 > get settingsGroups(): ISettingsGroup[] {
251 const groups = this._defaultSettings.getSettingsGroups(this.dirty);
252 this.dirty = false;
253 return [...groups, ...this.additionalGroups];
254 }
256 > /** For programmatically added groups outside of registered configurations */
257 > setAdditionalGroups(groups: ISettingsGroup[]) {
258 this.additionalGroups = groups;
259 }
261 > protected update(): IFilterResult {
262 throw new Error('Not supported');
263 }
265 >
266 function parse(model: ITextModel, isSettingsProperty: (currentProperty: string, previousParents: string[]) => boolean): ISettingsGroup[] {
267 const settings: ISetting[] = [];
434 } satisfies ISettingsGroup] : [];
435 }
437 > export class WorkspaceConfigurationEditorModel extends SettingsEditorModel {
438
439 private _configurationGroups: ISettingsGroup[] = [];
441 > get configurationGroups(): ISettingsGroup[] {
442 return this._configurationGroups;
443 }
445 > protected override parse(): void {
446 super.parse();
447 this._configurationGroups = parse(this.settingsModel, (property: string, previousParents: string[]): boolean => previousParents.length === 0);
448 }
450 > protected override isSettingsProperty(property: string, previousParents: string[]): boolean {
451 return property === 'settings' && previousParents.length === 1;
452 }
454 > }
455 >
456 > export class DefaultSettings extends Disposable {
457 >
458 > private _allSettingsGroups: ISettingsGroup[] | undefined;
459 > private _content: string | undefined;
460 > private _contentWithoutMostCommonlyUsed: string | undefined;
461 > private _settingsByName = new Map<string, ISetting>();
462 >
463 > private readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
464 > readonly onDidChange: Event<void> = this._onDidChange.event;
465 >
466 > constructor(
467 > private _mostCommonlyUsedSettingsKeys: string[],
468 > readonly target: ConfigurationTarget,
469 > readonly configurationService: IConfigurationService
470 > ) {
471 > super();
472 > this._register(configurationService.onDidChangeConfiguration(e => {
473 if (e.source === ConfigurationTarget.DEFAULT) {
474 this.reset();
475 this._onDidChange.fire();
476 }
478 > }
479 >
480 > getContent(forceUpdate = false): string {
481 if (!this._content || forceUpdate) {
482 this.initialize();
485 return this._content!;
486 }
488 > getContentWithoutMostCommonlyUsed(forceUpdate = false): string {
489 if (!this._contentWithoutMostCommonlyUsed || forceUpdate) {
490 this.initialize();
493 return this._contentWithoutMostCommonlyUsed!;
494 }
496 > getSettingsGroups(forceUpdate = false): ISettingsGroup[] {
497 if (!this._allSettingsGroups || forceUpdate) {
498 this.initialize();
501 return this._allSettingsGroups!;
502 }
504 > private initialize(): void {
505 this._allSettingsGroups = this.parse();
506 this._content = this.toContent(this._allSettingsGroups, 0);
507 this._contentWithoutMostCommonlyUsed = this.toContent(this._allSettingsGroups, 1);
508 }
510 > private reset(): void {
511 this._content = undefined;
512 this._contentWithoutMostCommonlyUsed = undefined;
513 this._allSettingsGroups = undefined;
514 }
516 > private parse(): ISettingsGroup[] {
517 const settingsGroups = this.getRegisteredGroups();
518 this.initAllSettingsMap(settingsGroups);
520 return [mostCommonlyUsed, ...settingsGroups];
521 }
523 > getRegisteredGroups(): ISettingsGroup[] {
524 > const registry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
525 > const allConfigurations: IStringDictionary<IRegisteredConfigurationPropertySchema> = { ...registry.getConfigurationProperties() };
526 > const excludedConfigurations = registry.getExcludedConfigurationProperties();
527 >
528 > for (const policyKey of this.configurationService.keys().policy ?? []) {
529 const policyConfiguration = excludedConfigurations[policyKey];
530 if (policyConfiguration) {
532 }
533 }
535 > const groups = this.removeEmptySettingsGroups(this.parseProperties(allConfigurations).sort(this.compareGroups));
536 > return this.sortGroups(groups);
537 > }
538 >
539 > private sortGroups(groups: ISettingsGroup[]): ISettingsGroup[] {
540 > groups.forEach(group => {
541 > group.sections.forEach(section => {
542 > section.settings.sort((a, b) => a.key.localeCompare(b.key));
543 > });
544 > });
545 >
546 > return groups;
547 > }
548 >
549 > private initAllSettingsMap(allSettingsGroups: ISettingsGroup[]): void {
550 this._settingsByName = new Map<string, ISetting>();
551 for (const group of allSettingsGroups) {
557 }
558 }
560 > private getMostCommonlyUsedSettings(): ISettingsGroup {
561 const settings = coalesce(this._mostCommonlyUsedSettingsKeys.map(key => {
562 const setting = this._settingsByName.get(key);
592 } satisfies ISettingsGroup;
593 }
595 > private parseProperties(properties: IStringDictionary<IRegisteredConfigurationPropertySchema>): ISettingsGroup[] {
596 > const result: ISettingsGroup[] = [];
597 > const byTitle = new Map<string, ISettingsGroup[]>();
598 > const byId = new Map<string, ISettingsGroup[]>();
599 > for (const [key, property] of Object.entries(properties)) {
600 > if (!property.section) {
601 continue;
602 }
604 > let settingsGroup: ISettingsGroup | undefined;
605 >
606 > if (property.section.title) {
607 const groups = byTitle.get(property.section.title);
608 if (groups) {
611 }
612 }
614 > if (!settingsGroup && property.section.id) {
615 > const groups = byId.get(property.section.id);
616 > if (groups) {
617 const extensionId = property.section.extensionInfo?.id;
618 settingsGroup = groups.find(g => g.extensionInfo?.id === extensionId && !g.title);
619 }
620 > if (settingsGroup && !settingsGroup?.title && property.section.title) { preferencesModels.ts
621 settingsGroup.title = property.section.title;
622 const byTitleGroups = byTitle.get(property.section.title);
627 }
628 }
630 >
631 > if (!settingsGroup) {
632 > settingsGroup = { sections: [{ title: property.section.title, settings: [] }], id: property.section.id || '', title: property.section.title ?? '', titleRange: nullRange, order: property.section.order, range: nullRange, extensionInfo: isString(property.source) ? undefined : property.source };
633 > result.push(settingsGroup);
634 > if (property.section.title) {
635 const byTitleGroups = byTitle.get(property.section.title);
636 if (byTitleGroups) {
640 }
641 }
642 > if (property.section.id) { preferencesModels.ts
643 > const byIdGroups = byId.get(property.section.id);
644 > if (byIdGroups) {
645 byIdGroups.push(settingsGroup);
646 > } else { preferencesModels.ts
647 > byId.set(property.section.id, [settingsGroup]);
648 > }
649 > }
650 > }
651 >
652 > const setting = this.parseSetting(key, property);
653 > if (setting) {
654 > settingsGroup.sections[0].settings.push(setting);
655 > }
656 > }
657 > return result;
658 > }
659 >
660 > private removeEmptySettingsGroups(settingsGroups: ISettingsGroup[]): ISettingsGroup[] {
661 > const result: ISettingsGroup[] = [];
662 > for (const settingsGroup of settingsGroups) {
663 > settingsGroup.sections = settingsGroup.sections.filter(section => section.settings.length > 0);
664 > if (settingsGroup.sections.length) {
665 > result.push(settingsGroup);
666 > }
667 > }
668 > return result;
669 > }
670 >
671 > private parseSetting(key: string, prop: IRegisteredConfigurationPropertySchema): ISetting | undefined {
672 > if (!this.matchesScope(prop)) {
673 return undefined;
674 }
676 > const value = prop.default;
677 > let description = (prop.markdownDescription || prop.description || '');
678 > if (typeof description !== 'string') {
679 description = '';
680 }
681 > const descriptionLines = description.split('\n'); preferencesModels.ts
682 > const overrides = OVERRIDE_PROPERTY_REGEX.test(key) ? this.parseOverrideSettings(prop.default) : [];
683 > let listItemType: string | undefined;
684 > if (prop.type === 'array' && prop.items && !Array.isArray(prop.items) && prop.items.type) {
685 if (prop.items.enum) {
686 listItemType = 'enum';
689 }
690 }
692 > const objectProperties = prop.type === 'object' ? prop.properties : undefined;
693 > const objectPatternProperties = prop.type === 'object' ? prop.patternProperties : undefined;
694 > const objectAdditionalProperties = prop.type === 'object' ? prop.additionalProperties : undefined;
695 > const propertyNames = prop.type === 'object' ? prop.propertyNames : undefined;
696 >
697 > let enumToUse = prop.enum;
698 > let enumDescriptions = prop.markdownEnumDescriptions ?? prop.enumDescriptions;
699 > let enumDescriptionsAreMarkdown = !!prop.markdownEnumDescriptions;
700 > if (listItemType === 'enum' && !Array.isArray(prop.items)) {
701 enumToUse = prop.items!.enum;
702 enumDescriptions = prop.items!.markdownEnumDescriptions ?? prop.items!.enumDescriptions;
703 enumDescriptionsAreMarkdown = !!prop.items!.markdownEnumDescriptions;
704 }
706 > let allKeysAreBoolean = false;
707 > if (prop.type === 'object' && !prop.additionalProperties && prop.properties && Object.keys(prop.properties).length) {
708 allKeysAreBoolean = Object.keys(prop.properties).every(key => {
709 return prop.properties![key].type === 'boolean';
710 });
711 }
713 > let isLanguageTagSetting = false;
714 > if (OVERRIDE_PROPERTY_REGEX.test(key)) {
715 isLanguageTagSetting = true;
716 }
718 > let defaultValueSource: ConfigurationDefaultValueSource | undefined;
719 > if (!isLanguageTagSetting) {
720 > const registeredConfigurationProp = prop as IRegisteredConfigurationPropertySchema;
721 > if (registeredConfigurationProp && registeredConfigurationProp.defaultValueSource) {
722 defaultValueSource = registeredConfigurationProp.defaultValueSource;
723 }
725 >
726 > if (!enumToUse && (prop.enumItemLabels || enumDescriptions || enumDescriptionsAreMarkdown)) {
727 console.error(`The setting ${key} has enum-related fields, but doesn't have an enum field. This setting may render improperly in the Settings editor.`);
728 }
730 > return {
731 > key,
732 > value,
733 > description: descriptionLines,
734 > descriptionIsMarkdown: !!prop.markdownDescription,
735 > keywords: prop.keywords,
736 > range: nullRange,
737 > keyRange: nullRange,
738 > valueRange: nullRange,
739 > descriptionRanges: [],
740 > overrides,
741 > scope: prop.scope,
742 > type: prop.type,
743 > arrayItemType: listItemType,
744 > objectProperties,
745 > objectPatternProperties,
746 > objectAdditionalProperties,
747 > propertyNames,
748 > enum: enumToUse,
749 > enumDescriptions: enumDescriptions,
750 > enumDescriptionsAreMarkdown: enumDescriptionsAreMarkdown,
751 > enumItemLabels: prop.enumItemLabels,
752 > uniqueItems: prop.uniqueItems,
753 > tags: prop.tags,
754 > disallowSyncIgnore: prop.disallowSyncIgnore,
755 > restricted: prop.restricted,
756 > extensionInfo: isString(prop.source) ? undefined : prop.source,
757 > deprecationMessage: prop.markdownDeprecationMessage || prop.deprecationMessage,
758 > deprecationMessageIsMarkdown: !!prop.markdownDeprecationMessage,
759 > validator: createValidator(prop),
760 > allKeysAreBoolean,
761 > editPresentation: prop.editPresentation,
762 > order: prop.order,
763 > nonLanguageSpecificDefaultValueSource: defaultValueSource,
764 > isLanguageTagSetting,
765 > categoryLabel: (isString(prop.source) ? undefined : prop.source?.id) === prop.section?.id ? prop.title : prop.section?.id
766 > };
767 > }
768 >
769 > private parseOverrideSettings(overrideSettings: any): ISetting[] {
770 return Object.keys(overrideSettings).map((key) => ({
771 key,
780 }));
781 }
783 > private matchesScope(property: IConfigurationNode): boolean {
784 > if (!property.scope) {
785 return true;
786 }
787 > if (this.target === ConfigurationTarget.WORKSPACE_FOLDER) { preferencesModels.ts
788 return FOLDER_SCOPES.indexOf(property.scope) !== -1;
789 }
790 > if (this.target === ConfigurationTarget.WORKSPACE) { preferencesModels.ts
791 return WORKSPACE_SCOPES.indexOf(property.scope) !== -1;
792 }
793 > return true; preferencesModels.ts
794 > }
795 >
796 > private compareGroups(c1: ISettingsGroup, c2: ISettingsGroup): number {
797 if (typeof c1?.order !== 'number') {
798 return 1;
808 return c1.order - c2.order;
809 }
811 > private toContent(settingsGroups: ISettingsGroup[], startIndex: number): string {
812 const builder = new SettingsContentBuilder();
813 for (let i = startIndex; i < settingsGroups.length; i++) {
816 return builder.getContent();
817 }
819 > }
820 >
821 > export class DefaultSettingsEditorModel extends AbstractSettingsModel implements ISettingsEditorModel {
822 >
823 > private _model: ITextModel;
824 >
825 > private readonly _onDidChangeGroups: Emitter<void> = this._register(new Emitter<void>());
826 > readonly onDidChangeGroups: Event<void> = this._onDidChangeGroups.event;
827 >
828 > constructor(
829 private _uri: URI,
830 reference: IReference<ITextEditorModel>,
837 this._register(this.onWillDispose(() => reference.dispose()));
838 }
840 > get uri(): URI {
841 return this._uri;
842 }
844 > get target(): ConfigurationTarget {
845 return this.defaultSettings.target;
846 }
848 > get settingsGroups(): ISettingsGroup[] {
849 return this.defaultSettings.getSettingsGroups();
850 }
852 > protected override get filterGroups(): ISettingsGroup[] {
853 // Don't look at "commonly used" for filter
854 return this.settingsGroups.slice(1);
855 }
857 > protected update(): IFilterResult | undefined {
858 if (this._model.isDisposed()) {
859 return undefined;
878 undefined;
879 }
881 > /**
882 > * Translate the ISearchResultGroups to text, and write it to the editor model
883 > */
884 > private writeResultGroups(groups: ISearchResultGroup[], startLine: number): { matches: IRange[]; settingsGroups: ISettingsGroup[] } {
885 const contentBuilderOffset = startLine - 1;
886 const builder = new SettingsContentBuilder(contentBuilderOffset);
915 return { matches, settingsGroups };
916 }
918 > private writeSettingsGroupToBuilder(builder: SettingsContentBuilder, settingsGroup: ISettingsGroup, filterMatches: ISettingMatch[]): IRange[] {
919 filterMatches = filterMatches
920 .map(filteredMatch => {
953 return fixedMatches;
954 }
956 > private copySetting(setting: ISetting): ISetting {
957 return {
958 description: setting.description,
974 };
975 }
977 > override getPreference(key: string): ISetting | undefined {
978 for (const group of this.settingsGroups) {
979 for (const section of group.sections) {
987 return undefined;
988 }
990 > private getGroup(resultGroup: ISearchResultGroup): ISettingsGroup {
991 return {
992 id: resultGroup.id,
1001 };
1002 }
1004 >
1005 > class SettingsContentBuilder {
1006 > private _contentByLines: string[];
1007 >
1008 > private get lineCountWithOffset(): number {
1009 > return this._contentByLines.length + this._rangeOffset;
1010 > }
1011 >
1012 > private get lastLine(): string {
1013 return this._contentByLines[this._contentByLines.length - 1] || '';
1014 }
1016 > constructor(private _rangeOffset = 0) {
1017 this._contentByLines = [];
1018 }
1020 > pushLine(...lineText: string[]): void {
1021 this._contentByLines.push(...lineText);
1022 }
1024 > pushGroup(settingsGroups: ISettingsGroup, isFirst?: boolean, isLast?: boolean): void {
1025 this._contentByLines.push(isFirst ? '[{' : '{');
1026 const lastSetting = this._pushGroup(settingsGroups, ' ');
1035 this._contentByLines.push(isLast ? '}]' : '},');
1036 }
1038 > protected _pushGroup(group: ISettingsGroup, indent: string): ISetting | null {
1039 let lastSetting: ISetting | null = null;
1040 const groupStart = this.lineCountWithOffset + 1;
1055 return lastSetting;
1056 }
1058 > getContent(): string {
1059 return this._contentByLines.join('\n');
1060 }
1062 > private pushSetting(setting: ISetting, indent: string): void {
1063 const settingStart = this.lineCountWithOffset + 1;
1064
1079 setting.range = { startLineNumber: settingStart, startColumn: 1, endLineNumber: this.lineCountWithOffset, endColumn: this.lastLine.length };
1080 }
1082 > private pushSettingDescription(setting: ISetting, indent: string): void {
1083 setting.descriptionRanges = [];
1084 const descriptionPreValue = indent + '// ';
1106 }
1107 }
1109 > private pushValue(setting: ISetting, preValueConent: string, indent: string): void {
1110 const valueString = JSON.stringify(setting.value, null, indent);
1111 if (valueString && (typeof setting.value === 'object')) {
1131 }
1132 }
1134 > private addDescription(description: string[], indent: string, result: string[]) {
1135 for (const line of description) {
1136 result.push(indent + '// ' + line);
1137 }
1138 }
1140 >
1141 > class RawSettingsContentBuilder extends SettingsContentBuilder {
1142 >
1143 > constructor(private indent: string = '\t') {
1144 super(0);
1145 }
1147 > override pushGroup(settingsGroups: ISettingsGroup): void {
1148 this._pushGroup(settingsGroups, this.indent);
1149 }
1151 > }
1152 >
1153 > export class DefaultRawSettingsEditorModel extends Disposable {
1154 >
1155 > private _content: string | null = null;
1156 >
1157 > private readonly _onDidContentChanged = this._register(new Emitter<void>());
1158 > readonly onDidContentChanged = this._onDidContentChanged.event;
1159 >
1160 > constructor(private defaultSettings: DefaultSettings) {
1161 super();
1162 this._register(defaultSettings.onDidChange(() => {
1165 }));
1166 }
1168 > get content(): string {
1169 if (this._content === null) {
1170 const builder = new RawSettingsContentBuilder();
1178 return this._content;
1179 }
1181 >
1182 function escapeInvisibleChars(enumValue: string): string {
1183 return enumValue && enumValue
1185 .replace(/\r/g, '\\r');
1186 }
1188 > export function defaultKeybindingsContents(keybindingService: IKeybindingService): string {
1189 const defaultsHeader = '// ' + nls.localize('defaultKeybindingsHeader', "Override key bindings by placing them into your key bindings file.");
1190 return defaultsHeader + '\n' + keybindingService.getDefaultKeybindingsContent();
1191 }
1193 > export class DefaultKeybindingsEditorModel implements IKeybindingsEditorModel<any> {
1194 >
1195 > private _content: string | undefined;
1196 >
1197 > constructor(private _uri: URI,
1198 @IKeybindingService private readonly keybindingService: IKeybindingService) {
1199 }
1201 > get uri(): URI {
1202 return this._uri;
1203 }
1205 > get content(): string {
1206 if (!this._content) {
1207 this._content = defaultKeybindingsContents(this.keybindingService);
src/vs/workbench/common/editor/editorModel.ts 35 introduced LOC · 6 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- editorModel.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 { Emitter } from '../../../base/common/event.js';
7 > import { Disposable } from '../../../base/common/lifecycle.js';
8 >
9 > /**
10 > * The editor model is the heavyweight counterpart of editor input. Depending on the editor input, it
11 > * resolves from a file system retrieve content and may allow for saving it back or reverting it.
12 > * Editor models are typically cached for some while because they are expensive to construct.
13 > */
14 > export class EditorModel extends Disposable {
15
16 private readonly _onWillDispose = this._register(new Emitter<void>());
18
19 private resolved = false;
21 > /**
22 > * Causes this model to resolve returning a promise when loading is completed.
23 > */
24 > async resolve(): Promise<void> {
25 this.resolved = true;
26 }
28 > /**
29 > * Returns whether this model was loaded or not.
30 > */
31 > isResolved(): boolean {
32 return this.resolved;
33 }
35 > /**
36 > * Find out if this model has been disposed.
37 > */
38 > isDisposed(): boolean {
39 return this._store.isDisposed;
40 }
42 > /**
43 > * Subclasses should implement to free resources that have been claimed through loading.
44 > */
45 > override dispose(): void {
46 this._onWillDispose.fire();
47
48 super.dispose();
49 }