Merge pull request #100729 from microsoft/alex/extension-host

This commit is contained in:
Alexandru Dima 2020-06-22 15:50:00 +02:00 committed by GitHub
commit 45a222d4c5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 350 additions and 257 deletions

View file

@ -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',

View file

@ -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
}
}
});

View file

@ -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;
}
}

View file

@ -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), []);
}

View file

@ -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,

View file

@ -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 {

View file

@ -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 {

View file

@ -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 {

View file

@ -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);

View file

@ -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) {