未验证 提交 a50399df 编写于 作者: M Martin Aeschlimann 提交者: GitHub

Merge pull request #90999 from microsoft/aeschli/remoteExtensionRecommendation

recommend resolver extension
......@@ -35,6 +35,7 @@ export enum RemoteAuthorityResolverErrorCode {
Unknown = 'Unknown',
NotAvailable = 'NotAvailable',
TemporarilyNotAvailable = 'TemporarilyNotAvailable',
NoResolverFound = 'NoResolverFound'
}
export class RemoteAuthorityResolverError extends Error {
......@@ -50,10 +51,11 @@ export class RemoteAuthorityResolverError extends Error {
}
public static isTemporarilyNotAvailable(err: any): boolean {
if (err instanceof RemoteAuthorityResolverError) {
return err._code === RemoteAuthorityResolverErrorCode.TemporarilyNotAvailable;
}
return false;
return (err instanceof RemoteAuthorityResolverError) && err._code === RemoteAuthorityResolverErrorCode.TemporarilyNotAvailable;
}
public static isNoResolverFound(err: any): boolean {
return (err instanceof RemoteAuthorityResolverError) && err._code === RemoteAuthorityResolverErrorCode.NoResolverFound;
}
public readonly _message: string | undefined;
......
......@@ -26,7 +26,7 @@ import { Schemas } from 'vs/base/common/network';
import { VSBuffer } from 'vs/base/common/buffer';
import { ExtensionMemento } from 'vs/workbench/api/common/extHostMemento';
import { RemoteAuthorityResolverError } from 'vs/workbench/api/common/extHostTypes';
import { ResolvedAuthority, ResolvedOptions } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { ResolvedAuthority, ResolvedOptions, RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
......@@ -641,7 +641,14 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio
const resolver = this._resolvers[authorityPrefix];
if (!resolver) {
throw new Error(`No remote extension installed to resolve ${authorityPrefix}.`);
return {
type: 'error',
error: {
code: RemoteAuthorityResolverErrorCode.NoResolverFound,
message: `No remote extension installed to resolve ${authorityPrefix}.`,
detail: undefined
}
};
}
try {
......
......@@ -11,8 +11,8 @@ import { AbstractExtensionService } from 'vs/workbench/services/extensions/commo
import * as nls from 'vs/nls';
import { runWhenIdle } from 'vs/base/common/async';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkbenchExtensionEnablementService, EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IInitDataProvider, RemoteExtensionHostClient } from 'vs/workbench/services/extensions/common/remoteExtensionHostClient';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
......@@ -41,6 +41,7 @@ import { Action } from 'vs/base/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { Registry } from 'vs/platform/registry/common/platform';
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
import { getRemoteName } from 'vs/platform/remote/common/remoteHosts';
class DeltaExtensionsQueueItem {
constructor(
......@@ -73,7 +74,8 @@ export class ExtensionService extends AbstractExtensionService implements IExten
@IElectronService private readonly _electronService: IElectronService,
@IHostService private readonly _hostService: IHostService,
@IElectronEnvironmentService private readonly _electronEnvironmentService: IElectronEnvironmentService,
@IRemoteExplorerService private readonly _remoteExplorerService: IRemoteExplorerService
@IRemoteExplorerService private readonly _remoteExplorerService: IRemoteExplorerService,
@IExtensionGalleryService private readonly _extensionGalleryService: IExtensionGalleryService,
) {
super(
instantiationService,
......@@ -441,13 +443,13 @@ export class ExtensionService extends AbstractExtensionService implements IExten
const remoteAuthority = this._environmentService.configuration.remoteAuthority;
const extensionHost = this._extensionHostProcessManagers[0];
let localExtensions = flatten(await Promise.all([this._extensionScanner.scannedExtensions, this._staticExtensions.getExtensions()]));
const allExtensions = flatten(await Promise.all([this._extensionScanner.scannedExtensions, this._staticExtensions.getExtensions()]));
// enable or disable proposed API per extension
this._checkEnableProposedApi(localExtensions);
this._checkEnableProposedApi(allExtensions);
// remove disabled extensions
localExtensions = remove(localExtensions, extension => this._isDisabled(extension));
let localExtensions = remove(allExtensions, extension => this._isDisabled(extension));
if (remoteAuthority) {
let resolvedAuthority: ResolverResult;
......@@ -455,13 +457,16 @@ export class ExtensionService extends AbstractExtensionService implements IExten
try {
resolvedAuthority = await extensionHost.resolveAuthority(remoteAuthority);
} catch (err) {
console.error(err);
const plusIndex = remoteAuthority.indexOf('+');
const authorityFriendlyName = plusIndex > 0 ? remoteAuthority.substr(0, plusIndex) : remoteAuthority;
if (!RemoteAuthorityResolverError.isHandledNotAvailable(err)) {
this._notificationService.notify({ severity: Severity.Error, message: nls.localize('resolveAuthorityFailure', "Resolving the authority `{0}` failed", authorityFriendlyName) });
const remoteName = getRemoteName(remoteAuthority);
if (RemoteAuthorityResolverError.isNoResolverFound(err)) {
this._handleNoResolverFound(remoteName, allExtensions);
} else {
console.log(`Not showing a notification for the error`);
console.log(err);
if (RemoteAuthorityResolverError.isHandledNotAvailable(err)) {
console.log(`Not showing a notification for the error`);
} else {
this._notificationService.notify({ severity: Severity.Error, message: nls.localize('resolveAuthorityFailure', "Resolving the authority `{0}` failed", remoteName) });
}
}
this._remoteAuthorityResolverService.setResolvedAuthorityError(remoteAuthority, err);
......@@ -578,6 +583,66 @@ export class ExtensionService extends AbstractExtensionService implements IExten
this._electronService.closeWindow();
}
}
private async _handleNoResolverFound(remoteName: string, allExtensions: IExtensionDescription[]): Promise<void> {
const recommendation = this._productService.remoteExtensionTips?.[remoteName];
if (!recommendation) {
return;
}
const sendTelemetry = (userReaction: 'install' | 'enable' | 'cancel') => {
/* __GDPR__
"remoteExtensionRecommendations:popup" : {
"userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }
}
*/
this._telemetryService.publicLog('remoteExtensionRecommendations:popup', { userReaction, extensionId: resolverExtensionId });
};
const resolverExtensionId = recommendation.extensionId;
const extension = allExtensions.filter(e => e.identifier.value === resolverExtensionId)[0];
if (extension) {
if (this._isDisabled(extension)) {
const message = nls.localize('enableResolver', "Extension '{0}' is required to open the remote window.\nOk to enable?", recommendation.friendlyName);
this._notificationService.prompt(Severity.Info, message,
[{
label: nls.localize('enable', 'Enable and Reload'),
run: async () => {
sendTelemetry('enable');
await this._extensionEnablementService.setEnablement([toExtension(extension)], EnablementState.EnabledGlobally);
await this._hostService.reload();
}
}],
{ sticky: true }
);
}
} else {
// Install the Extension and reload the window to handle.
const message = nls.localize('installResolver', "Extension '{0}' is required to open the remote window.\nOk to install?", recommendation.friendlyName);
this._notificationService.prompt(Severity.Info, message,
[{
label: nls.localize('install', 'Install and Reload'),
run: async () => {
sendTelemetry('install');
const galleryExtension = await this._extensionGalleryService.getCompatibleExtension({ id: resolverExtensionId });
if (galleryExtension) {
await this._extensionManagementService.installFromGallery(galleryExtension);
await this._hostService.reload();
} else {
this._notificationService.error(nls.localize('resolverExtensionNotFound', "`{0}` not found on marketplace"));
}
}
}],
{
sticky: true,
onCancel: () => sendTelemetry('cancel')
}
);
}
}
}
function remove(arr: IExtensionDescription[], predicate: (item: IExtensionDescription) => boolean): IExtensionDescription[];
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册