Merge remote-tracking branch 'origin' into electron-19.x.y

This commit is contained in:
deepak1556 2022-07-11 16:59:11 +09:00
commit d5dc423bd8
64 changed files with 994 additions and 624 deletions

View file

@ -15,6 +15,6 @@ module.exports = withDefaults({
mainFields: ['module', 'main']
},
entry: {
extension: './src/extension.node.ts',
extension: './src/extension.ts',
}
});

View file

@ -10,7 +10,7 @@
"engines": {
"vscode": "^1.20.0"
},
"main": "./out/extension.node",
"main": "./out/extension",
"browser": "./dist/browser/extension",
"categories": [
"Programming Languages"

View file

@ -85,12 +85,12 @@
"vscode-proxy-agent": "^0.12.0",
"vscode-regexpp": "^3.1.0",
"vscode-textmate": "7.0.1",
"xterm": "4.20.0-beta.5",
"xterm": "4.20.0-beta.6",
"xterm-addon-search": "0.10.0-beta.1",
"xterm-addon-serialize": "0.8.0-beta.1",
"xterm-addon-unicode11": "0.4.0-beta.3",
"xterm-addon-webgl": "0.13.0-beta.2",
"xterm-headless": "4.20.0-beta.5",
"xterm-addon-webgl": "0.13.0-beta.3",
"xterm-headless": "4.20.0-beta.6",
"yauzl": "^2.9.2",
"yazl": "^2.4.3"
},

View file

@ -24,12 +24,12 @@
"vscode-proxy-agent": "^0.12.0",
"vscode-regexpp": "^3.1.0",
"vscode-textmate": "7.0.1",
"xterm": "4.20.0-beta.5",
"xterm": "4.20.0-beta.6",
"xterm-addon-search": "0.10.0-beta.1",
"xterm-addon-serialize": "0.8.0-beta.1",
"xterm-addon-unicode11": "0.4.0-beta.3",
"xterm-addon-webgl": "0.13.0-beta.2",
"xterm-headless": "4.20.0-beta.5",
"xterm-addon-webgl": "0.13.0-beta.3",
"xterm-headless": "4.20.0-beta.6",
"yauzl": "^2.9.2",
"yazl": "^2.4.3"
},

View file

@ -11,9 +11,9 @@
"tas-client-umd": "0.1.6",
"vscode-oniguruma": "1.6.1",
"vscode-textmate": "7.0.1",
"xterm": "4.20.0-beta.5",
"xterm": "4.20.0-beta.6",
"xterm-addon-search": "0.10.0-beta.1",
"xterm-addon-unicode11": "0.4.0-beta.3",
"xterm-addon-webgl": "0.13.0-beta.2"
"xterm-addon-webgl": "0.13.0-beta.3"
}
}

View file

@ -78,12 +78,12 @@ xterm-addon-unicode11@0.4.0-beta.3:
resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.4.0-beta.3.tgz#f350184155fafd5ad0d6fbf31d13e6ca7dea1efa"
integrity sha512-FryZAVwbUjKTmwXnm1trch/2XO60F5JsDvOkZhzobV1hm10sFLVuZpFyHXiUx7TFeeFsvNP+S77LAtWoeT5z+Q==
xterm-addon-webgl@0.13.0-beta.2:
version "0.13.0-beta.2"
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.13.0-beta.2.tgz#f58a7a3641ad7c8ac82dd24cfb0165656ed9ac1c"
integrity sha512-98tX0BkpD402RoCO6SyikUXpzCn9/OQhlXsRmM/kRFCxMWWofStWTXzCPhN0MjIx2IdGueDjCmnShhidwihErg==
xterm-addon-webgl@0.13.0-beta.3:
version "0.13.0-beta.3"
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.13.0-beta.3.tgz#2b456c3105238e64b40a30787d6335f5f6f85abb"
integrity sha512-DFGcXAolA0VTsOLIKcORxUOp/FTJdD/YiRzKVLARjgOycwVRKvW2L5Tge8Z7ysZ16sKfnV2vCXyonXYfUWozXw==
xterm@4.20.0-beta.5:
version "4.20.0-beta.5"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.20.0-beta.5.tgz#d707b0dcb477a554135fb767b24003fced079866"
integrity sha512-KBWfk9UPBKRy662DVGGTZEcW1becEjYvlyWbn2hLj9h2gy6Q4EEEEbggJh8I7SGwdFizl+apHQGhEOZmFCA70w==
xterm@4.20.0-beta.6:
version "4.20.0-beta.6"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.20.0-beta.6.tgz#3ed87ba383a5cf44284098278f714df7113e3e3c"
integrity sha512-xJd6vyOuYo4Ht/hTY3DyXGIj0U6kHjr2vWQ1lRmearo3t7QKf7uqOAAfTLeWt/g1P8qe/r0DnsNTeag6vI9RVw==

View file

@ -803,20 +803,20 @@ xterm-addon-unicode11@0.4.0-beta.3:
resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.4.0-beta.3.tgz#f350184155fafd5ad0d6fbf31d13e6ca7dea1efa"
integrity sha512-FryZAVwbUjKTmwXnm1trch/2XO60F5JsDvOkZhzobV1hm10sFLVuZpFyHXiUx7TFeeFsvNP+S77LAtWoeT5z+Q==
xterm-addon-webgl@0.13.0-beta.2:
version "0.13.0-beta.2"
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.13.0-beta.2.tgz#f58a7a3641ad7c8ac82dd24cfb0165656ed9ac1c"
integrity sha512-98tX0BkpD402RoCO6SyikUXpzCn9/OQhlXsRmM/kRFCxMWWofStWTXzCPhN0MjIx2IdGueDjCmnShhidwihErg==
xterm-addon-webgl@0.13.0-beta.3:
version "0.13.0-beta.3"
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.13.0-beta.3.tgz#2b456c3105238e64b40a30787d6335f5f6f85abb"
integrity sha512-DFGcXAolA0VTsOLIKcORxUOp/FTJdD/YiRzKVLARjgOycwVRKvW2L5Tge8Z7ysZ16sKfnV2vCXyonXYfUWozXw==
xterm-headless@4.20.0-beta.5:
version "4.20.0-beta.5"
resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.20.0-beta.5.tgz#edcff27eb6437d158e6aea2ed7658e783bee5641"
integrity sha512-8SnVUsuNUrQ5P0XU/9Iau3uK7Tf8q/p0KHHwkwJXVxZDIlaDH9XKSs91U9BjJJE3sJgRxH4NSiDYR3vFLSFpxw==
xterm-headless@4.20.0-beta.6:
version "4.20.0-beta.6"
resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.20.0-beta.6.tgz#bd016379e9fac47e5b8870d567cdf330cf6f49fc"
integrity sha512-EV0V7pxMKI0OEcOCD+6vdXq6rBARr7dSN3PovTsZnDWg5dmvUb2eEmz6BTejJj3UVd/JXNEmEXM+tCh97rDCDg==
xterm@4.20.0-beta.5:
version "4.20.0-beta.5"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.20.0-beta.5.tgz#d707b0dcb477a554135fb767b24003fced079866"
integrity sha512-KBWfk9UPBKRy662DVGGTZEcW1becEjYvlyWbn2hLj9h2gy6Q4EEEEbggJh8I7SGwdFizl+apHQGhEOZmFCA70w==
xterm@4.20.0-beta.6:
version "4.20.0-beta.6"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.20.0-beta.6.tgz#3ed87ba383a5cf44284098278f714df7113e3e3c"
integrity sha512-xJd6vyOuYo4Ht/hTY3DyXGIj0U6kHjr2vWQ1lRmearo3t7QKf7uqOAAfTLeWt/g1P8qe/r0DnsNTeag6vI9RVw==
yallist@^4.0.0:
version "4.0.0"

View file

@ -42,8 +42,13 @@
outline-offset: -1px !important;
}
.monaco-button-dropdown > .monaco-dropdown-button {
margin-left: 1px;
.monaco-button-dropdown .monaco-button-dropdown-separator {
padding: 4px 0;
}
.monaco-button-dropdown .monaco-button-dropdown-separator > div {
height: 100%;
width: 1px;
}
.monaco-description-button {

View file

@ -247,6 +247,8 @@ export class ButtonWithDropdown extends Disposable implements IButton {
private readonly button: Button;
private readonly action: Action;
private readonly dropdownButton: Button;
private readonly separatorContainer: HTMLDivElement;
private readonly separator: HTMLDivElement;
readonly element: HTMLElement;
private readonly _onDidClick = this._register(new Emitter<Event | undefined>());
@ -263,6 +265,13 @@ export class ButtonWithDropdown extends Disposable implements IButton {
this._register(this.button.onDidClick(e => this._onDidClick.fire(e)));
this.action = this._register(new Action('primaryAction', this.button.label, undefined, true, async () => this._onDidClick.fire(undefined)));
this.separatorContainer = document.createElement('div');
this.separatorContainer.classList.add('monaco-button-dropdown-separator');
this.separator = document.createElement('div');
this.separatorContainer.appendChild(this.separator);
this.element.appendChild(this.separatorContainer);
this.dropdownButton = this._register(new Button(this.element, { ...options, title: false, supportIcons: true }));
this.dropdownButton.element.title = localize("button dropdown more actions", 'More Actions...');
this.dropdownButton.element.classList.add('monaco-dropdown-button');
@ -299,6 +308,10 @@ export class ButtonWithDropdown extends Disposable implements IButton {
style(styles: IButtonStyles): void {
this.button.style(styles);
this.dropdownButton.style(styles);
// Separator
this.separatorContainer.style.backgroundColor = styles.buttonBackground?.toString() ?? '';
this.separator.style.backgroundColor = styles.buttonForeground?.toString() ?? '';
}
focus(): void {

View file

@ -232,7 +232,7 @@ class SharedProcessMain extends Disposable {
fileService.registerProvider(Schemas.vscodeUserData, userDataFileSystemProvider);
// User Data Profiles
const userDataProfilesService = this._register(new UserDataProfilesNativeService(this.configuration.profiles, mainProcessService, environmentService, fileService, logService));
const userDataProfilesService = this._register(new UserDataProfilesNativeService(this.configuration.profiles, mainProcessService, environmentService));
services.set(IUserDataProfilesService, userDataProfilesService);
// Configuration

View file

@ -18,12 +18,10 @@ import {
ServerInstallOptions, ServerInstallVSIXOptions, ServerUninstallOptions, Metadata, ServerInstallExtensionEvent, ServerInstallExtensionResult, ServerUninstallExtensionEvent, ServerDidUninstallExtensionEvent
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions, ExtensionKey, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, getMaliciousExtensionsSet } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService';
import { ExtensionType, IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions';
import { ExtensionType, IExtensionManifest, isApplicationScopedExtension, TargetPlatform } from 'vs/platform/extensions/common/extensions';
import { ILogService } from 'vs/platform/log/common/log';
import { IProductService } from 'vs/platform/product/common/productService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
export interface IInstallExtensionTask {
@ -66,13 +64,11 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
private readonly participants: IExtensionManagementParticipant[] = [];
constructor(
@IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService,
@IUriIdentityService private readonly uriIdenityService: IUriIdentityService,
@IExtensionGalleryService protected readonly galleryService: IExtensionGalleryService,
@IExtensionsProfileScannerService protected readonly extensionsProfileScannerService: IExtensionsProfileScannerService,
@ITelemetryService protected readonly telemetryService: ITelemetryService,
@ILogService protected readonly logService: ILogService,
@IProductService protected readonly productService: IProductService
@IProductService protected readonly productService: IProductService,
@IUserDataProfilesService protected readonly userDataProfilesService: IUserDataProfilesService,
) {
super();
this._register(toDisposable(() => {
@ -120,7 +116,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
throw new Error(nls.localize('Not a Marketplace extension', "Only Marketplace Extensions can be reinstalled"));
}
await this.createDefaultUninstallExtensionTask(extension, { remove: true, versionOnly: true }).run();
await this.createUninstallExtensionTask(extension, { remove: true, versionOnly: true }).run();
await this.installFromGallery(galleryExtension);
}
@ -140,13 +136,15 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
}
protected async installExtension(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: ServerInstallOptions & ServerInstallVSIXOptions): Promise<ILocalExtension> {
const getInstallExtensionTaskKey = (extension: IGalleryExtension) => `${ExtensionKey.create(extension).toString()}${options.profileLocation ? `-${options.profileLocation.toString()}` : ''}`;
// only cache gallery extensions tasks
if (!URI.isUri(extension)) {
const installExtensionTask = this.installingExtensions.get(ExtensionKey.create(extension).toString());
const installExtensionTask = this.installingExtensions.get(getInstallExtensionTaskKey(extension));
if (installExtensionTask) {
this.logService.info('Extensions is already requested to install', extension.identifier.id);
const waitUntilTaskIsFinishedTask = this.createWaitUntilInstallExtensionTaskIsFinishedTask(installExtensionTask, options);
const { local } = await waitUntilTaskIsFinishedTask.waitUntilTaskIsFinished();
const { local } = await installExtensionTask.waitUntilTaskIsFinished();
return local;
}
options = { ...options, installOnlyNewlyAddedFromExtensionPack: true /* always true for gallery extensions */ };
@ -156,7 +154,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
const installResults: (ServerInstallExtensionResult & { local: ILocalExtension })[] = [];
const installExtensionTask = this.createInstallExtensionTask(manifest, extension, options);
if (!URI.isUri(extension)) {
this.installingExtensions.set(ExtensionKey.create(extension).toString(), installExtensionTask);
this.installingExtensions.set(getInstallExtensionTaskKey(extension), installExtensionTask);
}
this._onInstallExtension.fire({ identifier: installExtensionTask.identifier, source: extension, profileLocation: options.profileLocation });
this.logService.info('Installing extension:', installExtensionTask.identifier.id);
@ -171,7 +169,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
const allDepsAndPackExtensionsToInstall = await this.getAllDepsAndPackExtensionsToInstall(installExtensionTask.identifier, manifest, !!options.installOnlyNewlyAddedFromExtensionPack, !!options.installPreReleaseVersion, options.profileLocation);
for (const { gallery, manifest } of allDepsAndPackExtensionsToInstall) {
installExtensionHasDependents = installExtensionHasDependents || !!manifest.extensionDependencies?.some(id => areSameExtensions({ id }, installExtensionTask.identifier));
const key = ExtensionKey.create(gallery).toString();
const key = getInstallExtensionTaskKey(gallery);
if (this.installingExtensions.has(key)) {
this.logService.info('Extension is already requested to install', gallery.identifier.id);
} else {
@ -260,7 +258,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
// rollback installed extensions
if (installResults.length) {
try {
const result = await Promise.allSettled(installResults.map(({ local }) => this.createUninstallExtensionTask(local, { versionOnly: true }, options.profileLocation).run()));
const result = await Promise.allSettled(installResults.map(({ local }) => this.createUninstallExtensionTask(local, { versionOnly: true, profileLocation: options.profileLocation }).run()));
for (let index = 0; index < result.length; index++) {
const r = result[index];
const { identifier } = installResults[index];
@ -282,7 +280,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
/* Remove the gallery tasks from the cache */
for (const { task } of allInstallExtensionTasks) {
if (!URI.isUri(task.source)) {
const key = ExtensionKey.create(task.source).toString();
const key = getInstallExtensionTaskKey(task.source);
if (!this.installingExtensions.delete(key)) {
this.logService.warn('Installation task is not found in the cache', key);
}
@ -434,7 +432,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
}
const createUninstallExtensionTask = (extension: ILocalExtension, uninstallOptions: ServerUninstallOptions): IUninstallExtensionTask => {
const uninstallExtensionTask = this.createUninstallExtensionTask(extension, uninstallOptions, options.profileLocation);
const uninstallExtensionTask = this.createUninstallExtensionTask(extension, uninstallOptions);
this.uninstallingExtensions.set(getUninstallExtensionTaskKey(uninstallExtensionTask.extension.identifier), uninstallExtensionTask);
if (options.profileLocation) {
this.logService.info('Uninstalling extension from the profile:', `${extension.identifier.id}@${extension.manifest.version}`, options.profileLocation.toString());
@ -604,22 +602,17 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
}
private createInstallExtensionTask(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: ServerInstallOptions & ServerInstallVSIXOptions): IInstallExtensionTask {
const installTask = this.createDefaultInstallExtensionTask(manifest, extension, options);
return options.profileLocation && this.userDataProfilesService.defaultProfile.extensionsResource ? new InstallExtensionInProfileTask(installTask, options.profileLocation, this.userDataProfilesService.defaultProfile.extensionsResource, this.extensionsProfileScannerService) : installTask;
if (options.profileLocation && isApplicationScopedExtension(manifest)) {
options = { ...options, profileLocation: this.userDataProfilesService.defaultProfile.extensionsResource };
}
return this.doCreateInstallExtensionTask(manifest, extension, options);
}
private createWaitUntilInstallExtensionTaskIsFinishedTask(installTask: IInstallExtensionTask, options: ServerInstallOptions & ServerInstallVSIXOptions): IInstallExtensionTask {
if (!options.profileLocation || !this.userDataProfilesService.defaultProfile.extensionsResource) {
return installTask;
private createUninstallExtensionTask(extension: ILocalExtension, options: ServerUninstallOptions): IUninstallExtensionTask {
if (options.profileLocation && extension.isApplicationScoped) {
options = { ...options, profileLocation: this.userDataProfilesService.defaultProfile.extensionsResource };
}
if (installTask instanceof InstallExtensionInProfileTask && this.uriIdenityService.extUri.isEqual(installTask.profileLocation, options.profileLocation)) {
return installTask;
}
return new InstallExtensionInProfileTask(installTask, options.profileLocation, this.userDataProfilesService.defaultProfile.extensionsResource, this.extensionsProfileScannerService);
}
private createUninstallExtensionTask(extension: ILocalExtension, options: ServerUninstallOptions, profile?: URI): IUninstallExtensionTask {
return profile && this.userDataProfilesService.defaultProfile.extensionsResource ? new UninstallExtensionFromProfileTask(extension, profile, this.userDataProfilesService, this.extensionsProfileScannerService) : this.createDefaultUninstallExtensionTask(extension, options);
return this.doCreateUninstallExtensionTask(extension, options);
}
abstract getTargetPlatform(): Promise<TargetPlatform>;
@ -633,8 +626,8 @@ export abstract class AbstractExtensionManagementService extends Disposable impl
abstract updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): Promise<ILocalExtension>;
abstract updateExtensionScope(local: ILocalExtension, isMachineScoped: boolean): Promise<ILocalExtension>;
protected abstract createDefaultInstallExtensionTask(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: ServerInstallOptions & ServerInstallVSIXOptions): IInstallExtensionTask;
protected abstract createDefaultUninstallExtensionTask(extension: ILocalExtension, options: ServerUninstallOptions): IUninstallExtensionTask;
protected abstract doCreateInstallExtensionTask(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: ServerInstallOptions & ServerInstallVSIXOptions): IInstallExtensionTask;
protected abstract doCreateUninstallExtensionTask(extension: ILocalExtension, options: ServerUninstallOptions): IUninstallExtensionTask;
}
export function joinErrors(errorOrErrors: (Error | string) | (Array<Error | string>)): Error {
@ -731,63 +724,3 @@ export abstract class AbstractExtensionTask<T> {
protected abstract doRun(token: CancellationToken): Promise<T>;
}
class InstallExtensionInProfileTask implements IInstallExtensionTask {
readonly identifier = this.task.identifier;
readonly source = this.task.source;
readonly operation = this.task.operation;
private readonly promise: Promise<{ local: ILocalExtension; metadata: Metadata }>;
constructor(
private readonly task: IInstallExtensionTask,
readonly profileLocation: URI,
private readonly defaultProfileLocation: URI,
private readonly extensionsProfileScannerService: IExtensionsProfileScannerService,
) {
this.promise = this.waitAndAddExtensionToProfile();
}
private async waitAndAddExtensionToProfile(): Promise<{ local: ILocalExtension; metadata: Metadata }> {
const result = await this.task.waitUntilTaskIsFinished();
const profileLocation = result.local.isApplicationScoped ? this.defaultProfileLocation : this.profileLocation;
await this.extensionsProfileScannerService.addExtensionsToProfile([[result.local, result.metadata]], profileLocation);
return result;
}
async run(): Promise<{ local: ILocalExtension; metadata: Metadata }> {
await this.task.run();
return this.promise;
}
waitUntilTaskIsFinished(): Promise<{ local: ILocalExtension; metadata: Metadata }> {
return this.promise;
}
cancel(): void {
return this.task.cancel();
}
}
class UninstallExtensionFromProfileTask extends AbstractExtensionTask<void> implements IUninstallExtensionTask {
constructor(
readonly extension: ILocalExtension,
private readonly profileLocation: URI,
private readonly userDataProfilesService: IUserDataProfilesService,
private readonly extensionsProfileScannerService: IExtensionsProfileScannerService,
) {
super();
}
protected async doRun(token: CancellationToken): Promise<void> {
const promises: Promise<any>[] = [];
promises.push(this.extensionsProfileScannerService.removeExtensionFromProfile(this.extension.identifier, this.profileLocation));
if (this.extension.isApplicationScoped && this.userDataProfilesService.defaultProfile.extensionsResource) {
promises.push(this.extensionsProfileScannerService.removeExtensionFromProfile(this.extension.identifier, this.userDataProfilesService.defaultProfile.extensionsResource));
}
await Promise.all(promises);
}
}

View file

@ -27,13 +27,14 @@ import { areSameExtensions, computeTargetPlatform, ExtensionKey, getExtensionId,
import { ExtensionType, ExtensionIdentifier, IExtensionManifest, TargetPlatform, IExtensionIdentifier, IRelaxedExtensionManifest, UNDEFINED_PUBLISHER, IExtensionDescription, BUILTIN_MANIFEST_CACHE_FILE, USER_MANIFEST_CACHE_FILE, MANIFEST_CACHE_FOLDER } from 'vs/platform/extensions/common/extensions';
import { validateExtensionManifest } from 'vs/platform/extensions/common/extensionValidator';
import { FileOperationResult, IFileService, toFileOperationResult } from 'vs/platform/files/common/files';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ILogService } from 'vs/platform/log/common/log';
import { IProductService } from 'vs/platform/product/common/productService';
import { Emitter, Event } from 'vs/base/common/event';
import { revive } from 'vs/base/common/marshalling';
import { IExtensionsProfileScannerService, IScannedProfileExtension } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService';
import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
export type IScannedExtensionManifest = IRelaxedExtensionManifest & { __metadata?: Metadata };
@ -139,9 +140,9 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem
readonly onDidChangeCache = this._onDidChangeCache.event;
private readonly obsoleteFile = joinPath(this.userExtensionsLocation, '.obsolete');
private readonly systemExtensionsCachedScanner = this._register(new CachedExtensionsScanner(joinPath(this.cacheLocation, BUILTIN_MANIFEST_CACHE_FILE), this.obsoleteFile, this.extensionsProfileScannerService, this.fileService, this.logService));
private readonly userExtensionsCachedScanner = this._register(new CachedExtensionsScanner(joinPath(this.cacheLocation, USER_MANIFEST_CACHE_FILE), this.obsoleteFile, this.extensionsProfileScannerService, this.fileService, this.logService));
private readonly extensionsScanner = this._register(new ExtensionsScanner(this.obsoleteFile, this.extensionsProfileScannerService, this.fileService, this.logService));
private readonly systemExtensionsCachedScanner = this._register(this.instantiationService.createInstance(CachedExtensionsScanner, joinPath(this.cacheLocation, BUILTIN_MANIFEST_CACHE_FILE), this.obsoleteFile));
private readonly userExtensionsCachedScanner = this._register(this.instantiationService.createInstance(CachedExtensionsScanner, joinPath(this.cacheLocation, USER_MANIFEST_CACHE_FILE), this.obsoleteFile));
private readonly extensionsScanner = this._register(this.instantiationService.createInstance(ExtensionsScanner, this.obsoleteFile));
constructor(
readonly systemExtensionsLocation: URI,
@ -154,6 +155,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem
@ILogService protected readonly logService: ILogService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IProductService private readonly productService: IProductService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
) {
super();
@ -476,9 +478,10 @@ class ExtensionsScanner extends Disposable {
constructor(
private readonly obsoleteFile: URI,
protected readonly extensionsProfileScannerService: IExtensionsProfileScannerService,
protected readonly fileService: IFileService,
protected readonly logService: ILogService
@IExtensionsProfileScannerService protected readonly extensionsProfileScannerService: IExtensionsProfileScannerService,
@IUriIdentityService protected readonly uriIdentityService: IUriIdentityService,
@IFileService protected readonly fileService: IFileService,
@ILogService protected readonly logService: ILogService
) {
super();
}
@ -518,15 +521,13 @@ class ExtensionsScanner extends Disposable {
}
private async scanExtensionsFromProfile(input: ExtensionScannerInput): Promise<IRelaxedScannedExtension[]> {
const profileExtensions = await this.scanExtensionsFromProfileResource(input.location, () => true, input);
const applicationExtensions = await this.scanApplicationExtensions(input);
return [...profileExtensions, ...applicationExtensions];
}
private async scanApplicationExtensions(input: ExtensionScannerInput): Promise<IRelaxedScannedExtension[]> {
return input.applicationExtensionslocation
? this.scanExtensionsFromProfileResource(input.applicationExtensionslocation, (e) => !!e.metadata?.isApplicationScoped, input)
: [];
let profileExtensions = await this.scanExtensionsFromProfileResource(input.location, () => true, input);
if (input.applicationExtensionslocation && !this.uriIdentityService.extUri.isEqual(input.location, input.applicationExtensionslocation)) {
profileExtensions = profileExtensions.filter(e => !e.metadata?.isApplicationScoped);
const applicationExtensions = await this.scanExtensionsFromProfileResource(input.applicationExtensionslocation, (e) => !!e.metadata?.isApplicationScoped, input);
profileExtensions.push(...applicationExtensions);
}
return profileExtensions;
}
private async scanExtensionsFromProfileResource(profileResource: URI, filter: (extensionInfo: IScannedProfileExtension) => boolean, input: ExtensionScannerInput): Promise<IRelaxedScannedExtension[]> {
@ -845,11 +846,12 @@ class CachedExtensionsScanner extends ExtensionsScanner {
constructor(
private readonly cacheFile: URI,
obsoleteFile: URI,
extensionsProfileScannerService: IExtensionsProfileScannerService,
fileService: IFileService,
logService: ILogService
@IExtensionsProfileScannerService extensionsProfileScannerService: IExtensionsProfileScannerService,
@IUriIdentityService uriIdentityService: IUriIdentityService,
@IFileService fileService: IFileService,
@ILogService logService: ILogService
) {
super(obsoleteFile, extensionsProfileScannerService, fileService, logService);
super(obsoleteFile, extensionsProfileScannerService, uriIdentityService, fileService, logService);
}
override async scanExtensions(input: ExtensionScannerInput): Promise<IRelaxedScannedExtension[]> {
@ -947,13 +949,14 @@ export class NativeExtensionsScannerService extends AbstractExtensionsScannerSer
logService: ILogService,
environmentService: IEnvironmentService,
productService: IProductService,
instantiationService: IInstantiationService,
) {
super(
systemExtensionsLocation,
userExtensionsLocation,
joinPath(userHome, '.vscode-oss-dev', 'extensions', 'control.json'),
joinPath(userDataPath, MANIFEST_CACHE_FOLDER),
userDataProfilesService, extensionsProfileScannerService, fileService, logService, environmentService, productService);
userDataProfilesService, extensionsProfileScannerService, fileService, logService, environmentService, productService, instantiationService);
this.translationsPromise = (async () => {
if (platform.translationsConfigFile) {
try {

View file

@ -9,6 +9,7 @@ import { IExtensionsProfileScannerService } from 'vs/platform/extensionManagemen
import { IExtensionsScannerService, NativeExtensionsScannerService, } from 'vs/platform/extensionManagement/common/extensionsScannerService';
import { IFileService } from 'vs/platform/files/common/files';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ILogService } from 'vs/platform/log/common/log';
import { IProductService } from 'vs/platform/product/common/productService';
import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
@ -22,13 +23,14 @@ export class ExtensionsScannerService extends NativeExtensionsScannerService imp
@ILogService logService: ILogService,
@INativeEnvironmentService environmentService: INativeEnvironmentService,
@IProductService productService: IProductService,
@IInstantiationService instantiationService: IInstantiationService,
) {
super(
URI.file(environmentService.builtinExtensionsPath),
URI.file(environmentService.extensionsPath),
environmentService.userHome,
URI.file(environmentService.userDataPath),
userDataProfilesService, extensionsProfileScannerService, fileService, logService, environmentService, productService);
userDataProfilesService, extensionsProfileScannerService, fileService, logService, environmentService, productService, instantiationService);
}
}

View file

@ -65,13 +65,15 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi
private readonly manifestCache: ExtensionsManifestCache;
private readonly extensionsDownloader: ExtensionsDownloader;
private readonly installGalleryExtensionsTasks = new Map<string, InstallGalleryExtensionTask>();
constructor(
@IExtensionGalleryService galleryService: IExtensionGalleryService,
@ITelemetryService telemetryService: ITelemetryService,
@ILogService logService: ILogService,
@INativeEnvironmentService private readonly environmentService: INativeEnvironmentService,
@IExtensionsScannerService private readonly extensionsScannerService: IExtensionsScannerService,
@IExtensionsProfileScannerService extensionsProfileScannerService: IExtensionsProfileScannerService,
@IExtensionsProfileScannerService private readonly extensionsProfileScannerService: IExtensionsProfileScannerService,
@IDownloadService private downloadService: IDownloadService,
@IInstantiationService instantiationService: IInstantiationService,
@IFileService private readonly fileService: IFileService,
@ -79,7 +81,7 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi
@IUriIdentityService uriIdentityService: IUriIdentityService,
@IUserDataProfilesService userDataProfilesService: IUserDataProfilesService,
) {
super(userDataProfilesService, uriIdentityService, galleryService, extensionsProfileScannerService, telemetryService, logService, productService);
super(galleryService, telemetryService, logService, productService, userDataProfilesService);
const extensionLifecycle = this._register(instantiationService.createInstance(ExtensionsLifecycle));
this.extensionsScanner = this._register(instantiationService.createInstance(ExtensionsScanner, extension => extensionLifecycle.postUninstall(extension)));
this.manifestCache = this._register(new ExtensionsManifestCache(environmentService, this));
@ -176,11 +178,28 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi
return downloadedLocation;
}
protected createDefaultInstallExtensionTask(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: ServerInstallOptions & ServerInstallVSIXOptions): IInstallExtensionTask {
return URI.isUri(extension) ? new InstallVSIXTask(manifest, extension, options, this.galleryService, this.extensionsScanner, this.logService) : new InstallGalleryExtensionTask(manifest, extension, options, this.extensionsDownloader, this.extensionsScanner, this.logService);
protected doCreateInstallExtensionTask(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: ServerInstallOptions & ServerInstallVSIXOptions): IInstallExtensionTask {
let installExtensionTask: IInstallExtensionTask | undefined;
if (URI.isUri(extension)) {
installExtensionTask = new InstallVSIXTask(manifest, extension, options, this.galleryService, this.extensionsScanner, this.logService);
} else {
const key = ExtensionKey.create(extension).toString();
installExtensionTask = this.installGalleryExtensionsTasks.get(key);
if (!installExtensionTask) {
this.installGalleryExtensionsTasks.set(key, installExtensionTask = new InstallGalleryExtensionTask(manifest, extension, options, this.extensionsDownloader, this.extensionsScanner, this.logService));
installExtensionTask.waitUntilTaskIsFinished().then(() => this.installGalleryExtensionsTasks.delete(key));
}
}
if (options.profileLocation) {
return new InstallExtensionInProfileTask(installExtensionTask, options.profileLocation, this.extensionsProfileScannerService);
}
return installExtensionTask;
}
protected createDefaultUninstallExtensionTask(extension: ILocalExtension, options: ServerUninstallOptions): IUninstallExtensionTask {
protected doCreateUninstallExtensionTask(extension: ILocalExtension, options: ServerUninstallOptions): IUninstallExtensionTask {
if (options.profileLocation) {
return new UninstallExtensionFromProfileTask(extension, options.profileLocation, this.extensionsProfileScannerService);
}
return new UninstallExtensionTask(extension, options, this.extensionsScanner);
}
@ -706,6 +725,42 @@ class InstallVSIXTask extends InstallExtensionTask {
}
}
class InstallExtensionInProfileTask implements IInstallExtensionTask {
readonly identifier = this.task.identifier;
readonly source = this.task.source;
readonly operation = this.task.operation;
private readonly promise: Promise<{ local: ILocalExtension; metadata: Metadata }>;
constructor(
private readonly task: IInstallExtensionTask,
private readonly profileLocation: URI,
private readonly extensionsProfileScannerService: IExtensionsProfileScannerService,
) {
this.promise = this.waitAndAddExtensionToProfile();
}
private async waitAndAddExtensionToProfile(): Promise<{ local: ILocalExtension; metadata: Metadata }> {
const result = await this.task.waitUntilTaskIsFinished();
await this.extensionsProfileScannerService.addExtensionsToProfile([[result.local, result.metadata]], this.profileLocation);
return result;
}
async run(): Promise<{ local: ILocalExtension; metadata: Metadata }> {
await this.task.run();
return this.promise;
}
waitUntilTaskIsFinished(): Promise<{ local: ILocalExtension; metadata: Metadata }> {
return this.promise;
}
cancel(): void {
return this.task.cancel();
}
}
class UninstallExtensionTask extends AbstractExtensionTask<void> implements IUninstallExtensionTask {
constructor(
@ -745,3 +800,20 @@ class UninstallExtensionTask extends AbstractExtensionTask<void> implements IUni
}
}
class UninstallExtensionFromProfileTask extends AbstractExtensionTask<void> implements IUninstallExtensionTask {
constructor(
readonly extension: ILocalExtension,
private readonly profileLocation: URI,
private readonly extensionsProfileScannerService: IExtensionsProfileScannerService,
) {
super();
}
protected async doRun(token: CancellationToken): Promise<void> {
await this.extensionsProfileScannerService.removeExtensionFromProfile(this.extension.identifier, this.profileLocation);
}
}

View file

@ -8,6 +8,7 @@ import { INativeEnvironmentService } from 'vs/platform/environment/common/enviro
import { IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService';
import { IExtensionsScannerService, NativeExtensionsScannerService, } from 'vs/platform/extensionManagement/common/extensionsScannerService';
import { IFileService } from 'vs/platform/files/common/files';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ILogService } from 'vs/platform/log/common/log';
import { IProductService } from 'vs/platform/product/common/productService';
import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
@ -21,13 +22,14 @@ export class ExtensionsScannerService extends NativeExtensionsScannerService imp
@ILogService logService: ILogService,
@INativeEnvironmentService environmentService: INativeEnvironmentService,
@IProductService productService: IProductService,
@IInstantiationService instantiationService: IInstantiationService,
) {
super(
URI.file(environmentService.builtinExtensionsPath),
URI.file(environmentService.extensionsPath),
environmentService.userHome,
URI.file(environmentService.userDataPath),
userDataProfilesService, extensionsProfileScannerService, fileService, logService, environmentService, productService);
userDataProfilesService, extensionsProfileScannerService, fileService, logService, environmentService, productService, instantiationService);
}
}

View file

@ -14,9 +14,11 @@ import { ExtensionType, IExtensionManifest, MANIFEST_CACHE_FOLDER, TargetPlatfor
import { IFileService } from 'vs/platform/files/common/files';
import { FileService } from 'vs/platform/files/common/fileService';
import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { ILogService, NullLogService } from 'vs/platform/log/common/log';
import { IProductService } from 'vs/platform/product/common/productService';
import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService';
import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
let translations: Translations = Object.create(null);
@ -31,13 +33,14 @@ class ExtensionsScannerService extends AbstractExtensionsScannerService implemen
@ILogService logService: ILogService,
@INativeEnvironmentService nativeEnvironmentService: INativeEnvironmentService,
@IProductService productService: IProductService,
@IInstantiationService instantiationService: IInstantiationService,
) {
super(
URI.file(nativeEnvironmentService.builtinExtensionsPath),
URI.file(nativeEnvironmentService.extensionsPath),
joinPath(nativeEnvironmentService.userHome, '.vscode-oss-dev', 'extensions', 'control.json'),
joinPath(ROOT, MANIFEST_CACHE_FOLDER),
userDataProfilesService, extensionsProfileScannerService, fileService, logService, nativeEnvironmentService, productService);
userDataProfilesService, extensionsProfileScannerService, fileService, logService, nativeEnvironmentService, productService, instantiationService);
}
protected async getTranslations(language: string): Promise<Translations> {
@ -70,7 +73,7 @@ suite('NativeExtensionsScanerService Test', () => {
});
instantiationService.stub(IProductService, { version: '1.66.0' });
instantiationService.stub(IExtensionsProfileScannerService, new ExtensionsProfileScannerService(fileService, logService));
instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, fileService, logService));
instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, fileService, new UriIdentityService(fileService), logService));
await fileService.createFolder(systemExtensionsLocation);
await fileService.createFolder(userExtensionsLocation);
});

View file

@ -12,3 +12,26 @@ export function escapeNonWindowsPath(path: string): string {
newPath = newPath.replace(bannedChars, '');
return `'${newPath}'`;
}
/**
* Collapses the user's home directory into `~` if it exists within the path, this gives a shorter
* path that is more suitable within the context of a terminal.
*/
export function collapseTildePath(path: string | undefined, userHome: string | undefined, separator: string): string {
if (!path) {
return '';
}
if (!userHome) {
return path;
}
// Trim the trailing separator from the end if it exists
if (userHome.match(/[\/\\]$/)) {
userHome = userHome.slice(0, userHome.length - 1);
}
const normalizedPath = path.replace(/\\/g, '/').toLowerCase();
const normalizedUserHome = userHome.replace(/\\/g, '/').toLowerCase();
if (!normalizedPath.includes(normalizedUserHome)) {
return path;
}
return `~${separator}${path.slice(userHome.length + 1)}`;
}

View file

@ -0,0 +1,40 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { strictEqual } from 'assert';
import { collapseTildePath } from 'vs/platform/terminal/common/terminalEnvironment';
suite('terminalEnvironment', () => {
suite('collapseTildePath', () => {
test('should return empty string for a falsy path', () => {
strictEqual(collapseTildePath('', '/foo', '/'), '');
strictEqual(collapseTildePath(undefined, '/foo', '/'), '');
});
test('should return path for a falsy user home', () => {
strictEqual(collapseTildePath('/foo', '', '/'), '/foo');
strictEqual(collapseTildePath('/foo', undefined, '/'), '/foo');
});
test('should not collapse when user home isn\'t present', () => {
strictEqual(collapseTildePath('/foo', '/bar', '/'), '/foo');
strictEqual(collapseTildePath('C:\\foo', 'C:\\bar', '\\'), 'C:\\foo');
});
test('should collapse with Windows separators', () => {
strictEqual(collapseTildePath('C:\\foo\\bar', 'C:\\foo', '\\'), '~\\bar');
strictEqual(collapseTildePath('C:\\foo\\bar', 'C:\\foo\\', '\\'), '~\\bar');
strictEqual(collapseTildePath('C:\\foo\\bar\\baz', 'C:\\foo\\', '\\'), '~\\bar\\baz');
strictEqual(collapseTildePath('C:\\foo\\bar\\baz', 'C:\\foo', '\\'), '~\\bar\\baz');
});
test('should collapse mixed case with Windows separators', () => {
strictEqual(collapseTildePath('c:\\foo\\bar', 'C:\\foo', '\\'), '~\\bar');
strictEqual(collapseTildePath('C:\\foo\\bar\\baz', 'c:\\foo', '\\'), '~\\bar\\baz');
});
test('should collapse with Posix separators', () => {
strictEqual(collapseTildePath('/foo/bar', '/foo', '/'), '~/bar');
strictEqual(collapseTildePath('/foo/bar', '/foo/', '/'), '~/bar');
strictEqual(collapseTildePath('/foo/bar/baz', '/foo', '/'), '~/bar/baz');
strictEqual(collapseTildePath('/foo/bar/baz', '/foo/', '/'), '~/bar/baz');
});
});
});

View file

@ -19,6 +19,7 @@ import { AbstractNativeEnvironmentService } from 'vs/platform/environment/common
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import product from 'vs/platform/product/common/product';
import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService';
const ROOT = URI.file('tests').with({ scheme: 'vscode-tests' });
@ -52,7 +53,7 @@ suite('FileUserDataProvider', () => {
await testObject.createFolder(backupWorkspaceHomeOnDisk);
environmentService = new TestEnvironmentService(userDataHomeOnDisk);
userDataProfilesService = new UserDataProfilesService(environmentService, testObject, logService);
userDataProfilesService = new UserDataProfilesService(environmentService, testObject, new UriIdentityService(testObject), logService);
fileUserDataProvider = new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, logService);
disposables.add(fileUserDataProvider);

View file

@ -0,0 +1,66 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { revive } from 'vs/base/common/marshalling';
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 { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
import { IUserDataProfilesService, PROFILES_ENABLEMENT_CONFIG, StoredProfileAssociations, StoredUserDataProfile, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
export class BrowserUserDataProfilesService extends UserDataProfilesService implements IUserDataProfilesService {
protected override readonly defaultProfileShouldIncludeExtensionsResourceAlways: boolean = true;
constructor(
@IEnvironmentService environmentService: IEnvironmentService,
@IFileService fileService: IFileService,
@IUriIdentityService uriIdentityService: IUriIdentityService,
@ILogService logService: ILogService,
) {
super(environmentService, fileService, uriIdentityService, logService);
super.setEnablement(window.localStorage.getItem(PROFILES_ENABLEMENT_CONFIG) === 'true');
}
override setEnablement(enabled: boolean): void {
super.setEnablement(enabled);
window.localStorage.setItem(PROFILES_ENABLEMENT_CONFIG, enabled ? 'true' : 'false');
}
protected override getStoredProfiles(): StoredUserDataProfile[] {
try {
const value = window.localStorage.getItem(UserDataProfilesService.PROFILES_KEY);
if (value) {
return revive(JSON.parse(value));
}
} catch (error) {
/* ignore */
this.logService.error(error);
}
return [];
}
protected override saveStoredProfiles(storedProfiles: StoredUserDataProfile[]): void {
window.localStorage.setItem(UserDataProfilesService.PROFILES_KEY, JSON.stringify(storedProfiles));
}
protected override getStoredProfileAssociations(): StoredProfileAssociations {
try {
const value = window.localStorage.getItem(UserDataProfilesService.PROFILE_ASSOCIATIONS_KEY);
if (value) {
return revive(JSON.parse(value));
}
} catch (error) {
/* ignore */
this.logService.error(error);
}
return {};
}
protected override saveStoredProfileAssociations(storedProfileAssociations: StoredProfileAssociations): void {
window.localStorage.setItem(UserDataProfilesService.PROFILE_ASSOCIATIONS_KEY, JSON.stringify(storedProfileAssociations));
}
}

View file

@ -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.
@ -66,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;
@ -115,34 +129,249 @@ 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;
get defaultProfile(): IUserDataProfile { return this.profiles[0]; }
protected _profiles: IUserDataProfile[] = [this.createDefaultUserDataProfile(false)];
get profiles(): IUserDataProfile[] { return this._profiles; }
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');
}
protected createDefaultUserDataProfile(extensions: boolean): IUserDataProfile {
const profile = toUserDataProfile(localize('defaultProfile', "Default"), this.environmentService.userRoamingDataHome);
return { ...profile, isDefault: true, extensionsResource: extensions ? profile.extensionsResource : undefined };
setEnablement(enabled: boolean): void {
if (this.enabled !== enabled) {
this._profilesObject = undefined;
this.enabled = enabled;
}
}
createProfile(name: string, useDefaultFlags?: UseDefaultProfileFlags, workspaceIdentifier?: WorkspaceIdentifier): Promise<IUserDataProfile> { throw new Error('Not implemented'); }
setProfileForWorkspace(profile: IUserDataProfile, workspaceIdentifier: WorkspaceIdentifier): Promise<void> { throw new Error('Not implemented'); }
getProfile(workspaceIdentifier: WorkspaceIdentifier): IUserDataProfile { throw new Error('Not implemented'); }
removeProfile(profile: IUserDataProfile): Promise<void> { throw new Error('Not implemented'); }
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 getWorkspace(workspaceIdentifier: WorkspaceIdentifier): URI | EmptyWindowWorkspaceIdentifier {
if (isSingleFolderWorkspaceIdentifier(workspaceIdentifier)) {
return workspaceIdentifier.uri;
}
if (isWorkspaceIdentifier(workspaceIdentifier)) {
return workspaceIdentifier.configPath;
}
return 'empty-window';
}
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._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'); }
}

View file

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Emitter, Event } from 'vs/base/common/event';
import { Event } from 'vs/base/common/event';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IFileService } from 'vs/platform/files/common/files';
@ -11,22 +11,9 @@ import { refineServiceDecorator } from 'vs/platform/instantiation/common/instant
import { ILogService } from 'vs/platform/log/common/log';
import { IStateMainService } from 'vs/platform/state/electron-main/state';
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
import { IUserDataProfile, IUserDataProfilesService, PROFILES_ENABLEMENT_CONFIG, WorkspaceIdentifier, UseDefaultProfileFlags, toUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile';
import { Promises } from 'vs/base/common/async';
import { StoredProfileAssociations, StoredUserDataProfile, UserDataProfilesService } from 'vs/platform/userDataProfile/node/userDataProfile';
import { IUserDataProfilesService, WorkspaceIdentifier, StoredUserDataProfile, StoredProfileAssociations, WillCreateProfileEvent, WillRemoveProfileEvent } from 'vs/platform/userDataProfile/common/userDataProfile';
import { UserDataProfilesService } from 'vs/platform/userDataProfile/node/userDataProfile';
import { IStringDictionary } from 'vs/base/common/collections';
import { joinPath } from 'vs/base/common/resources';
import { hash } from 'vs/base/common/hash';
export type WillCreateProfileEvent = {
profile: IUserDataProfile;
join(promise: Promise<void>): void;
};
export type WillRemoveProfileEvent = {
profile: IUserDataProfile;
join(promise: Promise<void>): void;
};
export const IUserDataProfilesMainService = refineServiceDecorator<IUserDataProfilesService, IUserDataProfilesMainService>(IUserDataProfilesService);
export interface IUserDataProfilesMainService extends IUserDataProfilesService {
@ -37,12 +24,6 @@ export interface IUserDataProfilesMainService extends IUserDataProfilesService {
export class UserDataProfilesMainService extends UserDataProfilesService implements IUserDataProfilesMainService {
private readonly _onWillCreateProfile = this._register(new Emitter<WillCreateProfileEvent>());
readonly onWillCreateProfile = this._onWillCreateProfile.event;
private readonly _onWillRemoveProfile = this._register(new Emitter<WillRemoveProfileEvent>());
readonly onWillRemoveProfile = this._onWillRemoveProfile.event;
constructor(
@IStateMainService private readonly stateMainService: IStateMainService,
@IUriIdentityService uriIdentityService: IUriIdentityService,
@ -53,141 +34,12 @@ export class UserDataProfilesMainService extends UserDataProfilesService impleme
super(stateMainService, uriIdentityService, environmentService, fileService, logService);
}
override 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;
}
override 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);
}
override 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.saveStoredProfileAssociations();
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 });
}
protected override saveStoredProfiles(storedProfiles: StoredUserDataProfile[]): void {
this.stateMainService.setItem(UserDataProfilesMainService.PROFILES_KEY, storedProfiles);
this._profilesObject = undefined;
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.saveStoredProfileAssociations();
}
private saveStoredProfileAssociations() {
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.stateMainService.setItem(UserDataProfilesMainService.PROFILE_ASSOCIATIONS_KEY, { workspaces, emptyWindow });
this._profilesObject = undefined;
protected override saveStoredProfileAssociations(storedProfileAssociations: StoredProfileAssociations): void {
this.stateMainService.setItem(UserDataProfilesMainService.PROFILE_ASSOCIATIONS_KEY, storedProfileAssociations);
}
protected override getStoredProfileAssociations(): StoredProfileAssociations {

View file

@ -3,30 +3,40 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Emitter } from 'vs/base/common/event';
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 { 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 { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services';
import { ILogService } from 'vs/platform/log/common/log';
import { DidChangeProfilesEvent, IUserDataProfile, IUserDataProfilesService, reviveProfile, UseDefaultProfileFlags, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
import { DidChangeProfilesEvent, IUserDataProfile, IUserDataProfilesService, reviveProfile, UseDefaultProfileFlags, WorkspaceIdentifier } from 'vs/platform/userDataProfile/common/userDataProfile';
import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace';
export class UserDataProfilesNativeService extends UserDataProfilesService implements IUserDataProfilesService {
export class UserDataProfilesNativeService extends Disposable implements IUserDataProfilesService {
readonly _serviceBrand: undefined;
private readonly channel: IChannel;
override get profiles(): IUserDataProfile[] { return this._profiles; }
readonly profilesHome: URI;
get defaultProfile(): IUserDataProfile { return this.profiles[0]; }
private _profiles: IUserDataProfile[] = [];
get profiles(): IUserDataProfile[] { return this._profiles; }
private readonly _onDidChangeProfiles = this._register(new Emitter<DidChangeProfilesEvent>());
readonly onDidChangeProfiles = this._onDidChangeProfiles.event;
constructor(
profiles: UriDto<IUserDataProfile>[],
@IMainProcessService mainProcessService: IMainProcessService,
@IEnvironmentService environmentService: IEnvironmentService,
@IFileService fileService: IFileService,
@ILogService logService: ILogService,
) {
super(environmentService, fileService, logService);
super();
this.channel = mainProcessService.getChannel('userDataProfiles');
this.profilesHome = joinPath(environmentService.userRoamingDataHome, 'profiles');
this._profiles = profiles.map(profile => reviveProfile(profile, this.profilesHome.scheme));
this._register(this.channel.listen<DidChangeProfilesEvent>('onDidChangeProfiles')(e => {
const added = e.added.map(profile => reviveProfile(profile, this.profilesHome.scheme));
@ -36,17 +46,19 @@ export class UserDataProfilesNativeService extends UserDataProfilesService imple
}));
}
override async createProfile(name: string, useDefaultFlags?: UseDefaultProfileFlags, workspaceIdentifier?: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise<IUserDataProfile> {
async createProfile(name: string, useDefaultFlags?: UseDefaultProfileFlags, workspaceIdentifier?: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise<IUserDataProfile> {
const result = await this.channel.call<UriDto<IUserDataProfile>>('createProfile', [name, useDefaultFlags, workspaceIdentifier]);
return reviveProfile(result, this.profilesHome.scheme);
}
override async setProfileForWorkspace(profile: IUserDataProfile, workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise<void> {
async setProfileForWorkspace(profile: IUserDataProfile, workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise<void> {
await this.channel.call<UriDto<IUserDataProfile>>('setProfileForWorkspace', [profile, workspaceIdentifier]);
}
override removeProfile(profile: IUserDataProfile): Promise<void> {
removeProfile(profile: IUserDataProfile): Promise<void> {
return this.channel.call('removeProfile', [profile]);
}
getProfile(workspaceIdentifier: WorkspaceIdentifier): IUserDataProfile { throw new Error('Not implemented'); }
}

View file

@ -3,110 +3,32 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IStringDictionary } from 'vs/base/common/collections';
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 { IStateService } from 'vs/platform/state/node/state';
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
import { UseDefaultProfileFlags, IUserDataProfile, IUserDataProfilesService, UserDataProfilesService as BaseUserDataProfilesService, toUserDataProfile, WorkspaceIdentifier, EmptyWindowWorkspaceIdentifier } from 'vs/platform/userDataProfile/common/userDataProfile';
import { isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace';
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;
};
import { IUserDataProfilesService, UserDataProfilesService as BaseUserDataProfilesService, StoredUserDataProfile, StoredProfileAssociations } from 'vs/platform/userDataProfile/common/userDataProfile';
export class UserDataProfilesService extends BaseUserDataProfilesService implements IUserDataProfilesService {
protected static readonly PROFILES_KEY = 'userDataProfiles';
protected static readonly PROFILE_ASSOCIATIONS_KEY = 'profileAssociations';
protected enabled: boolean = false;
constructor(
@IStateService private readonly stateService: IStateService,
@IUriIdentityService protected readonly uriIdentityService: IUriIdentityService,
@IUriIdentityService uriIdentityService: IUriIdentityService,
@IEnvironmentService environmentService: IEnvironmentService,
@IFileService fileService: IFileService,
@ILogService logService: ILogService,
) {
super(environmentService, fileService, logService);
super(environmentService, fileService, uriIdentityService, logService);
}
setEnablement(enabled: boolean): void {
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));
}
}
profiles.unshift(this.createDefaultUserDataProfile(profiles.length > 0));
this._profilesObject = { profiles, workspaces, emptyWindow };
}
return this._profilesObject;
}
override get profiles(): IUserDataProfile[] { return this.profilesObject.profiles; }
override 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 getWorkspace(workspaceIdentifier: WorkspaceIdentifier): URI | EmptyWindowWorkspaceIdentifier {
if (isSingleFolderWorkspaceIdentifier(workspaceIdentifier)) {
return workspaceIdentifier.uri;
}
if (isWorkspaceIdentifier(workspaceIdentifier)) {
return workspaceIdentifier.configPath;
}
return 'empty-window';
}
protected getStoredProfiles(): StoredUserDataProfile[] {
protected override getStoredProfiles(): StoredUserDataProfile[] {
return revive(this.stateService.getItem<UriDto<StoredUserDataProfile>[]>(UserDataProfilesService.PROFILES_KEY, []));
}
protected getStoredProfileAssociations(): StoredProfileAssociations {
protected override getStoredProfileAssociations(): StoredProfileAssociations {
return revive(this.stateService.getItem<UriDto<StoredProfileAssociations>>(UserDataProfilesService.PROFILE_ASSOCIATIONS_KEY, {}));
}

View file

@ -14,6 +14,7 @@ import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFil
import { AbstractNativeEnvironmentService } from 'vs/platform/environment/common/environmentService';
import product from 'vs/platform/product/common/product';
import { UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService';
const ROOT = URI.file('tests').with({ scheme: 'vscode-tests' });
@ -37,7 +38,7 @@ suite('UserDataProfileService (Common)', () => {
disposables.add(fileService.registerProvider(ROOT.scheme, fileSystemProvider));
environmentService = new TestEnvironmentService(joinPath(ROOT, 'User'));
testObject = new UserDataProfilesService(environmentService, fileService, logService);
testObject = new UserDataProfilesService(environmentService, fileService, new UriIdentityService(fileService), logService);
});
teardown(() => disposables.clear());

View file

@ -184,7 +184,7 @@ export interface IUserDataSyncStoreClient {
delete(resource: ServerResource, ref: string | null): Promise<void>;
getAllRefs(resource: ServerResource): Promise<IResourceRefHandle[]>;
resolveContent(resource: ServerResource, ref: string): Promise<string | null>;
resolveContent(resource: ServerResource, ref: string, headers?: IHeaders): Promise<string | null>;
}
export const IUserDataSyncStoreService = createDecorator<IUserDataSyncStoreService>('IUserDataSyncStoreService');

View file

@ -244,13 +244,13 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync
return result.map(({ url, created }) => ({ ref: relativePath(uri, uri.with({ path: url }))!, created: created * 1000 /* Server returns in seconds */ }));
}
async resolveContent(resource: ServerResource, ref: string): Promise<string | null> {
async resolveContent(resource: ServerResource, ref: string, headers: IHeaders = {}): Promise<string | null> {
if (!this.userDataSyncStoreUrl) {
throw new Error('No settings sync store url configured.');
}
const url = joinPath(this.userDataSyncStoreUrl, 'resource', resource, ref).toString();
const headers: IHeaders = {};
headers = { ...headers };
headers['Cache-Control'] = 'no-cache';
const context = await this.request(url, { type: 'GET', headers }, [], CancellationToken.None);

View file

@ -83,14 +83,16 @@ export class UserDataSyncClient extends Disposable {
fileService.registerProvider(Schemas.inMemory, new InMemoryFileSystemProvider());
this.instantiationService.stub(IFileService, fileService);
const userDataProfilesService = this.instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, fileService, logService));
const uriIdentityService = this.instantiationService.createInstance(UriIdentityService);
this.instantiationService.stub(IUriIdentityService, uriIdentityService);
const userDataProfilesService = this.instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, fileService, uriIdentityService, logService));
this.instantiationService.stub(IStorageService, this._register(new InMemoryStorageService()));
const configurationService = this._register(new ConfigurationService(userDataProfilesService.defaultProfile.settingsResource, fileService, new NullPolicyService(), logService));
await configurationService.initialize();
this.instantiationService.stub(IConfigurationService, configurationService);
this.instantiationService.stub(IUriIdentityService, this.instantiationService.createInstance(UriIdentityService));
this.instantiationService.stub(IRequestService, this.testServer);

View file

@ -10,6 +10,7 @@ import { IExtensionsProfileScannerService } from 'vs/platform/extensionManagemen
import { AbstractExtensionsScannerService, IExtensionsScannerService, Translations } from 'vs/platform/extensionManagement/common/extensionsScannerService';
import { MANIFEST_CACHE_FOLDER } from 'vs/platform/extensions/common/extensions';
import { IFileService } from 'vs/platform/files/common/files';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ILogService } from 'vs/platform/log/common/log';
import { IProductService } from 'vs/platform/product/common/productService';
import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
@ -24,13 +25,14 @@ export class ExtensionsScannerService extends AbstractExtensionsScannerService i
@ILogService logService: ILogService,
@INativeEnvironmentService private readonly nativeEnvironmentService: INativeEnvironmentService,
@IProductService productService: IProductService,
@IInstantiationService instantiationService: IInstantiationService,
) {
super(
URI.file(nativeEnvironmentService.builtinExtensionsPath),
URI.file(nativeEnvironmentService.extensionsPath),
joinPath(nativeEnvironmentService.userHome, '.vscode-oss-dev', 'extensions', 'control.json'),
joinPath(URI.file(nativeEnvironmentService.userDataPath), MANIFEST_CACHE_FOLDER),
userDataProfilesService, extensionsProfileScannerService, fileService, logService, nativeEnvironmentService, productService);
userDataProfilesService, extensionsProfileScannerService, fileService, logService, nativeEnvironmentService, productService, instantiationService);
}
protected async getTranslations(language: string): Promise<Translations> {

View file

@ -93,8 +93,11 @@ class CliMain extends Disposable {
services.set(IFileService, fileService);
fileService.registerProvider(Schemas.file, this._register(new DiskFileSystemProvider(logService)));
const uriIdentityService = new UriIdentityService(fileService);
services.set(IUriIdentityService, uriIdentityService);
// User Data Profiles
const userDataProfilesService = this._register(new UserDataProfilesService(environmentService, fileService, logService));
const userDataProfilesService = this._register(new UserDataProfilesService(environmentService, fileService, uriIdentityService, logService));
services.set(IUserDataProfilesService, userDataProfilesService);
// Configuration
@ -102,7 +105,6 @@ class CliMain extends Disposable {
await configurationService.initialize();
services.set(IConfigurationService, configurationService);
services.set(IUriIdentityService, new UriIdentityService(fileService));
services.set(IRequestService, new SyncDescriptor(RequestService));
services.set(IDownloadService, new SyncDescriptor(DownloadService));
services.set(ITelemetryService, NullTelemetryService);

View file

@ -110,8 +110,12 @@ export async function setupServerServices(connectionToken: ServerConnectionToken
services.set(IFileService, fileService);
fileService.registerProvider(Schemas.file, disposables.add(new DiskFileSystemProvider(logService)));
// URI Identity
const uriIdentityService = new UriIdentityService(fileService);
services.set(IUriIdentityService, uriIdentityService);
// User Data Profiles
const userDataProfilesService = new UserDataProfilesService(environmentService, fileService, logService);
const userDataProfilesService = new UserDataProfilesService(environmentService, fileService, uriIdentityService, logService);
services.set(IUserDataProfilesService, userDataProfilesService);
// Configuration
@ -122,9 +126,6 @@ export async function setupServerServices(connectionToken: ServerConnectionToken
const extensionHostStatusService = new ExtensionHostStatusService();
services.set(IExtensionHostStatusService, extensionHostStatusService);
// URI Identity
services.set(IUriIdentityService, new UriIdentityService(fileService));
// Request
services.set(IRequestService, new SyncDescriptor(RequestService));

View file

@ -22,7 +22,7 @@ import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteA
import { IWorkbenchFileService } from 'vs/workbench/services/files/common/files';
import { FileService } from 'vs/platform/files/common/fileService';
import { Schemas, connectionTokenCookieName } from 'vs/base/common/network';
import { IAnyWorkspaceIdentifier, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IAnyWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IWorkbenchConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { onUnexpectedError } from 'vs/base/common/errors';
import { setFullscreen } from 'vs/base/browser/browser';
@ -73,13 +73,14 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { DelayedLogChannel } from 'vs/workbench/services/output/common/delayedLogChannel';
import { dirname, joinPath } from 'vs/base/common/resources';
import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
import { IUserDataProfilesService, PROFILES_ENABLEMENT_CONFIG } from 'vs/platform/userDataProfile/common/userDataProfile';
import { NullPolicyService } from 'vs/platform/policy/common/policy';
import { IRemoteExplorerService, TunnelSource } from 'vs/workbench/services/remote/common/remoteExplorerService';
import { DisposableTunnel } from 'vs/platform/tunnel/common/tunnel';
import { ILabelService } from 'vs/platform/label/common/label';
import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService';
import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
import { BrowserUserDataProfilesService } from 'vs/platform/userDataProfile/browser/userDataProfile';
export class BrowserMain extends Disposable {
@ -259,17 +260,16 @@ export class BrowserMain extends Disposable {
serviceCollection.set(IWorkbenchFileService, fileService);
await this.registerFileSystemProviders(environmentService, fileService, remoteAgentService, logService, logsPath);
// User Data Profiles
const userDataProfilesService = new UserDataProfilesService(environmentService, fileService, logService);
serviceCollection.set(IUserDataProfilesService, userDataProfilesService);
const userDataProfileService = new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService);
serviceCollection.set(IUserDataProfileService, userDataProfileService);
// URI Identity
const uriIdentityService = new UriIdentityService(fileService);
serviceCollection.set(IUriIdentityService, uriIdentityService);
// User Data Profiles
const userDataProfilesService = new BrowserUserDataProfilesService(environmentService, fileService, uriIdentityService, logService);
serviceCollection.set(IUserDataProfilesService, userDataProfilesService);
const userDataProfileService = new UserDataProfileService(userDataProfilesService.getProfile(isWorkspaceIdentifier(payload) || isSingleFolderWorkspaceIdentifier(payload) ? payload : 'empty-window'), userDataProfilesService);
serviceCollection.set(IUserDataProfileService, userDataProfileService);
// Long running services (workspace, config, storage)
const [configurationService, storageService] = await Promise.all([
this.createWorkspaceService(payload, environmentService, userDataProfileService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, logService).then(service => {
@ -292,6 +292,7 @@ export class BrowserMain extends Disposable {
})
]);
userDataProfilesService.setEnablement(!!configurationService.getValue(PROFILES_ENABLEMENT_CONFIG));
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//

View file

@ -85,7 +85,7 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo
super();
if (this.environmentService.editSessionId !== undefined) {
void this.applyEditSession(this.environmentService.editSessionId).finally(() => this.environmentService.editSessionId = undefined);
void this.resumeEditSession(this.environmentService.editSessionId).finally(() => this.environmentService.editSessionId = undefined);
}
this.configurationService.onDidChangeConfiguration((e) => {
@ -132,7 +132,7 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo
this.registerContinueEditSessionAction();
this.registerApplyLatestEditSessionAction();
this.registerResumeLatestEditSessionAction();
this.registerStoreLatestEditSessionAction();
this.registerContinueInLocalFolderAction();
@ -171,9 +171,9 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo
}));
}
private registerApplyLatestEditSessionAction(): void {
private registerResumeLatestEditSessionAction(): void {
const that = this;
this._register(registerAction2(class ApplyLatestEditSessionAction extends Action2 {
this._register(registerAction2(class ResumeLatestEditSessionAction extends Action2 {
constructor() {
super({
id: 'workbench.experimental.editSessions.actions.resumeLatest',
@ -186,8 +186,8 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo
async run(accessor: ServicesAccessor): Promise<void> {
await that.progressService.withProgress({
location: ProgressLocation.Notification,
title: localize('applying edit session', 'Applying edit session...')
}, async () => await that.applyEditSession());
title: localize('resuming edit session', 'Resuming edit session...')
}, async () => await that.resumeEditSession());
}
}));
}
@ -213,26 +213,24 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo
}));
}
async applyEditSession(ref?: string): Promise<void> {
if (ref !== undefined) {
this.logService.info(`Applying edit session with ref ${ref}.`);
}
async resumeEditSession(ref?: string): Promise<void> {
this.logService.info(ref !== undefined ? `Resuming edit session with ref ${ref}...` : 'Resuming edit session...');
const data = await this.editSessionsWorkbenchService.read(ref);
if (!data) {
if (ref === undefined) {
this.notificationService.info(localize('no edit session', 'There are no edit sessions to apply.'));
this.notificationService.info(localize('no edit session', 'There are no edit sessions to resume.'));
} else {
this.notificationService.warn(localize('no edit session content for ref', 'Could not apply edit session contents for ID {0}.', ref));
this.notificationService.warn(localize('no edit session content for ref', 'Could not resume edit session contents for ID {0}.', ref));
}
this.logService.info(`Aborting applying edit session as no edit session content is available to be applied from ref ${ref}.`);
this.logService.info(`Aborting resuming edit session as no edit session content is available to be applied from ref ${ref}.`);
return;
}
const editSession = data.editSession;
ref = data.ref;
if (editSession.version > EditSessionSchemaVersion) {
this.notificationService.error(localize('client too old', "Please upgrade to a newer version of {0} to apply this edit session.", this.productService.nameLong));
this.notificationService.error(localize('client too old', "Please upgrade to a newer version of {0} to resume this edit session.", this.productService.nameLong));
return;
}
@ -266,7 +264,7 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo
if (hasLocalUncommittedChanges) {
// TODO@joyceerhl Provide the option to diff files which would be overwritten by edit session contents
const result = await this.dialogService.confirm({
message: localize('apply edit session warning', 'Applying your edit session may overwrite your existing uncommitted changes. Do you want to proceed?'),
message: localize('resume edit session warning', 'Resuming your edit session may overwrite your existing uncommitted changes. Do you want to proceed?'),
type: 'warning',
title: EDIT_SESSION_SYNC_CATEGORY.value
});
@ -287,8 +285,8 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo
await this.editSessionsWorkbenchService.delete(ref);
this.logService.info(`Deleted edit session with ref ${ref}.`);
} catch (ex) {
this.logService.error('Failed to apply edit session, reason: ', (ex as Error).toString());
this.notificationService.error(localize('apply failed', "Failed to apply your edit session."));
this.logService.error('Failed to resume edit session, reason: ', (ex as Error).toString());
this.notificationService.error(localize('resume failed', "Failed to resume your edit session."));
}
}

View file

@ -14,11 +14,13 @@ import { IProductService } from 'vs/platform/product/common/productService';
import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
import { IRequestService } from 'vs/platform/request/common/request';
import { IStorageService, IStorageValueChangeEvent, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { IAuthenticationProvider } from 'vs/platform/userDataSync/common/userDataSync';
import { createSyncHeaders, IAuthenticationProvider } from 'vs/platform/userDataSync/common/userDataSync';
import { UserDataSyncStoreClient } from 'vs/platform/userDataSync/common/userDataSyncStoreService';
import { AuthenticationSession, AuthenticationSessionsChangeEvent, IAuthenticationService } from 'vs/workbench/services/authentication/common/authentication';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { EDIT_SESSIONS_SIGNED_IN, EditSession, EDIT_SESSION_SYNC_CATEGORY, IEditSessionsWorkbenchService, EDIT_SESSIONS_SIGNED_IN_KEY, IEditSessionsLogService } from 'vs/workbench/contrib/editSessions/common/editSessions';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { generateUuid } from 'vs/base/common/uuid';
type ExistingSession = IQuickPickItem & { session: AuthenticationSession & { providerId: string } };
type AuthenticationProviderOption = IQuickPickItem & { provider: IAuthenticationProvider };
@ -47,6 +49,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes
@IProductService private readonly productService: IProductService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IRequestService private readonly requestService: IRequestService,
@IDialogService private readonly dialogService: IDialogService,
) {
super();
@ -73,7 +76,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes
throw new Error('Please sign in to store your edit session.');
}
return this.storeClient!.write('editSessions', JSON.stringify(editSession), null);
return this.storeClient!.write('editSessions', JSON.stringify(editSession), null, createSyncHeaders(generateUuid()));
}
/**
@ -89,11 +92,12 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes
}
let content: string | undefined | null;
const headers = createSyncHeaders(generateUuid());
try {
if (ref !== undefined) {
content = await this.storeClient?.resolveContent('editSessions', ref);
content = await this.storeClient?.resolveContent('editSessions', ref, headers);
} else {
const result = await this.storeClient?.read('editSessions', null);
const result = await this.storeClient?.read('editSessions', null, headers);
content = result?.content;
ref = result?.ref;
}
@ -160,7 +164,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes
const existing = await this.getExistingSession();
if (existing !== undefined) {
this.logService.trace(`Found existing authentication session with ID ${existingSessionId}`);
this.#authenticationInfo = { sessionId: existing.session.id, token: existing.session.accessToken, providerId: existing.session.providerId };
this.#authenticationInfo = { sessionId: existing.session.id, token: existing.session.idToken ?? existing.session.accessToken, providerId: existing.session.providerId };
this.storeClient.setAuthToken(this.#authenticationInfo.token, this.#authenticationInfo.providerId);
return true;
}
@ -169,7 +173,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes
// Ask the user to pick a preferred account
const session = await this.getAccountPreference();
if (session !== undefined) {
this.#authenticationInfo = { sessionId: session.id, token: session.accessToken, providerId: session.providerId };
this.#authenticationInfo = { sessionId: session.id, token: session.idToken ?? session.accessToken, providerId: session.providerId };
this.storeClient.setAuthToken(this.#authenticationInfo.token, this.#authenticationInfo.providerId);
this.existingSessionId = session.id;
this.logService.trace(`Saving authentication session preference for ID ${session.id}.`);
@ -350,8 +354,19 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes
});
}
run() {
that.clearAuthenticationPreference();
async run() {
const result = await that.dialogService.confirm({
type: 'info',
message: localize('sign out of edit sessions clear data prompt', 'Do you want to sign out of edit sessions?'),
checkbox: { label: localize('delete all edit sessions', 'Delete all stored edit sessions from the cloud.') },
primaryButton: localize('clear data confirm', 'Yes'),
});
if (result.confirmed) {
if (result.checkboxChecked) {
that.storeClient?.delete('editSessions', null);
}
that.clearAuthenticationPreference();
}
}
}));
}

View file

@ -112,8 +112,8 @@ suite('Edit session sync', () => {
// Create root folder
await fileService.createFolder(folderUri);
// Apply edit session
await editSessionsContribution.applyEditSession();
// Resume edit session
await editSessionsContribution.resumeEditSession();
// Verify edit session was correctly applied
assert.equal((await fileService.readFile(fileUri)).value.toString(), fileContents);

View file

@ -135,15 +135,17 @@ class MarkerCodeColumnRenderer implements ITableRenderer<MarkerTableItem, IMarke
}
renderElement(element: MarkerTableItem, index: number, templateData: IMarkerCodeColumnTemplateData, height: number | undefined): void {
if (element.marker.source && element.marker.code) {
templateData.codeColumn.classList.toggle('code-link', typeof element.marker.code !== 'string');
DOM.show(templateData.codeLabel.element);
templateData.codeColumn.classList.remove('code-label');
templateData.codeColumn.classList.remove('code-link');
if (element.marker.source && element.marker.code) {
if (typeof element.marker.code === 'string') {
templateData.codeColumn.classList.add('code-label');
templateData.codeColumn.title = `${element.marker.source} (${element.marker.code})`;
templateData.sourceLabel.set(element.marker.source, element.sourceMatches);
templateData.codeLabel.set(element.marker.code, element.codeMatches);
} else {
templateData.codeColumn.classList.add('code-link');
templateData.codeColumn.title = `${element.marker.source} (${element.marker.code.value})`;
templateData.sourceLabel.set(element.marker.source, element.sourceMatches);
@ -159,7 +161,6 @@ class MarkerCodeColumnRenderer implements ITableRenderer<MarkerTableItem, IMarke
} else {
templateData.codeColumn.title = '';
templateData.sourceLabel.set('-');
DOM.hide(templateData.codeLabel.element);
}
}

View file

@ -279,19 +279,20 @@
content: ')';
}
.markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td > .code.code-link > .code-label {
.markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td > .code > .code-label,
.markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td > .code > .monaco-link {
display: none;
}
.markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td > .code.code-label > .code-label {
display: inline;
}
.markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td > .code.code-link > .monaco-link {
display: inline;
text-decoration: underline;
}
.markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td > .code > .monaco-link {
display: none;
}
.markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td > .file > .file-position {
margin-left: 6px;
opacity: 0.7;

View file

@ -41,6 +41,8 @@ import { Disposable } from 'vs/base/common/lifecycle';
const OpenInEditorCommandId = 'search.action.openInEditor';
const OpenNewEditorToSideCommandId = 'search.action.openNewEditorToSide';
const FocusQueryEditorWidgetCommandId = 'search.action.focusQueryEditorWidget';
const FocusQueryEditorFilesToIncludeCommandId = 'search.action.focusFilesToInclude';
const FocusQueryEditorFilesToExcludeCommandId = 'search.action.focusFilesToExclude';
const ToggleSearchEditorCaseSensitiveCommandId = 'toggleSearchEditorCaseSensitive';
const ToggleSearchEditorWholeWordCommandId = 'toggleSearchEditorWholeWord';
@ -374,6 +376,44 @@ registerAction2(class extends Action2 {
}
});
registerAction2(class extends Action2 {
constructor() {
super({
id: FocusQueryEditorFilesToIncludeCommandId,
title: { value: localize('search.action.focusFilesToInclude', "Focus Search Editor Files to Include"), original: 'Focus Search Editor Files to Include' },
category,
f1: true,
precondition: SearchEditorConstants.InSearchEditor,
});
}
async run(accessor: ServicesAccessor) {
const editorService = accessor.get(IEditorService);
const input = editorService.activeEditor;
if (input instanceof SearchEditorInput) {
(editorService.activeEditorPane as SearchEditor).focusFilesToIncludeInput();
}
}
});
registerAction2(class extends Action2 {
constructor() {
super({
id: FocusQueryEditorFilesToExcludeCommandId,
title: { value: localize('search.action.focusFilesToExclude', "Focus Search Editor Files to Exclude"), original: 'Focus Search Editor Files to Exclude' },
category,
f1: true,
precondition: SearchEditorConstants.InSearchEditor,
});
}
async run(accessor: ServicesAccessor) {
const editorService = accessor.get(IEditorService);
const input = editorService.activeEditor;
if (input instanceof SearchEditorInput) {
(editorService.activeEditorPane as SearchEditor).focusFilesToExcludeInput();
}
}
});
registerAction2(class extends Action2 {
constructor() {
super({

View file

@ -275,6 +275,20 @@ export class SearchEditor extends AbstractTextCodeEditor<SearchEditorViewState>
this.queryEditorWidget.searchInput.focus();
}
focusFilesToIncludeInput() {
if (!this.showingIncludesExcludes) {
this.toggleIncludesExcludes(true);
}
this.inputPatternIncludes.focus();
}
focusFilesToExcludeInput() {
if (!this.showingIncludesExcludes) {
this.toggleIncludesExcludes(true);
}
this.inputPatternExcludes.focus();
}
focusNextInput() {
if (this.queryEditorWidget.searchInputHasFocus()) {
if (this.showingIncludesExcludes) {

View file

@ -110,23 +110,22 @@ __vsc_precmd() {
}
__vsc_preexec() {
if [ "$__vsc_in_command_execution" = "0" ]; then
__vsc_initialized=1
__vsc_in_command_execution="1"
if [[ ! "$BASH_COMMAND" =~ ^__vsc_prompt* ]]; then
__vsc_current_command=$BASH_COMMAND
else
__vsc_current_command=""
fi
__vsc_command_output_start
__vsc_initialized=1
if [[ ! "$BASH_COMMAND" =~ ^__vsc_prompt* ]]; then
__vsc_current_command=$BASH_COMMAND
else
__vsc_current_command=""
fi
__vsc_command_output_start
}
# Debug trapping/preexec inspired by starship (ISC)
if [[ -n "${bash_preexec_imported:-}" ]]; then
__vsc_preexec_only() {
__vsc_status="$?"
__vsc_preexec
if [ "$__vsc_in_command_execution" = "0" ]; then
__vsc_in_command_execution="1"
__vsc_preexec
fi
}
precmd_functions+=(__vsc_prompt_cmd)
preexec_functions+=(__vsc_preexec_only)
@ -134,15 +133,19 @@ else
__vsc_dbg_trap="$(trap -p DEBUG | cut -d' ' -f3 | tr -d \')"
if [[ -z "$__vsc_dbg_trap" ]]; then
__vsc_preexec_only() {
__vsc_status="$?"
__vsc_preexec
if [ "$__vsc_in_command_execution" = "0" ]; then
__vsc_in_command_execution="1"
__vsc_preexec
fi
}
trap '__vsc_preexec_only "$_"' DEBUG
elif [[ "$__vsc_dbg_trap" != '__vsc_preexec "$_"' && "$__vsc_dbg_trap" != '__vsc_preexec_all "$_"' ]]; then
__vsc_preexec_all() {
__vsc_status="$?"
builtin eval ${__vsc_dbg_trap}
__vsc_preexec
if [ "$__vsc_in_command_execution" = "0" ]; then
__vsc_in_command_execution="1"
builtin eval ${__vsc_dbg_trap}
__vsc_preexec
fi
}
trap '__vsc_preexec_all "$_"' DEBUG
fi
@ -151,6 +154,7 @@ fi
__vsc_update_prompt
__vsc_prompt_cmd_original() {
__vsc_status="$?"
if [[ ${IFS+set} ]]; then
__vsc_original_ifs="$IFS"
fi

View file

@ -47,7 +47,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ITerminalCommand, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities';
import { TerminalCapabilityStoreMultiplexer } from 'vs/platform/terminal/common/capabilities/terminalCapabilityStore';
import { IProcessDataEvent, IProcessPropertyMap, IShellLaunchConfig, ITerminalDimensionsOverride, ITerminalLaunchError, PosixShellType, ProcessPropertyType, TerminalIcon, TerminalLocation, TerminalSettingId, TerminalShellType, TitleEventSource, WindowsShellType } from 'vs/platform/terminal/common/terminal';
import { escapeNonWindowsPath } from 'vs/platform/terminal/common/terminalEnvironment';
import { escapeNonWindowsPath, collapseTildePath } from 'vs/platform/terminal/common/terminalEnvironment';
import { activeContrastBorder, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground } from 'vs/platform/theme/common/colorRegistry';
import { IColorTheme, ICssStyleCollector, IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
@ -355,6 +355,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
private readonly _terminalHasFixedWidth: IContextKey<boolean>,
private readonly _terminalShellTypeContextKey: IContextKey<string>,
private readonly _terminalAltBufferActiveContextKey: IContextKey<boolean>,
private readonly _terminalInRunCommandPicker: IContextKey<boolean>,
private readonly _configHelper: TerminalConfigHelper,
private _shellLaunchConfig: IShellLaunchConfig,
resource: URI | undefined,
@ -851,7 +852,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
if (label.length === 0 || commandMap.has(label)) {
continue;
}
let description = `${entry.cwd}`;
let description = collapseTildePath(entry.cwd, this._userHome, this._processManager?.os === OperatingSystem.Windows ? '\\' : '/');
if (entry.exitCode) {
// Since you cannot get the last command's exit code on pwsh, just whether it failed
// or not, -1 is treated specially as simply failed
@ -1024,7 +1025,11 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
}
return new Promise<void>(r => {
quickPick.show();
quickPick.onDidHide(() => r());
this._terminalInRunCommandPicker.set(true);
quickPick.onDidHide(() => {
this._terminalInRunCommandPicker.set(false);
r();
});
});
}
@ -1455,9 +1460,15 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
}
async sendText(text: string, addNewLine: boolean): Promise<void> {
// Apply bracketed paste sequences if the terminal has the mode enabled, this will prevent
// the text from triggering keybindings https://github.com/microsoft/vscode/issues/153592
if (this.xterm?.raw.modes.bracketedPasteMode) {
text = `\x1b[200~${text}\x1b[201~`;
}
// Normalize line endings to 'enter' press.
text = text.replace(/\r?\n/g, '\r');
if (addNewLine && text.substr(text.length - 1) !== '\r') {
if (addNewLine && text[text.length - 1] !== '\r') {
text += '\r';
}
@ -1698,7 +1709,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
const parsedExitResult = parseExitResult(exitCodeOrError, this.shellLaunchConfig, this._processManager.processState, this._initialCwd);
if (this._usedShellIntegrationInjection && (this._processManager.processState === ProcessState.KilledDuringLaunch || this._processManager.processState === ProcessState.KilledByProcess)) {
if (this._usedShellIntegrationInjection && this._processManager.processState === ProcessState.KilledDuringLaunch && parsedExitResult?.code !== 0) {
this._relaunchWithShellIntegrationDisabled(parsedExitResult?.message);
this._onExit.fire(exitCodeOrError);
return;

View file

@ -23,6 +23,7 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst
private _terminalHasFixedWidth: IContextKey<boolean>;
private _terminalShellTypeContextKey: IContextKey<string>;
private _terminalAltBufferActiveContextKey: IContextKey<boolean>;
private _terminalInRunCommandPicker: IContextKey<boolean>;
private _configHelper: TerminalConfigHelper;
private readonly _onDidCreateInstance = new Emitter<ITerminalInstance>();
@ -37,6 +38,7 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst
this._terminalHasFixedWidth = TerminalContextKeys.terminalHasFixedWidth.bindTo(this._contextKeyService);
this._terminalShellTypeContextKey = TerminalContextKeys.shellType.bindTo(this._contextKeyService);
this._terminalAltBufferActiveContextKey = TerminalContextKeys.altBufferActive.bindTo(this._contextKeyService);
this._terminalInRunCommandPicker = TerminalContextKeys.inTerminalRunCommandPicker.bindTo(this._contextKeyService);
this._configHelper = _instantiationService.createInstance(TerminalConfigHelper);
}
@ -49,6 +51,7 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst
this._terminalHasFixedWidth,
this._terminalShellTypeContextKey,
this._terminalAltBufferActiveContextKey,
this._terminalInRunCommandPicker,
this._configHelper,
shellLaunchConfig,
resource

View file

@ -31,6 +31,7 @@ export const enum TerminalContextKeyStrings {
TabsSingularSelection = 'terminalTabsSingularSelection',
SplitTerminal = 'terminalSplitTerminal',
ShellType = 'terminalShellType',
InTerminalRunCommandPicker = 'inTerminalRunCommandPicker',
}
export namespace TerminalContextKeys {
@ -119,4 +120,7 @@ export namespace TerminalContextKeys {
/** Whether the focused tab's terminal is a split terminal. */
export const splitTerminal = new RawContextKey<boolean>(TerminalContextKeyStrings.SplitTerminal, false, localize('isSplitTerminalContextKey', "Whether the focused tab's terminal is a split terminal."));
/** Whether the terminal run command picker is currently open. */
export const inTerminalRunCommandPicker = new RawContextKey<boolean>(TerminalContextKeyStrings.InTerminalRunCommandPicker, false, localize('inTerminalRunCommandPickerContextKey', "Whether the terminal run command picker is currently open."));
}

View file

@ -35,7 +35,7 @@ const testFilterDescriptions: { [K in TestFilterTerm]: string } = {
export class TestingExplorerFilter extends BaseActionViewItem {
private input!: SuggestEnabledInputWithHistory;
private wrapper!: HTMLDivElement;
private readonly history: StoredValue<string[]> = this.instantiationService.createInstance(StoredValue, {
private readonly history: StoredValue<{ values: string[]; lastValue: string } | string[]> = this.instantiationService.createInstance(StoredValue, {
key: 'testing.filterHistory2',
scope: StorageScope.WORKSPACE,
target: StorageTarget.USER
@ -65,9 +65,12 @@ export class TestingExplorerFilter extends BaseActionViewItem {
const wrapper = this.wrapper = dom.$('.testing-filter-wrapper');
container.appendChild(wrapper);
const history = this.history.get([]);
if (history.length) {
this.state.setText(history[history.length - 1]);
let history = this.history.get({ lastValue: '', values: [] });
if (history instanceof Array) {
history = { lastValue: '', values: history };
}
if (history.lastValue) {
this.state.setText(history.lastValue);
}
const input = this.input = this._register(this.instantiationService.createInstance(ContextScopedSuggestEnabledInputWithHistory, {
@ -94,7 +97,7 @@ export class TestingExplorerFilter extends BaseActionViewItem {
value: this.state.text.value,
placeholderText: localize('testExplorerFilter', "Filter (e.g. text, !exclude, @tag)"),
},
history
history: history.values
}));
this._register(attachSuggestEnabledInputBoxStyler(input, this.themeService));
@ -145,12 +148,7 @@ export class TestingExplorerFilter extends BaseActionViewItem {
* Persists changes to the input history.
*/
public saveState() {
const history = this.input.getHistory();
if (history.length) {
this.history.store(history);
} else {
this.history.delete();
}
this.history.store({ lastValue: this.input.getValue(), values: this.input.getHistory() });
}
/**

View file

@ -6,7 +6,6 @@
import { Codicon } from 'vs/base/common/codicons';
import { Event } from 'vs/base/common/event';
import { Disposable, DisposableStore, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { isWeb } from 'vs/base/common/platform';
import { ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { localize } from 'vs/nls';
import { Action2, ISubmenuItem, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions';
@ -15,6 +14,7 @@ import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from '
import { IProductService } from 'vs/platform/product/common/productService';
import { Registry } from 'vs/platform/registry/common/platform';
import { registerColor } from 'vs/platform/theme/common/colorRegistry';
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
import { themeColorFromId } from 'vs/platform/theme/common/themeService';
import { IUserDataProfile, IUserDataProfilesService, PROFILES_ENABLEMENT_CONFIG } from 'vs/platform/userDataProfile/common/userDataProfile';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
@ -25,6 +25,8 @@ import { IUserDataProfileManagementService, IUserDataProfileService, ManageProfi
const CONTEXT_CURRENT_PROFILE = new RawContextKey<string>('currentUserDataProfile', '');
export const userDataProfilesIcon = registerIcon('settingsProfiles-icon', Codicon.settings, localize('settingsProfilesIcon', 'Icon for Settings Profiles.'));
export class UserDataProfilesWorkbenchContribution extends Disposable implements IWorkbenchContribution {
private readonly currentProfileContext: IContextKey<string>;
@ -53,7 +55,7 @@ export class UserDataProfilesWorkbenchContribution extends Disposable implements
}
private registerConfiguration(): void {
if (!isWeb && this.productService.quality !== 'stable') {
if (this.productService.quality !== 'stable') {
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).registerConfiguration({
...workbenchConfigurationNodeBase,
'properties': {
@ -115,7 +117,6 @@ export class UserDataProfilesWorkbenchContribution extends Disposable implements
id: `workbench.profiles.actions.profileEntry.${profile.id}`,
title: profile.name,
toggled: ContextKeyExpr.equals(CONTEXT_CURRENT_PROFILE.key, profile.id),
precondition: ContextKeyExpr.notEquals(CONTEXT_CURRENT_PROFILE.key, profile.id),
menu: [
{
id: ManageProfilesSubMenu,
@ -126,7 +127,9 @@ export class UserDataProfilesWorkbenchContribution extends Disposable implements
});
}
async run(accessor: ServicesAccessor) {
return that.userDataProfileManagementService.switchProfile(profile);
if (that.userDataProfileService.currentProfile.id !== profile.id) {
return that.userDataProfileManagementService.switchProfile(profile);
}
}
});
}
@ -138,7 +141,7 @@ export class UserDataProfilesWorkbenchContribution extends Disposable implements
name: PROFILES_CATEGORY,
command: 'workbench.profiles.actions.switchProfile',
ariaLabel: localize('currentProfile', "Current Settings Profile is {0}", this.userDataProfileService.currentProfile.name),
text: `$(${Codicon.multipleWindows.id}) ${this.userDataProfileService.currentProfile.name!}`,
text: `$(${userDataProfilesIcon.id}) ${this.userDataProfileService.currentProfile.name!}`,
tooltip: localize('profileTooltip', "{0}: {1}", PROFILES_CATEGORY, this.userDataProfileService.currentProfile.name),
color: themeColorFromId(STATUS_BAR_SETTINGS_PROFILE_FOREGROUND),
backgroundColor: themeColorFromId(STATUS_BAR_SETTINGS_PROFILE_BACKGROUND)

View file

@ -239,7 +239,7 @@ export class DesktopMain extends Disposable {
serviceCollection.set(IUriIdentityService, uriIdentityService);
// User Data Profiles
const userDataProfilesService = new UserDataProfilesNativeService(this.configuration.profiles.all, mainProcessService, environmentService, fileService, logService);
const userDataProfilesService = new UserDataProfilesNativeService(this.configuration.profiles.all, mainProcessService, environmentService);
serviceCollection.set(IUserDataProfilesService, userDataProfilesService);
const userDataProfileService = new UserDataProfileService(reviveProfile(this.configuration.profiles.current, userDataProfilesService.profilesHome.scheme), userDataProfilesService);
serviceCollection.set(IUserDataProfileService, userDataProfileService);

View file

@ -110,13 +110,14 @@ suite('ConfigurationEditingService', () => {
environmentService = TestEnvironmentService;
environmentService.policyFile = joinPath(workspaceFolder, 'policies.json');
instantiationService.stub(IEnvironmentService, environmentService);
const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, fileService, logService));
const uriIdentityService = new UriIdentityService(fileService);
const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, fileService, uriIdentityService, logService));
userDataProfileService = new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService);
const remoteAgentService = disposables.add(instantiationService.createInstance(RemoteAgentService, null));
disposables.add(fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, logService))));
instantiationService.stub(IFileService, fileService);
instantiationService.stub(IRemoteAgentService, remoteAgentService);
workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfileService, userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new FilePolicyService(environmentService.policyFile, fileService, logService)));
workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfileService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, new NullLogService(), new FilePolicyService(environmentService.policyFile, fileService, logService)));
await workspaceService.initialize({
id: hash(workspaceFolder.toString()).toString(16),
uri: workspaceFolder

View file

@ -86,8 +86,9 @@ suite('WorkspaceContextService - Folder', () => {
const environmentService = TestEnvironmentService;
fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService())));
const userDataProfilesService = new UserDataProfilesService(environmentService, fileService, logService);
testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService), userDataProfilesService, fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService(), new NullPolicyService()));
const uriIdentityService = new UriIdentityService(fileService);
const userDataProfilesService = new UserDataProfilesService(environmentService, fileService, uriIdentityService, logService);
testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService), userDataProfilesService, fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), uriIdentityService, new NullLogService(), new NullPolicyService()));
await (<WorkspaceService>testObject).initialize(convertToWorkspacePayload(folder));
});
@ -127,8 +128,9 @@ suite('WorkspaceContextService - Folder', () => {
const environmentService = TestEnvironmentService;
fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService())));
const userDataProfilesService = new UserDataProfilesService(environmentService, fileService, logService);
const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService), userDataProfilesService, fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService(), new NullPolicyService()));
const uriIdentityService = new UriIdentityService(fileService);
const userDataProfilesService = new UserDataProfilesService(environmentService, fileService, uriIdentityService, logService);
const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService), userDataProfilesService, fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), uriIdentityService, new NullLogService(), new NullPolicyService()));
await (<WorkspaceService>testObject).initialize(convertToWorkspacePayload(folder));
const actual = testObject.getWorkspaceFolder(joinPath(folder, 'a'));
@ -148,8 +150,9 @@ suite('WorkspaceContextService - Folder', () => {
const environmentService = TestEnvironmentService;
fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService())));
const userDataProfilesService = new UserDataProfilesService(environmentService, fileService, logService);
const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService), userDataProfilesService, fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService(), new NullPolicyService()));
const uriIdentityService = new UriIdentityService(fileService);
const userDataProfilesService = new UserDataProfilesService(environmentService, fileService, uriIdentityService, logService);
const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService), userDataProfilesService, fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), uriIdentityService, new NullLogService(), new NullPolicyService()));
await (<WorkspaceService>testObject).initialize(convertToWorkspacePayload(folder));
@ -196,8 +199,9 @@ suite('WorkspaceContextService - Workspace', () => {
const remoteAgentService = disposables.add(instantiationService.createInstance(RemoteAgentService, null));
instantiationService.stub(IRemoteAgentService, remoteAgentService);
fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService())));
const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, fileService, logService));
testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService), userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService()));
const uriIdentityService = new UriIdentityService(fileService);
const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, fileService, uriIdentityService, logService));
testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService), userDataProfilesService, fileService, remoteAgentService, uriIdentityService, new NullLogService(), new NullPolicyService()));
instantiationService.stub(IWorkspaceContextService, testObject);
instantiationService.stub(IConfigurationService, testObject);
@ -255,8 +259,9 @@ suite('WorkspaceContextService - Workspace Editing', () => {
const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null);
instantiationService.stub(IRemoteAgentService, remoteAgentService);
fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService())));
const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, fileService, logService));
testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService), userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService()));
const uriIdentityService = new UriIdentityService(fileService);
const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, fileService, uriIdentityService, logService));
testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService), userDataProfilesService, fileService, remoteAgentService, uriIdentityService, new NullLogService(), new NullPolicyService()));
instantiationService.stub(IFileService, fileService);
instantiationService.stub(IWorkspaceContextService, testObject);
@ -499,9 +504,10 @@ suite('WorkspaceService - Initialization', () => {
const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null);
instantiationService.stub(IRemoteAgentService, remoteAgentService);
fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService())));
const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, fileService, logService));
const uriIdentityService = new UriIdentityService(fileService);
const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, fileService, uriIdentityService, logService));
userDataProfileService = instantiationService.stub(IUserDataProfileService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService));
testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfileService, userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService()));
testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfileService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, new NullLogService(), new NullPolicyService()));
instantiationService.stub(IFileService, fileService);
instantiationService.stub(IWorkspaceContextService, testObject);
instantiationService.stub(IConfigurationService, testObject);
@ -759,9 +765,10 @@ suite('WorkspaceConfigurationService - Folder', () => {
const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null);
instantiationService.stub(IRemoteAgentService, remoteAgentService);
fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService())));
const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, fileService, logService));
const uriIdentityService = new UriIdentityService(fileService);
const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, fileService, uriIdentityService, logService));
userDataProfileService = instantiationService.stub(IUserDataProfileService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService));
workspaceService = testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfileService, userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new FilePolicyService(environmentService.policyFile, fileService, logService)));
workspaceService = testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfileService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, new NullLogService(), new FilePolicyService(environmentService.policyFile, fileService, logService)));
instantiationService.stub(IFileService, fileService);
instantiationService.stub(IWorkspaceContextService, testObject);
instantiationService.stub(IConfigurationService, testObject);
@ -1425,9 +1432,10 @@ suite('WorkspaceConfigurationService - Profiles', () => {
const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null);
instantiationService.stub(IRemoteAgentService, remoteAgentService);
fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService())));
const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, fileService, logService));
const uriIdentityService = new UriIdentityService(fileService);
const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, fileService, uriIdentityService, logService));
userDataProfileService = instantiationService.stub(IUserDataProfileService, new UserDataProfileService(toUserDataProfile('custom', joinPath(environmentService.userRoamingDataHome, 'profiles', 'temp')), userDataProfilesService));
workspaceService = testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfileService, userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new FilePolicyService(environmentService.policyFile, fileService, logService)));
workspaceService = testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfileService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, new NullLogService(), new FilePolicyService(environmentService.policyFile, fileService, logService)));
instantiationService.stub(IFileService, fileService);
instantiationService.stub(IWorkspaceContextService, testObject);
instantiationService.stub(IConfigurationService, testObject);
@ -1613,9 +1621,10 @@ suite('WorkspaceConfigurationService-Multiroot', () => {
const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null);
instantiationService.stub(IRemoteAgentService, remoteAgentService);
fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService())));
const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, fileService, logService));
const uriIdentityService = new UriIdentityService(fileService);
const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, fileService, uriIdentityService, logService));
userDataProfileService = instantiationService.stub(IUserDataProfileService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService));
const workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfileService, userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService()));
const workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfileService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, new NullLogService(), new NullPolicyService()));
instantiationService.stub(IFileService, fileService);
instantiationService.stub(IWorkspaceContextService, workspaceService);
@ -2276,9 +2285,10 @@ suite('WorkspaceConfigurationService - Remote Folder', () => {
const remoteAgentService = instantiationService.stub(IRemoteAgentService, <Partial<IRemoteAgentService>>{ getEnvironment: () => remoteEnvironmentPromise });
fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService())));
const configurationCache: IConfigurationCache = { read: () => Promise.resolve(''), write: () => Promise.resolve(), remove: () => Promise.resolve(), needsCaching: () => false };
const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, fileService, logService));
const uriIdentityService = new UriIdentityService(fileService);
const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, fileService, uriIdentityService, logService));
userDataProfileService = instantiationService.stub(IUserDataProfileService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService));
testObject = disposables.add(new WorkspaceService({ configurationCache, remoteAuthority }, environmentService, userDataProfileService, userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService()));
testObject = disposables.add(new WorkspaceService({ configurationCache, remoteAuthority }, environmentService, userDataProfileService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, new NullLogService(), new NullPolicyService()));
instantiationService.stub(IWorkspaceContextService, testObject);
instantiationService.stub(IConfigurationService, testObject);
instantiationService.stub(IEnvironmentService, environmentService);

View file

@ -40,6 +40,9 @@ import { IProductService } from 'vs/platform/product/common/productService';
import { validateExtensionManifest } from 'vs/platform/extensions/common/extensionValidator';
import Severity from 'vs/base/common/severity';
import { IStringDictionary } from 'vs/base/common/collections';
import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
type GalleryExtensionInfo = { readonly id: string; preRelease?: boolean; migrateStorageFrom?: string };
type ExtensionInfo = { readonly id: string; preRelease: boolean };
@ -83,7 +86,6 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
private readonly systemExtensionsCacheResource: URI | undefined = undefined;
private readonly customBuiltinExtensionsCacheResource: URI | undefined = undefined;
private readonly installedExtensionsResource: URI | undefined = undefined;
private readonly resourcesAccessQueueMap = new ResourceMap<Queue<IWebExtension[]>>();
constructor(
@ -97,11 +99,12 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
@IExtensionStorageService private readonly extensionStorageService: IExtensionStorageService,
@IStorageService private readonly storageService: IStorageService,
@IProductService private readonly productService: IProductService,
@IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService,
@IUriIdentityService private readonly uriIdentityService: IUriIdentityService,
@ILifecycleService lifecycleService: ILifecycleService,
) {
super();
if (isWeb) {
this.installedExtensionsResource = joinPath(environmentService.userRoamingDataHome, 'extensions.json');
this.systemExtensionsCacheResource = joinPath(environmentService.userRoamingDataHome, 'systemExtensionsCache.json');
this.customBuiltinExtensionsCacheResource = joinPath(environmentService.userRoamingDataHome, 'customBuiltinExtensionsCache.json');
this.registerActions();
@ -369,7 +372,7 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
return this.readSystemExtensions();
}
async scanUserExtensions(scanOptions?: ScanOptions): Promise<IScannedExtension[]> {
async scanUserExtensions(profileLocation?: URI, scanOptions?: ScanOptions): Promise<IScannedExtension[]> {
const extensions = new Map<string, IScannedExtension>();
// Custom builtin extensions defined through `additionalBuiltinExtensions` API
@ -379,7 +382,7 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
}
// User Installed extensions
const installedExtensions = await this.scanInstalledExtensions(scanOptions);
const installedExtensions = await this.scanInstalledExtensions(profileLocation, scanOptions);
for (const extension of installedExtensions) {
extensions.set(extension.identifier.id.toLowerCase(), extension);
}
@ -408,17 +411,17 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
return result;
}
async scanExistingExtension(extensionLocation: URI, extensionType: ExtensionType): Promise<IScannedExtension | null> {
async scanExistingExtension(extensionLocation: URI, extensionType: ExtensionType, profileLocation?: URI): Promise<IScannedExtension | null> {
if (extensionType === ExtensionType.System) {
const systemExtensions = await this.scanSystemExtensions();
return systemExtensions.find(e => e.location.toString() === extensionLocation.toString()) || null;
}
const userExtensions = await this.scanUserExtensions();
const userExtensions = await this.scanUserExtensions(profileLocation);
return userExtensions.find(e => e.location.toString() === extensionLocation.toString()) || null;
}
async scanMetadata(extensionLocation: URI): Promise<Metadata | undefined> {
const extension = await this.scanExistingExtension(extensionLocation, ExtensionType.User);
async scanMetadata(extensionLocation: URI, profileLocation?: URI): Promise<Metadata | undefined> {
const extension = await this.scanExistingExtension(extensionLocation, ExtensionType.User, profileLocation);
return extension?.metadata;
}
@ -435,21 +438,35 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
return null;
}
async addExtensionFromGallery(galleryExtension: IGalleryExtension, metadata?: Metadata): Promise<IExtension> {
async addExtensionFromGallery(galleryExtension: IGalleryExtension, metadata: Metadata, profileLocation?: URI): Promise<IScannedExtension> {
const webExtension = await this.toWebExtensionFromGallery(galleryExtension, metadata);
return this.addWebExtension(webExtension);
return this.addWebExtension(webExtension, profileLocation);
}
async addExtension(location: URI, metadata?: Metadata): Promise<IExtension> {
async addExtension(location: URI, metadata: Metadata, profileLocation?: URI): Promise<IScannedExtension> {
const webExtension = await this.toWebExtension(location, undefined, undefined, undefined, undefined, undefined, metadata);
return this.addWebExtension(webExtension);
return this.addWebExtension(webExtension, profileLocation);
}
async removeExtension(identifier: IExtensionIdentifier, version?: string): Promise<void> {
await this.writeInstalledExtensions(installedExtensions => installedExtensions.filter(extension => !(areSameExtensions(extension.identifier, identifier) && (version ? extension.version === version : true))));
async removeExtension(extension: IScannedExtension, profileLocation?: URI): Promise<void> {
await this.writeInstalledExtensions(profileLocation, installedExtensions => installedExtensions.filter(installedExtension => !areSameExtensions(installedExtension.identifier, extension.identifier)));
}
private async addWebExtension(webExtension: IWebExtension): Promise<IScannedExtension> {
async copyExtensions(fromProfileLocation: URI, toProfileLocation: URI, filter: (extension: IScannedExtension) => boolean): Promise<void> {
const extensionsToCopy: IWebExtension[] = [];
const fromWebExtensions = await this.readInstalledExtensions(fromProfileLocation);
await Promise.all(fromWebExtensions.map(async webExtension => {
const scannedExtension = await this.toScannedExtension(webExtension, false);
if (filter(scannedExtension)) {
extensionsToCopy.push(webExtension);
}
}));
if (extensionsToCopy.length) {
await this.addToInstalledExtensions(extensionsToCopy, toProfileLocation);
}
}
private async addWebExtension(webExtension: IWebExtension, profileLocation?: URI): Promise<IScannedExtension> {
const isSystem = !!(await this.scanSystemExtensions()).find(e => areSameExtensions(e.identifier, webExtension.identifier));
const isBuiltin = !!webExtension.metadata?.isBuiltin;
const extension = await this.toScannedExtension(webExtension, isBuiltin);
@ -473,30 +490,40 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
return customBuiltinExtensions;
});
const installedExtensions = await this.readInstalledExtensions();
const installedExtensions = await this.readInstalledExtensions(profileLocation);
// Also add to installed extensions if it is installed to update its version
if (installedExtensions.some(e => areSameExtensions(e.identifier, webExtension.identifier))) {
await this.addToInstalledExtensions(webExtension);
await this.addToInstalledExtensions([webExtension], profileLocation);
}
return extension;
}
// Add to installed extensions
await this.addToInstalledExtensions(webExtension);
await this.addToInstalledExtensions([webExtension], profileLocation);
return extension;
}
private async addToInstalledExtensions(webExtension: IWebExtension): Promise<void> {
await this.writeInstalledExtensions(installedExtensions => {
private async addToInstalledExtensions(webExtensions: IWebExtension[], profileLocation?: URI): Promise<void> {
await this.writeInstalledExtensions(profileLocation, installedExtensions => {
// Remove the existing extension to avoid duplicates
installedExtensions = installedExtensions.filter(e => !areSameExtensions(e.identifier, webExtension.identifier));
installedExtensions.push(webExtension);
installedExtensions = installedExtensions.filter(installedExtension => webExtensions.some(extension => !areSameExtensions(installedExtension.identifier, extension.identifier)));
installedExtensions.push(...webExtensions);
return installedExtensions;
});
}
private async scanInstalledExtensions(scanOptions?: ScanOptions): Promise<IScannedExtension[]> {
const installedExtensions = await this.readInstalledExtensions();
private async scanInstalledExtensions(profileLocation?: URI, scanOptions?: ScanOptions): Promise<IScannedExtension[]> {
let installedExtensions = await this.readInstalledExtensions(profileLocation);
// If current profile is not a default profile, then add the application extensions to the list
if (this.userDataProfilesService.defaultProfile.extensionsResource && !this.uriIdentityService.extUri.isEqual(profileLocation, this.userDataProfilesService.defaultProfile.extensionsResource)) {
// Remove application extensions from the non default profile
installedExtensions = installedExtensions.filter(i => !i.metadata?.isApplicationScoped);
// Add application extensions from the default profile to the list
const defaultProfileExtensions = await this.readInstalledExtensions(this.userDataProfilesService.defaultProfile.extensionsResource);
installedExtensions.push(...defaultProfileExtensions.filter(i => i.metadata?.isApplicationScoped));
}
installedExtensions.sort((a, b) => a.identifier.id < b.identifier.id ? -1 : a.identifier.id > b.identifier.id ? 1 : semver.rcompare(a.version, b.version));
const result = new Map<string, IScannedExtension>();
for (const webExtension of installedExtensions) {
@ -670,17 +697,12 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
return manifest;
}
private async readInstalledExtensions(): Promise<IWebExtension[]> {
await this.migratePackageNLSUris();
return this.withWebExtensions(this.installedExtensionsResource);
}
// TODO: @TylerLeonhardt/@Sandy081: Delete after 6 months
private _migratePackageNLSUrisPromise: Promise<void> | undefined;
private migratePackageNLSUris(): Promise<void> {
if (!this._migratePackageNLSUrisPromise) {
this._migratePackageNLSUrisPromise = (async () => {
const webExtensions = await this.withWebExtensions(this.installedExtensionsResource);
const webExtensions = await this.withWebExtensions(this.userDataProfilesService.defaultProfile.extensionsResource);
if (webExtensions.some(e => !e.packageNLSUris && e.packageNLSUri)) {
const migratedExtensions = await Promise.all(webExtensions.map(async e => {
if (!e.packageNLSUris && e.packageNLSUri) {
@ -691,15 +713,22 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
}
return e;
}));
await this.withWebExtensions(this.installedExtensionsResource, () => migratedExtensions);
await this.withWebExtensions(this.userDataProfilesService.defaultProfile.extensionsResource, () => migratedExtensions);
}
})();
}
return this._migratePackageNLSUrisPromise;
}
private writeInstalledExtensions(updateFn: (extensions: IWebExtension[]) => IWebExtension[]): Promise<IWebExtension[]> {
return this.withWebExtensions(this.installedExtensionsResource, updateFn);
private async readInstalledExtensions(profileLocation?: URI): Promise<IWebExtension[]> {
if (this.uriIdentityService.extUri.isEqual(profileLocation, this.userDataProfilesService.defaultProfile.extensionsResource)) {
await this.migratePackageNLSUris();
}
return this.withWebExtensions(profileLocation);
}
private writeInstalledExtensions(profileLocation: URI | undefined, updateFn: (extensions: IWebExtension[]) => IWebExtension[]): Promise<IWebExtension[]> {
return this.withWebExtensions(profileLocation, updateFn);
}
private readCustomBuiltinExtensionsCache(): Promise<IWebExtension[]> {
@ -797,7 +826,6 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
}
private registerActions(): void {
const that = this;
this._register(registerAction2(class extends Action2 {
constructor() {
super({
@ -809,7 +837,9 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten
});
}
run(serviceAccessor: ServicesAccessor): void {
serviceAccessor.get(IEditorService).openEditor({ resource: that.installedExtensionsResource });
const editorService = serviceAccessor.get(IEditorService);
const userDataProfileService = serviceAccessor.get(IUserDataProfileService);
editorService.openEditor({ resource: userDataProfileService.currentProfile.extensionsResource });
}
}));
}

View file

@ -6,7 +6,7 @@
import { Event } from 'vs/base/common/event';
import { createDecorator, refineServiceDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtension, ExtensionType, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { IExtensionManagementService, IGalleryExtension, IExtensionIdentifier, ILocalExtension, InstallOptions, InstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionResult, Metadata, InstallVSIXOptions, UninstallExtensionEvent } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IExtensionManagementService, IGalleryExtension, ILocalExtension, InstallOptions, InstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionResult, Metadata, InstallVSIXOptions, UninstallExtensionEvent } from 'vs/platform/extensionManagement/common/extensionManagement';
import { URI } from 'vs/base/common/uri';
import { FileAccess } from 'vs/base/common/network';
@ -158,14 +158,15 @@ export interface IWebExtensionsScannerService {
readonly _serviceBrand: undefined;
scanSystemExtensions(): Promise<IExtension[]>;
scanUserExtensions(options?: ScanOptions): Promise<IScannedExtension[]>;
scanUserExtensions(profileLocation: URI | undefined, options?: ScanOptions): Promise<IScannedExtension[]>;
scanExtensionsUnderDevelopment(): Promise<IExtension[]>;
scanExistingExtension(extensionLocation: URI, extensionType: ExtensionType): Promise<IScannedExtension | null>;
scanExistingExtension(extensionLocation: URI, extensionType: ExtensionType, profileLocation: URI | undefined): Promise<IScannedExtension | null>;
addExtension(location: URI, metadata?: Metadata): Promise<IExtension>;
addExtensionFromGallery(galleryExtension: IGalleryExtension, metadata?: Metadata): Promise<IExtension>;
removeExtension(identifier: IExtensionIdentifier, version?: string): Promise<void>;
addExtension(location: URI, metadata: Metadata, profileLocation: URI | undefined): Promise<IScannedExtension>;
addExtensionFromGallery(galleryExtension: IGalleryExtension, metadata: Metadata, profileLocation: URI | undefined): Promise<IScannedExtension>;
removeExtension(extension: IScannedExtension, profileLocation: URI | undefined): Promise<void>;
copyExtensions(fromProfileLocation: URI, toProfileLocation: URI, filter: (extension: IScannedExtension) => boolean): Promise<void>;
scanMetadata(extensionLocation: URI): Promise<Metadata | undefined>;
scanMetadata(extensionLocation: URI, profileLocation: URI | undefined): Promise<Metadata | undefined>;
scanExtensionManifest(extensionLocation: URI): Promise<IExtensionManifest | null>;
}

View file

@ -3,10 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ExtensionType, IExtension, IExtensionIdentifier, IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions';
import { ILocalExtension, IGalleryExtension, IGalleryMetadata, InstallOperation, IExtensionGalleryService, InstallOptions, Metadata, UninstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionIdentifier, ExtensionType, IExtension, IExtensionIdentifier, IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions';
import { ILocalExtension, IGalleryExtension, IGalleryMetadata, InstallOperation, IExtensionGalleryService, Metadata, ServerInstallOptions, ServerUninstallOptions, IServerExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { URI } from 'vs/base/common/uri';
import { Event } from 'vs/base/common/event';
import { Emitter } from 'vs/base/common/event';
import { areSameExtensions, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IProfileAwareExtensionManagementService, IScannedExtension, IWebExtensionsScannerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { ILogService } from 'vs/platform/log/common/log';
@ -16,15 +16,17 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService';
import { IProductService } from 'vs/platform/product/common/productService';
import { isBoolean, isUndefined } from 'vs/base/common/types';
import { IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService';
import { DidChangeUserDataProfileEvent, IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
import { delta } from 'vs/base/common/arrays';
import { compare } from 'vs/base/common/strings';
import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
export class WebExtensionManagementService extends AbstractExtensionManagementService implements IProfileAwareExtensionManagementService {
export class WebExtensionManagementService extends AbstractExtensionManagementService implements IProfileAwareExtensionManagementService, IServerExtensionManagementService {
declare readonly _serviceBrand: undefined;
readonly onDidChangeProfileExtensions = Event.None;
private readonly _onDidChangeProfileExtensions = this._register(new Emitter<{ readonly added: ILocalExtension[]; readonly removed: ILocalExtension[] }>());
readonly onDidChangeProfileExtensions = this._onDidChangeProfileExtensions.event;
constructor(
@IExtensionGalleryService extensionGalleryService: IExtensionGalleryService,
@ -32,12 +34,12 @@ export class WebExtensionManagementService extends AbstractExtensionManagementSe
@ILogService logService: ILogService,
@IWebExtensionsScannerService private readonly webExtensionsScannerService: IWebExtensionsScannerService,
@IExtensionManifestPropertiesService private readonly extensionManifestPropertiesService: IExtensionManifestPropertiesService,
@IExtensionsProfileScannerService extensionsProfileScannerService: IExtensionsProfileScannerService,
@IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService,
@IProductService productService: IProductService,
@IUserDataProfilesService userDataProfilesService: IUserDataProfilesService,
@IUriIdentityService uriIdentityService: IUriIdentityService,
) {
super(userDataProfilesService, uriIdentityService, extensionGalleryService, extensionsProfileScannerService, telemetryService, logService, productService);
super(extensionGalleryService, telemetryService, logService, productService, userDataProfilesService);
this._register(userDataProfileService.onDidChangeCurrentProfile(e => e.join(this.whenProfileChanged(e))));
}
async getTargetPlatform(): Promise<TargetPlatform> {
@ -61,13 +63,13 @@ export class WebExtensionManagementService extends AbstractExtensionManagementSe
extensions.push(...systemExtensions);
}
if (type === undefined || type === ExtensionType.User) {
const userExtensions = await this.webExtensionsScannerService.scanUserExtensions();
const userExtensions = await this.webExtensionsScannerService.scanUserExtensions(this.userDataProfileService.currentProfile.extensionsResource);
extensions.push(...userExtensions);
}
return Promise.all(extensions.map(e => toLocalExtension(e)));
}
async install(location: URI, options: InstallOptions = {}): Promise<ILocalExtension> {
async install(location: URI, options: ServerInstallOptions = {}): Promise<ILocalExtension> {
this.logService.trace('ExtensionManagementService#install', location.toString());
const manifest = await this.webExtensionsScannerService.scanExtensionManifest(location);
if (!manifest) {
@ -77,7 +79,7 @@ export class WebExtensionManagementService extends AbstractExtensionManagementSe
}
getMetadata(extension: ILocalExtension): Promise<Metadata | undefined> {
return this.webExtensionsScannerService.scanMetadata(extension.location);
return this.webExtensionsScannerService.scanMetadata(extension.location, this.userDataProfileService.currentProfile.extensionsResource);
}
protected override async getCompatibleVersion(extension: IGalleryExtension, sameVersion: boolean, includePreRelease: boolean): Promise<IGalleryExtension | null> {
@ -100,11 +102,11 @@ export class WebExtensionManagementService extends AbstractExtensionManagementSe
return local;
}
protected createDefaultInstallExtensionTask(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: InstallOptions): IInstallExtensionTask {
protected doCreateInstallExtensionTask(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: ServerInstallOptions): IInstallExtensionTask {
return new InstallExtensionTask(manifest, extension, options, this.webExtensionsScannerService);
}
protected createDefaultUninstallExtensionTask(extension: ILocalExtension, options: UninstallOptions): IUninstallExtensionTask {
protected doCreateUninstallExtensionTask(extension: ILocalExtension, options: ServerUninstallOptions): IUninstallExtensionTask {
return new UninstallExtensionTask(extension, options, this.webExtensionsScannerService);
}
@ -112,6 +114,24 @@ export class WebExtensionManagementService extends AbstractExtensionManagementSe
unzip(zipLocation: URI): Promise<IExtensionIdentifier> { throw new Error('unsupported'); }
getManifest(vsix: URI): Promise<IExtensionManifest> { throw new Error('unsupported'); }
updateExtensionScope(): Promise<ILocalExtension> { throw new Error('unsupported'); }
private async whenProfileChanged(e: DidChangeUserDataProfileEvent): Promise<void> {
const previousProfileLocation = e.previous.extensionsResource;
const currentProfileLocation = e.profile.extensionsResource;
if (!previousProfileLocation || !currentProfileLocation) {
throw new Error('This should not happen');
}
if (e.preserveData) {
await this.webExtensionsScannerService.copyExtensions(previousProfileLocation, currentProfileLocation, e => !e.metadata?.isApplicationScoped);
} else {
const oldExtensions = await this.webExtensionsScannerService.scanUserExtensions(previousProfileLocation);
const newExtensions = await this.webExtensionsScannerService.scanUserExtensions(currentProfileLocation);
const { added, removed } = delta(oldExtensions, newExtensions, (a, b) => compare(`${ExtensionIdentifier.toKey(a.identifier.id)}@${a.manifest.version}`, `${ExtensionIdentifier.toKey(b.identifier.id)}@${b.manifest.version}`));
if (added.length || removed.length) {
this._onDidChangeProfileExtensions.fire({ added: added.map(e => toLocalExtension(e)), removed: removed.map(e => toLocalExtension(e)) });
}
}
}
}
function toLocalExtension(extension: IExtension): ILocalExtension {
@ -131,7 +151,7 @@ function toLocalExtension(extension: IExtension): ILocalExtension {
};
}
function getMetadata(options?: InstallOptions, existingExtension?: IExtension): Metadata {
function getMetadata(options?: ServerInstallOptions, existingExtension?: IExtension): Metadata {
const metadata: Metadata = { ...((<IScannedExtension>existingExtension)?.metadata || {}) };
metadata.isMachineScoped = options?.isMachineScoped || metadata.isMachineScoped;
return metadata;
@ -148,7 +168,7 @@ class InstallExtensionTask extends AbstractExtensionTask<{ local: ILocalExtensio
constructor(
manifest: IExtensionManifest,
private readonly extension: URI | IGalleryExtension,
private readonly options: InstallOptions,
private readonly options: ServerInstallOptions,
private readonly webExtensionsScannerService: IWebExtensionsScannerService,
) {
super();
@ -157,7 +177,7 @@ class InstallExtensionTask extends AbstractExtensionTask<{ local: ILocalExtensio
}
protected async doRun(token: CancellationToken): Promise<{ local: ILocalExtension; metadata: Metadata }> {
const userExtensions = await this.webExtensionsScannerService.scanUserExtensions();
const userExtensions = await this.webExtensionsScannerService.scanUserExtensions(this.options.profileLocation);
const existingExtension = userExtensions.find(e => areSameExtensions(e.identifier, this.identifier));
if (existingExtension) {
this._operation = InstallOperation.Update;
@ -179,8 +199,8 @@ class InstallExtensionTask extends AbstractExtensionTask<{ local: ILocalExtensio
: metadata?.preRelease /* Respect the existing pre-release flag if it was set */);
}
const scannedExtension = URI.isUri(this.extension) ? await this.webExtensionsScannerService.addExtension(this.extension, metadata)
: await this.webExtensionsScannerService.addExtensionFromGallery(this.extension, metadata);
const scannedExtension = URI.isUri(this.extension) ? await this.webExtensionsScannerService.addExtension(this.extension, metadata, this.options.profileLocation)
: await this.webExtensionsScannerService.addExtensionFromGallery(this.extension, metadata, this.options.profileLocation);
return { local: toLocalExtension(scannedExtension), metadata };
}
}
@ -189,13 +209,13 @@ class UninstallExtensionTask extends AbstractExtensionTask<void> implements IUni
constructor(
readonly extension: ILocalExtension,
options: UninstallOptions,
private readonly options: ServerUninstallOptions,
private readonly webExtensionsScannerService: IWebExtensionsScannerService,
) {
super();
}
protected doRun(token: CancellationToken): Promise<void> {
return this.webExtensionsScannerService.removeExtension(this.extension.identifier);
return this.webExtensionsScannerService.removeExtension(this.extension, this.options.profileLocation);
}
}

View file

@ -30,6 +30,7 @@ import { IExtensionManifestPropertiesService } from 'vs/workbench/services/exten
import { IUserDataInitializationService } from 'vs/workbench/services/userData/browser/userDataInit';
import { IAutomatedWindow } from 'vs/platform/log/browser/log';
import { ILogService } from 'vs/platform/log/common/log';
import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
export class ExtensionService extends AbstractExtensionService implements IExtensionService {
@ -54,6 +55,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
@ILifecycleService lifecycleService: ILifecycleService,
@IRemoteAuthorityResolverService private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService,
@IUserDataInitializationService private readonly _userDataInitializationService: IUserDataInitializationService,
@IUserDataProfileService userDataProfileService: IUserDataProfileService,
) {
super(
instantiationService,
@ -70,7 +72,8 @@ export class ExtensionService extends AbstractExtensionService implements IExten
webExtensionsScannerService,
logService,
remoteAgentService,
lifecycleService
lifecycleService,
userDataProfileService
);
// Initialize installed extensions first and do it only after workbench is ready
@ -92,7 +95,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
return this._remoteAgentService.scanSingleExtension(extension.location, extension.type === ExtensionType.System);
}
const scannedExtension = await this._webExtensionsScannerService.scanExistingExtension(extension.location, extension.type);
const scannedExtension = await this._webExtensionsScannerService.scanExistingExtension(extension.location, extension.type, this._userDataProfileService.currentProfile.extensionsResource);
if (scannedExtension) {
return toExtensionDescription(scannedExtension);
}

View file

@ -37,6 +37,7 @@ import { ApiProposalName, allApiProposals } from 'vs/workbench/services/extensio
import { ILogService } from 'vs/platform/log/common/log';
import { IExtensionHostExitInfo, IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
const hasOwnProperty = Object.hasOwnProperty;
const NO_OP_VOID_PROMISE = Promise.resolve<void>(undefined);
@ -189,6 +190,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
@ILogService protected readonly _logService: ILogService,
@IRemoteAgentService protected readonly _remoteAgentService: IRemoteAgentService,
@ILifecycleService private readonly _lifecycleService: ILifecycleService,
@IUserDataProfileService protected readonly _userDataProfileService: IUserDataProfileService,
) {
super();
@ -1331,7 +1333,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
try {
await Promise.all([
this._webExtensionsScannerService.scanSystemExtensions().then(extensions => system.push(...extensions.map(e => toExtensionDescription(e)))),
this._webExtensionsScannerService.scanUserExtensions({ skipInvalidExtensions: true }).then(extensions => user.push(...extensions.map(e => toExtensionDescription(e)))),
this._webExtensionsScannerService.scanUserExtensions(this._userDataProfileService.currentProfile.extensionsResource, { skipInvalidExtensions: true }).then(extensions => user.push(...extensions.map(e => toExtensionDescription(e)))),
this._webExtensionsScannerService.scanExtensionsUnderDevelopment().then(extensions => development.push(...extensions.map(e => toExtensionDescription(e, true))))
]);
} catch (error) {

View file

@ -48,6 +48,7 @@ import { isCI } from 'vs/base/common/platform';
import { IResolveAuthorityErrorResult } from 'vs/workbench/services/extensions/common/extensionHostProxy';
import { URI } from 'vs/base/common/uri';
import { ILocalProcessExtensionHostDataProvider, ILocalProcessExtensionHostInitData, SandboxLocalProcessExtensionHost } from 'vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost';
import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
export abstract class ElectronExtensionService extends AbstractExtensionService implements IExtensionService {
@ -80,6 +81,7 @@ export abstract class ElectronExtensionService extends AbstractExtensionService
@IRemoteExplorerService private readonly _remoteExplorerService: IRemoteExplorerService,
@IExtensionGalleryService private readonly _extensionGalleryService: IExtensionGalleryService,
@IWorkspaceTrustManagementService private readonly _workspaceTrustManagementService: IWorkspaceTrustManagementService,
@IUserDataProfileService userDataProfileService: IUserDataProfileService,
) {
super(
instantiationService,
@ -96,7 +98,8 @@ export abstract class ElectronExtensionService extends AbstractExtensionService
webExtensionsScannerService,
logService,
remoteAgentService,
lifecycleService
lifecycleService,
userDataProfileService
);
[this._enableLocalWebWorker, this._lazyLocalWebWorker] = this._isLocalWebWorkerEnabled();

View file

@ -33,6 +33,11 @@ import { TestEnvironmentService, TestFileService, TestLifecycleService, TestRemo
import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices';
import { mock } from 'vs/base/test/common/mock';
import { IExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManager';
import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService';
import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService';
suite('BrowserExtensionService', () => {
test('pickRunningLocation', () => {
@ -175,7 +180,10 @@ suite('ExtensionService', () => {
[IWorkbenchExtensionEnablementService, TestWorkbenchExtensionEnablementService],
[ITelemetryService, NullTelemetryService],
[IEnvironmentService, TestEnvironmentService],
[IWorkspaceTrustEnablementService, WorkspaceTrustEnablementService]
[IWorkspaceTrustEnablementService, WorkspaceTrustEnablementService],
[IUserDataProfilesService, UserDataProfilesService],
[IUserDataProfileService, UserDataProfileService],
[IUriIdentityService, UriIdentityService],
]);
extService = <MyTestExtensionService>instantiationService.get(IExtensionService);
});

View file

@ -22,6 +22,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag
import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService';
import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService';
suite('ExtensionStorageMigration', () => {
@ -38,7 +39,7 @@ suite('ExtensionStorageMigration', () => {
fileService.registerProvider(ROOT.scheme, disposables.add(new InMemoryFileSystemProvider()));
instantiationService.stub(IFileService, fileService);
const environmentService = instantiationService.stub(IEnvironmentService, <Partial<IEnvironmentService>>{ userRoamingDataHome: ROOT, workspaceStorageHome });
const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, fileService, new NullLogService()));
const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, fileService, new UriIdentityService(fileService), new NullLogService()));
instantiationService.stub(IUserDataProfileService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService));
instantiationService.stub(IExtensionStorageService, instantiationService.createInstance(ExtensionStorageService));

View file

@ -31,6 +31,7 @@ import { VSBuffer } from 'vs/base/common/buffer';
import { UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService';
import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService';
interface Modifiers {
metaKey?: boolean;
@ -66,7 +67,7 @@ suite('KeybindingsEditing', () => {
const configService = new TestConfigurationService();
configService.setUserConfiguration('files', { 'eol': '\n' });
const userDataProfilesService = new UserDataProfilesService(environmentService, fileService, logService);
const userDataProfilesService = new UserDataProfilesService(environmentService, fileService, new UriIdentityService(fileService), logService);
userDataProfileService = new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService);
instantiationService = workbenchInstantiationService({

View file

@ -16,6 +16,7 @@ import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFil
import { NullLogService } from 'vs/platform/log/common/log';
import { StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { createSuite } from 'vs/platform/storage/test/common/storageService.test';
import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService';
import { IUserDataProfile, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
import { BrowserStorageService, IndexedDBStorageDatabase } from 'vs/workbench/services/storage/browser/storageService';
import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService';
@ -46,7 +47,7 @@ async function createStorageService(): Promise<[DisposableStore, BrowserStorageS
extensionsResource: joinPath(inMemoryExtraProfileRoot, 'extensionsResource')
};
const storageService = disposables.add(new BrowserStorageService({ id: 'workspace-storage-test' }, new UserDataProfileService(inMemoryExtraProfile, new UserDataProfilesService(TestEnvironmentService, fileService, logService)), logService));
const storageService = disposables.add(new BrowserStorageService({ id: 'workspace-storage-test' }, new UserDataProfileService(inMemoryExtraProfile, new UserDataProfilesService(TestEnvironmentService, fileService, new UriIdentityService(fileService), logService)), logService));
await storageService.initialize();

View file

@ -10,7 +10,7 @@ import { MenuId } from 'vs/platform/actions/common/actions';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IUserDataProfile, PROFILES_ENABLEMENT_CONFIG, UseDefaultProfileFlags } from 'vs/platform/userDataProfile/common/userDataProfile';
import { ContextKeyDefinedExpr, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IsWebContext, ProductQualityContext } from 'vs/platform/contextkey/common/contextkeys';
import { ProductQualityContext } from 'vs/platform/contextkey/common/contextkeys';
export interface DidChangeUserDataProfileEvent {
readonly preserveData: boolean;
@ -73,4 +73,4 @@ export const PROFILES_TTILE = { value: localize('settings profiles', "Settings P
export const PROFILES_CATEGORY = PROFILES_TTILE.value;
export const PROFILE_EXTENSION = 'code-profile';
export const PROFILE_FILTER = [{ name: localize('profile', "Settings Profile"), extensions: [PROFILE_EXTENSION] }];
export const PROFILES_ENABLEMENT_CONTEXT = ContextKeyExpr.and(ProductQualityContext.notEqualsTo('stable'), IsWebContext.negate(), ContextKeyDefinedExpr.create(`config.${PROFILES_ENABLEMENT_CONFIG}`));
export const PROFILES_ENABLEMENT_CONTEXT = ContextKeyExpr.and(ProductQualityContext.notEqualsTo('stable'), ContextKeyDefinedExpr.create(`config.${PROFILES_ENABLEMENT_CONFIG}`));

View file

@ -19,7 +19,10 @@ export class UserDataProfileService extends Disposable implements IUserDataProfi
private _currentProfile: IUserDataProfile;
get currentProfile(): IUserDataProfile { return this._currentProfile; }
constructor(currentProfile: IUserDataProfile, userDataProfilesService: IUserDataProfilesService) {
constructor(
currentProfile: IUserDataProfile,
@IUserDataProfilesService userDataProfilesService: IUserDataProfilesService
) {
super();
this._currentProfile = currentProfile;
this._register(userDataProfilesService.onDidChangeProfiles(() => {

View file

@ -163,7 +163,7 @@ import { ILayoutOffsetInfo } from 'vs/platform/layout/browser/layoutService';
import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService';
import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
import { EnablementState, IExtensionManagementServer, IScannedExtension, IWebExtensionsScannerService, IWorkbenchExtensionEnablementService, IWorkbenchExtensionManagementService, ScanOptions } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { EnablementState, IExtensionManagementServer, IScannedExtension, IWebExtensionsScannerService, IWorkbenchExtensionEnablementService, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { InstallVSIXOptions, ILocalExtension, IGalleryExtension, InstallOptions, IExtensionIdentifier, UninstallOptions, IExtensionsControlManifest, IGalleryMetadata, IExtensionManagementParticipant } from 'vs/platform/extensionManagement/common/extensionManagement';
import { Codicon } from 'vs/base/common/codicons';
@ -286,9 +286,10 @@ export function workbenchInstantiationService(
instantiationService.stub(IModelService, disposables.add(instantiationService.createInstance(ModelService)));
const fileService = overrides?.fileService ? overrides.fileService(instantiationService) : new TestFileService();
instantiationService.stub(IFileService, fileService);
const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, fileService, new NullLogService()));
const uriIdentityService = new UriIdentityService(fileService);
instantiationService.stub(IUriIdentityService, uriIdentityService);
const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(environmentService, fileService, uriIdentityService, new NullLogService()));
instantiationService.stub(IUserDataProfileService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService));
instantiationService.stub(IUriIdentityService, new UriIdentityService(fileService));
instantiationService.stub(IWorkingCopyBackupService, new TestWorkingCopyBackupService());
instantiationService.stub(ITelemetryService, NullTelemetryService);
instantiationService.stub(INotificationService, new TestNotificationService());
@ -2003,9 +2004,13 @@ export class TestWorkbenchExtensionManagementService implements IWorkbenchExtens
export class TestWebExtensionsScannerService implements IWebExtensionsScannerService {
_serviceBrand: undefined;
onDidChangeProfileExtensions = Event.None;
async scanSystemExtensions(): Promise<IExtension[]> { return []; }
async scanUserExtensions(options?: ScanOptions | undefined): Promise<IScannedExtension[]> { return []; }
async scanUserExtensions(): Promise<IScannedExtension[]> { return []; }
async scanExtensionsUnderDevelopment(): Promise<IExtension[]> { return []; }
async copyExtensions(): Promise<void> {
throw new Error('Method not implemented.');
}
scanExistingExtension(extensionLocation: URI, extensionType: ExtensionType): Promise<IScannedExtension | null> {
throw new Error('Method not implemented.');
}
@ -2015,7 +2020,7 @@ export class TestWebExtensionsScannerService implements IWebExtensionsScannerSer
addExtensionFromGallery(galleryExtension: IGalleryExtension, metadata?: Partial<IGalleryMetadata & { isApplicationScoped: boolean; isMachineScoped: boolean; isBuiltin: boolean; isSystem: boolean; updated: boolean; preRelease: boolean; installedTimestamp: number }> | undefined): Promise<IExtension> {
throw new Error('Method not implemented.');
}
removeExtension(identifier: IExtensionIdentifier, version?: string | undefined): Promise<void> {
removeExtension(): Promise<void> {
throw new Error('Method not implemented.');
}
scanMetadata(extensionLocation: URI): Promise<Partial<IGalleryMetadata & { isApplicationScoped: boolean; isMachineScoped: boolean; isBuiltin: boolean; isSystem: boolean; updated: boolean; preRelease: boolean; installedTimestamp: number }> | undefined> {

View file

@ -53,6 +53,7 @@ import { FileService } from 'vs/platform/files/common/fileService';
import { joinPath } from 'vs/base/common/resources';
import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService';
import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile';
import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService';
const args = parseArgs(process.argv, OPTIONS);
@ -289,7 +290,8 @@ export function workbenchInstantiationService(disposables = new DisposableStore(
instantiationService.stub(INativeEnvironmentService, TestEnvironmentService);
instantiationService.stub(IWorkbenchEnvironmentService, TestEnvironmentService);
instantiationService.stub(INativeWorkbenchEnvironmentService, TestEnvironmentService);
const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(TestEnvironmentService, new FileService(new NullLogService()), new NullLogService()));
const fileService = new FileService(new NullLogService());
const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(TestEnvironmentService, fileService, new UriIdentityService(fileService), new NullLogService()));
instantiationService.stub(IUserDataProfileService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService));
return instantiationService;

View file

@ -12096,20 +12096,20 @@ xterm-addon-unicode11@0.4.0-beta.3:
resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.4.0-beta.3.tgz#f350184155fafd5ad0d6fbf31d13e6ca7dea1efa"
integrity sha512-FryZAVwbUjKTmwXnm1trch/2XO60F5JsDvOkZhzobV1hm10sFLVuZpFyHXiUx7TFeeFsvNP+S77LAtWoeT5z+Q==
xterm-addon-webgl@0.13.0-beta.2:
version "0.13.0-beta.2"
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.13.0-beta.2.tgz#f58a7a3641ad7c8ac82dd24cfb0165656ed9ac1c"
integrity sha512-98tX0BkpD402RoCO6SyikUXpzCn9/OQhlXsRmM/kRFCxMWWofStWTXzCPhN0MjIx2IdGueDjCmnShhidwihErg==
xterm-addon-webgl@0.13.0-beta.3:
version "0.13.0-beta.3"
resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.13.0-beta.3.tgz#2b456c3105238e64b40a30787d6335f5f6f85abb"
integrity sha512-DFGcXAolA0VTsOLIKcORxUOp/FTJdD/YiRzKVLARjgOycwVRKvW2L5Tge8Z7ysZ16sKfnV2vCXyonXYfUWozXw==
xterm-headless@4.20.0-beta.5:
version "4.20.0-beta.5"
resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.20.0-beta.5.tgz#edcff27eb6437d158e6aea2ed7658e783bee5641"
integrity sha512-8SnVUsuNUrQ5P0XU/9Iau3uK7Tf8q/p0KHHwkwJXVxZDIlaDH9XKSs91U9BjJJE3sJgRxH4NSiDYR3vFLSFpxw==
xterm-headless@4.20.0-beta.6:
version "4.20.0-beta.6"
resolved "https://registry.yarnpkg.com/xterm-headless/-/xterm-headless-4.20.0-beta.6.tgz#bd016379e9fac47e5b8870d567cdf330cf6f49fc"
integrity sha512-EV0V7pxMKI0OEcOCD+6vdXq6rBARr7dSN3PovTsZnDWg5dmvUb2eEmz6BTejJj3UVd/JXNEmEXM+tCh97rDCDg==
xterm@4.20.0-beta.5:
version "4.20.0-beta.5"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.20.0-beta.5.tgz#d707b0dcb477a554135fb767b24003fced079866"
integrity sha512-KBWfk9UPBKRy662DVGGTZEcW1becEjYvlyWbn2hLj9h2gy6Q4EEEEbggJh8I7SGwdFizl+apHQGhEOZmFCA70w==
xterm@4.20.0-beta.6:
version "4.20.0-beta.6"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.20.0-beta.6.tgz#3ed87ba383a5cf44284098278f714df7113e3e3c"
integrity sha512-xJd6vyOuYo4Ht/hTY3DyXGIj0U6kHjr2vWQ1lRmearo3t7QKf7uqOAAfTLeWt/g1P8qe/r0DnsNTeag6vI9RVw==
y18n@^3.2.1:
version "3.2.2"