Merge pull request #100729 from microsoft/alex/extension-host
This commit is contained in:
commit
45a222d4c5
|
@ -47,6 +47,7 @@ const nodeModules = ['electron', 'original-fs']
|
|||
const vscodeEntryPoints = _.flatten([
|
||||
buildfile.entrypoint('vs/workbench/workbench.desktop.main'),
|
||||
buildfile.base,
|
||||
buildfile.workerExtensionHost,
|
||||
buildfile.workbenchDesktop,
|
||||
buildfile.code
|
||||
]);
|
||||
|
@ -72,6 +73,7 @@ const vscodeResources = [
|
|||
'out-build/vs/workbench/contrib/externalTerminal/**/*.scpt',
|
||||
'out-build/vs/workbench/contrib/webview/browser/pre/*.js',
|
||||
'out-build/vs/workbench/contrib/webview/electron-browser/pre/*.js',
|
||||
'out-build/vs/workbench/services/extensions/worker/extensionHostWorkerMain.js',
|
||||
'out-build/vs/**/markdown.css',
|
||||
'out-build/vs/workbench/contrib/tasks/**/*.json',
|
||||
'out-build/vs/platform/files/**/*.exe',
|
||||
|
|
|
@ -194,6 +194,11 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
|
|||
type: 'array',
|
||||
description: localize('handleUriConfirmedExtensions', "When an extension is listed here, a confirmation prompt will not be shown when that extension handles a URI."),
|
||||
default: []
|
||||
},
|
||||
'extensions.webWorker': {
|
||||
type: 'boolean',
|
||||
description: localize('extensionsWebWorker', "Enable web worker extension host."),
|
||||
default: false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -18,6 +18,7 @@ import { getExtensionKind } from 'vs/workbench/services/extensions/common/extens
|
|||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { StorageManager } from 'vs/platform/extensionManagement/common/extensionEnablementService';
|
||||
import { webWorkerExtHostConfig } from 'vs/workbench/services/extensions/common/extensions';
|
||||
|
||||
const SOURCE = 'IWorkbenchExtensionEnablementService';
|
||||
|
||||
|
@ -151,8 +152,13 @@ export class ExtensionEnablementService extends Disposable implements IWorkbench
|
|||
}
|
||||
}
|
||||
if (extensionKind === 'web') {
|
||||
// Web extensions are not yet supported to be disabled by kind. Enable them always on web.
|
||||
const enableLocalWebWorker = this.configurationService.getValue<boolean>(webWorkerExtHostConfig);
|
||||
if (enableLocalWebWorker) {
|
||||
// Web extensions are enabled on all configurations
|
||||
return false;
|
||||
}
|
||||
if (this.extensionManagementServerService.localExtensionManagementServer === null) {
|
||||
// Web extensions run only in the web
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,11 +14,9 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
|||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { AbstractExtensionService, parseScannedExtension } from 'vs/workbench/services/extensions/common/abstractExtensionService';
|
||||
import { RemoteExtensionHost, IInitDataProvider } from 'vs/workbench/services/extensions/common/remoteExtensionHost';
|
||||
import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment';
|
||||
import { RemoteExtensionHost, IRemoteExtensionHostDataProvider, IRemoteExtensionHostInitData } from 'vs/workbench/services/extensions/common/remoteExtensionHost';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { WebWorkerExtensionHost } from 'vs/workbench/services/extensions/browser/webWorkerExtensionHost';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { canExecuteOnWeb } from 'vs/workbench/services/extensions/common/extensionsUtil';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
|
@ -31,7 +29,7 @@ import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remot
|
|||
export class ExtensionService extends AbstractExtensionService implements IExtensionService {
|
||||
|
||||
private _disposables = new DisposableStore();
|
||||
private _remoteExtensionsEnvironmentData: IRemoteAgentEnvironment | null = null;
|
||||
private _remoteInitData: IRemoteExtensionHostInitData | null = null;
|
||||
|
||||
constructor(
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
|
@ -71,14 +69,25 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
|||
this._disposables.add(this._fileService.registerProvider(Schemas.https, provider));
|
||||
}
|
||||
|
||||
private _createProvider(remoteAuthority: string): IInitDataProvider {
|
||||
private _createLocalExtensionHostDataProvider() {
|
||||
return {
|
||||
getInitData: async () => {
|
||||
const allExtensions = await this.getExtensions();
|
||||
const webExtensions = allExtensions.filter(ext => canExecuteOnWeb(ext, this._productService, this._configService));
|
||||
return {
|
||||
autoStart: true,
|
||||
extensions: webExtensions
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private _createRemoteExtensionHostDataProvider(remoteAuthority: string): IRemoteExtensionHostDataProvider {
|
||||
return {
|
||||
remoteAuthority: remoteAuthority,
|
||||
getInitData: async () => {
|
||||
await this.whenInstalledExtensionsRegistered();
|
||||
const connectionData = this._remoteAuthorityResolverService.getConnectionData(remoteAuthority);
|
||||
const remoteEnvironment = this._remoteExtensionsEnvironmentData!;
|
||||
return { connectionData, remoteEnvironment };
|
||||
return this._remoteInitData!;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -86,14 +95,12 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
|||
protected _createExtensionHosts(_isInitialStart: boolean): IExtensionHost[] {
|
||||
const result: IExtensionHost[] = [];
|
||||
|
||||
const webExtensions = this.getExtensions().then(extensions => extensions.filter(ext => canExecuteOnWeb(ext, this._productService, this._configService)));
|
||||
const webWorkerExtHost = this._instantiationService.createInstance(WebWorkerExtensionHost, webExtensions, URI.file(this._environmentService.logsPath).with({ scheme: this._environmentService.logFile.scheme }));
|
||||
const webWorkerExtHost = this._instantiationService.createInstance(WebWorkerExtensionHost, this._createLocalExtensionHostDataProvider());
|
||||
result.push(webWorkerExtHost);
|
||||
|
||||
const remoteAgentConnection = this._remoteAgentService.getConnection();
|
||||
if (remoteAgentConnection) {
|
||||
const remoteExtensions = this.getExtensions().then(extensions => extensions.filter(ext => !canExecuteOnWeb(ext, this._productService, this._configService)));
|
||||
const remoteExtHost = this._instantiationService.createInstance(RemoteExtensionHost, remoteExtensions, this._createProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory);
|
||||
const remoteExtHost = this._instantiationService.createInstance(RemoteExtensionHost, this._createRemoteExtensionHostDataProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory);
|
||||
result.push(remoteExtHost);
|
||||
}
|
||||
|
||||
|
@ -107,13 +114,15 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
|||
this._webExtensionsScannerService.scanExtensions().then(extensions => extensions.map(parseScannedExtension))
|
||||
]);
|
||||
|
||||
const remoteAgentConnection = this._remoteAgentService.getConnection();
|
||||
|
||||
let result: DeltaExtensionsResult;
|
||||
|
||||
// local: only enabled and web'ish extension
|
||||
localExtensions = localExtensions!.filter(ext => this._isEnabled(ext) && canExecuteOnWeb(ext, this._productService, this._configService));
|
||||
this._checkEnableProposedApi(localExtensions);
|
||||
|
||||
if (!remoteEnv) {
|
||||
if (!remoteEnv || !remoteAgentConnection) {
|
||||
result = this._registry.deltaExtensions(localExtensions, []);
|
||||
|
||||
} else {
|
||||
|
@ -127,7 +136,17 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
|||
localExtensions = localExtensions.filter(extension => !isRemoteExtension.has(ExtensionIdentifier.toKey(extension.identifier)));
|
||||
|
||||
// save for remote extension's init data
|
||||
this._remoteExtensionsEnvironmentData = remoteEnv;
|
||||
this._remoteInitData = {
|
||||
connectionData: this._remoteAuthorityResolverService.getConnectionData(remoteAgentConnection.remoteAuthority),
|
||||
pid: remoteEnv.pid,
|
||||
appRoot: remoteEnv.appRoot,
|
||||
appSettingsHome: remoteEnv.appSettingsHome,
|
||||
extensionHostLogsPath: remoteEnv.extensionHostLogsPath,
|
||||
globalStorageHome: remoteEnv.globalStorageHome,
|
||||
userHome: remoteEnv.userHome,
|
||||
extensions: remoteEnv.extensions,
|
||||
allExtensions: remoteEnv.extensions.concat(localExtensions)
|
||||
};
|
||||
|
||||
result = this._registry.deltaExtensions(remoteEnv.extensions.concat(localExtensions), []);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,15 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
|||
import { IOutputChannelRegistry, Extensions } from 'vs/workbench/services/output/common/output';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
export interface IWebWorkerExtensionHostInitData {
|
||||
readonly autoStart: boolean;
|
||||
readonly extensions: IExtensionDescription[];
|
||||
}
|
||||
|
||||
export interface IWebWorkerExtensionHostDataProvider {
|
||||
getInitData(): Promise<IWebWorkerExtensionHostInitData>;
|
||||
}
|
||||
|
||||
export class WebWorkerExtensionHost implements IExtensionHost {
|
||||
|
||||
public readonly kind = ExtensionHostKind.LocalWebWorker;
|
||||
|
@ -37,11 +46,11 @@ export class WebWorkerExtensionHost implements IExtensionHost {
|
|||
private readonly _onDidExit = new Emitter<[number, string | null]>();
|
||||
readonly onExit: Event<[number, string | null]> = this._onDidExit.event;
|
||||
|
||||
private readonly _extensionHostLogsLocation: URI;
|
||||
private readonly _extensionHostLogFile: URI;
|
||||
|
||||
constructor(
|
||||
private readonly _extensions: Promise<IExtensionDescription[]>,
|
||||
private readonly _extensionHostLogsLocation: URI,
|
||||
private readonly _initDataProvider: IWebWorkerExtensionHostDataProvider,
|
||||
@ITelemetryService private readonly _telemetryService: ITelemetryService,
|
||||
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,
|
||||
@ILabelService private readonly _labelService: ILabelService,
|
||||
|
@ -49,6 +58,7 @@ export class WebWorkerExtensionHost implements IExtensionHost {
|
|||
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService,
|
||||
@IProductService private readonly _productService: IProductService,
|
||||
) {
|
||||
this._extensionHostLogsLocation = URI.file(this._environmentService.logsPath).with({ scheme: this._environmentService.logFile.scheme });
|
||||
this._extensionHostLogFile = joinPath(this._extensionHostLogsLocation, `${ExtensionHostLogFileName}.log`);
|
||||
}
|
||||
|
||||
|
@ -129,7 +139,7 @@ export class WebWorkerExtensionHost implements IExtensionHost {
|
|||
}
|
||||
|
||||
private async _createExtHostInitData(): Promise<IInitData> {
|
||||
const [telemetryInfo, extensionDescriptions] = await Promise.all([this._telemetryService.getTelemetryInfo(), this._extensions]);
|
||||
const [telemetryInfo, initData] = await Promise.all([this._telemetryService.getTelemetryInfo(), this._initDataProvider.getInitData()]);
|
||||
const workspace = this._contextService.getWorkspace();
|
||||
return {
|
||||
commit: this._productService.commit,
|
||||
|
@ -154,12 +164,12 @@ export class WebWorkerExtensionHost implements IExtensionHost {
|
|||
},
|
||||
resolvedExtensions: [],
|
||||
hostExtensions: [],
|
||||
extensions: extensionDescriptions,
|
||||
extensions: initData.extensions,
|
||||
telemetryInfo,
|
||||
logLevel: this._logService.getLevel(),
|
||||
logsLocation: this._extensionHostLogsLocation,
|
||||
logFile: this._extensionHostLogFile,
|
||||
autoStart: true,
|
||||
autoStart: initData.autoStart,
|
||||
remote: {
|
||||
authority: this._environmentService.configuration.remoteAuthority,
|
||||
connectionData: null,
|
||||
|
|
|
@ -286,6 +286,14 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
|
|||
}
|
||||
}
|
||||
|
||||
protected _checkEnabledAndProposedAPI(extensions: IExtensionDescription[]): IExtensionDescription[] {
|
||||
// enable or disable proposed API per extension
|
||||
this._checkEnableProposedApi(extensions);
|
||||
|
||||
// keep only enabled extensions
|
||||
return extensions.filter(extension => this._isEnabled(extension));
|
||||
}
|
||||
|
||||
private _isExtensionUnderDevelopment(extension: IExtensionDescription): boolean {
|
||||
if (this._environmentService.isExtensionDevelopment) {
|
||||
const extDevLocs = this._environmentService.extensionDevelopmentLocationURI;
|
||||
|
@ -302,21 +310,17 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
|
|||
}
|
||||
|
||||
protected _isEnabled(extension: IExtensionDescription): boolean {
|
||||
return !this._isDisabled(extension);
|
||||
}
|
||||
|
||||
protected _isDisabled(extension: IExtensionDescription): boolean {
|
||||
if (this._isExtensionUnderDevelopment(extension)) {
|
||||
// Never disable extensions under development
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ExtensionIdentifier.equals(extension.identifier, BetterMergeId)) {
|
||||
// Check if this is the better merge extension which was migrated to a built-in extension
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return !this._extensionEnablementService.isEnabled(toExtension(extension));
|
||||
return this._extensionEnablementService.isEnabled(toExtension(extension));
|
||||
}
|
||||
|
||||
protected _doHandleExtensionPoints(affectedExtensions: IExtensionDescription[]): void {
|
||||
|
|
|
@ -24,6 +24,8 @@ export const nullExtensionDescription = Object.freeze(<IExtensionDescription>{
|
|||
isBuiltin: false,
|
||||
});
|
||||
|
||||
export const webWorkerExtHostConfig = 'extensions.webWorker';
|
||||
|
||||
export const IExtensionService = createDecorator<IExtensionService>('extensionService');
|
||||
|
||||
export interface IMessage {
|
||||
|
|
|
@ -15,7 +15,6 @@ import { IInitData, UIKind } from 'vs/workbench/api/common/extHost.protocol';
|
|||
import { MessageType, createMessageOfType, isMessageOfType } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
|
||||
import { IExtensionHost, ExtensionHostLogFileName, ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions';
|
||||
import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment';
|
||||
import { IRemoteAuthorityResolverService, IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
@ -33,14 +32,21 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
|||
import { IOutputChannelRegistry, Extensions } from 'vs/workbench/services/output/common/output';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
export interface IRemoteInitData {
|
||||
export interface IRemoteExtensionHostInitData {
|
||||
readonly connectionData: IRemoteConnectionData | null;
|
||||
readonly remoteEnvironment: IRemoteAgentEnvironment;
|
||||
readonly pid: number;
|
||||
readonly appRoot: URI;
|
||||
readonly appSettingsHome: URI;
|
||||
readonly extensionHostLogsPath: URI;
|
||||
readonly globalStorageHome: URI;
|
||||
readonly userHome: URI;
|
||||
readonly extensions: IExtensionDescription[];
|
||||
readonly allExtensions: IExtensionDescription[];
|
||||
}
|
||||
|
||||
export interface IInitDataProvider {
|
||||
export interface IRemoteExtensionHostDataProvider {
|
||||
readonly remoteAuthority: string;
|
||||
getInitData(): Promise<IRemoteInitData>;
|
||||
getInitData(): Promise<IRemoteExtensionHostInitData>;
|
||||
}
|
||||
|
||||
export class RemoteExtensionHost extends Disposable implements IExtensionHost {
|
||||
|
@ -56,8 +62,7 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost {
|
|||
private readonly _isExtensionDevHost: boolean;
|
||||
|
||||
constructor(
|
||||
private readonly _allExtensions: Promise<IExtensionDescription[]>,
|
||||
private readonly _initDataProvider: IInitDataProvider,
|
||||
private readonly _initDataProvider: IRemoteExtensionHostDataProvider,
|
||||
private readonly _socketFactory: ISocketFactory,
|
||||
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,
|
||||
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService,
|
||||
|
@ -196,53 +201,51 @@ export class RemoteExtensionHost extends Disposable implements IExtensionHost {
|
|||
this._onExit.fire([0, null]);
|
||||
}
|
||||
|
||||
private _createExtHostInitData(isExtensionDevelopmentDebug: boolean): Promise<IInitData> {
|
||||
return Promise.all([this._allExtensions, this._telemetryService.getTelemetryInfo(), this._initDataProvider.getInitData()]).then(([allExtensions, telemetryInfo, remoteInitData]) => {
|
||||
// Collect all identifiers for extension ids which can be considered "resolved"
|
||||
const resolvedExtensions = allExtensions.filter(extension => !extension.main).map(extension => extension.identifier);
|
||||
const hostExtensions = allExtensions.filter(extension => extension.main && extension.api === 'none').map(extension => extension.identifier);
|
||||
const workspace = this._contextService.getWorkspace();
|
||||
const remoteEnv = remoteInitData.remoteEnvironment;
|
||||
const r: IInitData = {
|
||||
commit: this._productService.commit,
|
||||
version: this._productService.version,
|
||||
parentPid: remoteEnv.pid,
|
||||
environment: {
|
||||
isExtensionDevelopmentDebug,
|
||||
appRoot: remoteEnv.appRoot,
|
||||
appSettingsHome: remoteEnv.appSettingsHome,
|
||||
appName: this._productService.nameLong,
|
||||
appUriScheme: this._productService.urlProtocol,
|
||||
appLanguage: platform.language,
|
||||
extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI,
|
||||
extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI,
|
||||
globalStorageHome: remoteEnv.globalStorageHome,
|
||||
userHome: remoteEnv.userHome,
|
||||
webviewResourceRoot: this._environmentService.webviewResourceRoot,
|
||||
webviewCspSource: this._environmentService.webviewCspSource,
|
||||
},
|
||||
workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? null : {
|
||||
configuration: workspace.configuration,
|
||||
id: workspace.id,
|
||||
name: this._labelService.getWorkspaceLabel(workspace)
|
||||
},
|
||||
remote: {
|
||||
isRemote: true,
|
||||
authority: this._initDataProvider.remoteAuthority,
|
||||
connectionData: remoteInitData.connectionData
|
||||
},
|
||||
resolvedExtensions: resolvedExtensions,
|
||||
hostExtensions: hostExtensions,
|
||||
extensions: remoteEnv.extensions,
|
||||
telemetryInfo,
|
||||
logLevel: this._logService.getLevel(),
|
||||
logsLocation: remoteEnv.extensionHostLogsPath,
|
||||
logFile: joinPath(remoteEnv.extensionHostLogsPath, `${ExtensionHostLogFileName}.log`),
|
||||
autoStart: true,
|
||||
uiKind: platform.isWeb ? UIKind.Web : UIKind.Desktop
|
||||
};
|
||||
return r;
|
||||
});
|
||||
private async _createExtHostInitData(isExtensionDevelopmentDebug: boolean): Promise<IInitData> {
|
||||
const [telemetryInfo, remoteInitData] = await Promise.all([this._telemetryService.getTelemetryInfo(), this._initDataProvider.getInitData()]);
|
||||
|
||||
// Collect all identifiers for extension ids which can be considered "resolved"
|
||||
const resolvedExtensions = remoteInitData.allExtensions.filter(extension => !extension.main).map(extension => extension.identifier);
|
||||
const hostExtensions = remoteInitData.allExtensions.filter(extension => extension.main && extension.api === 'none').map(extension => extension.identifier);
|
||||
const workspace = this._contextService.getWorkspace();
|
||||
return {
|
||||
commit: this._productService.commit,
|
||||
version: this._productService.version,
|
||||
parentPid: remoteInitData.pid,
|
||||
environment: {
|
||||
isExtensionDevelopmentDebug,
|
||||
appRoot: remoteInitData.appRoot,
|
||||
appSettingsHome: remoteInitData.appSettingsHome,
|
||||
appName: this._productService.nameLong,
|
||||
appUriScheme: this._productService.urlProtocol,
|
||||
appLanguage: platform.language,
|
||||
extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI,
|
||||
extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI,
|
||||
globalStorageHome: remoteInitData.globalStorageHome,
|
||||
userHome: remoteInitData.userHome,
|
||||
webviewResourceRoot: this._environmentService.webviewResourceRoot,
|
||||
webviewCspSource: this._environmentService.webviewCspSource,
|
||||
},
|
||||
workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? null : {
|
||||
configuration: workspace.configuration,
|
||||
id: workspace.id,
|
||||
name: this._labelService.getWorkspaceLabel(workspace)
|
||||
},
|
||||
remote: {
|
||||
isRemote: true,
|
||||
authority: this._initDataProvider.remoteAuthority,
|
||||
connectionData: remoteInitData.connectionData
|
||||
},
|
||||
resolvedExtensions: resolvedExtensions,
|
||||
hostExtensions: hostExtensions,
|
||||
extensions: remoteInitData.extensions,
|
||||
telemetryInfo,
|
||||
logLevel: this._logService.getLevel(),
|
||||
logsLocation: remoteInitData.extensionHostLogsPath,
|
||||
logFile: joinPath(remoteInitData.extensionHostLogsPath, `${ExtensionHostLogFileName}.log`),
|
||||
autoStart: true,
|
||||
uiKind: platform.isWeb ? UIKind.Web : UIKind.Desktop
|
||||
};
|
||||
}
|
||||
|
||||
getInspectPort(): number | undefined {
|
||||
|
|
|
@ -13,17 +13,16 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/
|
|||
import { IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { IWorkbenchExtensionEnablementService, EnablementState, IWebExtensionsScannerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IInitDataProvider, RemoteExtensionHost } from 'vs/workbench/services/extensions/common/remoteExtensionHost';
|
||||
import { IRemoteExtensionHostDataProvider, RemoteExtensionHost, IRemoteExtensionHostInitData } from 'vs/workbench/services/extensions/common/remoteExtensionHost';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { IRemoteAuthorityResolverService, RemoteAuthorityResolverError, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { getExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil';
|
||||
import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { IExtensionService, toExtension, ExtensionHostKind, IExtensionHost } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IExtensionService, toExtension, ExtensionHostKind, IExtensionHost, webWorkerExtHostConfig } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ExtensionHostManager } from 'vs/workbench/services/extensions/common/extensionHostManager';
|
||||
import { ExtensionIdentifier, IExtension, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
@ -40,6 +39,8 @@ 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';
|
||||
import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment';
|
||||
import { WebWorkerExtensionHost } from 'vs/workbench/services/extensions/browser/webWorkerExtensionHost';
|
||||
|
||||
class DeltaExtensionsQueueItem {
|
||||
constructor(
|
||||
|
@ -50,8 +51,9 @@ class DeltaExtensionsQueueItem {
|
|||
|
||||
export class ExtensionService extends AbstractExtensionService implements IExtensionService {
|
||||
|
||||
private readonly _remoteEnvironment: Map<string, IRemoteAgentEnvironment>;
|
||||
|
||||
private readonly _enableLocalWebWorker: boolean;
|
||||
private readonly _remoteInitData: Map<string, IRemoteExtensionHostInitData>;
|
||||
private _runningLocation: Map<string, ExtensionRunningLocation>;
|
||||
private readonly _extensionScanner: CachedExtensionScanner;
|
||||
private _deltaExtensionsQueue: DeltaExtensionsQueueItem[];
|
||||
|
||||
|
@ -84,6 +86,8 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
|||
productService
|
||||
);
|
||||
|
||||
this._enableLocalWebWorker = this._configurationService.getValue<boolean>(webWorkerExtHostConfig);
|
||||
|
||||
if (this._extensionEnablementService.allUserExtensionsDisabled) {
|
||||
this._notificationService.prompt(Severity.Info, nls.localize('extensionsDisabled', "All installed extensions are temporarily disabled. Reload the window to return to the previous state."), [{
|
||||
label: nls.localize('Reload', "Reload"),
|
||||
|
@ -93,7 +97,8 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
|||
}]);
|
||||
}
|
||||
|
||||
this._remoteEnvironment = new Map<string, IRemoteAgentEnvironment>();
|
||||
this._remoteInitData = new Map<string, IRemoteExtensionHostInitData>();
|
||||
this._runningLocation = new Map<string, ExtensionRunningLocation>();
|
||||
|
||||
this._extensionScanner = instantiationService.createInstance(CachedExtensionScanner);
|
||||
this._deltaExtensionsQueue = [];
|
||||
|
@ -143,6 +148,14 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
|||
});
|
||||
}
|
||||
|
||||
private _getExtensionHostManager(kind: ExtensionHostKind): ExtensionHostManager | null {
|
||||
for (const extensionHostManager of this._extensionHostManagers) {
|
||||
if (extensionHostManager.kind === kind) {
|
||||
return extensionHostManager;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//#region deltaExtensions
|
||||
|
||||
|
@ -221,11 +234,12 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
|||
this._checkEnableProposedApi(toAdd);
|
||||
|
||||
// Update extension points
|
||||
this._rehandleExtensionPoints((<IExtensionDescription[]>[]).concat(toAdd).concat(toRemove));
|
||||
this._doHandleExtensionPoints((<IExtensionDescription[]>[]).concat(toAdd).concat(toRemove));
|
||||
|
||||
// Update the extension host
|
||||
if (this._extensionHostManagers.length > 0) {
|
||||
await this._extensionHostManagers[0].deltaExtensions(toAdd, toRemove.map(e => e.identifier));
|
||||
const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess);
|
||||
if (localProcessExtensionHost) {
|
||||
await localProcessExtensionHost.deltaExtensions(toAdd, toRemove.map(e => e.identifier));
|
||||
}
|
||||
|
||||
for (let i = 0; i < toAdd.length; i++) {
|
||||
|
@ -233,15 +247,11 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
|||
}
|
||||
}
|
||||
|
||||
private _rehandleExtensionPoints(extensionDescriptions: IExtensionDescription[]): void {
|
||||
this._doHandleExtensionPoints(extensionDescriptions);
|
||||
}
|
||||
|
||||
public canAddExtension(extensionDescription: IExtensionDescription): boolean {
|
||||
return this._canAddExtension(toExtension(extensionDescription));
|
||||
}
|
||||
|
||||
public _canAddExtension(extension: IExtension): boolean {
|
||||
private _canAddExtension(extension: IExtension): boolean {
|
||||
if (this._environmentService.configuration.remoteAuthority) {
|
||||
return false;
|
||||
}
|
||||
|
@ -339,38 +349,61 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
|||
|
||||
//#endregion
|
||||
|
||||
private _createProvider(remoteAuthority: string): IInitDataProvider {
|
||||
private async _scanAllLocalExtensions(): Promise<IExtensionDescription[]> {
|
||||
return flatten(await Promise.all([
|
||||
this._extensionScanner.scannedExtensions,
|
||||
this._webExtensionsScannerService.scanExtensions().then(extensions => extensions.map(parseScannedExtension))
|
||||
]));
|
||||
}
|
||||
|
||||
private _createLocalExtensionHostDataProvider(isInitialStart: boolean, desiredRunningLocation: ExtensionRunningLocation) {
|
||||
return {
|
||||
getInitData: async () => {
|
||||
if (isInitialStart) {
|
||||
const localExtensions = this._checkEnabledAndProposedAPI(await this._scanAllLocalExtensions());
|
||||
const runningLocation = determineRunningLocation(this._productService, this._configurationService, localExtensions, [], false, this._enableLocalWebWorker);
|
||||
const localProcessExtensions = filterByRunningLocation(localExtensions, runningLocation, desiredRunningLocation);
|
||||
return {
|
||||
autoStart: false,
|
||||
extensions: localProcessExtensions
|
||||
};
|
||||
} else {
|
||||
// restart case
|
||||
const allExtensions = await this.getExtensions();
|
||||
const localProcessExtensions = filterByRunningLocation(allExtensions, this._runningLocation, desiredRunningLocation);
|
||||
return {
|
||||
autoStart: true,
|
||||
extensions: localProcessExtensions
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private _createRemoteExtensionHostDataProvider(remoteAuthority: string): IRemoteExtensionHostDataProvider {
|
||||
return {
|
||||
remoteAuthority: remoteAuthority,
|
||||
getInitData: async () => {
|
||||
await this.whenInstalledExtensionsRegistered();
|
||||
const connectionData = this._remoteAuthorityResolverService.getConnectionData(remoteAuthority);
|
||||
const remoteEnvironment = this._remoteEnvironment.get(remoteAuthority)!;
|
||||
return { connectionData, remoteEnvironment };
|
||||
return this._remoteInitData.get(remoteAuthority)!;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected _createExtensionHosts(isInitialStart: boolean): IExtensionHost[] {
|
||||
let autoStart: boolean;
|
||||
let extensions: Promise<IExtensionDescription[]>;
|
||||
if (isInitialStart) {
|
||||
autoStart = false;
|
||||
extensions = this._extensionScanner.scannedExtensions.then(extensions => extensions.filter(extension => this._isEnabled(extension))); // remove disabled extensions
|
||||
} else {
|
||||
// restart case
|
||||
autoStart = true;
|
||||
extensions = this.getExtensions().then((extensions) => extensions.filter(ext => ext.extensionLocation.scheme === Schemas.file));
|
||||
}
|
||||
|
||||
const result: IExtensionHost[] = [];
|
||||
|
||||
const localProcessExtHost = this._instantiationService.createInstance(LocalProcessExtensionHost, autoStart, extensions, this._environmentService.extHostLogsPath);
|
||||
const localProcessExtHost = this._instantiationService.createInstance(LocalProcessExtensionHost, this._createLocalExtensionHostDataProvider(isInitialStart, ExtensionRunningLocation.LocalProcess));
|
||||
result.push(localProcessExtHost);
|
||||
|
||||
if (this._enableLocalWebWorker) {
|
||||
const webWorkerExtHost = this._instantiationService.createInstance(WebWorkerExtensionHost, this._createLocalExtensionHostDataProvider(isInitialStart, ExtensionRunningLocation.LocalWebWorker));
|
||||
result.push(webWorkerExtHost);
|
||||
}
|
||||
|
||||
const remoteAgentConnection = this._remoteAgentService.getConnection();
|
||||
if (remoteAgentConnection) {
|
||||
const remoteExtHost = this._instantiationService.createInstance(RemoteExtensionHost, this.getExtensions(), this._createProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory);
|
||||
const remoteExtHost = this._instantiationService.createInstance(RemoteExtensionHost, this._createRemoteExtensionHostDataProvider(remoteAgentConnection.remoteAuthority), this._remoteAgentService.socketFactory);
|
||||
result.push(remoteExtHost);
|
||||
}
|
||||
|
||||
|
@ -429,10 +462,10 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
|||
return;
|
||||
}
|
||||
|
||||
const extensionHost = this._extensionHostManagers[0];
|
||||
const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess)!;
|
||||
this._remoteAuthorityResolverService._clearResolvedAuthority(remoteAuthority);
|
||||
try {
|
||||
const result = await extensionHost.resolveAuthority(remoteAuthority);
|
||||
const result = await localProcessExtensionHost.resolveAuthority(remoteAuthority);
|
||||
this._remoteAuthorityResolverService._setResolvedAuthority(result.authority, result.options);
|
||||
} catch (err) {
|
||||
this._remoteAuthorityResolverService._setResolvedAuthorityError(remoteAuthority, err);
|
||||
|
@ -443,28 +476,19 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
|||
this._extensionScanner.startScanningExtensions(this.createLogger());
|
||||
|
||||
const remoteAuthority = this._environmentService.configuration.remoteAuthority;
|
||||
const extensionHost = this._extensionHostManagers[0];
|
||||
const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess)!;
|
||||
|
||||
const allExtensions = flatten(await Promise.all([
|
||||
this._extensionScanner.scannedExtensions,
|
||||
this._webExtensionsScannerService.scanExtensions().then(extensions => extensions.map(parseScannedExtension))
|
||||
]));
|
||||
|
||||
// enable or disable proposed API per extension
|
||||
this._checkEnableProposedApi(allExtensions);
|
||||
|
||||
// remove disabled extensions
|
||||
let localExtensions = remove(allExtensions, extension => this._isDisabled(extension));
|
||||
let localExtensions = this._checkEnabledAndProposedAPI(await this._scanAllLocalExtensions());
|
||||
let remoteEnv: IRemoteAgentEnvironment | null = null;
|
||||
|
||||
if (remoteAuthority) {
|
||||
let resolvedAuthority: ResolverResult;
|
||||
let resolverResult: ResolverResult;
|
||||
|
||||
try {
|
||||
resolvedAuthority = await extensionHost.resolveAuthority(remoteAuthority);
|
||||
resolverResult = await localProcessExtensionHost.resolveAuthority(remoteAuthority);
|
||||
} catch (err) {
|
||||
const remoteName = getRemoteName(remoteAuthority);
|
||||
if (RemoteAuthorityResolverError.isNoResolverFound(err)) {
|
||||
err.isHandled = await this._handleNoResolverFound(remoteName, allExtensions);
|
||||
err.isHandled = await this._handleNoResolverFound(remoteAuthority);
|
||||
} else {
|
||||
console.log(err);
|
||||
if (RemoteAuthorityResolverError.isHandled(err)) {
|
||||
|
@ -474,22 +498,18 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
|||
this._remoteAuthorityResolverService._setResolvedAuthorityError(remoteAuthority, err);
|
||||
|
||||
// Proceed with the local extension host
|
||||
await this._startLocalExtensionHost(extensionHost, localExtensions, localExtensions.map(extension => extension.identifier));
|
||||
await this._startLocalExtensionHost(localExtensions);
|
||||
return;
|
||||
}
|
||||
|
||||
// set the resolved authority
|
||||
this._remoteAuthorityResolverService._setResolvedAuthority(resolvedAuthority.authority, resolvedAuthority.options);
|
||||
this._remoteExplorerService.setTunnelInformation(resolvedAuthority.tunnelInformation);
|
||||
this._remoteAuthorityResolverService._setResolvedAuthority(resolverResult.authority, resolverResult.options);
|
||||
this._remoteExplorerService.setTunnelInformation(resolverResult.tunnelInformation);
|
||||
|
||||
// monitor for breakage
|
||||
const connection = this._remoteAgentService.getConnection();
|
||||
if (connection) {
|
||||
connection.onDidStateChange(async (e) => {
|
||||
const remoteAuthority = this._environmentService.configuration.remoteAuthority;
|
||||
if (!remoteAuthority) {
|
||||
return;
|
||||
}
|
||||
if (e.type === PersistentConnectionEventType.ConnectionLost) {
|
||||
this._remoteAuthorityResolverService._clearResolvedAuthority(remoteAuthority);
|
||||
}
|
||||
|
@ -498,80 +518,64 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
|||
}
|
||||
|
||||
// fetch the remote environment
|
||||
const remoteEnv = (await this._remoteAgentService.getEnvironment());
|
||||
remoteEnv = await this._remoteAgentService.getEnvironment();
|
||||
|
||||
if (!remoteEnv) {
|
||||
this._notificationService.notify({ severity: Severity.Error, message: nls.localize('getEnvironmentFailure', "Could not fetch remote environment") });
|
||||
// Proceed with the local extension host
|
||||
await this._startLocalExtensionHost(extensionHost, localExtensions, localExtensions.map(extension => extension.identifier));
|
||||
await this._startLocalExtensionHost(localExtensions);
|
||||
return;
|
||||
}
|
||||
|
||||
// enable or disable proposed API per extension
|
||||
this._checkEnableProposedApi(remoteEnv.extensions);
|
||||
|
||||
// remove disabled extensions
|
||||
remoteEnv.extensions = remove(remoteEnv.extensions, extension => this._isDisabled(extension));
|
||||
|
||||
// Determine where each extension will execute, based on extensionKind
|
||||
const isInstalledLocally = new Set<string>();
|
||||
localExtensions.forEach(ext => isInstalledLocally.add(ExtensionIdentifier.toKey(ext.identifier)));
|
||||
|
||||
const isInstalledRemotely = new Set<string>();
|
||||
remoteEnv.extensions.forEach(ext => isInstalledRemotely.add(ExtensionIdentifier.toKey(ext.identifier)));
|
||||
|
||||
const enum RunningLocation { None, Local, Remote }
|
||||
const pickRunningLocation = (extension: IExtensionDescription): RunningLocation => {
|
||||
for (const extensionKind of getExtensionKind(extension, this._productService, this._configurationService)) {
|
||||
if (extensionKind === 'ui') {
|
||||
if (isInstalledLocally.has(ExtensionIdentifier.toKey(extension.identifier))) {
|
||||
return RunningLocation.Local;
|
||||
}
|
||||
} else if (extensionKind === 'workspace') {
|
||||
if (isInstalledRemotely.has(ExtensionIdentifier.toKey(extension.identifier))) {
|
||||
return RunningLocation.Remote;
|
||||
}
|
||||
}
|
||||
}
|
||||
return RunningLocation.None;
|
||||
};
|
||||
|
||||
const runningLocation = new Map<string, RunningLocation>();
|
||||
localExtensions.forEach(ext => runningLocation.set(ExtensionIdentifier.toKey(ext.identifier), pickRunningLocation(ext)));
|
||||
remoteEnv.extensions.forEach(ext => runningLocation.set(ExtensionIdentifier.toKey(ext.identifier), pickRunningLocation(ext)));
|
||||
|
||||
// remove non-UI extensions from the local extensions
|
||||
localExtensions = localExtensions.filter(ext => runningLocation.get(ExtensionIdentifier.toKey(ext.identifier)) === RunningLocation.Local);
|
||||
|
||||
// in case of UI extensions overlap, the local extension wins
|
||||
remoteEnv.extensions = remoteEnv.extensions.filter(ext => runningLocation.get(ExtensionIdentifier.toKey(ext.identifier)) === RunningLocation.Remote);
|
||||
|
||||
// save for remote extension's init data
|
||||
this._remoteEnvironment.set(remoteAuthority, remoteEnv);
|
||||
|
||||
await this._startLocalExtensionHost(extensionHost, remoteEnv.extensions.concat(localExtensions), localExtensions.map(extension => extension.identifier));
|
||||
} else {
|
||||
await this._startLocalExtensionHost(extensionHost, localExtensions, localExtensions.map(extension => extension.identifier));
|
||||
}
|
||||
|
||||
await this._startLocalExtensionHost(localExtensions, remoteAuthority, remoteEnv);
|
||||
}
|
||||
|
||||
private async _startLocalExtensionHost(extensionHost: ExtensionHostManager, allExtensions: IExtensionDescription[], localExtensions: ExtensionIdentifier[]): Promise<void> {
|
||||
this._registerAndHandleExtensions(allExtensions);
|
||||
extensionHost.start(localExtensions.filter(id => this._registry.containsExtension(id)));
|
||||
}
|
||||
private async _startLocalExtensionHost(localExtensions: IExtensionDescription[], remoteAuthority: string | undefined = undefined, remoteEnv: IRemoteAgentEnvironment | null = null): Promise<void> {
|
||||
|
||||
private _registerAndHandleExtensions(allExtensions: IExtensionDescription[]): void {
|
||||
const result = this._registry.deltaExtensions(allExtensions, []);
|
||||
let remoteExtensions = remoteEnv ? this._checkEnabledAndProposedAPI(remoteEnv.extensions) : [];
|
||||
|
||||
this._runningLocation = determineRunningLocation(this._productService, this._configurationService, localExtensions, remoteExtensions, Boolean(remoteAuthority), this._enableLocalWebWorker);
|
||||
|
||||
// remove non-UI extensions from the local extensions
|
||||
const localProcessExtensions = filterByRunningLocation(localExtensions, this._runningLocation, ExtensionRunningLocation.LocalProcess);
|
||||
const localWebWorkerExtensions = filterByRunningLocation(localExtensions, this._runningLocation, ExtensionRunningLocation.LocalWebWorker);
|
||||
remoteExtensions = filterByRunningLocation(remoteExtensions, this._runningLocation, ExtensionRunningLocation.Remote);
|
||||
|
||||
const result = this._registry.deltaExtensions(remoteExtensions.concat(localProcessExtensions).concat(localWebWorkerExtensions), []);
|
||||
if (result.removedDueToLooping.length > 0) {
|
||||
this._logOrShowMessage(Severity.Error, nls.localize('looping', "The following extensions contain dependency loops and have been disabled: {0}", result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', ')));
|
||||
}
|
||||
|
||||
if (remoteAuthority && remoteEnv) {
|
||||
this._remoteInitData.set(remoteAuthority, {
|
||||
connectionData: this._remoteAuthorityResolverService.getConnectionData(remoteAuthority),
|
||||
pid: remoteEnv.pid,
|
||||
appRoot: remoteEnv.appRoot,
|
||||
appSettingsHome: remoteEnv.appSettingsHome,
|
||||
extensionHostLogsPath: remoteEnv.extensionHostLogsPath,
|
||||
globalStorageHome: remoteEnv.globalStorageHome,
|
||||
userHome: remoteEnv.userHome,
|
||||
extensions: remoteExtensions,
|
||||
allExtensions: this._registry.getAllExtensionDescriptions(),
|
||||
});
|
||||
}
|
||||
|
||||
this._doHandleExtensionPoints(this._registry.getAllExtensionDescriptions());
|
||||
|
||||
const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess)!;
|
||||
localProcessExtensionHost.start(localProcessExtensions.map(extension => extension.identifier).filter(id => this._registry.containsExtension(id)));
|
||||
|
||||
const localWebWorkerExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalWebWorker);
|
||||
if (localWebWorkerExtensionHost) {
|
||||
localWebWorkerExtensionHost.start(localWebWorkerExtensions.map(extension => extension.identifier).filter(id => this._registry.containsExtension(id)));
|
||||
}
|
||||
}
|
||||
|
||||
public async getInspectPort(tryEnableInspector: boolean): Promise<number> {
|
||||
if (this._extensionHostManagers.length > 0) {
|
||||
return this._extensionHostManagers[0].getInspectPort(tryEnableInspector);
|
||||
const localProcessExtensionHost = this._getExtensionHostManager(ExtensionHostKind.LocalProcess);
|
||||
if (localProcessExtensionHost) {
|
||||
return localProcessExtensionHost.getInspectPort(tryEnableInspector);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -586,7 +590,8 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
|||
}
|
||||
}
|
||||
|
||||
private async _handleNoResolverFound(remoteName: string, allExtensions: IExtensionDescription[]): Promise<boolean> {
|
||||
private async _handleNoResolverFound(remoteAuthority: string): Promise<boolean> {
|
||||
const remoteName = getRemoteName(remoteAuthority);
|
||||
const recommendation = this._productService.remoteExtensionTips?.[remoteName];
|
||||
if (!recommendation) {
|
||||
return false;
|
||||
|
@ -602,9 +607,10 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
|||
};
|
||||
|
||||
const resolverExtensionId = recommendation.extensionId;
|
||||
const allExtensions = await this._scanAllLocalExtensions();
|
||||
const extension = allExtensions.filter(e => e.identifier.value === resolverExtensionId)[0];
|
||||
if (extension) {
|
||||
if (this._isDisabled(extension)) {
|
||||
if (!this._isEnabled(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,
|
||||
[{
|
||||
|
@ -644,27 +650,59 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
|||
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function remove(arr: IExtensionDescription[], predicate: (item: IExtensionDescription) => boolean): IExtensionDescription[];
|
||||
function remove(arr: IExtensionDescription[], toRemove: IExtensionDescription[]): IExtensionDescription[];
|
||||
function remove(arr: IExtensionDescription[], arg2: ((item: IExtensionDescription) => boolean) | IExtensionDescription[]): IExtensionDescription[] {
|
||||
if (typeof arg2 === 'function') {
|
||||
return _removePredicate(arr, arg2);
|
||||
}
|
||||
return _removeSet(arr, arg2);
|
||||
const enum ExtensionRunningLocation {
|
||||
None,
|
||||
LocalProcess,
|
||||
LocalWebWorker,
|
||||
Remote
|
||||
}
|
||||
|
||||
function _removePredicate(arr: IExtensionDescription[], predicate: (item: IExtensionDescription) => boolean): IExtensionDescription[] {
|
||||
return arr.filter(extension => !predicate(extension));
|
||||
function determineRunningLocation(productService: IProductService, configurationService: IConfigurationService, localExtensions: IExtensionDescription[], remoteExtensions: IExtensionDescription[], hasRemote: boolean, hasLocalWebWorker: boolean): Map<string, ExtensionRunningLocation> {
|
||||
const localExtensionsSet = new Set<string>();
|
||||
localExtensions.forEach(ext => localExtensionsSet.add(ExtensionIdentifier.toKey(ext.identifier)));
|
||||
|
||||
const remoteExtensionsSet = new Set<string>();
|
||||
remoteExtensions.forEach(ext => remoteExtensionsSet.add(ExtensionIdentifier.toKey(ext.identifier)));
|
||||
|
||||
const pickRunningLocation = (extension: IExtensionDescription): ExtensionRunningLocation => {
|
||||
const isInstalledLocally = localExtensionsSet.has(ExtensionIdentifier.toKey(extension.identifier));
|
||||
const isInstalledRemotely = remoteExtensionsSet.has(ExtensionIdentifier.toKey(extension.identifier));
|
||||
for (const extensionKind of getExtensionKind(extension, productService, configurationService)) {
|
||||
if (extensionKind === 'ui' && isInstalledLocally) {
|
||||
// ui extensions run locally if possible
|
||||
return ExtensionRunningLocation.LocalProcess;
|
||||
}
|
||||
if (extensionKind === 'workspace' && isInstalledRemotely) {
|
||||
// workspace extensions run remotely if possible
|
||||
return ExtensionRunningLocation.Remote;
|
||||
}
|
||||
if (extensionKind === 'workspace' && !hasRemote) {
|
||||
// workspace extensions also run locally if there is no remote
|
||||
return ExtensionRunningLocation.LocalProcess;
|
||||
}
|
||||
if (extensionKind === 'web' && isInstalledLocally && hasLocalWebWorker) {
|
||||
// web worker extensions run in the local web worker if possible
|
||||
if (typeof extension.browser !== 'undefined') {
|
||||
// The "browser" field determines the entry point
|
||||
(<any>extension).main = extension.browser;
|
||||
}
|
||||
return ExtensionRunningLocation.LocalWebWorker;
|
||||
}
|
||||
}
|
||||
return ExtensionRunningLocation.None;
|
||||
};
|
||||
|
||||
const runningLocation = new Map<string, ExtensionRunningLocation>();
|
||||
localExtensions.forEach(ext => runningLocation.set(ExtensionIdentifier.toKey(ext.identifier), pickRunningLocation(ext)));
|
||||
remoteExtensions.forEach(ext => runningLocation.set(ExtensionIdentifier.toKey(ext.identifier), pickRunningLocation(ext)));
|
||||
return runningLocation;
|
||||
}
|
||||
|
||||
function _removeSet(arr: IExtensionDescription[], toRemove: IExtensionDescription[]): IExtensionDescription[] {
|
||||
const toRemoveSet = new Set<string>();
|
||||
toRemove.forEach(extension => toRemoveSet.add(ExtensionIdentifier.toKey(extension.identifier)));
|
||||
return arr.filter(extension => !toRemoveSet.has(ExtensionIdentifier.toKey(extension.identifier)));
|
||||
function filterByRunningLocation(extensions: IExtensionDescription[], runningLocation: Map<string, ExtensionRunningLocation>, desiredRunningLocation: ExtensionRunningLocation): IExtensionDescription[] {
|
||||
return extensions.filter(ext => runningLocation.get(ExtensionIdentifier.toKey(ext.identifier)) === desiredRunningLocation);
|
||||
}
|
||||
|
||||
registerSingleton(IExtensionService, ExtensionService);
|
||||
|
|
|
@ -45,6 +45,15 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
|||
import { IOutputChannelRegistry, Extensions } from 'vs/workbench/services/output/common/output';
|
||||
import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService';
|
||||
|
||||
export interface ILocalProcessExtensionHostInitData {
|
||||
readonly autoStart: boolean;
|
||||
readonly extensions: IExtensionDescription[];
|
||||
}
|
||||
|
||||
export interface ILocalProcessExtensionHostDataProvider {
|
||||
getInitData(): Promise<ILocalProcessExtensionHostInitData>;
|
||||
}
|
||||
|
||||
export class LocalProcessExtensionHost implements IExtensionHost {
|
||||
|
||||
public readonly kind = ExtensionHostKind.LocalProcess;
|
||||
|
@ -76,9 +85,7 @@ export class LocalProcessExtensionHost implements IExtensionHost {
|
|||
private readonly _extensionHostLogFile: URI;
|
||||
|
||||
constructor(
|
||||
private readonly _autoStart: boolean,
|
||||
private readonly _extensions: Promise<IExtensionDescription[]>,
|
||||
private readonly _extensionHostLogsLocation: URI,
|
||||
private readonly _initDataProvider: ILocalProcessExtensionHostDataProvider,
|
||||
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,
|
||||
@INotificationService private readonly _notificationService: INotificationService,
|
||||
@IElectronService private readonly _electronService: IElectronService,
|
||||
|
@ -106,7 +113,7 @@ export class LocalProcessExtensionHost implements IExtensionHost {
|
|||
this._extensionHostConnection = null;
|
||||
this._messageProtocol = null;
|
||||
|
||||
this._extensionHostLogFile = joinPath(this._extensionHostLogsLocation, `${ExtensionHostLogFileName}.log`);
|
||||
this._extensionHostLogFile = joinPath(this._environmentService.extHostLogsPath, `${ExtensionHostLogFileName}.log`);
|
||||
|
||||
this._toDispose.add(this._onExit);
|
||||
this._toDispose.add(this._lifecycleService.onWillShutdown(e => this._onWillShutdown(e)));
|
||||
|
@ -413,51 +420,48 @@ export class LocalProcessExtensionHost implements IExtensionHost {
|
|||
});
|
||||
}
|
||||
|
||||
private _createExtHostInitData(): Promise<IInitData> {
|
||||
return Promise.all([this._telemetryService.getTelemetryInfo(), this._extensions])
|
||||
.then(([telemetryInfo, extensionDescriptions]) => {
|
||||
const workspace = this._contextService.getWorkspace();
|
||||
const r: IInitData = {
|
||||
commit: this._productService.commit,
|
||||
version: this._productService.version,
|
||||
parentPid: process.pid,
|
||||
environment: {
|
||||
isExtensionDevelopmentDebug: this._isExtensionDevDebug,
|
||||
appRoot: this._environmentService.appRoot ? URI.file(this._environmentService.appRoot) : undefined,
|
||||
appSettingsHome: this._environmentService.appSettingsHome ? this._environmentService.appSettingsHome : undefined,
|
||||
appName: this._productService.nameLong,
|
||||
appUriScheme: this._productService.urlProtocol,
|
||||
appLanguage: platform.language,
|
||||
extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI,
|
||||
extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI,
|
||||
globalStorageHome: URI.file(this._environmentService.globalStorageHome),
|
||||
userHome: this._environmentService.userHome,
|
||||
webviewResourceRoot: this._environmentService.webviewResourceRoot,
|
||||
webviewCspSource: this._environmentService.webviewCspSource,
|
||||
},
|
||||
workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? undefined : {
|
||||
configuration: withNullAsUndefined(workspace.configuration),
|
||||
id: workspace.id,
|
||||
name: this._labelService.getWorkspaceLabel(workspace),
|
||||
isUntitled: workspace.configuration ? isUntitledWorkspace(workspace.configuration, this._environmentService) : false
|
||||
},
|
||||
remote: {
|
||||
authority: this._environmentService.configuration.remoteAuthority,
|
||||
connectionData: null,
|
||||
isRemote: false
|
||||
},
|
||||
resolvedExtensions: [],
|
||||
hostExtensions: [],
|
||||
extensions: extensionDescriptions,
|
||||
telemetryInfo,
|
||||
logLevel: this._logService.getLevel(),
|
||||
logsLocation: this._extensionHostLogsLocation,
|
||||
logFile: this._extensionHostLogFile,
|
||||
autoStart: this._autoStart,
|
||||
uiKind: UIKind.Desktop
|
||||
};
|
||||
return r;
|
||||
});
|
||||
private async _createExtHostInitData(): Promise<IInitData> {
|
||||
const [telemetryInfo, initData] = await Promise.all([this._telemetryService.getTelemetryInfo(), this._initDataProvider.getInitData()]);
|
||||
const workspace = this._contextService.getWorkspace();
|
||||
return {
|
||||
commit: this._productService.commit,
|
||||
version: this._productService.version,
|
||||
parentPid: process.pid,
|
||||
environment: {
|
||||
isExtensionDevelopmentDebug: this._isExtensionDevDebug,
|
||||
appRoot: this._environmentService.appRoot ? URI.file(this._environmentService.appRoot) : undefined,
|
||||
appSettingsHome: this._environmentService.appSettingsHome ? this._environmentService.appSettingsHome : undefined,
|
||||
appName: this._productService.nameLong,
|
||||
appUriScheme: this._productService.urlProtocol,
|
||||
appLanguage: platform.language,
|
||||
extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI,
|
||||
extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI,
|
||||
globalStorageHome: URI.file(this._environmentService.globalStorageHome),
|
||||
userHome: this._environmentService.userHome,
|
||||
webviewResourceRoot: this._environmentService.webviewResourceRoot,
|
||||
webviewCspSource: this._environmentService.webviewCspSource,
|
||||
},
|
||||
workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? undefined : {
|
||||
configuration: withNullAsUndefined(workspace.configuration),
|
||||
id: workspace.id,
|
||||
name: this._labelService.getWorkspaceLabel(workspace),
|
||||
isUntitled: workspace.configuration ? isUntitledWorkspace(workspace.configuration, this._environmentService) : false
|
||||
},
|
||||
remote: {
|
||||
authority: this._environmentService.configuration.remoteAuthority,
|
||||
connectionData: null,
|
||||
isRemote: false
|
||||
},
|
||||
resolvedExtensions: [],
|
||||
hostExtensions: [],
|
||||
extensions: initData.extensions,
|
||||
telemetryInfo,
|
||||
logLevel: this._logService.getLevel(),
|
||||
logsLocation: this._environmentService.extHostLogsPath,
|
||||
logFile: this._extensionHostLogFile,
|
||||
autoStart: initData.autoStart,
|
||||
uiKind: UIKind.Desktop
|
||||
};
|
||||
}
|
||||
|
||||
private _logExtensionHostMessage(entry: IRemoteConsoleLog) {
|
||||
|
|
Loading…
Reference in a new issue