userDataProfile.ts ×28

Frontier kind: Code frontier

unlabeled · c_874993313acc

1954 tests · 11413 LOC · 46 files · introduces 0 tests · 256 LOC · 1 file

Introduces — evidence that enters the hierarchy at this concept

Code
28 ranges256 lines · 1 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
1598 ranges11413 lines · 46 files · Browse complete extent
All tests (intent)
1954 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.

1 file ranked by introduced lines: 256 introduced LOC across 28 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/platform/userDataProfile/common/userDataProfile.ts 256 introduced LOC · 28 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- userDataProfile.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 { hash } from '../../../base/common/hash.js';
7 > import { Emitter, Event } from '../../../base/common/event.js';
8 > import { Disposable } from '../../../base/common/lifecycle.js';
9 > import { basename, joinPath } from '../../../base/common/resources.js';
10 > import { URI, UriDto } from '../../../base/common/uri.js';
11 > import { localize } from '../../../nls.js';
12 > import { IEnvironmentService } from '../../environment/common/environment.js';
13 > import { FileOperationResult, IFileService, toFileOperationResult } from '../../files/common/files.js';
14 > import { createDecorator } from '../../instantiation/common/instantiation.js';
15 > import { ILogService } from '../../log/common/log.js';
16 > import { IAnyWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from '../../workspace/common/workspace.js';
17 > import { IStringDictionary } from '../../../base/common/collections.js';
18 > import { IUriIdentityService } from '../../uriIdentity/common/uriIdentity.js';
19 > import { Promises } from '../../../base/common/async.js';
20 > import { generateUuid } from '../../../base/common/uuid.js';
21 > import { escapeRegExpCharacters } from '../../../base/common/strings.js';
22 > import { isString, Mutable } from '../../../base/common/types.js';
23 >
24 > export const AGENTS_WINDOW_PROFILE_ID = 'agents';
25 >
26 > const AGENTS_WINDOW_PROFILE_FLAGS: UseDefaultProfileFlags = {
27 > settings: true,
28 > keybindings: true,
29 > prompts: true,
30 > mcp: true,
31 > languageModels: true,
32 > snippets: true,
33 > tasks: true,
34 > extensions: true,
35 > };
36 >
37 > export const enum ProfileResourceType {
38 > Settings = 'settings',
39 > Keybindings = 'keybindings',
40 > Snippets = 'snippets',
41 > Prompts = 'prompts',
42 > Tasks = 'tasks',
43 > Extensions = 'extensions',
44 > GlobalState = 'globalState',
45 > Mcp = 'mcp',
46 > LanguageModels = 'languageModels',
47 > }
48 >
49 > /**
50 > * Flags to indicate whether to use the default profile or not.
51 > */
52 > export type UseDefaultProfileFlags = { [key in ProfileResourceType]?: boolean };
53 > export type ProfileResourceTypeFlags = UseDefaultProfileFlags;
54 > export type SettingValue = string | boolean | number | undefined | null | object;
55 > export type ISettingsDictionary = Record<string, SettingValue>;
56 >
57 > export interface IUserDataProfile {
58 > readonly id: string;
59 > readonly isDefault: boolean;
60 > readonly name: string;
61 > readonly icon?: string;
62 > readonly location: URI;
63 > readonly globalStorageHome: URI;
64 > readonly settingsResource: URI;
65 > readonly keybindingsResource: URI;
66 > readonly tasksResource: URI;
67 > readonly snippetsHome: URI;
68 > readonly promptsHome: URI;
69 > readonly extensionsResource: URI;
70 > readonly mcpResource: URI;
71 > readonly languageModelsResource: URI;
72 > readonly agentPluginsHome: URI;
73 > readonly cacheHome: URI;
74 > readonly useDefaultFlags?: UseDefaultProfileFlags;
75 > readonly isInternal?: boolean;
76 > readonly isTransient?: boolean;
77 > readonly isAgentsWindowProfile?: boolean;
78 > readonly workspaces?: readonly URI[];
79 > }
80 >
81 > export function isUserDataProfile(thing: unknown): thing is IUserDataProfile {
82 const candidate = thing as IUserDataProfile | undefined;
83
99 );
100 }
102 > export interface IParsedUserDataProfileTemplate {
103 > readonly name: string;
104 > readonly icon?: string;
105 > readonly settings?: ISettingsDictionary;
106 > readonly globalState?: IStringDictionary<string>;
107 > }
108 >
109 > export interface ISystemProfileTemplate extends IParsedUserDataProfileTemplate {
110 > readonly id: string;
111 > }
112 >
113 > export type DidChangeProfilesEvent = { readonly added: readonly IUserDataProfile[]; readonly removed: readonly IUserDataProfile[]; readonly updated: readonly IUserDataProfile[]; readonly all: readonly IUserDataProfile[] };
114 >
115 > export type WillCreateProfileEvent = {
116 > profile: IUserDataProfile;
117 > join(promise: Promise<void>): void;
118 > };
119 >
120 > export type WillRemoveProfileEvent = {
121 > profile: IUserDataProfile;
122 > join(promise: Promise<void>): void;
123 > };
124 >
125 > export interface IUserDataProfileOptions {
126 > readonly icon?: string;
127 > readonly useDefaultFlags?: UseDefaultProfileFlags;
128 > readonly transient?: boolean;
129 > readonly workspaces?: readonly URI[];
130 > }
131 >
132 > export interface IUserDataProfileUpdateOptions extends Omit<IUserDataProfileOptions, 'icon'> {
133 > readonly name?: string;
134 > readonly icon?: string | null;
135 > }
136 >
137 > export const IUserDataProfilesService = createDecorator<IUserDataProfilesService>('IUserDataProfilesService');
138 > export interface IUserDataProfilesService {
139 > readonly _serviceBrand: undefined;
140 >
141 > readonly profilesHome: URI;
142 > readonly defaultProfile: IUserDataProfile;
143 >
144 > readonly onDidChangeProfiles: Event<DidChangeProfilesEvent>;
145 > readonly profiles: readonly IUserDataProfile[];
146 >
147 > readonly onDidResetWorkspaces: Event<void>;
148 >
149 > createNamedProfile(name: string, options?: IUserDataProfileOptions, workspaceIdentifier?: IAnyWorkspaceIdentifier): Promise<IUserDataProfile>;
150 > createTransientProfile(workspaceIdentifier?: IAnyWorkspaceIdentifier): Promise<IUserDataProfile>;
151 > createProfile(id: string, name: string, options?: IUserDataProfileOptions, workspaceIdentifier?: IAnyWorkspaceIdentifier): Promise<IUserDataProfile>;
152 > updateProfile(profile: IUserDataProfile, options?: IUserDataProfileUpdateOptions,): Promise<IUserDataProfile>;
153 > removeProfile(profile: IUserDataProfile): Promise<void>;
154 >
155 > setProfileForWorkspace(workspaceIdentifier: IAnyWorkspaceIdentifier, profile: IUserDataProfile): Promise<void>;
156 > resetWorkspaces(): Promise<void>;
157 >
158 > cleanUp(): Promise<void>;
159 > cleanUpTransientProfiles(): Promise<void>;
160 > }
161 >
162 > export function reviveProfile(profile: UriDto<IUserDataProfile>, scheme: string): IUserDataProfile {
163 return {
164 id: profile.id,
185 };
186 }
188 > export function toUserDataProfile(id: string, name: string, location: URI, profilesCacheHome: URI, options?: IUserDataProfileOptions, defaultProfile?: IUserDataProfile): IUserDataProfile {
189 const isAgentsWindowProfile = id === AGENTS_WINDOW_PROFILE_ID;
190 return {
212 };
213 }
215 > export type UserDataProfilesObject = {
216 > profiles: IUserDataProfile[];
217 > emptyWindows: Map<string, IUserDataProfile>;
218 > };
219 >
220 > export type StoredUserDataProfile = {
221 > name: string;
222 > location: URI;
223 > icon?: string;
224 > useDefaultFlags?: UseDefaultProfileFlags;
225 > };
226 >
227 > export type StoredProfileAssociations = {
228 > workspaces?: IStringDictionary<string>;
229 > emptyWindows?: IStringDictionary<string>;
230 > };
231 >
232 > const SYSTEM_PROFILES_HOME = 'builtin';
233 >
234 > export class UserDataProfilesService extends Disposable implements IUserDataProfilesService {
235 >
236 > readonly _serviceBrand: undefined;
237 >
238 > protected static readonly PROFILES_KEY = 'userDataProfiles';
239 > protected static readonly PROFILE_ASSOCIATIONS_KEY = 'profileAssociations';
240 >
241 > readonly profilesHome: URI;
242 > private readonly profilesCacheHome: URI;
243 >
244 > get defaultProfile(): IUserDataProfile { return this.profiles[0]; }
245 > get profiles(): IUserDataProfile[] { return [...this.profilesObject.profiles, ...this.transientProfilesObject.profiles]; }
246 >
247 > protected readonly _onDidChangeProfiles = this._register(new Emitter<DidChangeProfilesEvent>());
248 > readonly onDidChangeProfiles = this._onDidChangeProfiles.event;
249 >
250 > protected readonly _onWillCreateProfile = this._register(new Emitter<WillCreateProfileEvent>());
251 > readonly onWillCreateProfile = this._onWillCreateProfile.event;
252 >
253 > protected readonly _onWillRemoveProfile = this._register(new Emitter<WillRemoveProfileEvent>());
254 > readonly onWillRemoveProfile = this._onWillRemoveProfile.event;
255 >
256 > private readonly _onDidResetWorkspaces = this._register(new Emitter<void>());
257 > readonly onDidResetWorkspaces = this._onDidResetWorkspaces.event;
258 >
259 > private profileCreationPromises = new Map<string, Promise<IUserDataProfile>>();
260 >
261 > protected readonly transientProfilesObject: UserDataProfilesObject = {
262 > profiles: [],
263 > emptyWindows: new Map()
264 > };
265 >
266 > constructor(
267 @IEnvironmentService protected environmentService: IEnvironmentService,
268 @IFileService protected fileService: IFileService,
274 this.profilesCacheHome = joinPath(this.environmentService.cacheHome, 'CachedProfilesData');
275 }
277 > init(): void {
278 this._profilesObject = undefined;
279 }
281 > protected _profilesObject: UserDataProfilesObject | undefined;
282 > protected get profilesObject(): UserDataProfilesObject {
283 if (!this._profilesObject) {
284 const defaultProfile = this.createDefaultProfile();
336 return this._profilesObject;
337 }
339 > private isInvalidProfile(storedProfile: StoredUserDataProfile): boolean {
340 if (!storedProfile.name) {
341 return true;
349 return false;
350 }
352 > protected createDefaultProfile() {
353 const defaultProfile = toUserDataProfile('__default__profile__', localize('defaultProfile', "Default"), this.environmentService.userRoamingDataHome, this.profilesCacheHome);
354 return { ...defaultProfile, extensionsResource: this.getDefaultProfileExtensionsLocation() ?? defaultProfile.extensionsResource, isDefault: true };
355 }
357 > async createTransientProfile(workspaceIdentifier?: IAnyWorkspaceIdentifier): Promise<IUserDataProfile> {
358 const namePrefix = `Temp`;
359 const nameRegEx = new RegExp(`${escapeRegExpCharacters(namePrefix)}\\s(\\d+)`);
367 return this.createProfile(hash(generateUuid()).toString(16), name, { transient: true }, workspaceIdentifier);
368 }
370 > async createNamedProfile(name: string, options?: IUserDataProfileOptions, workspaceIdentifier?: IAnyWorkspaceIdentifier): Promise<IUserDataProfile> {
371 return this.createProfile(hash(generateUuid()).toString(16), name, options, workspaceIdentifier);
372 }
374 > async createProfile(id: string, name: string, options?: IUserDataProfileOptions, workspaceIdentifier?: IAnyWorkspaceIdentifier): Promise<IUserDataProfile> {
375 const profile = await this.doCreateProfile(id, name, options, workspaceIdentifier);
376
377 return profile;
378 }
380 > private async doCreateProfile(id: string, name: string, options?: IUserDataProfileOptions, workspaceIdentifier?: IAnyWorkspaceIdentifier): Promise<IUserDataProfile> {
381 if (!isString(name) || !name) {
382 throw new Error('Name of the profile is mandatory and must be of type `string`');
428 return profileCreationPromise;
429 }
431 > async updateProfile(profile: IUserDataProfile, options: IUserDataProfileUpdateOptions): Promise<IUserDataProfile> {
432 if (profile.isAgentsWindowProfile) {
433 throw new Error('Cannot update agents window profile');
481 return updatedProfile;
482 }
484 > async removeProfile(profileToRemove: IUserDataProfile): Promise<void> {
485 if (profileToRemove.isDefault) {
486 throw new Error('Cannot remove default profile');
515 }
516 }
518 > async setProfileForWorkspace(workspaceIdentifier: IAnyWorkspaceIdentifier, profileToSet: IUserDataProfile): Promise<void> {
519 const profile = this.profiles.find(p => p.id === profileToSet.id);
520 if (!profile) {
534 }
535 }
537 > unsetWorkspace(workspaceIdentifier: IAnyWorkspaceIdentifier, transient: boolean = false): void {
538 const workspace = this.getWorkspace(workspaceIdentifier);
539 if (URI.isUri(workspace)) {
547 }
548 }
550 > async resetWorkspaces(): Promise<void> {
551 this.transientProfilesObject.emptyWindows.clear();
552 this.profilesObject.emptyWindows.clear();
557 this._onDidResetWorkspaces.fire();
558 }
560 > async cleanUp(): Promise<void> {
561 try {
562 if (await this.fileService.exists(this.profilesHome)) {
587 }
588 }
590 > async cleanUpTransientProfiles(): Promise<void> {
591 const unAssociatedTransientProfiles = this.transientProfilesObject.profiles.filter(p => !this.isProfileAssociatedToWorkspace(p));
592 await Promise.allSettled(unAssociatedTransientProfiles.map(p => this.removeProfile(p)));
593 }
595 > getProfileForWorkspace(workspaceIdentifier: IAnyWorkspaceIdentifier): IUserDataProfile | undefined {
596 const workspace = this.getWorkspace(workspaceIdentifier);
597
604 : (this.profilesObject.emptyWindows.get(workspace) ?? this.transientProfilesObject.emptyWindows.get(workspace));
605 }
607 > protected getWorkspace(workspaceIdentifier: IAnyWorkspaceIdentifier): URI | string {
608 if (isSingleFolderWorkspaceIdentifier(workspaceIdentifier)) {
609 return workspaceIdentifier.uri;
614 return workspaceIdentifier.id;
615 }
617 > private isProfileAssociatedToWorkspace(profile: IUserDataProfile): boolean {
618 if (profile.workspaces?.length) {
619 return true;
627 return false;
628 }
630 > private updateProfiles(added: IUserDataProfile[], removed: IUserDataProfile[], updated: IUserDataProfile[], donotTrigger: boolean = false): void {
631 const allProfiles: Mutable<IUserDataProfile>[] = [...this.profiles, ...added];
632
679 }
680 }
682 > protected triggerProfilesChanges(added: IUserDataProfile[], removed: IUserDataProfile[], updated: IUserDataProfile[]) {
683 this._onDidChangeProfiles.fire({ added, removed, updated, all: this.profiles });
684 }
686 > private updateEmptyWindowAssociation(windowId: string, newProfile: IUserDataProfile | undefined, transient: boolean): void {
687 // Force transient if the new profile to associate is transient
688 transient = newProfile?.isTransient ? true : transient;
706 }
707 }
709 > private updateStoredProfiles(profiles: IUserDataProfile[]): void {
710 const storedProfiles: StoredUserDataProfile[] = [];
711 const workspaces: IStringDictionary<string> = {};
739 this._profilesObject = undefined;
740 }
742 > protected getStoredProfiles(): StoredUserDataProfile[] { return []; }
743 > protected saveStoredProfiles(storedProfiles: StoredUserDataProfile[]): void { throw new Error('not implemented'); }
744 >
745 > protected getStoredProfileAssociations(): StoredProfileAssociations { return {}; }
746 > protected saveStoredProfileAssociations(storedProfileAssociations: StoredProfileAssociations): void { throw new Error('not implemented'); }
747 > protected getDefaultProfileExtensionsLocation(): URI | undefined { return undefined; }
748 > }
749 >
750 > export class InMemoryUserDataProfilesService extends UserDataProfilesService {
751 private storedProfiles: StoredUserDataProfile[] = [];
752 protected override getStoredProfiles(): StoredUserDataProfile[] { return this.storedProfiles; }
754
755 private storedProfileAssociations: StoredProfileAssociations = {};
756 > protected override getStoredProfileAssociations(): StoredProfileAssociations { return this.storedProfileAssociations; } userDataProfile.ts
757 > protected override saveStoredProfileAssociations(storedProfileAssociations: StoredProfileAssociations): void { this.storedProfileAssociations = storedProfileAssociations; }
758 > }