Merge VS Code 1.23.1 (#1520)

This commit is contained in:
Matt Irvine
2018-06-05 11:24:51 -07:00
committed by GitHub
parent e3baf5c443
commit 0c58f09e59
3651 changed files with 74249 additions and 48599 deletions

View File

@@ -8,7 +8,7 @@ import Severity from 'vs/base/common/severity';
import { TPromise } from 'vs/base/common/winjs.base';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import Event from 'vs/base/common/event';
import { Event } from 'vs/base/common/event';
export interface IExtensionDescription {
readonly id: string;

View File

@@ -14,7 +14,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { EXTENSION_IDENTIFIER_PATTERN } from 'vs/platform/extensionManagement/common/extensionManagement';
const hasOwnProperty = Object.hasOwnProperty;
const schemaRegistry = <IJSONContributionRegistry>Registry.as(Extensions.JSONContribution);
const schemaRegistry = Registry.as<IJSONContributionRegistry>(Extensions.JSONContribution);
export class ExtensionMessageCollector {
@@ -125,12 +125,12 @@ const schema: IJSONSchema = {
properties: {
engines: {
type: 'object',
description: nls.localize('vscode.extension.engines', "Engine compatibility."),
properties: {
'vscode': {
type: 'string',
description: nls.localize('vscode.extension.engines.vscode', 'For VS Code extensions, specifies the VS Code version that the extension is compatible with. Cannot be *. For example: ^0.10.5 indicates compatibility with a minimum VS Code version of 0.10.5.'),
default: '^0.10.0',
default: '^1.22.0',
}
}
},
@@ -147,8 +147,15 @@ const schema: IJSONSchema = {
type: 'array',
uniqueItems: true,
items: {
type: 'string',
enum: ['Languages', 'Snippets', 'Linters', 'Themes', 'Debuggers', 'Other', 'Keymaps', 'Formatters', 'Extension Packs', 'SCM Providers', 'Azure', 'Language Packs']
oneOf: [{
type: 'string',
enum: ['Programming Languages', 'Snippets', 'Linters', 'Themes', 'Debuggers', 'Other', 'Keymaps', 'Formatters', 'Extension Packs', 'SCM Providers', 'Azure', 'Language Packs'],
},
{
type: 'string',
const: 'Languages',
deprecationMessage: nls.localize('vscode.extension.category.languages.deprecated', 'Use \'Programming Languages\' instead'),
}]
}
},
galleryBanner: {
@@ -219,6 +226,11 @@ const schema: IJSONSchema = {
body: 'onView:${5:viewId}',
description: nls.localize('vscode.extension.activationEvents.onView', 'An activation event emitted whenever the specified view is expanded.'),
},
{
label: 'onUri',
body: 'onUri',
description: nls.localize('vscode.extension.activationEvents.onUri', 'An activation event emitted whenever a system-wide Uri directed towards this extension is open.'),
},
{
label: '*',
description: nls.localize('vscode.extension.activationEvents.star', 'An activation event emitted on VS Code startup. To ensure a great end user experience, please use this activation event in your extension only when no other activation events combination works in your use-case.'),
@@ -249,6 +261,25 @@ const schema: IJSONSchema = {
}
}
},
markdown: {
type: 'string',
description: nls.localize('vscode.extension.markdown', "Controls the Markdown rendering engine used in the Marketplace. Either github (default) or standard."),
enum: ['github', 'standard'],
default: 'github'
},
qna: {
default: 'marketplace',
description: nls.localize('vscode.extension.qna', "Controls the Q&A link in the Marketplace. Set to marketplace to enable the default Marketplace Q & A site. Set to a string to provide the URL of a custom Q & A site. Set to false to disable Q & A altogether."),
anyOf: [
{
type: ['string', 'boolean'],
enum: ['marketplace', false]
},
{
type: 'string'
}
]
},
extensionDependencies: {
description: nls.localize('vscode.extension.extensionDependencies', 'Dependencies to other extensions. The identifier of an extension is always ${publisher}.${name}. For example: vscode.csharp.'),
type: 'array',

View File

@@ -23,7 +23,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc';
import { generateRandomPipeName, Protocol } from 'vs/base/parts/ipc/node/ipc.net';
import { createServer, Server, Socket } from 'net';
import Event, { Emitter, debounceEvent, mapEvent, anyEvent, fromNodeEventEmitter } from 'vs/base/common/event';
import { Event, Emitter, debounceEvent, mapEvent, anyEvent, fromNodeEventEmitter } from 'vs/base/common/event';
import { IInitData, IWorkspaceData, IConfigurationInitData } from 'vs/workbench/api/node/extHost.protocol';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
@@ -36,11 +36,10 @@ import { IRemoteConsoleLog, log, parse } from 'vs/base/node/console';
import { getScopes } from 'vs/platform/configuration/common/configurationRegistry';
import { ILogService } from 'vs/platform/log/common/log';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { IChoiceService } from 'vs/platform/dialogs/common/dialogs';
export class ExtensionHostProcessWorker {
private _onCrashed: Emitter<[number, string]> = new Emitter<[number, string]>();
private readonly _onCrashed: Emitter<[number, string]> = new Emitter<[number, string]>();
public readonly onCrashed: Event<[number, string]> = this._onCrashed.event;
private readonly _toDispose: IDisposable[];
@@ -73,7 +72,6 @@ export class ExtensionHostProcessWorker {
@IWorkspaceConfigurationService private readonly _configurationService: IWorkspaceConfigurationService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@ICrashReporterService private readonly _crashReporterService: ICrashReporterService,
@IChoiceService private readonly _choiceService: IChoiceService,
@ILogService private readonly _logService: ILogService
) {
// handle extension host lifecycle a bit special when we know we are developing an extension that runs inside
@@ -143,7 +141,6 @@ export class ExtensionHostProcessWorker {
AMD_ENTRYPOINT: 'vs/workbench/node/extensionHostProcess',
PIPE_LOGGING: 'true',
VERBOSE_LOGGING: true,
VSCODE_WINDOW_ID: String(this._windowService.getCurrentWindowId()),
VSCODE_IPC_HOOK_EXTHOST: pipeName,
VSCODE_HANDLES_UNCAUGHT_ERRORS: true,
VSCODE_LOG_STACK: !this._isExtensionDevTestFromCli && (this._isExtensionDevHost || !this._environmentService.isBuilt || product.quality !== 'stable' || this._environmentService.verbose)
@@ -238,11 +235,12 @@ export class ExtensionHostProcessWorker {
? nls.localize('extensionHostProcess.startupFailDebug', "Extension host did not start in 10 seconds, it might be stopped on the first line and needs a debugger to continue.")
: nls.localize('extensionHostProcess.startupFail', "Extension host did not start in 10 seconds, that might be a problem.");
this._choiceService.choose(Severity.Warning, msg, [nls.localize('reloadWindow', "Reload Window")]).then(choice => {
if (choice === 0) {
this._windowService.reloadWindow();
}
});
this._notificationService.prompt(Severity.Warning, msg,
[{
label: nls.localize('reloadWindow', "Reload Window"),
run: () => this._windowService.reloadWindow()
}]
);
}, 10000);
}
@@ -372,22 +370,17 @@ export class ExtensionHostProcessWorker {
appRoot: this._environmentService.appRoot,
appSettingsHome: this._environmentService.appSettingsHome,
disableExtensions: this._environmentService.disableExtensions,
userExtensionsHome: this._environmentService.extensionsPath,
extensionDevelopmentPath: this._environmentService.extensionDevelopmentPath,
extensionTestsPath: this._environmentService.extensionTestsPath,
// globally disable proposed api when built and not insiders developing extensions
enableProposedApiForAll: !this._environmentService.isBuilt || (!!this._environmentService.extensionDevelopmentPath && product.nameLong.indexOf('Insiders') >= 0),
enableProposedApiFor: this._environmentService.args['enable-proposed-api'] || []
extensionTestsPath: this._environmentService.extensionTestsPath
},
workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? null : <IWorkspaceData>this._contextService.getWorkspace(),
extensions: extensionDescriptions,
// Send configurations scopes only in development mode.
configuration: !this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment ? { ...configurationData, configurationScopes: getScopes() } : configurationData,
telemetryInfo,
args: this._environmentService.args,
execPath: this._environmentService.execPath,
windowId: this._windowService.getCurrentWindowId(),
logLevel: this._logService.getLevel()
logLevel: this._logService.getLevel(),
logsPath: this._environmentService.logsPath
};
return r;
});

View File

@@ -17,8 +17,8 @@ import * as platform from 'vs/base/common/platform';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
import { IMessage, IExtensionDescription, IExtensionsStatus, IExtensionService, ExtensionPointContribution, ActivationTimes, ProfileSession } from 'vs/workbench/services/extensions/common/extensions';
import { USER_MANIFEST_CACHE_FILE, BUILTIN_MANIFEST_CACHE_FILE, MANIFEST_CACHE_FOLDER } from 'vs/platform/extensions/common/extensions';
import { IExtensionEnablementService, IExtensionIdentifier, EnablementState } from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions, BetterMergeId, BetterMergeDisabledNowKey } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IExtensionEnablementService, IExtensionIdentifier, EnablementState, IExtensionManagementService, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions, BetterMergeId, BetterMergeDisabledNowKey, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionsRegistry, ExtensionPoint, IExtensionPointUser, ExtensionMessageCollector, IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { ExtensionScanner, ILog, ExtensionScannerInput, IExtensionResolver, IExtensionReference, Translations } from 'vs/workbench/services/extensions/node/extensionPoints';
import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier';
@@ -35,13 +35,13 @@ import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { mark, time } from 'vs/base/common/performance';
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { Barrier } from 'vs/base/common/async';
import Event, { Emitter } from 'vs/base/common/event';
import { Event, Emitter } from 'vs/base/common/event';
import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler';
import product from 'vs/platform/node/product';
import * as strings from 'vs/base/common/strings';
import { RPCProtocol } from 'vs/workbench/services/extensions/node/rpcProtocol';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { IChoiceService } from 'vs/platform/dialogs/common/dialogs';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
let _SystemExtensionsRoot: string = null;
function getSystemExtensionsRoot(): string {
@@ -111,10 +111,160 @@ function messageWithSource2(source: string, message: string): string {
const hasOwnProperty = Object.hasOwnProperty;
const NO_OP_VOID_PROMISE = TPromise.wrap<void>(void 0);
export class ExtensionHostProcessManager extends Disposable {
public readonly onDidCrash: Event<[number, string]>;
/**
* A map of already activated events to speed things up if the same activation event is triggered multiple times.
*/
private readonly _extensionHostProcessFinishedActivateEvents: { [activationEvent: string]: boolean; };
private readonly _extensionHostProcessActivationTimes: { [id: string]: ActivationTimes; };
private readonly _extensionHostExtensionRuntimeErrors: { [id: string]: Error[]; };
private _extensionHostProcessRPCProtocol: RPCProtocol;
private readonly _extensionHostProcessCustomers: IDisposable[];
private readonly _extensionHostProcessWorker: ExtensionHostProcessWorker;
/**
* winjs believes a proxy is a promise because it has a `then` method, so wrap the result in an object.
*/
private readonly _extensionHostProcessProxy: TPromise<{ value: ExtHostExtensionServiceShape; }>;
constructor(
extensionHostProcessWorker: ExtensionHostProcessWorker,
initialActivationEvents: string[],
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
) {
super();
this._extensionHostProcessFinishedActivateEvents = Object.create(null);
this._extensionHostProcessActivationTimes = Object.create(null);
this._extensionHostExtensionRuntimeErrors = Object.create(null);
this._extensionHostProcessRPCProtocol = null;
this._extensionHostProcessCustomers = [];
this._extensionHostProcessProxy = null;
this._extensionHostProcessWorker = extensionHostProcessWorker;
this.onDidCrash = this._extensionHostProcessWorker.onCrashed;
this._extensionHostProcessProxy = this._extensionHostProcessWorker.start().then(
(protocol) => {
return { value: this._createExtensionHostCustomers(protocol) };
},
(err) => {
console.error('Error received from starting extension host');
console.error(err);
return null;
}
);
this._extensionHostProcessProxy.then(() => {
initialActivationEvents.forEach((activationEvent) => this.activateByEvent(activationEvent));
});
}
public dispose(): void {
if (this._extensionHostProcessWorker) {
this._extensionHostProcessWorker.dispose();
}
if (this._extensionHostProcessRPCProtocol) {
this._extensionHostProcessRPCProtocol.dispose();
}
for (let i = 0, len = this._extensionHostProcessCustomers.length; i < len; i++) {
const customer = this._extensionHostProcessCustomers[i];
try {
customer.dispose();
} catch (err) {
errors.onUnexpectedError(err);
}
}
super.dispose();
}
public getActivatedExtensionIds(): string[] {
return Object.keys(this._extensionHostProcessActivationTimes);
}
public getActivationTimes(): { [id: string]: ActivationTimes; } {
return this._extensionHostProcessActivationTimes;
}
public getRuntimeErrors(): { [id: string]: Error[]; } {
return this._extensionHostExtensionRuntimeErrors;
}
public canProfileExtensionHost(): boolean {
return this._extensionHostProcessWorker && Boolean(this._extensionHostProcessWorker.getInspectPort());
}
private _createExtensionHostCustomers(protocol: IMessagePassingProtocol): ExtHostExtensionServiceShape {
if (logExtensionHostCommunication || this._environmentService.logExtensionHostCommunication) {
protocol = asLoggingProtocol(protocol);
}
this._extensionHostProcessRPCProtocol = new RPCProtocol(protocol);
const extHostContext: IExtHostContext = this._extensionHostProcessRPCProtocol;
// Named customers
const namedCustomers = ExtHostCustomersRegistry.getNamedCustomers();
for (let i = 0, len = namedCustomers.length; i < len; i++) {
const [id, ctor] = namedCustomers[i];
const instance = this._instantiationService.createInstance(ctor, extHostContext);
this._extensionHostProcessCustomers.push(instance);
this._extensionHostProcessRPCProtocol.set(id, instance);
}
// Customers
const customers = ExtHostCustomersRegistry.getCustomers();
for (let i = 0, len = customers.length; i < len; i++) {
const ctor = customers[i];
const instance = this._instantiationService.createInstance(ctor, extHostContext);
this._extensionHostProcessCustomers.push(instance);
}
// Check that no named customers are missing
const expected: ProxyIdentifier<any>[] = Object.keys(MainContext).map((key) => MainContext[key]);
this._extensionHostProcessRPCProtocol.assertRegistered(expected);
return this._extensionHostProcessRPCProtocol.getProxy(ExtHostContext.ExtHostExtensionService);
}
public activateByEvent(activationEvent: string): TPromise<void> {
if (this._extensionHostProcessFinishedActivateEvents[activationEvent] || !this._extensionHostProcessProxy) {
return NO_OP_VOID_PROMISE;
}
return this._extensionHostProcessProxy.then((proxy) => {
return proxy.value.$activateByEvent(activationEvent);
}).then(() => {
this._extensionHostProcessFinishedActivateEvents[activationEvent] = true;
});
}
public startExtensionHostProfile(): TPromise<ProfileSession> {
if (this._extensionHostProcessWorker) {
let port = this._extensionHostProcessWorker.getInspectPort();
if (port) {
return this._instantiationService.createInstance(ExtensionHostProfiler, port).start();
}
}
throw new Error('Extension host not running or no inspect port available');
}
public onExtensionActivated(extensionId: string, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string): void {
this._extensionHostProcessActivationTimes[extensionId] = new ActivationTimes(startup, codeLoadingTime, activateCallTime, activateResolvedTime, activationEvent);
}
public onExtensionRuntimeError(extensionId: string, err: Error): void {
if (!this._extensionHostExtensionRuntimeErrors[extensionId]) {
this._extensionHostExtensionRuntimeErrors[extensionId] = [];
}
this._extensionHostExtensionRuntimeErrors[extensionId].push(err);
}
}
export class ExtensionService extends Disposable implements IExtensionService {
public _serviceBrand: any;
private _onDidRegisterExtensions: Emitter<void>;
private readonly _onDidRegisterExtensions: Emitter<void>;
private _registry: ExtensionDescriptionRegistry;
private readonly _installedExtensionsReady: Barrier;
@@ -126,31 +276,18 @@ export class ExtensionService extends Disposable implements IExtensionService {
public readonly onDidChangeExtensionsStatus: Event<string[]> = this._onDidChangeExtensionsStatus.event;
// --- Members used per extension host process
/**
* A map of already activated events to speed things up if the same activation event is triggered multiple times.
*/
private _extensionHostProcessFinishedActivateEvents: { [activationEvent: string]: boolean; };
private _extensionHostProcessActivationTimes: { [id: string]: ActivationTimes; };
private _extensionHostExtensionRuntimeErrors: { [id: string]: Error[]; };
private _extensionHostProcessWorker: ExtensionHostProcessWorker;
private _extensionHostProcessRPCProtocol: RPCProtocol;
private _extensionHostProcessCustomers: IDisposable[];
/**
* winjs believes a proxy is a promise because it has a `then` method, so wrap the result in an object.
*/
private _extensionHostProcessProxy: TPromise<{ value: ExtHostExtensionServiceShape; }>;
private _extensionHostProcessManager: ExtensionHostProcessManager;
constructor(
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@INotificationService private readonly _notificationService: INotificationService,
@IChoiceService private readonly _choiceService: IChoiceService,
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@IExtensionEnablementService private readonly _extensionEnablementService: IExtensionEnablementService,
@IStorageService private readonly _storageService: IStorageService,
@IWindowService private readonly _windowService: IWindowService,
@ILifecycleService lifecycleService: ILifecycleService
@ILifecycleService lifecycleService: ILifecycleService,
@IExtensionManagementService private extensionManagementService: IExtensionManagementService
) {
super();
this._registry = null;
@@ -161,15 +298,12 @@ export class ExtensionService extends Disposable implements IExtensionService {
this._onDidRegisterExtensions = new Emitter<void>();
this._extensionHostProcessFinishedActivateEvents = Object.create(null);
this._extensionHostProcessActivationTimes = Object.create(null);
this._extensionHostExtensionRuntimeErrors = Object.create(null);
this._extensionHostProcessWorker = null;
this._extensionHostProcessRPCProtocol = null;
this._extensionHostProcessCustomers = [];
this._extensionHostProcessProxy = null;
this._extensionHostProcessManager = null;
this.startDelayed(lifecycleService);
if (this._environmentService.disableExtensions) {
this._notificationService.info(nls.localize('extensionsDisabled', "All extensions are disabled."));
}
}
private startDelayed(lifecycleService: ILifecycleService): void {
@@ -223,51 +357,25 @@ export class ExtensionService extends Disposable implements IExtensionService {
}
private _stopExtensionHostProcess(): void {
const previouslyActivatedExtensionIds = Object.keys(this._extensionHostProcessActivationTimes);
let previouslyActivatedExtensionIds: string[] = [];
this._extensionHostProcessFinishedActivateEvents = Object.create(null);
this._extensionHostProcessActivationTimes = Object.create(null);
this._extensionHostExtensionRuntimeErrors = Object.create(null);
if (this._extensionHostProcessWorker) {
this._extensionHostProcessWorker.dispose();
this._extensionHostProcessWorker = null;
if (this._extensionHostProcessManager) {
previouslyActivatedExtensionIds = this._extensionHostProcessManager.getActivatedExtensionIds();
this._extensionHostProcessManager.dispose();
this._extensionHostProcessManager = null;
}
if (this._extensionHostProcessRPCProtocol) {
this._extensionHostProcessRPCProtocol.dispose();
this._extensionHostProcessRPCProtocol = null;
}
for (let i = 0, len = this._extensionHostProcessCustomers.length; i < len; i++) {
const customer = this._extensionHostProcessCustomers[i];
try {
customer.dispose();
} catch (err) {
errors.onUnexpectedError(err);
}
}
this._extensionHostProcessCustomers = [];
this._extensionHostProcessProxy = null;
this._onDidChangeExtensionsStatus.fire(previouslyActivatedExtensionIds);
if (previouslyActivatedExtensionIds.length > 0) {
this._onDidChangeExtensionsStatus.fire(previouslyActivatedExtensionIds);
}
}
private _startExtensionHostProcess(initialActivationEvents: string[]): void {
this._stopExtensionHostProcess();
this._extensionHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, this);
this._extensionHostProcessWorker.onCrashed(([code, signal]) => this._onExtensionHostCrashed(code, signal));
this._extensionHostProcessProxy = this._extensionHostProcessWorker.start().then(
(protocol) => {
return { value: this._createExtensionHostCustomers(protocol) };
},
(err) => {
console.error('Error received from starting extension host');
console.error(err);
return null;
}
);
this._extensionHostProcessProxy.then(() => {
initialActivationEvents.forEach((activationEvent) => this.activateByEvent(activationEvent));
});
const extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, this);
this._extensionHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, extHostProcessWorker, initialActivationEvents);
this._extensionHostProcessManager.onDidCrash(([code, signal]) => this._onExtensionHostCrashed(code, signal));
}
private _onExtensionHostCrashed(code: number, signal: string): void {
@@ -279,49 +387,16 @@ export class ExtensionService extends Disposable implements IExtensionService {
message = nls.localize('extensionHostProcess.unresponsiveCrash', "Extension host terminated because it was not responsive.");
}
this._choiceService.choose(Severity.Error, message, [nls.localize('devTools', "Developer Tools"), nls.localize('restart', "Restart Extension Host")]).then(choice => {
switch (choice) {
case 0 /* Open Dev Tools */:
this._windowService.openDevTools();
break;
case 1 /* Restart Extension Host */:
this._startExtensionHostProcess(Object.keys(this._allRequestedActivateEvents));
break;
}
});
}
private _createExtensionHostCustomers(protocol: IMessagePassingProtocol): ExtHostExtensionServiceShape {
if (logExtensionHostCommunication || this._environmentService.logExtensionHostCommunication) {
protocol = asLoggingProtocol(protocol);
}
this._extensionHostProcessRPCProtocol = new RPCProtocol(protocol);
const extHostContext: IExtHostContext = this._extensionHostProcessRPCProtocol;
// Named customers
const namedCustomers = ExtHostCustomersRegistry.getNamedCustomers();
for (let i = 0, len = namedCustomers.length; i < len; i++) {
const [id, ctor] = namedCustomers[i];
const instance = this._instantiationService.createInstance(ctor, extHostContext);
this._extensionHostProcessCustomers.push(instance);
this._extensionHostProcessRPCProtocol.set(id, instance);
}
// Customers
const customers = ExtHostCustomersRegistry.getCustomers();
for (let i = 0, len = customers.length; i < len; i++) {
const ctor = customers[i];
const instance = this._instantiationService.createInstance(ctor, extHostContext);
this._extensionHostProcessCustomers.push(instance);
}
// Check that no named customers are missing
const expected: ProxyIdentifier<any>[] = Object.keys(MainContext).map((key) => MainContext[key]);
this._extensionHostProcessRPCProtocol.assertRegistered(expected);
return this._extensionHostProcessRPCProtocol.getProxy(ExtHostContext.ExtHostExtensionService);
this._notificationService.prompt(Severity.Error, message,
[{
label: nls.localize('devTools', "Open Developer Tools"),
run: () => this._windowService.openDevTools()
},
{
label: nls.localize('restart', "Restart Extension Host"),
run: () => this._startExtensionHostProcess(Object.keys(this._allRequestedActivateEvents))
}]
);
}
// ---- begin IExtensionService
@@ -349,15 +424,11 @@ export class ExtensionService extends Disposable implements IExtensionService {
}
}
protected _activateByEvent(activationEvent: string): TPromise<void> {
if (this._extensionHostProcessFinishedActivateEvents[activationEvent] || !this._extensionHostProcessProxy) {
return NO_OP_VOID_PROMISE;
private _activateByEvent(activationEvent: string): TPromise<void> {
if (this._extensionHostProcessManager) {
return this._extensionHostProcessManager.activateByEvent(activationEvent);
}
return this._extensionHostProcessProxy.then((proxy) => {
return proxy.value.$activateByEvent(activationEvent);
}).then(() => {
this._extensionHostProcessFinishedActivateEvents[activationEvent] = true;
});
return NO_OP_VOID_PROMISE;
}
public whenInstalledExtensionsRegistered(): TPromise<boolean> {
@@ -388,6 +459,9 @@ export class ExtensionService extends Disposable implements IExtensionService {
}
public getExtensionsStatus(): { [id: string]: IExtensionsStatus; } {
const activationTimes = this._extensionHostProcessManager ? this._extensionHostProcessManager.getActivationTimes() : {};
const runtimeErrors = this._extensionHostProcessManager ? this._extensionHostProcessManager.getRuntimeErrors() : {};
let result: { [id: string]: IExtensionsStatus; } = Object.create(null);
if (this._registry) {
const extensions = this._registry.getAllExtensionDescriptions();
@@ -396,8 +470,8 @@ export class ExtensionService extends Disposable implements IExtensionService {
const id = extension.id;
result[id] = {
messages: this._extensionsMessages[id],
activationTimes: this._extensionHostProcessActivationTimes[id],
runtimeErrors: this._extensionHostExtensionRuntimeErrors[id],
activationTimes: activationTimes[id],
runtimeErrors: runtimeErrors[id],
};
}
}
@@ -405,15 +479,15 @@ export class ExtensionService extends Disposable implements IExtensionService {
}
public canProfileExtensionHost(): boolean {
return this._extensionHostProcessWorker && Boolean(this._extensionHostProcessWorker.getInspectPort());
if (this._extensionHostProcessManager) {
return this._extensionHostProcessManager.canProfileExtensionHost();
}
return false;
}
public startExtensionHostProfile(): TPromise<ProfileSession> {
if (this._extensionHostProcessWorker) {
let port = this._extensionHostProcessWorker.getInspectPort();
if (port) {
return this._instantiationService.createInstance(ExtensionHostProfiler, port).start();
}
if (this._extensionHostProcessManager) {
return this._extensionHostProcessManager.startExtensionHostProfile();
}
throw new Error('Extension host not running or no inspect port available');
}
@@ -424,7 +498,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
private _scanAndHandleExtensions(): void {
this._getRuntimeExtension()
this._getRuntimeExtensions()
.then(runtimeExtensons => {
this._registry = new ExtensionDescriptionRegistry(runtimeExtensons);
@@ -449,14 +523,13 @@ export class ExtensionService extends Disposable implements IExtensionService {
});
}
private _getRuntimeExtension(): TPromise<IExtensionDescription[]> {
private _getRuntimeExtensions(): TPromise<IExtensionDescription[]> {
const log = new Logger((severity, source, message) => {
this._logOrShowMessage(severity, this._isDev ? messageWithSource2(source, message) : message);
});
return ExtensionService._scanInstalledExtensions(this._windowService, this._choiceService, this._environmentService, log)
return ExtensionService._scanInstalledExtensions(this._windowService, this._notificationService, this._environmentService, log)
.then(({ system, user, development }) => {
this._extensionEnablementService.migrateToIdentifiers(user); // TODO: @sandy Remove it after couple of milestones
return this._extensionEnablementService.getDisabledExtensions()
.then(disabledExtensions => {
let result: { [extensionId: string]: IExtensionDescription; } = {};
@@ -501,7 +574,11 @@ export class ExtensionService extends Disposable implements IExtensionService {
});
if (extensionsToDisable.length) {
return TPromise.join(extensionsToDisable.map(e => this._extensionEnablementService.setEnablement(e, EnablementState.Disabled)))
return this.extensionManagementService.getInstalled(LocalExtensionType.User)
.then(installed => {
const toDisable = installed.filter(i => extensionsToDisable.some(e => areSameExtensions({ id: getGalleryExtensionIdFromLocal(i) }, e)));
return TPromise.join(toDisable.map(e => this._extensionEnablementService.setEnablement(e, EnablementState.Disabled)));
})
.then(() => {
this._storageService.store(BetterMergeDisabledNowKey, true);
return runtimeExtensions;
@@ -510,7 +587,36 @@ export class ExtensionService extends Disposable implements IExtensionService {
return runtimeExtensions;
}
});
});
}).then(extensions => this._updateEnableProposedApi(extensions));
}
private _updateEnableProposedApi(extensions: IExtensionDescription[]): IExtensionDescription[] {
const enableProposedApiForAll = !this._environmentService.isBuilt || (!!this._environmentService.extensionDevelopmentPath && product.nameLong.indexOf('Insiders') >= 0);
const enableProposedApiFor = this._environmentService.args['enable-proposed-api'] || [];
for (const extension of extensions) {
if (!isFalsyOrEmpty(product.extensionAllowedProposedApi)
&& product.extensionAllowedProposedApi.indexOf(extension.id) >= 0
) {
// fast lane -> proposed api is available to all extensions
// that are listed in product.json-files
extension.enableProposedApi = true;
} else if (extension.enableProposedApi && !extension.isBuiltin) {
if (
!enableProposedApiForAll &&
enableProposedApiFor.indexOf(extension.id) < 0
) {
extension.enableProposedApi = false;
console.error(`Extension '${extension.id} cannot use PROPOSED API (must started out of dev or enabled via --enable-proposed-api)`);
} else {
// proposed api is available when developing or when an extension was explicitly
// spelled out via a command line argument
console.warn(`Extension '${extension.id}' uses PROPOSED API which is subject to change and removal without notice.`);
}
}
}
return extensions;
}
private _handleExtensionPointMessage(msg: IMessage) {
@@ -531,7 +637,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
const { type, extensionId, extensionPointId, message } = msg;
/* __GDPR__
"extensionsMessage" : {
"type" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
"type" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
"extensionId": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
"extensionPointId": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
"message": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }
@@ -543,7 +649,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
}
}
private static async _validateExtensionsCache(windowService: IWindowService, choiceService: IChoiceService, environmentService: IEnvironmentService, cacheKey: string, input: ExtensionScannerInput): TPromise<void> {
private static async _validateExtensionsCache(windowService: IWindowService, notificationService: INotificationService, environmentService: IEnvironmentService, cacheKey: string, input: ExtensionScannerInput): TPromise<void> {
const cacheFolder = path.join(environmentService.userDataPath, MANIFEST_CACHE_FOLDER);
const cacheFile = path.join(cacheFolder, cacheKey);
@@ -568,11 +674,14 @@ export class ExtensionService extends Disposable implements IExtensionService {
console.error(err);
}
choiceService.choose(Severity.Error, nls.localize('extensionCache.invalid', "Extensions have been modified on disk. Please reload the window."), [nls.localize('reloadWindow', "Reload Window")]).then(choice => {
if (choice === 0) {
windowService.reloadWindow();
}
});
notificationService.prompt(
Severity.Error,
nls.localize('extensionCache.invalid', "Extensions have been modified on disk. Please reload the window."),
[{
label: nls.localize('reloadWindow', "Reload Window"),
run: () => windowService.reloadWindow()
}]
);
}
private static async _readExtensionCache(environmentService: IEnvironmentService, cacheKey: string): TPromise<IExtensionCacheData> {
@@ -606,7 +715,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
}
}
private static async _scanExtensionsWithCache(windowService: IWindowService, choiceService: IChoiceService, environmentService: IEnvironmentService, cacheKey: string, input: ExtensionScannerInput, log: ILog): TPromise<IExtensionDescription[]> {
private static async _scanExtensionsWithCache(windowService: IWindowService, notificationService: INotificationService, environmentService: IEnvironmentService, cacheKey: string, input: ExtensionScannerInput, log: ILog): TPromise<IExtensionDescription[]> {
if (input.devMode) {
// Do not cache when running out of sources...
return ExtensionScanner.scanExtensions(input, log);
@@ -624,7 +733,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
// Validate the cache asynchronously after 5s
setTimeout(async () => {
try {
await this._validateExtensionsCache(windowService, choiceService, environmentService, cacheKey, input);
await this._validateExtensionsCache(windowService, notificationService, environmentService, cacheKey, input);
} catch (err) {
errors.onUnexpectedError(err);
}
@@ -646,7 +755,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
return result;
}
private static _scanInstalledExtensions(windowService: IWindowService, choiceService: IChoiceService, environmentService: IEnvironmentService, log: ILog): TPromise<{ system: IExtensionDescription[], user: IExtensionDescription[], development: IExtensionDescription[] }> {
private static _scanInstalledExtensions(windowService: IWindowService, notificationService: INotificationService, environmentService: IEnvironmentService, log: ILog): TPromise<{ system: IExtensionDescription[], user: IExtensionDescription[], development: IExtensionDescription[] }> {
const translationConfig: TPromise<Translations> = platform.translationsConfigFile
? pfs.readFile(platform.translationsConfigFile, 'utf8').then((content) => {
@@ -668,7 +777,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
const builtinExtensions = this._scanExtensionsWithCache(
windowService,
choiceService,
notificationService,
environmentService,
BUILTIN_MANIFEST_CACHE_FILE,
new ExtensionScannerInput(version, commit, locale, devMode, getSystemExtensionsRoot(), true, translations),
@@ -722,7 +831,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
? TPromise.as([])
: this._scanExtensionsWithCache(
windowService,
choiceService,
notificationService,
environmentService,
USER_MANIFEST_CACHE_FILE,
new ExtensionScannerInput(version, commit, locale, devMode, environmentService.extensionsPath, false, translations),
@@ -798,15 +907,12 @@ export class ExtensionService extends Disposable implements IExtensionService {
}
public _onExtensionActivated(extensionId: string, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string): void {
this._extensionHostProcessActivationTimes[extensionId] = new ActivationTimes(startup, codeLoadingTime, activateCallTime, activateResolvedTime, activationEvent);
this._extensionHostProcessManager.onExtensionActivated(extensionId, startup, codeLoadingTime, activateCallTime, activateResolvedTime, activationEvent);
this._onDidChangeExtensionsStatus.fire([extensionId]);
}
public _onExtensionRuntimeError(extensionId: string, err: Error): void {
if (!this._extensionHostExtensionRuntimeErrors[extensionId]) {
this._extensionHostExtensionRuntimeErrors[extensionId] = [];
}
this._extensionHostExtensionRuntimeErrors[extensionId].push(err);
this._extensionHostProcessManager.onExtensionRuntimeError(extensionId, err);
this._onDidChangeExtensionsStatus.fire([extensionId]);
}

View File

@@ -33,6 +33,12 @@ export class ExtensionDescriptionRegistry {
if (Array.isArray(extensionDescription.activationEvents)) {
for (let j = 0, lenJ = extensionDescription.activationEvents.length; j < lenJ; j++) {
let activationEvent = extensionDescription.activationEvents[j];
// TODO@joao: there's no easy way to contribute this
if (activationEvent === 'onUri') {
activationEvent = `onUri:${extensionDescription.id}`;
}
this._activationMap[activationEvent] = this._activationMap[activationEvent] || [];
this._activationMap[activationEvent].push(extensionDescription);
}

View File

@@ -496,7 +496,7 @@ export class ExtensionScanner {
/**
* Read the extension defined in `absoluteFolderPath`
*/
private static scanExtension(version: string, log: ILog, absoluteFolderPath: string, isBuiltin: boolean, nlsConfig: NlsConfiguration): TPromise<IExtensionDescription> {
public static scanExtension(version: string, log: ILog, absoluteFolderPath: string, isBuiltin: boolean, nlsConfig: NlsConfiguration): TPromise<IExtensionDescription> {
absoluteFolderPath = normalize(absoluteFolderPath);
let parser = new ExtensionManifestParser(version, log, absoluteFolderPath, isBuiltin);
@@ -550,11 +550,13 @@ export class ExtensionScanner {
const gallery: IExtensionReference[] = [];
refs.forEach(ref => {
const { id, version } = getIdAndVersionFromLocalExtensionId(ref.name);
if (!id || !version) {
nonGallery.push(ref);
} else {
gallery.push(ref);
if (ref.name.indexOf('.') !== 0) { // Do not consider user extension folder starting with `.`
const { id, version } = getIdAndVersionFromLocalExtensionId(ref.name);
if (!id || !version) {
nonGallery.push(ref);
} else {
gallery.push(ref);
}
}
});
refs = [...nonGallery, ...gallery];

View File

@@ -10,11 +10,88 @@ import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc';
import { LazyPromise } from 'vs/workbench/services/extensions/node/lazyPromise';
import { ProxyIdentifier, IRPCProtocol } from 'vs/workbench/services/extensions/node/proxyIdentifier';
import { CharCode } from 'vs/base/common/charCode';
import URI, { UriComponents } from 'vs/base/common/uri';
import { MarshalledObject } from 'vs/base/common/marshalling';
declare var Proxy: any; // TODO@TypeScript
export interface IURITransformer {
transformIncoming(uri: UriComponents): UriComponents;
transformOutgoing(uri: URI): URI;
}
function _transformOutgoingURIs(obj: any, transformer: IURITransformer, depth: number): any {
if (!obj || depth > 200) {
return null;
}
if (typeof obj === 'object') {
if (obj instanceof URI) {
return transformer.transformOutgoing(obj);
}
// walk object (or array)
for (let key in obj) {
if (Object.hasOwnProperty.call(obj, key)) {
const r = _transformOutgoingURIs(obj[key], transformer, depth + 1);
if (r !== null) {
obj[key] = r;
}
}
}
}
return null;
}
function transformOutgoingURIs(obj: any, transformer: IURITransformer): any {
const result = _transformOutgoingURIs(obj, transformer, 0);
if (result === null) {
// no change
return obj;
}
return result;
}
function _transformIncomingURIs(obj: any, transformer: IURITransformer, depth: number): any {
if (!obj || depth > 200) {
return null;
}
if (typeof obj === 'object') {
if ((<MarshalledObject>obj).$mid === 1) {
return transformer.transformIncoming(obj);
}
// walk object (or array)
for (let key in obj) {
if (Object.hasOwnProperty.call(obj, key)) {
const r = _transformIncomingURIs(obj[key], transformer, depth + 1);
if (r !== null) {
obj[key] = r;
}
}
}
}
return null;
}
function transformIncomingURIs(obj: any, transformer: IURITransformer): any {
const result = _transformIncomingURIs(obj, transformer, 0);
if (result === null) {
// no change
return obj;
}
return result;
}
export class RPCProtocol implements IRPCProtocol {
private readonly _uriTransformer: IURITransformer;
private _isDisposed: boolean;
private readonly _locals: { [id: string]: any; };
private readonly _proxies: { [id: string]: any; };
@@ -23,7 +100,8 @@ export class RPCProtocol implements IRPCProtocol {
private readonly _pendingRPCReplies: { [msgId: string]: LazyPromise; };
private readonly _multiplexor: RPCMultiplexer;
constructor(protocol: IMessagePassingProtocol) {
constructor(protocol: IMessagePassingProtocol, transformer: IURITransformer = null) {
this._uriTransformer = transformer;
this._isDisposed = false;
this._locals = Object.create(null);
this._proxies = Object.create(null);
@@ -43,6 +121,13 @@ export class RPCProtocol implements IRPCProtocol {
});
}
public transformIncomingURIs<T>(obj: T): T {
if (!this._uriTransformer) {
return obj;
}
return transformIncomingURIs(obj, this._uriTransformer);
}
public getProxy<T>(identifier: ProxyIdentifier<T>): T {
if (!this._proxies[identifier.id]) {
this._proxies[identifier.id] = this._createProxy(identifier.id);
@@ -84,6 +169,9 @@ export class RPCProtocol implements IRPCProtocol {
}
let msg = <RPCMessage>JSON.parse(rawmsg);
if (this._uriTransformer) {
msg = transformIncomingURIs(msg, this._uriTransformer);
}
switch (msg.type) {
case MessageType.Request:
@@ -109,6 +197,9 @@ export class RPCProtocol implements IRPCProtocol {
this._invokedHandlers[callId].then((r) => {
delete this._invokedHandlers[callId];
if (this._uriTransformer) {
r = transformOutgoingURIs(r, this._uriTransformer);
}
this._multiplexor.send(MessageFactory.replyOK(callId, r));
}, (err) => {
delete this._invokedHandlers[callId];
@@ -185,6 +276,9 @@ export class RPCProtocol implements IRPCProtocol {
});
this._pendingRPCReplies[callId] = result;
if (this._uriTransformer) {
args = transformOutgoingURIs(args, this._uriTransformer);
}
this._multiplexor.send(MessageFactory.request(callId, proxyId, methodName, args));
return result;
}