提交 aa3ea85b 编写于 作者: A Alex Dima

Explorations

上级 3b6578a2
......@@ -42,9 +42,6 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { DownloadService } from 'vs/platform/download/node/downloadService';
import { IDownloadService } from 'vs/platform/download/common/download';
import { RemoteAuthorityResolverService } from 'vs/platform/remote/node/remoteAuthorityResolverService';
import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { RemoteAuthorityResolverChannel } from 'vs/platform/remote/node/remoteAuthorityResolverChannel';
import { StaticRouter } from 'vs/base/parts/ipc/node/ipc';
import { DefaultURITransformer } from 'vs/base/common/uriIpc';
......@@ -130,16 +127,11 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I
services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService));
services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService));
services.set(ILocalizationsService, new SyncDescriptor(LocalizationsService));
services.set(IRemoteAuthorityResolverService, new SyncDescriptor(RemoteAuthorityResolverService));
const instantiationService2 = instantiationService.createChild(services);
instantiationService2.invokeFunction(accessor => {
const remoteAuthorityResolverService = accessor.get(IRemoteAuthorityResolverService);
const remoteAuthorityResolverChannel = new RemoteAuthorityResolverChannel(remoteAuthorityResolverService);
server.registerChannel('remoteAuthorityResolver', remoteAuthorityResolverChannel);
const extensionManagementService = accessor.get(IExtensionManagementService);
const channel = new ExtensionManagementChannel(extensionManagementService, () => DefaultURITransformer);
server.registerChannel('extensions', channel);
......@@ -153,7 +145,6 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I
createSharedProcessContributions(instantiationService2);
disposables.push(extensionManagementService as ExtensionManagementService);
disposables.push(remoteAuthorityResolverService as RemoteAuthorityResolverService);
});
});
}
......
......@@ -209,7 +209,7 @@ export class CodeApplication extends Disposable {
} else {
const [host, strPort] = authority.split(':');
const port = parseInt(strPort, 10);
return { authority, host, port };
return { authority, host, port, syncExtensions: false };
}
};
......
......@@ -11,7 +11,6 @@ import { ILocalization } from 'vs/platform/localizations/common/localizations';
import { URI } from 'vs/base/common/uri';
import { IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IRemoteAuthorityResolver } from 'vs/platform/remote/common/remoteAuthorityResolver';
export const EXTENSION_IDENTIFIER_PATTERN = '^([a-z0-9A-Z][a-z0-9\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\-A-Z]*)$';
export const EXTENSION_IDENTIFIER_REGEX = new RegExp(EXTENSION_IDENTIFIER_PATTERN);
......@@ -109,7 +108,6 @@ export interface IExtensionContributions {
views?: { [location: string]: IView[] };
colors?: IColor[];
localizations?: ILocalization[];
remoteAuthorityResolvers?: IRemoteAuthorityResolver[];
}
export type ExtensionKind = 'ui' | 'workspace';
......
......@@ -14,7 +14,7 @@ import { URI } from 'vs/base/common/uri';
import { Disposable } from 'vs/base/common/lifecycle';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IRemoteAuthorityResolverService, IRemoteAuthorityResolver } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { IRemoteAuthorityResolverService, ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { getManifest } from 'vs/platform/extensionManagement/node/extensionManagementUtil';
export class MulitExtensionManagementService extends Disposable implements IExtensionManagementService {
......@@ -100,13 +100,13 @@ export class MulitExtensionManagementService extends Disposable implements IExte
return this.extensionManagementServerService.getExtensionManagementServer(extension.location);
}
private _remoteAuthorityResolverPromise: Thenable<IRemoteAuthorityResolver>;
private _remoteAuthorityResolverPromise: Thenable<ResolvedAuthority>;
private hasToSyncExtensions(): Thenable<boolean> {
if (!this.extensionManagementServerService.remoteExtensionManagementServer) {
return Promise.resolve(false);
}
if (!this._remoteAuthorityResolverPromise) {
this._remoteAuthorityResolverPromise = this.remoteAuthorityResolverService.getRemoteAuthorityResolver(this.extensionManagementServerService.remoteExtensionManagementServer.authority);
this._remoteAuthorityResolverPromise = this.remoteAuthorityResolverService.resolveAuthority(this.extensionManagementServerService.remoteExtensionManagementServer.authority);
}
return this._remoteAuthorityResolverPromise.then(({ syncExtensions }) => !!syncExtensions);
}
......
......@@ -4,8 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IProgressStep } from 'vs/platform/progress/common/progress';
import { Event } from 'vs/base/common/event';
export const IRemoteAuthorityResolverService = createDecorator<IRemoteAuthorityResolverService>('remoteAuthorityResolverService');
......@@ -13,27 +11,14 @@ export interface ResolvedAuthority {
readonly authority: string;
readonly host: string;
readonly port: number;
readonly syncExtensions: boolean;
}
export type IResolvingProgressEvent =
{ type: 'progress', authority: string, data: IProgressStep }
| { type: 'finished', authority: string }
| { type: 'output', authority: string, data: { channel: string, message: string; isErr?: boolean; } };
export interface IRemoteAuthorityResolverService {
_serviceBrand: any;
onResolvingProgress: Event<IResolvingProgressEvent>;
resolveAuthority(authority: string): Thenable<ResolvedAuthority>;
getRemoteAuthorityResolver(authority: string): Thenable<IRemoteAuthorityResolver | null>;
}
export interface IRemoteAuthorityResolver {
label: string;
path: string;
authorityPrefix: string;
syncExtensions?: boolean;
setResolvedAuthority(resolvedAuthority: ResolvedAuthority): void;
}
......@@ -3,48 +3,53 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IChannel } from 'vs/base/parts/ipc/node/ipc';
import { Event, buffer } from 'vs/base/common/event';
import { ResolvedAuthority, IResolvingProgressEvent, IRemoteAuthorityResolverService, IRemoteAuthorityResolver } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { ResolvedAuthority, IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { ipcRenderer as ipc } from 'electron';
export class RemoteAuthorityResolverChannelClient implements IRemoteAuthorityResolverService {
class PendingResolveAuthorityRequest {
constructor(
public readonly resolve: (value: ResolvedAuthority) => void,
public readonly reject: (err: any) => void,
public readonly promise: Promise<ResolvedAuthority>,
) {
}
}
export class RemoteAuthorityResolverService implements IRemoteAuthorityResolverService {
_serviceBrand: any;
private _resolveAuthorityCache: { [authority: string]: Thenable<ResolvedAuthority>; };
get onResolvingProgress(): Event<IResolvingProgressEvent> { return buffer(this.channel.listen('onResolvingProgress'), true); }
private _pendingResolveAuthorityRequests: { [authority: string]: PendingResolveAuthorityRequest; };
private _resolvedAuthorities: { [authority: string]: ResolvedAuthority; };
constructor(private channel: IChannel) {
this._resolveAuthorityCache = Object.create(null);
constructor() {
this._pendingResolveAuthorityRequests = Object.create(null);
this._resolvedAuthorities = Object.create(null);
}
resolveAuthority(authority: string): Thenable<ResolvedAuthority> {
if (!this._resolveAuthorityCache[authority]) {
this._resolveAuthorityCache[authority] = this._resolveAuthority(authority);
this._resolveAuthorityCache[authority].then((r) => {
ipc.send('vscode:remoteAuthorityResolved', {
authority: authority,
host: r.host,
port: r.port
});
});
if (this._resolvedAuthorities[authority]) {
return Promise.resolve(this._resolvedAuthorities[authority]);
}
return this._resolveAuthorityCache[authority];
if (!this._pendingResolveAuthorityRequests[authority]) {
let resolve: (value: ResolvedAuthority) => void;
let reject: (err: any) => void;
let promise = new Promise<ResolvedAuthority>((_resolve, _reject) => {
resolve = _resolve;
reject = _reject;
});
this._pendingResolveAuthorityRequests[authority] = new PendingResolveAuthorityRequest(resolve, reject, promise);
}
getRemoteAuthorityResolver(authority: string): Thenable<IRemoteAuthorityResolver | null> {
return this.channel.call('getRemoteAuthorityResolver', [authority]);
return this._pendingResolveAuthorityRequests[authority].promise;
}
private _resolveAuthority(authority: string): Thenable<ResolvedAuthority> {
if (authority.indexOf('+') >= 0) {
return this.channel.call('resolveAuthority', [authority]);
} else {
const [host, strPort] = authority.split(':');
const port = parseInt(strPort, 10);
return Promise.resolve({ authority, host, port });
setResolvedAuthority(resolvedAuthority: ResolvedAuthority) {
this._resolvedAuthorities[resolvedAuthority.authority] = resolvedAuthority;
if (this._pendingResolveAuthorityRequests[resolvedAuthority.authority]) {
let request = this._pendingResolveAuthorityRequests[resolvedAuthority.authority];
delete this._pendingResolveAuthorityRequests[resolvedAuthority.authority];
ipc.send('vscode:remoteAuthorityResolved', resolvedAuthority);
request.resolve(resolvedAuthority);
}
}
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IServerChannel } from 'vs/base/parts/ipc/node/ipc';
import { Event, buffer } from 'vs/base/common/event';
import { IResolvingProgressEvent, IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver';
export class RemoteAuthorityResolverChannel implements IServerChannel {
onResolvingProgress: Event<IResolvingProgressEvent>;
constructor(private service: IRemoteAuthorityResolverService) {
this.onResolvingProgress = buffer(service.onResolvingProgress, true);
}
listen(_, event: string): Event<any> {
switch (event) {
case 'onResolvingProgress': return this.onResolvingProgress;
}
throw new Error('Invalid listen');
}
call(_, command: string, args?: any): Thenable<any> {
switch (command) {
case 'resolveAuthority': return this.service.resolveAuthority(args[0]);
case 'getRemoteAuthorityResolver': return this.service.getRemoteAuthorityResolver(args[0]);
}
throw new Error('Invalid call');
}
}
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IRemoteAuthorityResolverService, ResolvedAuthority, IResolvingProgressEvent, IRemoteAuthorityResolver } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ILogService } from 'vs/platform/log/common/log';
import { Disposable } from 'vs/base/common/lifecycle';
import { Emitter, Event } from 'vs/base/common/event';
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
export class RemoteAuthorityResolverService extends Disposable implements IRemoteAuthorityResolverService {
_serviceBrand: any;
private _onResolvingProgress: Emitter<IResolvingProgressEvent> = this._register(new Emitter<IResolvingProgressEvent>());
readonly onResolvingProgress: Event<IResolvingProgressEvent> = this._onResolvingProgress.event;
constructor(
@IEnvironmentService environmentService: IEnvironmentService,
@IConfigurationService configurationService: IConfigurationService,
@ILogService logService: ILogService,
@IExtensionManagementService extensionManagementService: IExtensionManagementService
) {
super();
}
async resolveAuthority(authority: string): Promise<ResolvedAuthority> {
throw new Error(`Not implemented`);
}
async getRemoteAuthorityResolver(authority: string): Promise<IRemoteAuthorityResolver | null> {
throw new Error(`Not implemented`);
}
}
......@@ -61,10 +61,11 @@ import { ExtHostWindow } from 'vs/workbench/api/node/extHostWindow';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { IExtensionDescription, throwProposedApiError, checkProposedApiEnabled, nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
import * as vscode from 'vscode';
export interface IExtensionApiFactory {
(extension: IExtensionDescription): typeof vscode;
(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry): typeof vscode;
}
function proposedApiFunction<T>(extension: IExtensionDescription, fn: T): T {
......@@ -140,7 +141,7 @@ export function createApiFactory(
// Register API-ish commands
ExtHostApiCommands.register(extHostCommands);
return function (extension: IExtensionDescription): typeof vscode {
return function (extension: IExtensionDescription, extensionRegistry: ExtensionDescriptionRegistry): typeof vscode {
// Check document selectors for being overly generic. Technically this isn't a problem but
// in practice many extensions say they support `fooLang` but need fs-access to do so. Those
......@@ -259,14 +260,14 @@ export function createApiFactory(
// namespace: extensions
const extensions: typeof vscode.extensions = {
getExtension(extensionId: string): Extension<any> {
let desc = extensionService.getExtensionDescription(extensionId);
let desc = extensionRegistry.getExtensionDescription(extensionId);
if (desc) {
return new Extension(extensionService, desc);
}
return undefined;
},
get all(): Extension<any>[] {
return extensionService.getAllExtensionDescriptions().map((desc) => new Extension(extensionService, desc));
return extensionRegistry.getAllExtensionDescriptions().map((desc) => new Extension(extensionService, desc));
}
};
......@@ -860,11 +861,11 @@ class Extension<T> implements vscode.Extension<T> {
}
}
export function initializeExtensionApi(extensionService: ExtHostExtensionService, apiFactory: IExtensionApiFactory): Promise<void> {
return extensionService.getExtensionPathIndex().then(trie => defineAPI(apiFactory, trie));
export function initializeExtensionApi(extensionService: ExtHostExtensionService, apiFactory: IExtensionApiFactory, extensionRegistry: ExtensionDescriptionRegistry): Promise<void> {
return extensionService.getExtensionPathIndex().then(trie => defineAPI(apiFactory, trie, extensionRegistry));
}
function defineAPI(factory: IExtensionApiFactory, extensionPaths: TernarySearchTree<IExtensionDescription>): void {
function defineAPI(factory: IExtensionApiFactory, extensionPaths: TernarySearchTree<IExtensionDescription>, extensionRegistry: ExtensionDescriptionRegistry): void {
// each extension is meant to get its own api implementation
const extApiImpl = new Map<string, typeof vscode>();
......@@ -882,7 +883,7 @@ function defineAPI(factory: IExtensionApiFactory, extensionPaths: TernarySearchT
if (ext) {
let apiImpl = extApiImpl.get(ext.id);
if (!apiImpl) {
apiImpl = factory(ext);
apiImpl = factory(ext, extensionRegistry);
extApiImpl.set(ext.id, apiImpl);
}
return apiImpl;
......@@ -893,7 +894,7 @@ function defineAPI(factory: IExtensionApiFactory, extensionPaths: TernarySearchT
let extensionPathsPretty = '';
extensionPaths.forEach((value, index) => extensionPathsPretty += `\t${index} -> ${value.id}\n`);
console.warn(`Could not identify extension for 'vscode' require call from ${parent.filename}. These are the extension path mappings: \n${extensionPathsPretty}`);
defaultApiImpl = factory(nullExtensionDescription);
defaultApiImpl = factory(nullExtensionDescription, extensionRegistry);
}
return defaultApiImpl;
};
......
......@@ -43,6 +43,7 @@ import { IProgressOptions, IProgressStep } from 'vs/platform/progress/common/pro
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import * as vscode from 'vscode';
import { IMarkdownString } from 'vs/base/common/htmlContent';
import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver';
export interface IEnvironment {
isExtensionDevelopmentDebug: boolean;
......@@ -50,7 +51,7 @@ export interface IEnvironment {
appSettingsHome: URI;
extensionDevelopmentLocationURI: URI;
extensionTestsPath: string;
globalStorageHome: string;
globalStorageHome: URI;
}
export interface IWorkspaceData {
......@@ -70,6 +71,7 @@ export interface IInitData {
telemetryInfo: ITelemetryInfo;
logLevel: LogLevel;
logsLocation: URI;
autoStart: boolean;
remoteAuthority?: string | null;
}
......@@ -732,6 +734,8 @@ export interface ExtHostSearchShape {
}
export interface ExtHostExtensionServiceShape {
$resolveAuthority(remoteAuthority: string): Thenable<ResolvedAuthority>;
$startExtensionHost(enabledExtensionIds: string[]): Thenable<void>;
$activateByEvent(activationEvent: string): Thenable<void>;
}
......
......@@ -31,6 +31,7 @@ import { IConfigurationResolverService } from 'vs/workbench/services/configurati
import { CancellationToken } from 'vs/base/common/cancellation';
import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
export class ExtHostDebugService implements ExtHostDebugServiceShape {
......@@ -119,10 +120,10 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
this._breakpoints = new Map<string, vscode.Breakpoint>();
this._breakpointEventsActive = false;
this._extensionService.getExtensionRegistry().then((extensionRegistry: ExtensionDescriptionRegistry) => {
// register all debug extensions
const debugTypes: string[] = [];
for (const ed of this._extensionService.getAllExtensionDescriptions()) {
for (const ed of extensionRegistry.getAllExtensionDescriptions()) {
if (ed.contributes) {
const debuggers = <IDebuggerContribution[]>ed.contributes['debuggers'];
if (debuggers && debuggers.length > 0) {
......@@ -141,6 +142,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
if (debugTypes.length > 0) {
this._debugServiceProxy.$registerDebugTypes(debugTypes);
}
});
}
// extension debug API
......@@ -722,7 +724,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
});
}
private getAdapterDescriptor(adapterProvider: vscode.DebugAdapterDescriptorFactory, session: ExtHostDebugSession): Thenable<vscode.DebugAdapterDescriptor> {
private async getAdapterDescriptor(adapterProvider: vscode.DebugAdapterDescriptorFactory, session: ExtHostDebugSession): Promise<vscode.DebugAdapterDescriptor> {
// a "debugServer" attribute in the launch config takes precedence
const serverPort = session.configuration.debugServer;
......@@ -739,7 +741,8 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
}
if (adapterProvider) {
return asThenable(() => adapterProvider.createDebugAdapterDescriptor(session, this.daExecutableFromPackage(session)));
const extensionRegistry = await this._extensionService.getExtensionRegistry();
return asThenable(() => adapterProvider.createDebugAdapterDescriptor(session, this.daExecutableFromPackage(session, extensionRegistry)));
}
// try deprecated command based extension API "adapterExecutableCommand" to determine the executable
......@@ -754,11 +757,12 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape {
}
// fallback: use executable information from package.json
return Promise.resolve(this.daExecutableFromPackage(session));
const extensionRegistry = await this._extensionService.getExtensionRegistry();
return Promise.resolve(this.daExecutableFromPackage(session, extensionRegistry));
}
private daExecutableFromPackage(session: ExtHostDebugSession): DebugAdapterExecutable | undefined {
const dae = ExecutableDebugAdapter.platformAdapterExecutable(this._extensionService.getAllExtensionDescriptions(), session.type);
private daExecutableFromPackage(session: ExtHostDebugSession, extensionRegistry: ExtensionDescriptionRegistry): DebugAdapterExecutable | undefined {
const dae = ExecutableDebugAdapter.platformAdapterExecutable(extensionRegistry.getAllExtensionDescriptions(), session.type);
if (dae) {
return new DebugAdapterExecutable(dae.command, dae.args, dae.options);
}
......
......@@ -103,7 +103,7 @@ import { DownloadServiceChannel } from 'vs/platform/download/node/downloadIpc';
import { TextResourcePropertiesService } from 'vs/workbench/services/textfile/electron-browser/textResourcePropertiesService';
import { MulitExtensionManagementService } from 'vs/platform/extensionManagement/node/multiExtensionManagement';
import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { RemoteAuthorityResolverChannelClient } from 'vs/platform/remote/electron-browser/remoteAuthorityResolverService';
import { RemoteAuthorityResolverService } from 'vs/platform/remote/electron-browser/remoteAuthorityResolverService';
/**
* Services that we require for the Shell
......@@ -437,8 +437,7 @@ export class WorkbenchShell extends Disposable {
serviceCollection.set(IDownloadService, new SyncDescriptor(DownloadService));
serviceCollection.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService));
const remoteAuthorityResolverChannel = getDelayedChannel(sharedProcess.then(c => c.getChannel('remoteAuthorityResolver')));
const remoteAuthorityResolverService = new RemoteAuthorityResolverChannelClient(remoteAuthorityResolverChannel);
const remoteAuthorityResolverService = new RemoteAuthorityResolverService();
serviceCollection.set(IRemoteAuthorityResolverService, remoteAuthorityResolverService);
const remoteAgentService = new RemoteAgentService(this.configuration, this.notificationService, this.environmentService, remoteAuthorityResolverService);
......
......@@ -3,20 +3,15 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { join } from 'path';
import { timeout } from 'vs/base/common/async';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import * as errors from 'vs/base/common/errors';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Counter } from 'vs/base/common/numbers';
import { URI, setUriThrowOnMissingScheme } from 'vs/base/common/uri';
import { IURITransformer } from 'vs/base/common/uriIpc';
import * as pfs from 'vs/base/node/pfs';
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc';
import { IEnvironment, IInitData, IWorkspaceData, MainContext, MainThreadWorkspaceShape } from 'vs/workbench/api/node/extHost.protocol';
import { IEnvironment, IInitData, MainContext } from 'vs/workbench/api/node/extHost.protocol';
import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration';
import { ExtensionActivatedByEvent } from 'vs/workbench/api/node/extHostExtensionActivator';
import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService';
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
......@@ -48,33 +43,26 @@ export function exit(code?: number) {
nativeExit(code);
}
interface ITestRunner {
run(testsRoot: string, clb: (error: Error, failures?: number) => void): void;
}
export class ExtensionHostMain {
private static readonly WORKSPACE_CONTAINS_TIMEOUT = 7000;
private _isTerminating: boolean = false;
private _workspace: IWorkspaceData;
private _environment: IEnvironment;
private _extensionService: ExtHostExtensionService;
private _extHostConfiguration: ExtHostConfiguration;
private _extHostLogService: ExtHostLogService;
private _isTerminating: boolean;
private readonly _environment: IEnvironment;
private readonly _extensionService: ExtHostExtensionService;
private readonly _extHostConfiguration: ExtHostConfiguration;
private readonly _extHostLogService: ExtHostLogService;
private disposables: IDisposable[] = [];
private _searchRequestIdProvider: Counter;
private _mainThreadWorkspace: MainThreadWorkspaceShape;
constructor(protocol: IMessagePassingProtocol, initData: IInitData) {
this._isTerminating = false;
const uriTransformer: IURITransformer = null;
const rpcProtocol = new RPCProtocol(protocol, null, uriTransformer);
// ensure URIs are transformed and revived
initData = this.transform(initData, rpcProtocol);
this._environment = initData.environment;
this._workspace = initData.workspace;
const allowExit = !!this._environment.extensionTestsPath; // to support other test frameworks like Jasmin that use process.exit (https://github.com/Microsoft/vscode/issues/37708)
patchProcess(allowExit);
......@@ -90,8 +78,7 @@ export class ExtensionHostMain {
this._extHostLogService.trace('initData', initData);
this._extHostConfiguration = new ExtHostConfiguration(rpcProtocol.getProxy(MainContext.MainThreadConfiguration), extHostWorkspace, initData.configuration);
const mainThreadTelemetry = rpcProtocol.getProxy(MainContext.MainThreadTelemetry);
this._extensionService = new ExtHostExtensionService(initData, rpcProtocol, extHostWorkspace, this._extHostConfiguration, this._extHostLogService, mainThreadTelemetry);
this._extensionService = new ExtHostExtensionService(nativeExit, initData, rpcProtocol, extHostWorkspace, this._extHostConfiguration, this._extHostLogService);
// error forwarding and stack trace scanning
Error.stackTraceLimit = 100; // increase number of stack frames (from 10, https://github.com/v8/v8/wiki/Stack-Trace-API)
......@@ -125,17 +112,6 @@ export class ExtensionHostMain {
mainThreadErrors.$onUnexpectedError(data);
}
});
this._mainThreadWorkspace = rpcProtocol.getProxy(MainContext.MainThreadWorkspace);
}
start(): Thenable<void> {
return this._extensionService.onExtensionAPIReady()
.then(() => this.handleEagerExtensions())
.then(() => this.handleExtensionTests())
.then(() => {
this._extHostLogService.info(`eager extensions activated`);
});
}
terminate(): void {
......@@ -151,184 +127,22 @@ export class ExtensionHostMain {
// TODO: write to log once we have one
});
let allPromises: Thenable<void>[] = [];
try {
const allExtensions = this._extensionService.getAllExtensionDescriptions();
const allExtensionsIds = allExtensions.map(ext => ext.id);
const activatedExtensions = allExtensionsIds.filter(id => this._extensionService.isActivated(id));
const extensionsDeactivated = this._extensionService.deactivateAll();
allPromises = activatedExtensions.map((extensionId) => {
return this._extensionService.deactivate(extensionId);
});
} catch (err) {
// TODO: write to log once we have one
}
const extensionsDeactivated = Promise.all(allPromises).then<void>(() => void 0);
// Give extensions 1 second to wrap up any async dispose, then exit
// Give extensions 1 second to wrap up any async dispose, then exit in at most 4 seconds
setTimeout(() => {
Promise.race([timeout(4000), extensionsDeactivated]).then(() => exit(), () => exit());
}, 1000);
}
// Handle "eager" activation extensions
private handleEagerExtensions(): Promise<void> {
this._extensionService.activateByEvent('*', true).then(null, (err) => {
console.error(err);
});
return this.handleWorkspaceContainsEagerExtensions();
}
private handleWorkspaceContainsEagerExtensions(): Promise<void> {
if (!this._workspace || this._workspace.folders.length === 0) {
return Promise.resolve(null);
}
return Promise.all(
this._extensionService.getAllExtensionDescriptions().map((desc) => {
return this.handleWorkspaceContainsEagerExtension(desc);
})
).then(() => { });
}
private handleWorkspaceContainsEagerExtension(desc: IExtensionDescription): Promise<void> {
const activationEvents = desc.activationEvents;
if (!activationEvents) {
return Promise.resolve(void 0);
}
const fileNames: string[] = [];
const globPatterns: string[] = [];
for (let i = 0; i < activationEvents.length; i++) {
if (/^workspaceContains:/.test(activationEvents[i])) {
const fileNameOrGlob = activationEvents[i].substr('workspaceContains:'.length);
if (fileNameOrGlob.indexOf('*') >= 0 || fileNameOrGlob.indexOf('?') >= 0) {
globPatterns.push(fileNameOrGlob);
} else {
fileNames.push(fileNameOrGlob);
}
}
}
if (fileNames.length === 0 && globPatterns.length === 0) {
return Promise.resolve(void 0);
}
const fileNamePromise = Promise.all(fileNames.map((fileName) => this.activateIfFileName(desc.id, fileName))).then(() => { });
const globPatternPromise = this.activateIfGlobPatterns(desc.id, globPatterns);
return Promise.all([fileNamePromise, globPatternPromise]).then(() => { });
}
private async activateIfFileName(extensionId: string, fileName: string): Promise<void> {
// find exact path
for (const { uri } of this._workspace.folders) {
if (await pfs.exists(join(URI.revive(uri).fsPath, fileName))) {
// the file was found
return (
this._extensionService.activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContains:${fileName}`))
.then(null, err => console.error(err))
);
}
}
return undefined;
}
private async activateIfGlobPatterns(extensionId: string, globPatterns: string[]): Promise<void> {
this._extHostLogService.trace(`extensionHostMain#activateIfGlobPatterns: fileSearch, extension: ${extensionId}, entryPoint: workspaceContains`);
if (globPatterns.length === 0) {
return Promise.resolve(void 0);
}
const tokenSource = new CancellationTokenSource();
const searchP = this._mainThreadWorkspace.$checkExists(globPatterns, tokenSource.token);
const timer = setTimeout(async () => {
tokenSource.cancel();
this._extensionService.activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContainsTimeout:${globPatterns.join(',')}`))
.then(null, err => console.error(err));
}, ExtensionHostMain.WORKSPACE_CONTAINS_TIMEOUT);
let exists: boolean;
try {
exists = await searchP;
} catch (err) {
if (!errors.isPromiseCanceledError(err)) {
console.error(err);
}
}
tokenSource.dispose();
clearTimeout(timer);
if (exists) {
// a file was found matching one of the glob patterns
return (
this._extensionService.activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContains:${globPatterns.join(',')}`))
.then(null, err => console.error(err))
);
}
return Promise.resolve(void 0);
}
private handleExtensionTests(): Promise<void> {
if (!this._environment.extensionTestsPath || !this._environment.extensionDevelopmentLocationURI) {
return Promise.resolve(null);
}
// Require the test runner via node require from the provided path
let testRunner: ITestRunner;
let requireError: Error;
try {
testRunner = <any>require.__$__nodeRequire(this._environment.extensionTestsPath);
} catch (error) {
requireError = error;
}
// Execute the runner if it follows our spec
if (testRunner && typeof testRunner.run === 'function') {
return new Promise<void>((c, e) => {
testRunner.run(this._environment.extensionTestsPath, (error, failures) => {
if (error) {
e(error.toString());
} else {
c(null);
}
// after tests have run, we shutdown the host
this.gracefulExit(failures && failures > 0 ? 1 /* ERROR */ : 0 /* OK */);
});
});
}
// Otherwise make sure to shutdown anyway even in case of an error
else {
this.gracefulExit(1 /* ERROR */);
}
return Promise.reject(new Error(requireError ? requireError.toString() : nls.localize('extensionTestError', "Path {0} does not point to a valid extension test runner.", this._environment.extensionTestsPath)));
}
private transform(initData: IInitData, rpcProtocol: RPCProtocol): IInitData {
initData.extensions.forEach((ext) => (<any>ext).extensionLocation = URI.revive(rpcProtocol.transformIncomingURIs(ext.extensionLocation)));
initData.environment.appRoot = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.appRoot));
initData.environment.appSettingsHome = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.appSettingsHome));
initData.environment.extensionDevelopmentLocationURI = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.extensionDevelopmentLocationURI));
initData.environment.globalStorageHome = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.globalStorageHome));
initData.logsLocation = URI.revive(rpcProtocol.transformIncomingURIs(initData.logsLocation));
initData.workspace = rpcProtocol.transformIncomingURIs(initData.workspace);
return initData;
}
private gracefulExit(code: number): void {
// to give the PH process a chance to flush any outstanding console
// messages to the main process, we delay the exit() by some time
setTimeout(() => exit(code), 500);
}
}
......@@ -167,7 +167,6 @@ createExtHostProtocol().then(protocol => {
// setup things
const extensionHostMain = new ExtensionHostMain(renderer.protocol, renderer.initData);
onTerminate = () => extensionHostMain.terminate();
return extensionHostMain.start();
}).catch(err => console.error(err));
function patchExecArgv() {
......
......@@ -38,7 +38,7 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { randomPort } from 'vs/base/node/ports';
import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { ILabelService } from 'vs/platform/label/common/label';
import { renderOcticons } from 'vs/base/browser/ui/octiconLabel/octiconLabel';
import { join } from 'path';
import { onUnexpectedError } from 'vs/base/common/errors';
......@@ -117,7 +117,7 @@ export class RuntimeExtensionsEditor extends BaseEditor {
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IExtensionHostProfileService private readonly _extensionHostProfileService: IExtensionHostProfileService,
@IStorageService storageService: IStorageService,
@IRemoteAuthorityResolverService private remoteAuthorityResolverService: IRemoteAuthorityResolverService
@ILabelService private readonly _labelService: ILabelService
) {
super(RuntimeExtensionsEditor.ID, telemetryService, themeService, storageService);
......@@ -375,11 +375,11 @@ export class RuntimeExtensionsEditor extends BaseEditor {
const el = $('span');
el.innerHTML = renderOcticons(`$(rss) ${element.description.extensionLocation.authority}`);
data.msgContainer.appendChild(el);
this.remoteAuthorityResolverService.getRemoteAuthorityResolver(element.description.extensionLocation.authority).then(resolver => {
if (resolver && resolver.label.length) {
el.innerHTML = renderOcticons(`$(rss) ${resolver.label}`);
const hostLabel = this._labelService.getHostLabel();
if (hostLabel) {
el.innerHTML = renderOcticons(`$(rss) ${hostLabel}`);
}
});
}
if (this._profileInfo) {
......
......@@ -42,7 +42,7 @@ import { IExtensionDescription } from 'vs/workbench/services/extensions/common/e
export interface IExtensionHostStarter {
readonly onCrashed: Event<[number, string]>;
start(): Promise<IMessagePassingProtocol>;
start(): Thenable<IMessagePassingProtocol>;
getInspectPort(): number;
dispose(): void;
}
......@@ -93,6 +93,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
private _messageProtocol: Promise<IMessagePassingProtocol>;
constructor(
private readonly _autoStart: boolean,
private readonly _extensions: Promise<IExtensionDescription[]>,
private readonly _extensionHostLogsLocation: URI,
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,
......@@ -425,7 +426,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
appSettingsHome: this._environmentService.appSettingsHome ? URI.file(this._environmentService.appSettingsHome) : void 0,
extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI,
extensionTestsPath: this._environmentService.extensionTestsPath,
globalStorageHome: this._environmentService.globalStorageHome
globalStorageHome: URI.file(this._environmentService.globalStorageHome)
},
workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? null : {
configuration: workspace.configuration,
......@@ -438,7 +439,8 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
configuration: !this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment ? { ...configurationData, configurationScopes: getScopes() } : configurationData,
telemetryInfo,
logLevel: this._logService.getLevel(),
logsLocation: this._extensionHostLogsLocation
logsLocation: this._extensionHostLogsLocation,
autoStart: this._autoStart
};
return r;
});
......
......@@ -17,6 +17,7 @@ import { IExtensionHostStarter } from 'vs/workbench/services/extensions/electron
import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler';
import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier';
import { IRPCProtocolLogger, RPCProtocol, RequestInitiator, ResponsiveState } from 'vs/workbench/services/extensions/node/rpcProtocol';
import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver';
// Enable to see detailed message communication between window and extension host
const LOG_EXTENSION_HOST_COMMUNICATION = false;
......@@ -171,6 +172,14 @@ export class ExtensionHostProcessManager extends Disposable {
}
return 0;
}
public resolveAuthority(remoteAuthority: string): Thenable<ResolvedAuthority> {
return this._extensionHostProcessProxy.then(proxy => proxy.value.$resolveAuthority(remoteAuthority));
}
public start(enabledExtensionIds: string[]): Thenable<void> {
return this._extensionHostProcessProxy.then(proxy => proxy.value.$startExtensionHost(enabledExtensionIds));
}
}
const colorTables = [
......
......@@ -112,8 +112,8 @@ export class ExtensionService extends Disposable implements IExtensionService {
// reschedule to ensure this runs after restoring viewlets, panels, and editors
runWhenIdle(() => {
perf.mark('willLoadExtensions');
this._startExtensionHostProcess(true, []);
this._scanAndHandleExtensions();
this._startExtensionHostProcess([]);
this.whenInstalledExtensionsRegistered().then(() => perf.mark('didLoadExtensions'));
}, 50 /*max delay*/);
});
......@@ -127,11 +127,11 @@ export class ExtensionService extends Disposable implements IExtensionService {
public restartExtensionHost(): void {
this._stopExtensionHostProcess();
this._startExtensionHostProcess(Object.keys(this._allRequestedActivateEvents));
this._startExtensionHostProcess(false, Object.keys(this._allRequestedActivateEvents));
}
public startExtensionHost(): void {
this._startExtensionHostProcess(Object.keys(this._allRequestedActivateEvents));
this._startExtensionHostProcess(false, Object.keys(this._allRequestedActivateEvents));
}
public stopExtensionHost(): void {
......@@ -153,10 +153,10 @@ export class ExtensionService extends Disposable implements IExtensionService {
}
}
private _startExtensionHostProcess(initialActivationEvents: string[]): void {
private _startExtensionHostProcess(isInitialStart: boolean, initialActivationEvents: string[]): void {
this._stopExtensionHostProcess();
const extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, this.getExtensions(), this._extensionHostLogsLocation);
const extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, !isInitialStart, this.getExtensions(), this._extensionHostLogsLocation);
const extHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, extHostProcessWorker, null, initialActivationEvents);
extHostProcessManager.onDidCrash(([code, signal]) => this._onExtensionHostCrashed(code, signal));
extHostProcessManager.onDidChangeResponsiveState((responsiveState) => { this._onDidChangeResponsiveChange.fire({ target: extHostProcessManager, isResponsive: responsiveState === ResponsiveState.Responsive }); });
......@@ -196,7 +196,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
},
{
label: nls.localize('restart', "Restart Extension Host"),
run: () => this._startExtensionHostProcess(Object.keys(this._allRequestedActivateEvents))
run: () => this._startExtensionHostProcess(false, Object.keys(this._allRequestedActivateEvents))
}]
);
}
......@@ -318,7 +318,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
// --- impl
private _scanAndHandleExtensions(): void {
private async _scanAndHandleExtensions(): Promise<void> {
this._extensionScanner.startScanningExtensions(new Logger((severity, source, message) => {
if (this._isDev && source) {
this._logOrShowMessage(severity, `[${source}]: ${message}`);
......@@ -327,9 +327,14 @@ export class ExtensionService extends Disposable implements IExtensionService {
}
}));
this._extensionScanner.scannedExtensions
.then(allExtensions => this._getRuntimeExtensions(allExtensions))
.then(allExtensions => {
const extensionHost = this._extensionHostProcessManagers[0];
const extensions = await this._extensionScanner.scannedExtensions;
const enabledExtensions = await this._getRuntimeExtensions(extensions);
extensionHost.start(enabledExtensions.map(extension => extension.id));
this._onHasExtensions(enabledExtensions);
}
private _onHasExtensions(allExtensions: IExtensionDescription[]): void {
this._registry = new ExtensionDescriptionRegistry(allExtensions);
let availableExtensions = this._registry.getAllExtensionDescriptions();
......@@ -345,7 +350,6 @@ export class ExtensionService extends Disposable implements IExtensionService {
this._installedExtensionsReady.open();
this._onDidRegisterExtensions.fire(void 0);
this._onDidChangeExtensionsStatus.fire(availableExtensions.map(e => e.id));
});
}
private _getRuntimeExtensions(allExtensions: IExtensionDescription[]): Promise<IExtensionDescription[]> {
......
......@@ -8,17 +8,23 @@ import { IExtensionDescription } from 'vs/workbench/services/extensions/common/e
const hasOwnProperty = Object.hasOwnProperty;
export class ExtensionDescriptionRegistry {
private _extensionDescriptions: IExtensionDescription[];
private _extensionsMap: { [extensionId: string]: IExtensionDescription; };
private _extensionsArr: IExtensionDescription[];
private _activationMap: { [activationEvent: string]: IExtensionDescription[]; };
constructor(extensionDescriptions: IExtensionDescription[]) {
this._extensionDescriptions = extensionDescriptions;
this._initialize();
}
private _initialize(): void {
this._extensionsMap = {};
this._extensionsArr = [];
this._activationMap = {};
for (let i = 0, len = extensionDescriptions.length; i < len; i++) {
let extensionDescription = extensionDescriptions[i];
for (let i = 0, len = this._extensionDescriptions.length; i < len; i++) {
let extensionDescription = this._extensionDescriptions[i];
if (hasOwnProperty.call(this._extensionsMap, extensionDescription.id)) {
// No overwriting allowed!
......@@ -45,6 +51,13 @@ export class ExtensionDescriptionRegistry {
}
}
public keepOnly(extensionIds: string[]): void {
let toKeep = new Set<string>();
extensionIds.forEach(extensionId => toKeep.add(extensionId));
this._extensionDescriptions = this._extensionDescriptions.filter(extension => toKeep.has(extension.id));
this._initialize();
}
public containsActivationEvent(activationEvent: string): boolean {
return hasOwnProperty.call(this._activationMap, activationEvent);
}
......
......@@ -41,7 +41,7 @@ class RemoteAgentConnection extends Disposable implements IRemoteAgentConnection
readonly remoteAuthority: string;
private _connection: Thenable<Client<RemoteAgentConnectionContext>> | null;
private _environment: Thenable<IRemoteAgentEnvironment | null> | null;
private _environment: Thenable<IRemoteAgentEnvironment> | null;
constructor(
remoteAuthority: string,
......@@ -55,7 +55,7 @@ class RemoteAgentConnection extends Disposable implements IRemoteAgentConnection
this._environment = null;
}
getEnvironment(): Thenable<IRemoteAgentEnvironment | null> {
getEnvironment(): Thenable<IRemoteAgentEnvironment> {
if (!this._environment) {
const client = new RemoteExtensionEnvironmentChannelClient(this.getChannel('remoteextensionsenvironment'));
......
......@@ -16,6 +16,7 @@ export interface IRemoteAgentEnvironmentDTO {
logsPath: UriComponents;
extensionsPath: UriComponents;
extensionHostLogsPath: UriComponents;
globalStorageHome: UriComponents;
extensions: IExtensionDescription[];
os: OperatingSystem;
}
......@@ -34,6 +35,7 @@ export class RemoteExtensionEnvironmentChannelClient {
logsPath: URI.revive(data.logsPath),
extensionsPath: URI.revive(data.extensionsPath),
extensionHostLogsPath: URI.revive(data.extensionHostLogsPath),
globalStorageHome: URI.revive(data.globalStorageHome),
extensions: data.extensions.map(ext => { (<any>ext).extensionLocation = URI.revive(ext.extensionLocation); return ext; }),
os: data.os
};
......
......@@ -21,6 +21,7 @@ export interface IRemoteAgentEnvironment {
logsPath: URI;
extensionsPath: URI;
extensionHostLogsPath: URI;
globalStorageHome: URI;
extensions: IExtensionDescription[];
os: OperatingSystem;
}
......@@ -34,7 +35,7 @@ export interface IRemoteAgentService {
export interface IRemoteAgentConnection {
readonly remoteAuthority: string;
getEnvironment(): Thenable<IRemoteAgentEnvironment | null>;
getEnvironment(): Thenable<IRemoteAgentEnvironment>;
getChannel<T extends IChannel>(channelName: string): T;
registerChannel<T extends IServerChannel<RemoteAgentConnectionContext>>(channelName: string, channel: T);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册