Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/microsoft/vscode.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/vs/platform/userDataProfile/common/userDataProfile.ts')
-rw-r--r--src/vs/platform/userDataProfile/common/userDataProfile.ts266
1 files changed, 246 insertions, 20 deletions
diff --git a/src/vs/platform/userDataProfile/common/userDataProfile.ts b/src/vs/platform/userDataProfile/common/userDataProfile.ts
index 0705d48af4f..3bc2eb4ea3d 100644
--- a/src/vs/platform/userDataProfile/common/userDataProfile.ts
+++ b/src/vs/platform/userDataProfile/common/userDataProfile.ts
@@ -14,7 +14,11 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
import { IFileService } from 'vs/platform/files/common/files';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ILogService } from 'vs/platform/log/common/log';
-import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace';
+import { ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace';
+import { ResourceMap } from 'vs/base/common/map';
+import { IStringDictionary } from 'vs/base/common/collections';
+import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
+import { Promises } from 'vs/base/common/async';
/**
* Flags to indicate whether to use the default profile or not.
@@ -42,8 +46,6 @@ export interface IUserDataProfile {
readonly useDefaultFlags?: UseDefaultProfileFlags;
}
-export type CustomUserDataProfile = IUserDataProfile & { readonly extensionsResource: URI; readonly isDefault: false };
-
export function isUserDataProfile(thing: unknown): thing is IUserDataProfile {
const candidate = thing as IUserDataProfile | undefined;
@@ -68,6 +70,16 @@ export type WorkspaceIdentifier = ISingleFolderWorkspaceIdentifier | IWorkspaceI
export type DidChangeProfilesEvent = { readonly added: IUserDataProfile[]; readonly removed: IUserDataProfile[]; readonly all: IUserDataProfile[] };
+export type WillCreateProfileEvent = {
+ profile: IUserDataProfile;
+ join(promise: Promise<void>): void;
+};
+
+export type WillRemoveProfileEvent = {
+ profile: IUserDataProfile;
+ join(promise: Promise<void>): void;
+};
+
export const IUserDataProfilesService = createDecorator<IUserDataProfilesService>('IUserDataProfilesService');
export interface IUserDataProfilesService {
readonly _serviceBrand: undefined;
@@ -78,9 +90,8 @@ export interface IUserDataProfilesService {
readonly onDidChangeProfiles: Event<DidChangeProfilesEvent>;
readonly profiles: IUserDataProfile[];
- newProfile(name: string, useDefaultFlags?: UseDefaultProfileFlags): CustomUserDataProfile;
- createProfile(profile: IUserDataProfile, workspaceIdentifier?: WorkspaceIdentifier): Promise<IUserDataProfile>;
- setProfileForWorkspace(profile: IUserDataProfile, workspaceIdentifier: WorkspaceIdentifier): Promise<IUserDataProfile>;
+ createProfile(name: string, useDefaultFlags?: UseDefaultProfileFlags, workspaceIdentifier?: WorkspaceIdentifier): Promise<IUserDataProfile>;
+ setProfileForWorkspace(profile: IUserDataProfile, workspaceIdentifier: WorkspaceIdentifier): Promise<void>;
getProfile(workspaceIdentifier: WorkspaceIdentifier): IUserDataProfile;
removeProfile(profile: IUserDataProfile): Promise<void>;
}
@@ -102,9 +113,9 @@ export function reviveProfile(profile: UriDto<IUserDataProfile>, scheme: string)
export const EXTENSIONS_RESOURCE_NAME = 'extensions.json';
-export function toUserDataProfile(name: string, location: URI, useDefaultFlags?: UseDefaultProfileFlags): CustomUserDataProfile {
+export function toUserDataProfile(name: string, location: URI, useDefaultFlags?: UseDefaultProfileFlags): IUserDataProfile {
return {
- id: hash(location.toString()).toString(16),
+ id: hash(location.path).toString(16),
name: name,
location: location,
isDefault: false,
@@ -118,38 +129,253 @@ export function toUserDataProfile(name: string, location: URI, useDefaultFlags?:
};
}
+export type UserDataProfilesObject = {
+ profiles: IUserDataProfile[];
+ workspaces: ResourceMap<IUserDataProfile>;
+ emptyWindow?: IUserDataProfile;
+};
+
+export type StoredUserDataProfile = {
+ name: string;
+ location: URI;
+ useDefaultFlags?: UseDefaultProfileFlags;
+};
+
+export type StoredProfileAssociations = {
+ workspaces?: IStringDictionary<string>;
+ emptyWindow?: string;
+};
+
export class UserDataProfilesService extends Disposable implements IUserDataProfilesService {
+
+ protected static readonly PROFILES_KEY = 'userDataProfiles';
+ protected static readonly PROFILE_ASSOCIATIONS_KEY = 'profileAssociations';
+
readonly _serviceBrand: undefined;
+ private enabled: boolean = false;
+ protected readonly defaultProfileShouldIncludeExtensionsResourceAlways: boolean = false;
readonly profilesHome: URI;
- private readonly _defaultProfile = this.createDefaultUserDataProfile(false);
- get defaultProfile(): IUserDataProfile { return this.profiles[0] ?? this._defaultProfile; }
- get profiles(): IUserDataProfile[] { return []; }
+ get defaultProfile(): IUserDataProfile { return this.profiles[0]; }
+ get profiles(): IUserDataProfile[] { return this.profilesObject.profiles; }
protected readonly _onDidChangeProfiles = this._register(new Emitter<DidChangeProfilesEvent>());
readonly onDidChangeProfiles = this._onDidChangeProfiles.event;
+ protected readonly _onWillCreateProfile = this._register(new Emitter<WillCreateProfileEvent>());
+ readonly onWillCreateProfile = this._onWillCreateProfile.event;
+
+ protected readonly _onWillRemoveProfile = this._register(new Emitter<WillRemoveProfileEvent>());
+ readonly onWillRemoveProfile = this._onWillRemoveProfile.event;
+
constructor(
@IEnvironmentService protected readonly environmentService: IEnvironmentService,
@IFileService protected readonly fileService: IFileService,
+ @IUriIdentityService protected readonly uriIdentityService: IUriIdentityService,
@ILogService protected readonly logService: ILogService
) {
super();
this.profilesHome = joinPath(this.environmentService.userRoamingDataHome, 'profiles');
}
- newProfile(name: string, useDefaultFlags?: UseDefaultProfileFlags): CustomUserDataProfile {
- return toUserDataProfile(name, joinPath(this.profilesHome, hash(name).toString(16)), useDefaultFlags);
+ setEnablement(enabled: boolean): void {
+ if (this.enabled !== enabled) {
+ this._profilesObject = undefined;
+ this.enabled = enabled;
+ }
+ }
+
+ protected _profilesObject: UserDataProfilesObject | undefined;
+ protected get profilesObject(): UserDataProfilesObject {
+ if (!this._profilesObject) {
+ const profiles = this.enabled ? this.getStoredProfiles().map<IUserDataProfile>(storedProfile => toUserDataProfile(storedProfile.name, storedProfile.location, storedProfile.useDefaultFlags)) : [];
+ let emptyWindow: IUserDataProfile | undefined;
+ const workspaces = new ResourceMap<IUserDataProfile>();
+ if (profiles.length) {
+ const profileAssicaitions = this.getStoredProfileAssociations();
+ if (profileAssicaitions.workspaces) {
+ for (const [workspacePath, profilePath] of Object.entries(profileAssicaitions.workspaces)) {
+ const workspace = URI.parse(workspacePath);
+ const profileLocation = URI.parse(profilePath);
+ const profile = profiles.find(p => this.uriIdentityService.extUri.isEqual(p.location, profileLocation));
+ if (profile) {
+ workspaces.set(workspace, profile);
+ }
+ }
+ }
+ if (profileAssicaitions.emptyWindow) {
+ const emptyWindowProfileLocation = URI.parse(profileAssicaitions.emptyWindow);
+ emptyWindow = profiles.find(p => this.uriIdentityService.extUri.isEqual(p.location, emptyWindowProfileLocation));
+ }
+ }
+ const profile = toUserDataProfile(localize('defaultProfile', "Default"), this.environmentService.userRoamingDataHome);
+ profiles.unshift({ ...profile, isDefault: true, extensionsResource: this.defaultProfileShouldIncludeExtensionsResourceAlways || profiles.length > 0 ? profile.extensionsResource : undefined });
+ this._profilesObject = { profiles, workspaces, emptyWindow };
+ }
+ return this._profilesObject;
+ }
+
+ getProfile(workspaceIdentifier: WorkspaceIdentifier): IUserDataProfile {
+ const workspace = this.getWorkspace(workspaceIdentifier);
+ const profile = URI.isUri(workspace) ? this.profilesObject.workspaces.get(workspace) : this.profilesObject.emptyWindow;
+ return profile ?? this.defaultProfile;
}
- protected createDefaultUserDataProfile(extensions: boolean): IUserDataProfile {
- const profile = toUserDataProfile(localize('defaultProfile', "Default"), this.environmentService.userRoamingDataHome);
- return { ...profile, isDefault: true, extensionsResource: extensions ? profile.extensionsResource : undefined };
+ protected getWorkspace(workspaceIdentifier: WorkspaceIdentifier): URI | EmptyWindowWorkspaceIdentifier {
+ if (isSingleFolderWorkspaceIdentifier(workspaceIdentifier)) {
+ return workspaceIdentifier.uri;
+ }
+ if (isWorkspaceIdentifier(workspaceIdentifier)) {
+ return workspaceIdentifier.configPath;
+ }
+ return 'empty-window';
}
- createProfile(profile: IUserDataProfile, workspaceIdentifier?: WorkspaceIdentifier): Promise<IUserDataProfile> { throw new Error('Not implemented'); }
- setProfileForWorkspace(profile: IUserDataProfile, workspaceIdentifier: WorkspaceIdentifier): Promise<IUserDataProfile> { throw new Error('Not implemented'); }
- getProfile(workspaceIdentifier: WorkspaceIdentifier): IUserDataProfile { throw new Error('Not implemented'); }
- removeProfile(profile: IUserDataProfile): Promise<void> { throw new Error('Not implemented'); }
+ async createProfile(name: string, useDefaultFlags?: UseDefaultProfileFlags, workspaceIdentifier?: WorkspaceIdentifier): Promise<IUserDataProfile> {
+ if (!this.enabled) {
+ throw new Error(`Settings Profiles are disabled. Enable them via the '${PROFILES_ENABLEMENT_CONFIG}' setting.`);
+ }
+ if (this.getStoredProfiles().some(p => p.name === name)) {
+ throw new Error(`Profile with name ${name} already exists`);
+ }
+
+ const profile = toUserDataProfile(name, joinPath(this.profilesHome, hash(name).toString(16)), useDefaultFlags);
+ await this.fileService.createFolder(profile.location);
+
+ const joiners: Promise<void>[] = [];
+ this._onWillCreateProfile.fire({
+ profile,
+ join(promise) {
+ joiners.push(promise);
+ }
+ });
+ await Promises.settled(joiners);
+
+ this.updateProfiles([profile], []);
+
+ if (workspaceIdentifier) {
+ await this.setProfileForWorkspace(profile, workspaceIdentifier);
+ }
+
+ return profile;
+ }
+
+ async setProfileForWorkspace(profileToSet: IUserDataProfile, workspaceIdentifier: WorkspaceIdentifier): Promise<void> {
+ if (!this.enabled) {
+ throw new Error(`Settings Profiles are disabled. Enable them via the '${PROFILES_ENABLEMENT_CONFIG}' setting.`);
+ }
+
+ const profile = this.profiles.find(p => p.id === profileToSet.id);
+ if (!profile) {
+ throw new Error(`Profile '${profileToSet.name}' does not exist`);
+ }
+
+ this.updateWorkspaceAssociation(workspaceIdentifier, profile);
+ }
+
+ async unsetWorkspace(workspaceIdentifier: WorkspaceIdentifier): Promise<void> {
+ if (!this.enabled) {
+ throw new Error(`Settings Profiles are disabled. Enable them via the '${PROFILES_ENABLEMENT_CONFIG}' setting.`);
+ }
+ this.updateWorkspaceAssociation(workspaceIdentifier);
+ }
+
+ async removeProfile(profileToRemove: IUserDataProfile): Promise<void> {
+ if (!this.enabled) {
+ throw new Error(`Settings Profiles are disabled. Enable them via the '${PROFILES_ENABLEMENT_CONFIG}' setting.`);
+ }
+ if (profileToRemove.isDefault) {
+ throw new Error('Cannot remove default profile');
+ }
+ const profile = this.profiles.find(p => p.id === profileToRemove.id);
+ if (!profile) {
+ throw new Error(`Profile '${profileToRemove.name}' does not exist`);
+ }
+
+ const joiners: Promise<void>[] = [];
+ this._onWillRemoveProfile.fire({
+ profile,
+ join(promise) {
+ joiners.push(promise);
+ }
+ });
+ await Promises.settled(joiners);
+
+ if (profile.id === this.profilesObject.emptyWindow?.id) {
+ this.profilesObject.emptyWindow = undefined;
+ }
+ for (const workspace of [...this.profilesObject.workspaces.keys()]) {
+ if (profile.id === this.profilesObject.workspaces.get(workspace)?.id) {
+ this.profilesObject.workspaces.delete(workspace);
+ }
+ }
+ this.updateStoredProfileAssociations();
+
+ this.updateProfiles([], [profile]);
+
+ try {
+ if (this.profiles.length === 1) {
+ await this.fileService.del(this.profilesHome, { recursive: true });
+ } else {
+ await this.fileService.del(profile.location, { recursive: true });
+ }
+ } catch (error) {
+ this.logService.error(error);
+ }
+ }
+
+ private updateProfiles(added: IUserDataProfile[], removed: IUserDataProfile[]) {
+ const storedProfiles: StoredUserDataProfile[] = [];
+ for (const profile of [...this.profilesObject.profiles, ...added]) {
+ if (profile.isDefault) {
+ continue;
+ }
+ if (removed.some(p => profile.id === p.id)) {
+ continue;
+ }
+ storedProfiles.push({ location: profile.location, name: profile.name, useDefaultFlags: profile.useDefaultFlags });
+ }
+ this.saveStoredProfiles(storedProfiles);
+ this._profilesObject = undefined;
+ this.triggerProfilesChanges(added, removed);
+ }
+
+ protected triggerProfilesChanges(added: IUserDataProfile[], removed: IUserDataProfile[]) {
+ this._onDidChangeProfiles.fire({ added, removed, all: this.profiles });
+ }
+
+ private updateWorkspaceAssociation(workspaceIdentifier: WorkspaceIdentifier, newProfile?: IUserDataProfile) {
+ const workspace = this.getWorkspace(workspaceIdentifier);
+
+ // Folder or Multiroot workspace
+ if (URI.isUri(workspace)) {
+ this.profilesObject.workspaces.delete(workspace);
+ if (newProfile && !newProfile.isDefault) {
+ this.profilesObject.workspaces.set(workspace, newProfile);
+ }
+ }
+ // Empty Window
+ else {
+ this.profilesObject.emptyWindow = !newProfile?.isDefault ? newProfile : undefined;
+ }
+
+ this.updateStoredProfileAssociations();
+ }
+
+ private updateStoredProfileAssociations() {
+ const workspaces: IStringDictionary<string> = {};
+ for (const [workspace, profile] of this.profilesObject.workspaces.entries()) {
+ workspaces[workspace.toString()] = profile.location.toString();
+ }
+ const emptyWindow = this.profilesObject.emptyWindow?.location.toString();
+ this.saveStoredProfileAssociations({ workspaces, emptyWindow });
+ this._profilesObject = undefined;
+ }
+
+ protected getStoredProfiles(): StoredUserDataProfile[] { return []; }
+ protected saveStoredProfiles(storedProfiles: StoredUserDataProfile[]): void { throw new Error('not implemented'); }
+
+ protected getStoredProfileAssociations(): StoredProfileAssociations { return {}; }
+ protected saveStoredProfileAssociations(storedProfileAssociations: StoredProfileAssociations): void { throw new Error('not implemented'); }
}