mirror of
https://github.com/microsoft/vscode.git
synced 2026-02-15 07:28:05 +00:00
- implement profile CRUD operations
- enable profile actions in dev mode
This commit is contained in:
@@ -102,7 +102,6 @@ import { IExtensionsScannerService } from 'vs/platform/extensionManagement/commo
|
||||
import { ExtensionsScannerService } from 'vs/platform/extensionManagement/node/extensionsScannerService';
|
||||
import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
|
||||
import { ExtensionsProfileScannerService, IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService';
|
||||
import { revive } from 'vs/base/common/marshalling';
|
||||
import { PolicyChannelClient } from 'vs/platform/policy/common/policyIpc';
|
||||
import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy';
|
||||
import { OneDataSystemAppender } from 'vs/platform/telemetry/node/1dsAppender';
|
||||
@@ -233,7 +232,7 @@ class SharedProcessMain extends Disposable {
|
||||
fileService.registerProvider(Schemas.vscodeUserData, userDataFileSystemProvider);
|
||||
|
||||
// User Data Profiles
|
||||
const userDataProfilesService = this._register(new UserDataProfilesService(revive(this.configuration.profiles.default), revive(this.configuration.profiles.current), environmentService, fileService, logService));
|
||||
const userDataProfilesService = this._register(new UserDataProfilesService(this.configuration.defaultProfile, undefined, environmentService, fileService, logService));
|
||||
services.set(IUserDataProfilesService, userDataProfilesService);
|
||||
|
||||
// Configuration
|
||||
|
||||
@@ -68,6 +68,8 @@ import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/pol
|
||||
import { NativePolicyService } from 'vs/platform/policy/node/nativePolicyService';
|
||||
import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
|
||||
import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService';
|
||||
|
||||
/**
|
||||
* The main VS Code entry point.
|
||||
@@ -170,6 +172,10 @@ class CodeMain {
|
||||
const diskFileSystemProvider = new DiskFileSystemProvider(logService);
|
||||
fileService.registerProvider(Schemas.file, diskFileSystemProvider);
|
||||
|
||||
// URI Identity
|
||||
const uriIdentityService = new UriIdentityService(fileService);
|
||||
services.set(IUriIdentityService, uriIdentityService);
|
||||
|
||||
// Logger
|
||||
services.set(ILoggerService, new LoggerService(logService, fileService));
|
||||
|
||||
@@ -178,7 +184,7 @@ class CodeMain {
|
||||
services.set(IStateMainService, stateMainService);
|
||||
|
||||
// User Data Profiles
|
||||
const userDataProfilesMainService = new UserDataProfilesMainService(stateMainService, environmentMainService, fileService, logService);
|
||||
const userDataProfilesMainService = new UserDataProfilesMainService(stateMainService, uriIdentityService, environmentMainService, fileService, logService);
|
||||
services.set(IUserDataProfilesService, userDataProfilesMainService);
|
||||
|
||||
// Policy
|
||||
@@ -244,10 +250,7 @@ class CodeMain {
|
||||
].map(path => path ? FSPromises.mkdir(path, { recursive: true }) : undefined)),
|
||||
|
||||
// State service
|
||||
stateMainService.init(),
|
||||
|
||||
// User Data Profiles Service
|
||||
userDataProfilesMainService.init(),
|
||||
stateMainService.init().then(() => userDataProfilesMainService.init()),
|
||||
|
||||
// Configuration service
|
||||
configurationService.initialize()
|
||||
|
||||
@@ -242,7 +242,7 @@ export class SharedProcess extends Disposable implements ISharedProcess {
|
||||
appRoot: this.environmentMainService.appRoot,
|
||||
codeCachePath: this.environmentMainService.codeCachePath,
|
||||
backupWorkspacesPath: this.environmentMainService.backupWorkspacesPath,
|
||||
profiles: this.userDataProfilesService.serialize(),
|
||||
defaultProfile: this.userDataProfilesService.defaultProfile,
|
||||
userEnv: this.userEnv,
|
||||
args: this.environmentMainService.args,
|
||||
logLevel: this.logService.getLevel(),
|
||||
|
||||
@@ -7,8 +7,9 @@ import { IStringDictionary } from 'vs/base/common/collections';
|
||||
import { ISandboxConfiguration } from 'vs/base/parts/sandbox/common/sandboxTypes';
|
||||
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
|
||||
import { LogLevel } from 'vs/platform/log/common/log';
|
||||
import { IUserDataProfilesDto } from 'vs/platform/userDataProfile/common/userDataProfile';
|
||||
import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile';
|
||||
import { PolicyDefinition, PolicyValue } from 'vs/platform/policy/common/policy';
|
||||
import { UriDto } from 'vs/base/common/types';
|
||||
|
||||
export interface ISharedProcess {
|
||||
|
||||
@@ -28,7 +29,7 @@ export interface ISharedProcessConfiguration extends ISandboxConfiguration {
|
||||
|
||||
readonly backupWorkspacesPath: string;
|
||||
|
||||
readonly profiles: IUserDataProfilesDto;
|
||||
readonly defaultProfile: UriDto<IUserDataProfile>;
|
||||
|
||||
readonly policiesData?: IStringDictionary<{ definition: PolicyDefinition; value: PolicyValue }>;
|
||||
}
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { UriDto } from 'vs/base/common/types';
|
||||
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest } from 'vs/base/parts/storage/common/storage';
|
||||
import { IUserDataProfileDto, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
|
||||
import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
|
||||
import { ISerializedSingleFolderWorkspaceIdentifier, ISerializedWorkspaceIdentifier, IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace';
|
||||
|
||||
export type Key = string;
|
||||
@@ -21,7 +22,7 @@ export interface IBaseSerializableStorageRequest {
|
||||
* workspace is provided. Can be undefined to denote
|
||||
* application scope.
|
||||
*/
|
||||
readonly profile: IUserDataProfileDto | undefined;
|
||||
readonly profile: UriDto<IUserDataProfile> | undefined;
|
||||
|
||||
/**
|
||||
* Workspace to correlate storage. Can be undefined to
|
||||
@@ -46,7 +47,7 @@ abstract class BaseStorageDatabaseClient extends Disposable implements IStorageD
|
||||
|
||||
constructor(
|
||||
protected channel: IChannel,
|
||||
protected profile: IUserDataProfileDto | undefined,
|
||||
protected profile: UriDto<IUserDataProfile> | undefined,
|
||||
protected workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined
|
||||
) {
|
||||
super();
|
||||
@@ -81,7 +82,7 @@ abstract class BaseProfileAwareStorageDatabaseClient extends BaseStorageDatabase
|
||||
private readonly _onDidChangeItemsExternal = this._register(new Emitter<IStorageItemsChangeEvent>());
|
||||
readonly onDidChangeItemsExternal = this._onDidChangeItemsExternal.event;
|
||||
|
||||
constructor(channel: IChannel, profile: IUserDataProfileDto | undefined) {
|
||||
constructor(channel: IChannel, profile: UriDto<IUserDataProfile> | undefined) {
|
||||
super(channel, profile, undefined);
|
||||
|
||||
this.registerListeners();
|
||||
@@ -119,7 +120,7 @@ class ApplicationStorageDatabaseClient extends BaseProfileAwareStorageDatabaseCl
|
||||
|
||||
class GlobalStorageDatabaseClient extends BaseProfileAwareStorageDatabaseClient {
|
||||
|
||||
constructor(channel: IChannel, profile: IUserDataProfileDto) {
|
||||
constructor(channel: IChannel, profile: UriDto<IUserDataProfile>) {
|
||||
super(channel, profile);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,17 +3,35 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { coalesce } from 'vs/base/common/arrays';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { hash } from 'vs/base/common/hash';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { joinPath } from 'vs/base/common/resources';
|
||||
import { UriDto } from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files';
|
||||
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';
|
||||
|
||||
export type ProfileOptions = {
|
||||
settings?: boolean;
|
||||
keybindings?: boolean;
|
||||
tasks?: boolean;
|
||||
snippets?: boolean;
|
||||
extensions?: boolean;
|
||||
uiState?: boolean;
|
||||
};
|
||||
|
||||
export const DefaultOptions: ProfileOptions = {
|
||||
settings: true,
|
||||
keybindings: true,
|
||||
tasks: true,
|
||||
snippets: true,
|
||||
extensions: true,
|
||||
uiState: true
|
||||
};
|
||||
|
||||
export interface IUserDataProfile {
|
||||
readonly id: string;
|
||||
@@ -28,30 +46,23 @@ export interface IUserDataProfile {
|
||||
readonly extensionsResource: URI | undefined;
|
||||
}
|
||||
|
||||
export type IUserDataProfileDto = UriDto<IUserDataProfile>;
|
||||
export type IUserDataProfilesDto = {
|
||||
readonly current: IUserDataProfileDto;
|
||||
readonly default: IUserDataProfileDto;
|
||||
};
|
||||
|
||||
export const IUserDataProfilesService = createDecorator<IUserDataProfilesService>('IUserDataProfilesService');
|
||||
export interface IUserDataProfilesService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
readonly profilesHome: URI;
|
||||
readonly defaultProfile: IUserDataProfile;
|
||||
|
||||
readonly onDidChangeCurrentProfile: Event<IUserDataProfile>;
|
||||
readonly currentProfile: IUserDataProfile;
|
||||
|
||||
createProfile(name: string): IUserDataProfile;
|
||||
setProfile(name: string): Promise<void>;
|
||||
newProfile(name: string, options?: ProfileOptions): IUserDataProfile;
|
||||
createProfile(profile: IUserDataProfile, options: ProfileOptions, workspaceIdentifier?: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise<IUserDataProfile>;
|
||||
setProfileForWorkspace(profile: IUserDataProfile, workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise<IUserDataProfile>;
|
||||
getProfile(workspace: URI): IUserDataProfile;
|
||||
getAllProfiles(): Promise<IUserDataProfile[]>;
|
||||
|
||||
serialize(): IUserDataProfilesDto;
|
||||
removeProfile(profile: IUserDataProfile): Promise<void>;
|
||||
}
|
||||
|
||||
function reviveProfile(profile: IUserDataProfile, scheme: string): IUserDataProfile {
|
||||
export function reviveProfile(profile: UriDto<IUserDataProfile>, scheme: string): IUserDataProfile {
|
||||
return {
|
||||
id: profile.id,
|
||||
isDefault: profile.isDefault,
|
||||
@@ -69,74 +80,49 @@ function reviveProfile(profile: IUserDataProfile, scheme: string): IUserDataProf
|
||||
export class UserDataProfilesService extends Disposable implements IUserDataProfilesService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
protected static DEFAULT_PROFILE_NAME = 'default';
|
||||
readonly profilesHome: URI;
|
||||
|
||||
protected _currentProfile: IUserDataProfile;
|
||||
get currentProfile(): IUserDataProfile { return this._currentProfile; }
|
||||
|
||||
readonly profilesHome: URI;
|
||||
protected _defaultProfile: IUserDataProfile;
|
||||
get defaultProfile(): IUserDataProfile { return this._defaultProfile; }
|
||||
|
||||
private readonly _onDidChangeCurrentProfile = this._register(new Emitter<IUserDataProfile>());
|
||||
readonly onDidChangeCurrentProfile = this._onDidChangeCurrentProfile.event;
|
||||
|
||||
constructor(
|
||||
defaultProfile: IUserDataProfile | undefined,
|
||||
currentProfile: IUserDataProfile | undefined,
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService,
|
||||
defaultProfile: UriDto<IUserDataProfile> | undefined,
|
||||
currentProfile: UriDto<IUserDataProfile> | undefined,
|
||||
@IEnvironmentService protected readonly environmentService: IEnvironmentService,
|
||||
@IFileService protected readonly fileService: IFileService,
|
||||
@ILogService protected readonly logService: ILogService
|
||||
) {
|
||||
super();
|
||||
this.profilesHome = joinPath(this.environmentService.userRoamingDataHome, 'profiles');
|
||||
this._defaultProfile = defaultProfile ? reviveProfile(defaultProfile, this.profilesHome.scheme) : this.createProfile(undefined);
|
||||
this._defaultProfile = defaultProfile ? reviveProfile(defaultProfile, this.profilesHome.scheme) : this.toUserDataProfile(localize('defaultProfile', "Default"), environmentService.userRoamingDataHome, { ...DefaultOptions, extensions: false }, true);
|
||||
this._currentProfile = currentProfile ? reviveProfile(currentProfile, this.profilesHome.scheme) : this._defaultProfile;
|
||||
}
|
||||
|
||||
createProfile(name: string | undefined): IUserDataProfile {
|
||||
const isDefault = !name || name === UserDataProfilesService.DEFAULT_PROFILE_NAME;
|
||||
const location = name && name !== UserDataProfilesService.DEFAULT_PROFILE_NAME ? joinPath(this.profilesHome, name) : this.environmentService.userRoamingDataHome;
|
||||
newProfile(name: string, options: ProfileOptions = DefaultOptions): IUserDataProfile {
|
||||
return this.toUserDataProfile(name, joinPath(this.profilesHome, hash(name).toString(16)), options, this.defaultProfile);
|
||||
}
|
||||
|
||||
protected toUserDataProfile(name: string, location: URI, options: ProfileOptions, defaultProfile: true | IUserDataProfile): IUserDataProfile {
|
||||
return {
|
||||
id: hash(location.toString()).toString(16),
|
||||
isDefault,
|
||||
name: name ?? UserDataProfilesService.DEFAULT_PROFILE_NAME,
|
||||
location,
|
||||
globalStorageHome: joinPath(location, 'globalStorage'),
|
||||
settingsResource: joinPath(location, 'settings.json'),
|
||||
keybindingsResource: joinPath(location, 'keybindings.json'),
|
||||
tasksResource: joinPath(location, 'tasks.json'),
|
||||
snippetsHome: joinPath(location, 'snippets'),
|
||||
extensionsResource: name ? joinPath(location, 'extensions.json') : undefined
|
||||
name: name,
|
||||
location: location,
|
||||
isDefault: defaultProfile === true,
|
||||
globalStorageHome: defaultProfile === true || options.uiState ? joinPath(location, 'globalStorage') : defaultProfile.globalStorageHome,
|
||||
settingsResource: defaultProfile === true || options.settings ? joinPath(location, 'settings.json') : defaultProfile.settingsResource,
|
||||
keybindingsResource: defaultProfile === true || options.keybindings ? joinPath(location, 'keybindings.json') : defaultProfile.keybindingsResource,
|
||||
tasksResource: defaultProfile === true || options.tasks ? joinPath(location, 'tasks.json') : defaultProfile.tasksResource,
|
||||
snippetsHome: defaultProfile === true || options.snippets ? joinPath(location, 'snippets') : defaultProfile.snippetsHome,
|
||||
extensionsResource: defaultProfile === true && !options.extensions ? undefined : joinPath(location, 'extensions.json'),
|
||||
};
|
||||
}
|
||||
|
||||
async getAllProfiles(): Promise<IUserDataProfile[]> {
|
||||
try {
|
||||
const stat = await this.fileService.resolve(this.profilesHome);
|
||||
const profiles = coalesce(stat.children?.map(stat => stat.isDirectory ? this.createProfile(stat.name) : undefined) ?? []);
|
||||
if (profiles.length) {
|
||||
profiles.unshift(this._defaultProfile);
|
||||
}
|
||||
return profiles;
|
||||
} catch (error) {
|
||||
if ((<FileOperationError>error).fileOperationResult !== FileOperationResult.FILE_NOT_FOUND) {
|
||||
this.logService.error('Error while getting all profiles', error);
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
protected createCurrentProfile(profile: string | undefined): IUserDataProfile {
|
||||
return profile === UserDataProfilesService.DEFAULT_PROFILE_NAME ? this._defaultProfile : this.createProfile(profile);
|
||||
}
|
||||
|
||||
setProfile(name: string): Promise<void> { throw new Error('Not implemented'); }
|
||||
|
||||
serialize(): IUserDataProfilesDto {
|
||||
return {
|
||||
default: this.defaultProfile,
|
||||
current: this.currentProfile
|
||||
};
|
||||
}
|
||||
getAllProfiles(): Promise<IUserDataProfile[]> { throw new Error('Not implemented'); }
|
||||
createProfile(profile: IUserDataProfile, options: ProfileOptions, workspaceIdentifier?: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise<IUserDataProfile> { throw new Error('Not implemented'); }
|
||||
setProfileForWorkspace(profile: IUserDataProfile, workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise<IUserDataProfile> { throw new Error('Not implemented'); }
|
||||
getProfile(workspace: URI): IUserDataProfile { throw new Error('Not implemented'); }
|
||||
removeProfile(profile: IUserDataProfile): Promise<void> { throw new Error('Not implemented'); }
|
||||
}
|
||||
|
||||
@@ -3,18 +3,42 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ResourceMap } from 'vs/base/common/map';
|
||||
import { revive } from 'vs/base/common/marshalling';
|
||||
import { UriDto } from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IStateMainService } from 'vs/platform/state/electron-main/state';
|
||||
import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
|
||||
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
|
||||
import { ProfileOptions, DefaultOptions, IUserDataProfile, IUserDataProfilesService, UserDataProfilesService, reviveProfile } from 'vs/platform/userDataProfile/common/userDataProfile';
|
||||
import { ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace';
|
||||
|
||||
type UserDataProfiles = {
|
||||
profiles: IUserDataProfile[];
|
||||
workspaces: ResourceMap<IUserDataProfile>;
|
||||
};
|
||||
|
||||
type StoredUserDataProfile = {
|
||||
name: string;
|
||||
location: URI;
|
||||
options: ProfileOptions;
|
||||
};
|
||||
|
||||
type StoredWorkspaceInfo = {
|
||||
workspace: URI;
|
||||
profile: URI;
|
||||
};
|
||||
|
||||
export class UserDataProfilesMainService extends UserDataProfilesService implements IUserDataProfilesService {
|
||||
|
||||
private static CURRENT_PROFILE_KEY = 'currentUserDataProfile';
|
||||
private static readonly PROFILES_KEY = 'userDataProfiles';
|
||||
private static readonly WORKSPACE_PROFILE_INFO_KEY = 'workspaceAndProfileInfo';
|
||||
|
||||
constructor(
|
||||
@IStateMainService private readonly stateMainService: IStateMainService,
|
||||
@IUriIdentityService private readonly uriIdentityService: IUriIdentityService,
|
||||
@IEnvironmentService environmentService: IEnvironmentService,
|
||||
@IFileService fileService: IFileService,
|
||||
@ILogService logService: ILogService,
|
||||
@@ -22,23 +46,90 @@ export class UserDataProfilesMainService extends UserDataProfilesService impleme
|
||||
super(undefined, undefined, environmentService, fileService, logService);
|
||||
}
|
||||
|
||||
async init(): Promise<void> {
|
||||
const profileName = this.stateMainService.getItem<string>(UserDataProfilesMainService.CURRENT_PROFILE_KEY);
|
||||
if (profileName) {
|
||||
const profiles = await this.getAllProfiles();
|
||||
const profile = profiles.find(p => p.name === profileName);
|
||||
if (profile || (profileName === UserDataProfilesMainService.DEFAULT_PROFILE_NAME && profiles.length > 1)) {
|
||||
this._defaultProfile = this.createProfile(UserDataProfilesService.DEFAULT_PROFILE_NAME);
|
||||
this._currentProfile = profileName === UserDataProfilesMainService.DEFAULT_PROFILE_NAME ? this._defaultProfile : profile ?? this._defaultProfile;
|
||||
} else {
|
||||
this.stateMainService?.removeItem(UserDataProfilesMainService.CURRENT_PROFILE_KEY);
|
||||
}
|
||||
init(): void {
|
||||
if (this.storedProfiles.length) {
|
||||
this._defaultProfile = this.toUserDataProfile(this.defaultProfile.name, this.defaultProfile.location, DefaultOptions, true);
|
||||
}
|
||||
}
|
||||
|
||||
override async setProfile(name: string): Promise<void> {
|
||||
this.stateMainService?.setItem(UserDataProfilesMainService.CURRENT_PROFILE_KEY, name);
|
||||
private _profiles: UserDataProfiles | undefined;
|
||||
private get profiles(): UserDataProfiles {
|
||||
if (!this._profiles) {
|
||||
const profiles = this.storedProfiles.map(storedProfile => this.toUserDataProfile(storedProfile.name, storedProfile.location, storedProfile.options, this.defaultProfile));
|
||||
profiles.unshift(this.defaultProfile);
|
||||
const workspaces = this.storedWorskpaceInfos.reduce((workspaces, workspaceProfileInfo) => {
|
||||
const profile = profiles.find(p => this.uriIdentityService.extUri.isEqual(p.location, workspaceProfileInfo.profile));
|
||||
if (profile) {
|
||||
workspaces.set(workspaceProfileInfo.workspace, profile);
|
||||
}
|
||||
return workspaces;
|
||||
}, new ResourceMap<IUserDataProfile>());
|
||||
this._profiles = { profiles: profiles, workspaces: workspaces };
|
||||
}
|
||||
return this._profiles;
|
||||
}
|
||||
|
||||
override async getAllProfiles(): Promise<IUserDataProfile[]> {
|
||||
return this.profiles.profiles;
|
||||
}
|
||||
|
||||
override getProfile(workspace: URI): IUserDataProfile {
|
||||
return this.profiles.workspaces.get(workspace) ?? this.defaultProfile;
|
||||
}
|
||||
|
||||
override async createProfile(profile: IUserDataProfile, options: ProfileOptions, workspaceIdentifier?: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise<IUserDataProfile> {
|
||||
profile = reviveProfile(profile, this.profilesHome.scheme);
|
||||
if (this.storedProfiles.some(p => p.name === profile.name)) {
|
||||
throw new Error(`Profile with name ${profile.name} already exists`);
|
||||
}
|
||||
const storedProfile: StoredUserDataProfile = { name: profile.name, location: profile.location, options };
|
||||
const storedProfiles = [...this.storedProfiles, storedProfile];
|
||||
this.storedProfiles = storedProfiles;
|
||||
if (workspaceIdentifier) {
|
||||
await this.setProfileForWorkspace(profile, workspaceIdentifier);
|
||||
}
|
||||
return this.profiles.profiles.find(p => this.uriIdentityService.extUri.isEqual(p.location, profile.location))!;
|
||||
}
|
||||
|
||||
override async setProfileForWorkspace(profile: IUserDataProfile, workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise<IUserDataProfile> {
|
||||
profile = reviveProfile(profile, this.profilesHome.scheme);
|
||||
const workspace = isSingleFolderWorkspaceIdentifier(workspaceIdentifier) ? workspaceIdentifier.uri : workspaceIdentifier.configPath;
|
||||
const storedWorkspaceInfos = this.storedWorskpaceInfos.filter(info => !this.uriIdentityService.extUri.isEqual(info.workspace, workspace));
|
||||
if (!profile.isDefault) {
|
||||
storedWorkspaceInfos.push({ workspace, profile: profile.location });
|
||||
}
|
||||
this.storedWorskpaceInfos = storedWorkspaceInfos;
|
||||
return this.profiles.profiles.find(p => this.uriIdentityService.extUri.isEqual(p.location, profile.location))!;
|
||||
}
|
||||
|
||||
override async removeProfile(profile: IUserDataProfile): Promise<void> {
|
||||
if (profile.isDefault) {
|
||||
throw new Error('Cannot remove default profile');
|
||||
}
|
||||
profile = reviveProfile(profile, this.profilesHome.scheme);
|
||||
if (!this.storedProfiles.some(p => this.uriIdentityService.extUri.isEqual(p.location, profile.location))) {
|
||||
throw new Error(`Profile with name ${profile.name} does not exist`);
|
||||
}
|
||||
this.storedWorskpaceInfos = this.storedWorskpaceInfos.filter(p => !this.uriIdentityService.extUri.isEqual(p.profile, profile.location));
|
||||
this.storedProfiles = this.storedProfiles.filter(p => !this.uriIdentityService.extUri.isEqual(p.location, profile.location));
|
||||
}
|
||||
|
||||
private get storedProfiles(): StoredUserDataProfile[] {
|
||||
return revive(this.stateMainService.getItem<UriDto<StoredUserDataProfile>[]>(UserDataProfilesMainService.PROFILES_KEY, []));
|
||||
}
|
||||
|
||||
private set storedProfiles(storedProfiles: StoredUserDataProfile[]) {
|
||||
this.stateMainService.setItem(UserDataProfilesMainService.PROFILES_KEY, storedProfiles);
|
||||
this._profiles = undefined;
|
||||
}
|
||||
|
||||
private get storedWorskpaceInfos(): StoredWorkspaceInfo[] {
|
||||
return revive(this.stateMainService.getItem<UriDto<StoredWorkspaceInfo>[]>(UserDataProfilesMainService.WORKSPACE_PROFILE_INFO_KEY, []));
|
||||
}
|
||||
|
||||
private set storedWorskpaceInfos(storedWorkspaceInfos: StoredWorkspaceInfo[]) {
|
||||
this.stateMainService.setItem(UserDataProfilesMainService.WORKSPACE_PROFILE_INFO_KEY, storedWorkspaceInfos);
|
||||
this._profiles = undefined;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { UriDto } from 'vs/base/common/types';
|
||||
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IUserDataProfile, IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
|
||||
import { ProfileOptions, IUserDataProfile, IUserDataProfilesService, reviveProfile, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
|
||||
import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace';
|
||||
|
||||
export class UserDataProfilesNativeService extends UserDataProfilesService implements IUserDataProfilesService {
|
||||
|
||||
@@ -22,8 +24,23 @@ export class UserDataProfilesNativeService extends UserDataProfilesService imple
|
||||
super(defaultProfile, currentProfile, environmentService, fileService, logService);
|
||||
}
|
||||
|
||||
override setProfile(name: string): Promise<void> {
|
||||
return this.channel.call('setProfile', [name]);
|
||||
override async createProfile(profile: IUserDataProfile, options: ProfileOptions, workspaceIdentifier?: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise<IUserDataProfile> {
|
||||
const result = await this.channel.call<UriDto<IUserDataProfile>>('createProfile', [profile, options, workspaceIdentifier]);
|
||||
return reviveProfile(result, this.profilesHome.scheme);
|
||||
}
|
||||
|
||||
override async setProfileForWorkspace(profile: IUserDataProfile, workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise<IUserDataProfile> {
|
||||
const result = await this.channel.call<UriDto<IUserDataProfile>>('setProfileForWorkspace', [profile, workspaceIdentifier]);
|
||||
return reviveProfile(result, this.profilesHome.scheme);
|
||||
}
|
||||
|
||||
override async getAllProfiles(): Promise<IUserDataProfile[]> {
|
||||
const result = await this.channel.call<UriDto<IUserDataProfile>[]>('getAllProfiles');
|
||||
return result.map(profile => reviveProfile(profile, this.profilesHome.scheme));
|
||||
}
|
||||
|
||||
override removeProfile(profile: IUserDataProfile): Promise<void> {
|
||||
return this.channel.call('removeProfile', [profile]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { IStringDictionary } from 'vs/base/common/collections';
|
||||
import { PerformanceMark } from 'vs/base/common/performance';
|
||||
import { isLinux, isMacintosh, isNative, isWeb, isWindows } from 'vs/base/common/platform';
|
||||
import { UriDto } from 'vs/base/common/types';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { ISandboxConfiguration } from 'vs/base/parts/sandbox/common/sandboxTypes';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
@@ -16,7 +17,7 @@ import { FileType } from 'vs/platform/files/common/files';
|
||||
import { LogLevel } from 'vs/platform/log/common/log';
|
||||
import { PolicyDefinition, PolicyValue } from 'vs/platform/policy/common/policy';
|
||||
import { IPartsSplash } from 'vs/platform/theme/common/themeService';
|
||||
import { IUserDataProfilesDto } from 'vs/platform/userDataProfile/common/userDataProfile';
|
||||
import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile';
|
||||
import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace';
|
||||
|
||||
export const WindowMinimumSize = {
|
||||
@@ -283,7 +284,10 @@ export interface INativeWindowConfiguration extends IWindowConfiguration, Native
|
||||
execPath: string;
|
||||
backupPath?: string;
|
||||
|
||||
profiles: IUserDataProfilesDto;
|
||||
profiles: {
|
||||
default: UriDto<IUserDataProfile>;
|
||||
current: UriDto<IUserDataProfile>;
|
||||
};
|
||||
|
||||
homeDir: string;
|
||||
tmpDir: string;
|
||||
|
||||
@@ -40,7 +40,7 @@ import { IWorkspacesManagementMainService } from 'vs/platform/workspaces/electro
|
||||
import { IWindowState, ICodeWindow, ILoadEvent, WindowMode, WindowError, LoadReason, defaultWindowState } from 'vs/platform/window/electron-main/window';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { IPolicyService } from 'vs/platform/policy/common/policy';
|
||||
import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile';
|
||||
import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
|
||||
import { revive } from 'vs/base/common/marshalling';
|
||||
|
||||
export interface IWindowCreationOptions {
|
||||
@@ -156,6 +156,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService,
|
||||
@IPolicyService private readonly policyService: IPolicyService,
|
||||
@IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService,
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
@IApplicationStorageMainService private readonly applicationStorageMainService: IApplicationStorageMainService,
|
||||
@IStorageMainService private readonly storageMainService: IStorageMainService,
|
||||
@@ -882,6 +883,10 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
|
||||
configuration.isInitialStartup = false; // since this is a reload
|
||||
configuration.policiesData = this.policyService.serialize(); // set policies data again
|
||||
configuration.profiles = {
|
||||
default: this.userDataProfilesService.defaultProfile,
|
||||
current: configuration.workspace ? this.userDataProfilesService.getProfile(isWorkspaceIdentifier(configuration.workspace) ? configuration.workspace.configPath : configuration.workspace.uri) : this.userDataProfilesService.defaultProfile,
|
||||
};
|
||||
|
||||
// Load config
|
||||
this.load(configuration, { isReload: true, disableExtensions: cli?.['disable-extensions'] });
|
||||
|
||||
@@ -1300,7 +1300,10 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
// loading the window.
|
||||
backupPath: options.emptyWindowBackupInfo ? join(this.environmentMainService.backupHome, options.emptyWindowBackupInfo.backupFolder) : undefined,
|
||||
|
||||
profiles: this.userDataProfilesService.serialize(),
|
||||
profiles: {
|
||||
default: this.userDataProfilesService.defaultProfile,
|
||||
current: options.workspace ? this.userDataProfilesService.getProfile(isWorkspaceIdentifier(options.workspace) ? options.workspace.configPath : options.workspace.uri) : this.userDataProfilesService.defaultProfile,
|
||||
},
|
||||
|
||||
homeDir: this.environmentMainService.userHome.fsPath,
|
||||
tmpDir: this.environmentMainService.tmpDir.fsPath,
|
||||
|
||||
@@ -3,28 +3,33 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { localize } from 'vs/nls';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions } from 'vs/workbench/common/contributions';
|
||||
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
import { IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar';
|
||||
import { PROFILES_CATEGORY } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
|
||||
import '../common/profileActions';
|
||||
// import '../common/userDataProfileActions';
|
||||
import '../common/userDataProfileActions';
|
||||
|
||||
class UserDataProfileStatusBarEntryContribution implements IWorkbenchContribution {
|
||||
class UserDataProfileStatusBarEntryContribution extends Disposable implements IWorkbenchContribution {
|
||||
|
||||
constructor(
|
||||
@IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService,
|
||||
@IStatusbarService private readonly statusBarService: IStatusbarService
|
||||
@IStatusbarService private readonly statusBarService: IStatusbarService,
|
||||
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
|
||||
) {
|
||||
super();
|
||||
this.updateStatus();
|
||||
this._register(this.workspaceContextService.onDidChangeWorkbenchState(() => this.updateStatus()));
|
||||
}
|
||||
|
||||
private async updateStatus(): Promise<void> {
|
||||
const profiles = await this.userDataProfilesService.getAllProfiles();
|
||||
if (profiles.length) {
|
||||
if (profiles.length > 1 && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY) {
|
||||
this.statusBarService.addEntry({
|
||||
name: this.userDataProfilesService.currentProfile.name!,
|
||||
command: 'workbench.profiles.actions.switchProfile',
|
||||
|
||||
@@ -17,6 +17,11 @@ import { asJson, asText, IRequestService } from 'vs/platform/request/common/requ
|
||||
import { IUserDataProfileTemplate, isProfile, IUserDataProfileManagementService, IUserDataProfileWorkbenchService, PROFILES_CATEGORY, PROFILE_EXTENSION, PROFILE_FILTER } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
|
||||
import { IsDevelopmentContext } from 'vs/platform/contextkey/common/contextkeys';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { WorkbenchStateContext } from 'vs/workbench/common/contextkeys';
|
||||
import { CATEGORIES } from 'vs/workbench/common/actions';
|
||||
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
|
||||
|
||||
registerAction2(class SaveProfileAsAction extends Action2 {
|
||||
constructor() {
|
||||
@@ -27,7 +32,8 @@ registerAction2(class SaveProfileAsAction extends Action2 {
|
||||
original: 'Save Settings Profile As...'
|
||||
},
|
||||
category: PROFILES_CATEGORY,
|
||||
f1: true
|
||||
f1: true,
|
||||
precondition: ContextKeyExpr.and(IsDevelopmentContext, WorkbenchStateContext.notEqualsTo('empty')),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -39,7 +45,7 @@ registerAction2(class SaveProfileAsAction extends Action2 {
|
||||
title: localize('save profile as', "Save Settings Profile As..."),
|
||||
});
|
||||
if (name) {
|
||||
await userDataProfileManagementService.createAndEnterProfile(name);
|
||||
await userDataProfileManagementService.createAndEnterProfile(name, undefined, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -53,7 +59,8 @@ registerAction2(class SwitchProfileAction extends Action2 {
|
||||
original: 'Switch Settings Profile'
|
||||
},
|
||||
category: PROFILES_CATEGORY,
|
||||
f1: true
|
||||
f1: true,
|
||||
precondition: ContextKeyExpr.and(IsDevelopmentContext, WorkbenchStateContext.notEqualsTo('empty')),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -85,7 +92,8 @@ registerAction2(class RemoveProfileAction extends Action2 {
|
||||
original: 'Remove Settings Profile'
|
||||
},
|
||||
category: PROFILES_CATEGORY,
|
||||
f1: true
|
||||
f1: true,
|
||||
precondition: ContextKeyExpr.and(IsDevelopmentContext, WorkbenchStateContext.notEqualsTo('empty')),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -104,16 +112,70 @@ registerAction2(class RemoveProfileAction extends Action2 {
|
||||
}
|
||||
});
|
||||
|
||||
registerAction2(class CleanupProfilesAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.profiles.actions.cleanupProfiles',
|
||||
title: {
|
||||
value: localize('cleanup profile', "Cleanup Profiles"),
|
||||
original: 'Cleanup Profiles'
|
||||
},
|
||||
category: CATEGORIES.Developer,
|
||||
f1: true,
|
||||
precondition: IsDevelopmentContext,
|
||||
});
|
||||
}
|
||||
|
||||
async run(accessor: ServicesAccessor) {
|
||||
const userDataProfilesService = accessor.get(IUserDataProfilesService);
|
||||
const fileService = accessor.get(IFileService);
|
||||
const uriIdentityService = accessor.get(IUriIdentityService);
|
||||
|
||||
const allProfiles = await userDataProfilesService.getAllProfiles();
|
||||
const stat = await fileService.resolve(userDataProfilesService.profilesHome);
|
||||
await Promise.all((stat.children || [])?.filter(child => child.isDirectory && allProfiles.every(p => !uriIdentityService.extUri.isEqual(p.location, child.resource)))
|
||||
.map(child => fileService.del(child.resource, { recursive: true })));
|
||||
}
|
||||
});
|
||||
|
||||
registerAction2(class CreateAndEnterEmptyProfileAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.profiles.actions.createAndEnterEmptyProfile',
|
||||
title: {
|
||||
value: localize('create and enter empty profile', "Create and Enter Empty Profile..."),
|
||||
original: 'Create and Enter Empty Profile...'
|
||||
},
|
||||
category: PROFILES_CATEGORY,
|
||||
f1: true,
|
||||
precondition: ContextKeyExpr.and(IsDevelopmentContext, WorkbenchStateContext.notEqualsTo('empty')),
|
||||
});
|
||||
}
|
||||
|
||||
async run(accessor: ServicesAccessor) {
|
||||
const quickInputService = accessor.get(IQuickInputService);
|
||||
const userDataProfileManagementService = accessor.get(IUserDataProfileManagementService);
|
||||
const name = await quickInputService.input({
|
||||
placeHolder: localize('name', "Profile name"),
|
||||
title: localize('create and enter empty profile', "Create and Enter Empty Profile..."),
|
||||
});
|
||||
if (name) {
|
||||
await userDataProfileManagementService.createAndEnterProfile(name);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
registerAction2(class ExportProfileAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.profiles.actions.exportProfile',
|
||||
id: 'workbench.profiles.actions.exportProfile2',
|
||||
title: {
|
||||
value: localize('export profile', "Export Settings Profile as a Template..."),
|
||||
original: 'Export Settings as a Profile as a Template...'
|
||||
value: localize('export profile', "Export Settings as a Profile (2)..."),
|
||||
original: 'Export Settings as a Profile as a Profile (2)...'
|
||||
},
|
||||
category: PROFILES_CATEGORY,
|
||||
f1: true
|
||||
f1: true,
|
||||
precondition: ContextKeyExpr.and(IsDevelopmentContext, WorkbenchStateContext.notEqualsTo('empty')),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -140,16 +202,17 @@ registerAction2(class ExportProfileAction extends Action2 {
|
||||
}
|
||||
});
|
||||
|
||||
registerAction2(class CreateProfileFromTemplateAction extends Action2 {
|
||||
registerAction2(class ImportProfileAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.profiles.actions.createProfileFromTemplate',
|
||||
id: 'workbench.profiles.actions.importProfile2',
|
||||
title: {
|
||||
value: localize('create profile from template', "Create Settings Profile from Template..."),
|
||||
original: 'Create Settings Profile from Template...'
|
||||
value: localize('import profile', "Import Settings from a Profile (2)..."),
|
||||
original: 'Import Settings from a Profile (2)...'
|
||||
},
|
||||
category: PROFILES_CATEGORY,
|
||||
f1: true
|
||||
f1: true,
|
||||
precondition: ContextKeyExpr.and(IsDevelopmentContext, WorkbenchStateContext.notEqualsTo('empty')),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
|
||||
import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
|
||||
import { ISingleFolderWorkspaceIdentifier, IWorkspaceContextService, IWorkspaceIdentifier, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IExtensionManagementServerService, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { CreationOptions, IUserDataProfileManagementService, IUserDataProfileTemplate, PROFILES_CATEGORY } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
|
||||
@@ -42,38 +43,48 @@ export class UserDataProfileManagementService extends Disposable implements IUse
|
||||
@IHostService private readonly hostService: IHostService,
|
||||
@IDialogService private readonly dialogService: IDialogService,
|
||||
@IProgressService private readonly progressService: IProgressService,
|
||||
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
|
||||
@ILogService logService: ILogService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async createAndEnterProfile(name: string, options: CreationOptions = DefaultOptions): Promise<void> {
|
||||
async createAndEnterProfile(name: string, options: CreationOptions = DefaultOptions, fromExisting?: boolean): Promise<void> {
|
||||
const workspaceIdentifier = this.getWorkspaceIdentifier();
|
||||
if (!workspaceIdentifier) {
|
||||
throw new Error(localize('cannotCreateProfileInEmptyWorkbench', "Cannot create a profile in an empty workspace"));
|
||||
}
|
||||
const promises: Promise<any>[] = [];
|
||||
const newProfile = this.userDataProfilesService.createProfile(name);
|
||||
const newProfile = this.userDataProfilesService.newProfile(name);
|
||||
await this.fileService.createFolder(newProfile.location);
|
||||
if (options?.uiState) {
|
||||
promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.globalStorageHome, newProfile.globalStorageHome));
|
||||
}
|
||||
if (options?.settings) {
|
||||
promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.settingsResource, newProfile.settingsResource));
|
||||
}
|
||||
if (options?.extensions && newProfile.extensionsResource) {
|
||||
promises.push((async () => {
|
||||
const extensionsProfileResource = this.userDataProfilesService.currentProfile.extensionsResource ?? await this.createDefaultExtensionsProfile(joinPath(this.userDataProfilesService.defaultProfile.location, basename(newProfile.extensionsResource!)));
|
||||
this.fileService.copy(extensionsProfileResource, newProfile.extensionsResource!);
|
||||
})());
|
||||
}
|
||||
if (options?.keybindings) {
|
||||
promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.keybindingsResource, newProfile.keybindingsResource));
|
||||
}
|
||||
if (options?.tasks) {
|
||||
promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.tasksResource, newProfile.tasksResource));
|
||||
}
|
||||
if (options?.snippets) {
|
||||
promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.snippetsHome, newProfile.snippetsHome));
|
||||
if (fromExisting) {
|
||||
if (options?.uiState) {
|
||||
promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.globalStorageHome, newProfile.globalStorageHome));
|
||||
}
|
||||
if (options?.settings) {
|
||||
promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.settingsResource, newProfile.settingsResource));
|
||||
}
|
||||
if (options?.extensions && newProfile.extensionsResource) {
|
||||
promises.push((async () => {
|
||||
const extensionsProfileResource = this.userDataProfilesService.currentProfile.extensionsResource ?? await this.createDefaultExtensionsProfile(joinPath(this.userDataProfilesService.defaultProfile.location, basename(newProfile.extensionsResource!)));
|
||||
this.fileService.copy(extensionsProfileResource, newProfile.extensionsResource!);
|
||||
})());
|
||||
}
|
||||
if (options?.keybindings) {
|
||||
promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.keybindingsResource, newProfile.keybindingsResource));
|
||||
}
|
||||
if (options?.tasks) {
|
||||
promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.tasksResource, newProfile.tasksResource));
|
||||
}
|
||||
if (options?.snippets) {
|
||||
promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.snippetsHome, newProfile.snippetsHome));
|
||||
}
|
||||
} else {
|
||||
promises.push(this.fileService.createFolder(newProfile.globalStorageHome));
|
||||
}
|
||||
await Promise.allSettled(promises);
|
||||
await this.doSwitchProfile(name);
|
||||
await this.userDataProfilesService.createProfile(newProfile, options, workspaceIdentifier);
|
||||
await this.enterProfile();
|
||||
}
|
||||
|
||||
async removeProfile(name: string): Promise<void> {
|
||||
@@ -88,6 +99,7 @@ export class UserDataProfileManagementService extends Disposable implements IUse
|
||||
if (!profile) {
|
||||
throw new Error(`Profile ${name} does not exist`);
|
||||
}
|
||||
await this.userDataProfilesService.removeProfile(profile);
|
||||
if (profiles.length === 2) {
|
||||
await this.fileService.del(this.userDataProfilesService.profilesHome, { recursive: true });
|
||||
} else {
|
||||
@@ -96,21 +108,30 @@ export class UserDataProfileManagementService extends Disposable implements IUse
|
||||
}
|
||||
|
||||
async switchProfile(name: string): Promise<void> {
|
||||
const workspaceIdentifier = this.getWorkspaceIdentifier();
|
||||
if (!workspaceIdentifier) {
|
||||
throw new Error(localize('cannotSwitchProfileInEmptyWorkbench', "Cannot switch a profile in an empty workspace"));
|
||||
}
|
||||
const profiles = await this.userDataProfilesService.getAllProfiles();
|
||||
const profile = profiles.find(p => p.name === name);
|
||||
if (!profile) {
|
||||
throw new Error(`Profile ${name} does not exist`);
|
||||
}
|
||||
await this.doSwitchProfile(name);
|
||||
await this.userDataProfilesService.setProfileForWorkspace(profile, workspaceIdentifier);
|
||||
await this.enterProfile();
|
||||
}
|
||||
|
||||
async createAndEnterProfileFromTemplate(name: string, template: IUserDataProfileTemplate, options: CreationOptions = DefaultOptions): Promise<void> {
|
||||
const workspaceIdentifier = this.getWorkspaceIdentifier();
|
||||
if (!workspaceIdentifier) {
|
||||
throw new Error(localize('cannotCreateProfileInEmptyWorkbench', "Cannot create a profile in an empty workspace"));
|
||||
}
|
||||
await this.progressService.withProgress({
|
||||
location: ProgressLocation.Notification,
|
||||
title: localize('profiles.creating', "{0}: Creating...", PROFILES_CATEGORY),
|
||||
}, async progress => {
|
||||
const promises: Promise<any>[] = [];
|
||||
const newProfile = this.userDataProfilesService.createProfile(name);
|
||||
const newProfile = this.userDataProfilesService.newProfile(name);
|
||||
await this.fileService.createFolder(newProfile.location);
|
||||
if (template.globalState) {
|
||||
// todo: create global state
|
||||
@@ -122,26 +143,30 @@ export class UserDataProfileManagementService extends Disposable implements IUse
|
||||
promises.push(this.fileService.writeFile(newProfile.extensionsResource, VSBuffer.fromString(template.extensions)));
|
||||
}
|
||||
await Promise.allSettled(promises);
|
||||
await this.userDataProfilesService.createProfile(newProfile, options, workspaceIdentifier);
|
||||
});
|
||||
await this.doSwitchProfile(name);
|
||||
await this.enterProfile();
|
||||
}
|
||||
|
||||
async reset(): Promise<void> {
|
||||
if (this.userDataProfilesService.currentProfile.name !== this.userDataProfilesService.defaultProfile.name) {
|
||||
throw new Error('Please switch to default profile to reset');
|
||||
private getWorkspaceIdentifier(): ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier | undefined {
|
||||
const workspace = this.workspaceContextService.getWorkspace();
|
||||
switch (this.workspaceContextService.getWorkbenchState()) {
|
||||
case WorkbenchState.FOLDER:
|
||||
return { uri: workspace.folders[0].uri, id: workspace.id };
|
||||
case WorkbenchState.WORKSPACE:
|
||||
return { configPath: workspace.configuration!, id: workspace.id };
|
||||
}
|
||||
await this.fileService.del(this.userDataProfilesService.profilesHome);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private async doSwitchProfile(name: string): Promise<void> {
|
||||
await this.userDataProfilesService.setProfile(name);
|
||||
private async enterProfile(): Promise<void> {
|
||||
const result = await this.dialogService.confirm({
|
||||
type: 'info',
|
||||
message: localize('restart message', "Switching a profile requires restarting VS Code."),
|
||||
primaryButton: localize('restart button', "&&Restart"),
|
||||
message: localize('reload message', "Switching a profile requires reloading VS Code."),
|
||||
primaryButton: localize('reload button', "&&Reload"),
|
||||
});
|
||||
if (result.confirmed) {
|
||||
await this.hostService.restart();
|
||||
await this.hostService.reload();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,12 +20,11 @@ export const IUserDataProfileManagementService = createDecorator<IUserDataProfil
|
||||
export interface IUserDataProfileManagementService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
createAndEnterProfile(name: string, options?: CreationOptions): Promise<void>;
|
||||
createAndEnterProfile(name: string, options?: CreationOptions, fromExisting?: boolean): Promise<void>;
|
||||
createAndEnterProfileFromTemplate(name: string, template: IUserDataProfileTemplate, options?: CreationOptions): Promise<void>;
|
||||
removeProfile(name: string): Promise<void>;
|
||||
switchProfile(name: string): Promise<void>;
|
||||
|
||||
reset(): Promise<void>;
|
||||
}
|
||||
|
||||
export interface IUserDataProfileTemplate {
|
||||
|
||||
Reference in New Issue
Block a user