mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 21:09:43 +00:00
Merge remote-tracking branch 'origin' into electron-19.x.y
This commit is contained in:
commit
d5dc423bd8
|
@ -15,6 +15,6 @@ module.exports = withDefaults({
|
|||
mainFields: ['module', 'main']
|
||||
},
|
||||
entry: {
|
||||
extension: './src/extension.node.ts',
|
||||
extension: './src/extension.ts',
|
||||
}
|
||||
});
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
"engines": {
|
||||
"vscode": "^1.20.0"
|
||||
},
|
||||
"main": "./out/extension.node",
|
||||
"main": "./out/extension",
|
||||
"browser": "./dist/browser/extension",
|
||||
"categories": [
|
||||
"Programming Languages"
|
||||
|
|
|
@ -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"
|
||||
},
|
||||
|
|
|
@ -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"
|
||||
},
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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==
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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)}`;
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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);
|
||||
|
|
66
src/vs/platform/userDataProfile/browser/userDataProfile.ts
Normal file
66
src/vs/platform/userDataProfile/browser/userDataProfile.ts
Normal 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));
|
||||
}
|
||||
|
||||
}
|
|
@ -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'); }
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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'); }
|
||||
}
|
||||
|
||||
|
|
|
@ -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, {}));
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
//
|
||||
|
|
|
@ -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."));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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."));
|
||||
}
|
||||
|
|
|
@ -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() });
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 });
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -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>;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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}`));
|
||||
|
|
|
@ -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(() => {
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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;
|
||||
|
|
24
yarn.lock
24
yarn.lock
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue