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

github.com/microsoft/vscode.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--extensions/markdown-language-features/extension.webpack.config.js2
-rw-r--r--extensions/markdown-language-features/package.json2
-rw-r--r--extensions/markdown-language-features/src/extension.ts (renamed from extensions/markdown-language-features/src/extension.node.ts)0
-rw-r--r--package.json6
-rw-r--r--remote/package.json6
-rw-r--r--remote/web/package.json4
-rw-r--r--remote/web/yarn.lock18
-rw-r--r--remote/yarn.lock28
-rw-r--r--src/vs/base/browser/ui/button/button.css9
-rw-r--r--src/vs/base/browser/ui/button/button.ts13
-rw-r--r--src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts2
-rw-r--r--src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts115
-rw-r--r--src/vs/platform/extensionManagement/common/extensionsScannerService.ts45
-rw-r--r--src/vs/platform/extensionManagement/electron-sandbox/extensionsScannerService.ts4
-rw-r--r--src/vs/platform/extensionManagement/node/extensionManagementService.ts82
-rw-r--r--src/vs/platform/extensionManagement/node/extensionsScannerService.ts4
-rw-r--r--src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts7
-rw-r--r--src/vs/platform/terminal/common/terminalEnvironment.ts23
-rw-r--r--src/vs/platform/terminal/test/common/terminalEnvironment.test.ts40
-rw-r--r--src/vs/platform/userData/test/browser/fileUserDataProvider.test.ts3
-rw-r--r--src/vs/platform/userDataProfile/browser/userDataProfile.ts66
-rw-r--r--src/vs/platform/userDataProfile/common/userDataProfile.ts249
-rw-r--r--src/vs/platform/userDataProfile/electron-main/userDataProfile.ts160
-rw-r--r--src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts34
-rw-r--r--src/vs/platform/userDataProfile/node/userDataProfile.ts88
-rw-r--r--src/vs/platform/userDataProfile/test/common/userDataProfileService.test.ts3
-rw-r--r--src/vs/platform/userDataSync/common/userDataSync.ts2
-rw-r--r--src/vs/platform/userDataSync/common/userDataSyncStoreService.ts4
-rw-r--r--src/vs/platform/userDataSync/test/common/userDataSyncClient.ts6
-rw-r--r--src/vs/server/node/extensionsScannerService.ts4
-rw-r--r--src/vs/server/node/remoteExtensionHostAgentCli.ts6
-rw-r--r--src/vs/server/node/serverServices.ts9
-rw-r--r--src/vs/workbench/browser/web.main.ts19
-rw-r--r--src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts32
-rw-r--r--src/vs/workbench/contrib/editSessions/browser/editSessionsWorkbenchService.ts31
-rw-r--r--src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts4
-rw-r--r--src/vs/workbench/contrib/markers/browser/markersTable.ts9
-rw-r--r--src/vs/workbench/contrib/markers/browser/media/markers.css11
-rw-r--r--src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts40
-rw-r--r--src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts14
-rwxr-xr-xsrc/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh36
-rw-r--r--src/vs/workbench/contrib/terminal/browser/terminalInstance.ts21
-rw-r--r--src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts3
-rw-r--r--src/vs/workbench/contrib/terminal/common/terminalContextKey.ts4
-rw-r--r--src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts20
-rw-r--r--src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts13
-rw-r--r--src/vs/workbench/electron-sandbox/desktop.main.ts2
-rw-r--r--src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts5
-rw-r--r--src/vs/workbench/services/configuration/test/browser/configurationService.test.ts50
-rw-r--r--src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts100
-rw-r--r--src/vs/workbench/services/extensionManagement/common/extensionManagement.ts15
-rw-r--r--src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts64
-rw-r--r--src/vs/workbench/services/extensions/browser/extensionService.ts7
-rw-r--r--src/vs/workbench/services/extensions/common/abstractExtensionService.ts4
-rw-r--r--src/vs/workbench/services/extensions/electron-sandbox/electronExtensionService.ts5
-rw-r--r--src/vs/workbench/services/extensions/test/browser/extensionService.test.ts10
-rw-r--r--src/vs/workbench/services/extensions/test/browser/extensionStorageMigration.test.ts3
-rw-r--r--src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts3
-rw-r--r--src/vs/workbench/services/storage/test/browser/storageService.test.ts3
-rw-r--r--src/vs/workbench/services/userDataProfile/common/userDataProfile.ts4
-rw-r--r--src/vs/workbench/services/userDataProfile/common/userDataProfileService.ts5
-rw-r--r--src/vs/workbench/test/browser/workbenchTestServices.ts15
-rw-r--r--src/vs/workbench/test/electron-browser/workbenchTestServices.ts4
-rw-r--r--yarn.lock28
64 files changed, 999 insertions, 629 deletions
diff --git a/extensions/markdown-language-features/extension.webpack.config.js b/extensions/markdown-language-features/extension.webpack.config.js
index 756d6735a60..de88398eca0 100644
--- a/extensions/markdown-language-features/extension.webpack.config.js
+++ b/extensions/markdown-language-features/extension.webpack.config.js
@@ -15,6 +15,6 @@ module.exports = withDefaults({
mainFields: ['module', 'main']
},
entry: {
- extension: './src/extension.node.ts',
+ extension: './src/extension.ts',
}
});
diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json
index ec4b4d19e62..8cee84a8d74 100644
--- a/extensions/markdown-language-features/package.json
+++ b/extensions/markdown-language-features/package.json
@@ -10,7 +10,7 @@
"engines": {
"vscode": "^1.20.0"
},
- "main": "./out/extension.node",
+ "main": "./out/extension",
"browser": "./dist/browser/extension",
"categories": [
"Programming Languages"
diff --git a/extensions/markdown-language-features/src/extension.node.ts b/extensions/markdown-language-features/src/extension.ts
index 45dba90d3ca..45dba90d3ca 100644
--- a/extensions/markdown-language-features/src/extension.node.ts
+++ b/extensions/markdown-language-features/src/extension.ts
diff --git a/package.json b/package.json
index d57d78b4625..b2590fdc25d 100644
--- a/package.json
+++ b/package.json
@@ -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"
},
diff --git a/remote/package.json b/remote/package.json
index 8da57bba60b..16d1d06eb07 100644
--- a/remote/package.json
+++ b/remote/package.json
@@ -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"
},
diff --git a/remote/web/package.json b/remote/web/package.json
index c3a32f4d4ec..9d6478ab4c7 100644
--- a/remote/web/package.json
+++ b/remote/web/package.json
@@ -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"
}
}
diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock
index 75a41e50c82..590ab4f8c93 100644
--- a/remote/web/yarn.lock
+++ b/remote/web/yarn.lock
@@ -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@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-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.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==
diff --git a/remote/yarn.lock b/remote/yarn.lock
index bc084bb74dc..444fb613090 100644
--- a/remote/yarn.lock
+++ b/remote/yarn.lock
@@ -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-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@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-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.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.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"
diff --git a/src/vs/base/browser/ui/button/button.css b/src/vs/base/browser/ui/button/button.css
index 5327fb0e1e3..66e61c3c398 100644
--- a/src/vs/base/browser/ui/button/button.css
+++ b/src/vs/base/browser/ui/button/button.css
@@ -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 {
diff --git a/src/vs/base/browser/ui/button/button.ts b/src/vs/base/browser/ui/button/button.ts
index 3187254843b..4ae8b9d6239 100644
--- a/src/vs/base/browser/ui/button/button.ts
+++ b/src/vs/base/browser/ui/button/button.ts
@@ -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 {
diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts
index 9cf505e2470..7c128863922 100644
--- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts
+++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts
@@ -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
diff --git a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts
index 66e250a2df1..0e19fa5b37b 100644
--- a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts
+++ b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts
@@ -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;
- }
-
- private createWaitUntilInstallExtensionTaskIsFinishedTask(installTask: IInstallExtensionTask, options: ServerInstallOptions & ServerInstallVSIXOptions): IInstallExtensionTask {
- if (!options.profileLocation || !this.userDataProfilesService.defaultProfile.extensionsResource) {
- return installTask;
- }
- if (installTask instanceof InstallExtensionInProfileTask && this.uriIdenityService.extUri.isEqual(installTask.profileLocation, options.profileLocation)) {
- return installTask;
+ if (options.profileLocation && isApplicationScopedExtension(manifest)) {
+ options = { ...options, profileLocation: this.userDataProfilesService.defaultProfile.extensionsResource };
}
- return new InstallExtensionInProfileTask(installTask, options.profileLocation, this.userDataProfilesService.defaultProfile.extensionsResource, this.extensionsProfileScannerService);
+ return this.doCreateInstallExtensionTask(manifest, extension, options);
}
- 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);
+ private createUninstallExtensionTask(extension: ILocalExtension, options: ServerUninstallOptions): IUninstallExtensionTask {
+ if (options.profileLocation && extension.isApplicationScoped) {
+ options = { ...options, profileLocation: this.userDataProfilesService.defaultProfile.extensionsResource };
+ }
+ 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);
- }
-
-}
diff --git a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts
index 823a0be2541..194ff9965ae 100644
--- a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts
+++ b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts
@@ -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 {
diff --git a/src/vs/platform/extensionManagement/electron-sandbox/extensionsScannerService.ts b/src/vs/platform/extensionManagement/electron-sandbox/extensionsScannerService.ts
index cfc0d5dacf3..9503799fef8 100644
--- a/src/vs/platform/extensionManagement/electron-sandbox/extensionsScannerService.ts
+++ b/src/vs/platform/extensionManagement/electron-sandbox/extensionsScannerService.ts
@@ -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);
}
}
diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts
index 56ea181d99e..a5f367b4124 100644
--- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts
+++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts
@@ -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);
+ }
+
+}
+
diff --git a/src/vs/platform/extensionManagement/node/extensionsScannerService.ts b/src/vs/platform/extensionManagement/node/extensionsScannerService.ts
index 9d90bf687d6..20a1fd0a61d 100644
--- a/src/vs/platform/extensionManagement/node/extensionsScannerService.ts
+++ b/src/vs/platform/extensionManagement/node/extensionsScannerService.ts
@@ -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);
}
}
diff --git a/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts b/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts
index cdfaa38b842..b0a7ec8ad9a 100644
--- a/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts
+++ b/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts
@@ -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);
});
diff --git a/src/vs/platform/terminal/common/terminalEnvironment.ts b/src/vs/platform/terminal/common/terminalEnvironment.ts
index 66b5ad31a3f..1d24a24f60d 100644
--- a/src/vs/platform/terminal/common/terminalEnvironment.ts
+++ b/src/vs/platform/terminal/common/terminalEnvironment.ts
@@ -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)}`;
+}
diff --git a/src/vs/platform/terminal/test/common/terminalEnvironment.test.ts b/src/vs/platform/terminal/test/common/terminalEnvironment.test.ts
new file mode 100644
index 00000000000..2c58f9ec1fd
--- /dev/null
+++ b/src/vs/platform/terminal/test/common/terminalEnvironment.test.ts
@@ -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');
+ });
+ });
+});
diff --git a/src/vs/platform/userData/test/browser/fileUserDataProvider.test.ts b/src/vs/platform/userData/test/browser/fileUserDataProvider.test.ts
index f6a9b0d6993..58fd53e21ec 100644
--- a/src/vs/platform/userData/test/browser/fileUserDataProvider.test.ts
+++ b/src/vs/platform/userData/test/browser/fileUserDataProvider.test.ts
@@ -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);
diff --git a/src/vs/platform/userDataProfile/browser/userDataProfile.ts b/src/vs/platform/userDataProfile/browser/userDataProfile.ts
new file mode 100644
index 00000000000..56edfc09480
--- /dev/null
+++ b/src/vs/platform/userDataProfile/browser/userDataProfile.ts
@@ -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));
+ }
+
+}
diff --git a/src/vs/platform/userDataProfile/common/userDataProfile.ts b/src/vs/platform/userDataProfile/common/userDataProfile.ts
index 89a181cda49..53ab77f58ec 100644
--- a/src/vs/platform/userDataProfile/common/userDataProfile.ts
+++ b/src/vs/platform/userDataProfile/common/userDataProfile.ts
@@ -14,7 +14,11 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
import { IFileService } from 'vs/platform/files/common/files';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ILogService } from 'vs/platform/log/common/log';
-import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace';
+import { ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace';
+import { ResourceMap } from 'vs/base/common/map';
+import { IStringDictionary } from 'vs/base/common/collections';
+import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
+import { Promises } from 'vs/base/common/async';
/**
* Flags to indicate whether to use the default profile or not.
@@ -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;
+ }
+ }
+
+ 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;
}
- 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 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'); }
}
diff --git a/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts b/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts
index e52855d4867..0ac191f058a 100644
--- a/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts
+++ b/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts
@@ -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 {
diff --git a/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts b/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts
index 1666539da3e..085e035b676 100644
--- a/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts
+++ b/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts
@@ -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'); }
}
diff --git a/src/vs/platform/userDataProfile/node/userDataProfile.ts b/src/vs/platform/userDataProfile/node/userDataProfile.ts
index 34428dd9689..1405ca08e76 100644
--- a/src/vs/platform/userDataProfile/node/userDataProfile.ts
+++ b/src/vs/platform/userDataProfile/node/userDataProfile.ts
@@ -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);
- }
-
- 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';
+ super(environmentService, fileService, uriIdentityService, logService);
}
- 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, {}));
}
diff --git a/src/vs/platform/userDataProfile/test/common/userDataProfileService.test.ts b/src/vs/platform/userDataProfile/test/common/userDataProfileService.test.ts
index 549c686644c..662a70fe033 100644
--- a/src/vs/platform/userDataProfile/test/common/userDataProfileService.test.ts
+++ b/src/vs/platform/userDataProfile/test/common/userDataProfileService.test.ts
@@ -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());
diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts
index ec0a973bf60..6973141914f 100644
--- a/src/vs/platform/userDataSync/common/userDataSync.ts
+++ b/src/vs/platform/userDataSync/common/userDataSync.ts
@@ -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');
diff --git a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts
index 427babbd4f6..2b7bd79e2b8 100644
--- a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts
+++ b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts
@@ -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);
diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts
index 645181d8a3c..2252b873b35 100644
--- a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts
+++ b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts
@@ -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);
diff --git a/src/vs/server/node/extensionsScannerService.ts b/src/vs/server/node/extensionsScannerService.ts
index 5f2be934b4b..b780ab7d6e8 100644
--- a/src/vs/server/node/extensionsScannerService.ts
+++ b/src/vs/server/node/extensionsScannerService.ts
@@ -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> {
diff --git a/src/vs/server/node/remoteExtensionHostAgentCli.ts b/src/vs/server/node/remoteExtensionHostAgentCli.ts
index 653748724e0..52647624f79 100644
--- a/src/vs/server/node/remoteExtensionHostAgentCli.ts
+++ b/src/vs/server/node/remoteExtensionHostAgentCli.ts
@@ -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);
diff --git a/src/vs/server/node/serverServices.ts b/src/vs/server/node/serverServices.ts
index c20bd776c55..ab5f5cb7d57 100644
--- a/src/vs/server/node/serverServices.ts
+++ b/src/vs/server/node/serverServices.ts
@@ -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));
diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts
index e9b7f4fc8b0..d0791572ccf 100644
--- a/src/vs/workbench/browser/web.main.ts
+++ b/src/vs/workbench/browser/web.main.ts
@@ -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));
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
diff --git a/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts b/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts
index deb0cb486bc..12f620df779 100644
--- a/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts
+++ b/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts
@@ -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."));
}
}
diff --git a/src/vs/workbench/contrib/editSessions/browser/editSessionsWorkbenchService.ts b/src/vs/workbench/contrib/editSessions/browser/editSessionsWorkbenchService.ts
index af25b5ee15a..b63b550ab35 100644
--- a/src/vs/workbench/contrib/editSessions/browser/editSessionsWorkbenchService.ts
+++ b/src/vs/workbench/contrib/editSessions/browser/editSessionsWorkbenchService.ts
@@ -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();
+ }
}
}));
}
diff --git a/src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts b/src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts
index b7caca6f077..07917f285e5 100644
--- a/src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts
+++ b/src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts
@@ -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);
diff --git a/src/vs/workbench/contrib/markers/browser/markersTable.ts b/src/vs/workbench/contrib/markers/browser/markersTable.ts
index a4938d08d58..4d39773daed 100644
--- a/src/vs/workbench/contrib/markers/browser/markersTable.ts
+++ b/src/vs/workbench/contrib/markers/browser/markersTable.ts
@@ -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);
}
}
diff --git a/src/vs/workbench/contrib/markers/browser/media/markers.css b/src/vs/workbench/contrib/markers/browser/media/markers.css
index 4ce704bb880..4a47972350e 100644
--- a/src/vs/workbench/contrib/markers/browser/media/markers.css
+++ b/src/vs/workbench/contrib/markers/browser/media/markers.css
@@ -279,17 +279,18 @@
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-link > .monaco-link {
+.markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td > .code.code-label > .code-label {
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 > .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 > .file > .file-position {
diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts
index ed23a5f2632..a75b4184bd2 100644
--- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts
+++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts
@@ -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';
@@ -377,6 +379,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({
id: ToggleSearchEditorCaseSensitiveCommandId,
title: { value: localize('searchEditor.action.toggleSearchEditorCaseSensitive', "Toggle Match Case"), original: 'Toggle Match Case' },
category,
diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts
index 380f8dbcc8f..54be4156b6e 100644
--- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts
+++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts
@@ -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) {
diff --git a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh
index 88498fbfb9a..a2f4afa1400 100755
--- a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh
+++ b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh
@@ -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
diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts
index e4187f6ab3c..b791e1e8472 100644
--- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts
+++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts
@@ -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;
diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts
index c9da9557b27..a7732ab7c62 100644
--- a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts
+++ b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts
@@ -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
diff --git a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts
index 72f1792fc71..1d292d45536 100644
--- a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts
+++ b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts
@@ -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."));
}
diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts
index 52d57770b21..e9d136fdc9f 100644
--- a/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts
+++ b/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts
@@ -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() });
}
/**
diff --git a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts
index 44aa262be18..13f2e154c6d 100644
--- a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts
+++ b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts
@@ -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)
diff --git a/src/vs/workbench/electron-sandbox/desktop.main.ts b/src/vs/workbench/electron-sandbox/desktop.main.ts
index f5878699fdf..4986c87d42a 100644
--- a/src/vs/workbench/electron-sandbox/desktop.main.ts
+++ b/src/vs/workbench/electron-sandbox/desktop.main.ts
@@ -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);
diff --git a/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts
index 6412bd0e4c3..867ff2532bf 100644
--- a/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts
+++ b/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts
@@ -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
diff --git a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts
index 41573109ed5..fb05bed96f1 100644
--- a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts
+++ b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts
@@ -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);
diff --git a/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts b/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts
index fb5c4babf4a..bc5e140d42a 100644
--- a/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts
+++ b/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts
@@ -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 });
}
}));
}
diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts
index 9c9683c1c3b..1cad71d1c3a 100644
--- a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts
+++ b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts
@@ -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>;
}
diff --git a/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts
index 2c371645082..23413b1b130 100644
--- a/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts
+++ b/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts
@@ -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);
}
}
diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts
index acdf6ca2308..8c48d48d131 100644
--- a/src/vs/workbench/services/extensions/browser/extensionService.ts
+++ b/src/vs/workbench/services/extensions/browser/extensionService.ts
@@ -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);
}
diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts
index 76589d23182..057e5467ee9 100644
--- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts
+++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts
@@ -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) {
diff --git a/src/vs/workbench/services/extensions/electron-sandbox/electronExtensionService.ts b/src/vs/workbench/services/extensions/electron-sandbox/electronExtensionService.ts
index 3c267c40962..87e9f45b545 100644
--- a/src/vs/workbench/services/extensions/electron-sandbox/electronExtensionService.ts
+++ b/src/vs/workbench/services/extensions/electron-sandbox/electronExtensionService.ts
@@ -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();
diff --git a/src/vs/workbench/services/extensions/test/browser/extensionService.test.ts b/src/vs/workbench/services/extensions/test/browser/extensionService.test.ts
index 18d684cacaa..eaa0069f098 100644
--- a/src/vs/workbench/services/extensions/test/browser/extensionService.test.ts
+++ b/src/vs/workbench/services/extensions/test/browser/extensionService.test.ts
@@ -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);
});
diff --git a/src/vs/workbench/services/extensions/test/browser/extensionStorageMigration.test.ts b/src/vs/workbench/services/extensions/test/browser/extensionStorageMigration.test.ts
index d6843185c30..a02b86def5c 100644
--- a/src/vs/workbench/services/extensions/test/browser/extensionStorageMigration.test.ts
+++ b/src/vs/workbench/services/extensions/test/browser/extensionStorageMigration.test.ts
@@ -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));
diff --git a/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts b/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts
index a20bd496d4b..40e00516362 100644
--- a/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts
+++ b/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts
@@ -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({
diff --git a/src/vs/workbench/services/storage/test/browser/storageService.test.ts b/src/vs/workbench/services/storage/test/browser/storageService.test.ts
index 4205c92d7a8..8491d3f9301 100644
--- a/src/vs/workbench/services/storage/test/browser/storageService.test.ts
+++ b/src/vs/workbench/services/storage/test/browser/storageService.test.ts
@@ -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();
diff --git a/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts b/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts
index bed76364445..6e09fd1d153 100644
--- a/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts
+++ b/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts
@@ -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}`));
diff --git a/src/vs/workbench/services/userDataProfile/common/userDataProfileService.ts b/src/vs/workbench/services/userDataProfile/common/userDataProfileService.ts
index 57c6e4161a2..e03387a0b29 100644
--- a/src/vs/workbench/services/userDataProfile/common/userDataProfileService.ts
+++ b/src/vs/workbench/services/userDataProfile/common/userDataProfileService.ts
@@ -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(() => {
diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts
index 1a2209c0e43..9b0dc8d9c43 100644
--- a/src/vs/workbench/test/browser/workbenchTestServices.ts
+++ b/src/vs/workbench/test/browser/workbenchTestServices.ts
@@ -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> {
diff --git a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts
index 1880f3377d6..3a35d0f4018 100644
--- a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts
+++ b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts
@@ -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;
diff --git a/yarn.lock b/yarn.lock
index b0e565c38b0..68918b4a06a 100644
--- a/yarn.lock
+++ b/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-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@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-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.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.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"