Initial VS Code 1.19 source merge (#571)

* Initial 1.19 xcopy

* Fix yarn build

* Fix numerous build breaks

* Next batch of build break fixes

* More build break fixes

* Runtime breaks

* Additional post merge fixes

* Fix windows setup file

* Fix test failures.

* Update license header blocks to refer to source eula
This commit is contained in:
Karl Burtram
2018-01-28 23:37:17 -08:00
committed by GitHub
parent 9a1ac20710
commit 251ae01c3e
8009 changed files with 93378 additions and 35634 deletions

View File

@@ -25,12 +25,11 @@ 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 } from 'vs/base/common/event';
import { fromEventEmitter } from 'vs/base/node/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/platform/extensions/common/extensions';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { ICrashReporterService } from 'vs/workbench/services/crashReporter/common/crashReporterService';
import { ICrashReporterService } from 'vs/workbench/services/crashReporter/electron-browser/crashReporterService';
import { IBroadcastService, IBroadcast } from 'vs/platform/broadcast/electron-browser/broadcastService';
import { isEqual } from 'vs/base/common/paths';
import { EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, EXTENSION_RELOAD_BROADCAST_CHANNEL, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL } from 'vs/platform/extensions/common/extensionHost';
@@ -56,6 +55,7 @@ export class ExtensionHostProcessWorker {
// Resources, in order they get acquired/created when .start() is called:
private _namedPipeServer: Server;
private _inspectPort: number;
private _extensionHostProcess: ChildProcess;
private _extensionHostConnection: Socket;
private _messageProtocol: TPromise<IMessagePassingProtocol>;
@@ -131,13 +131,12 @@ export class ExtensionHostProcessWorker {
}
if (!this._messageProtocol) {
this._messageProtocol = TPromise.join<any>([this._tryListenOnPipe(), this._tryFindDebugPort()]).then((data: [string, number]) => {
this._messageProtocol = TPromise.join([this._tryListenOnPipe(), this._tryFindDebugPort()]).then(data => {
const pipeName = data[0];
// The port will be 0 if there's no need to debug or if a free port was not found
const port = data[1];
const portData = data[1];
const opts = {
env: objects.mixin(objects.clone(process.env), {
env: objects.mixin(objects.deepClone(process.env), {
AMD_ENTRYPOINT: 'vs/workbench/node/extensionHostProcess',
PIPE_LOGGING: 'true',
VERBOSE_LOGGING: true,
@@ -152,12 +151,22 @@ export class ExtensionHostProcessWorker {
// We detach because we have noticed that when the renderer exits, its child processes
// (i.e. extension host) are taken down in a brutal fashion by the OS
detached: !!isWindows,
execArgv: port
? ['--nolazy', (this._isExtensionDevDebugBrk ? '--inspect-brk=' : '--inspect=') + port]
: undefined,
execArgv: <string[]>undefined,
silent: true
};
if (portData.actual) {
opts.execArgv = [
'--nolazy',
(this._isExtensionDevDebugBrk ? '--inspect-brk=' : '--inspect=') + portData.actual
];
if (!portData.expected) {
// No one asked for 'inspect' or 'inspect-brk', only us. We add another
// option such that the extension host can manipulate the execArgv array
opts.env.VSCODE_PREVENT_FOREIGN_INSPECT = true;
}
}
const crashReporterOptions = this._crashReporterService.getChildProcessStartOptions('extensionHost');
if (crashReporterOptions) {
opts.env.CRASH_REPORTER_START_OPTIONS = JSON.stringify(crashReporterOptions);
@@ -170,8 +179,8 @@ export class ExtensionHostProcessWorker {
type Output = { data: string, format: string[] };
this._extensionHostProcess.stdout.setEncoding('utf8');
this._extensionHostProcess.stderr.setEncoding('utf8');
const onStdout = fromEventEmitter<string>(this._extensionHostProcess.stdout, 'data');
const onStderr = fromEventEmitter<string>(this._extensionHostProcess.stderr, 'data');
const onStdout = fromNodeEventEmitter<string>(this._extensionHostProcess.stdout, 'data');
const onStderr = fromNodeEventEmitter<string>(this._extensionHostProcess.stderr, 'data');
const onOutput = anyEvent(
mapEvent(onStdout, o => ({ data: `%c${o}`, format: [''] })),
mapEvent(onStderr, o => ({ data: `%c${o}`, format: ['color: red'] }))
@@ -208,15 +217,16 @@ export class ExtensionHostProcessWorker {
this._extensionHostProcess.on('exit', (code: number, signal: string) => this._onExtHostProcessExit(code, signal));
// Notify debugger that we are ready to attach to the process if we run a development extension
if (this._isExtensionDevHost && port) {
if (this._isExtensionDevHost && portData.actual) {
this._broadcastService.broadcast({
channel: EXTENSION_ATTACH_BROADCAST_CHANNEL,
payload: {
debugId: this._environmentService.debugExtensionHost.debugId,
port
port: portData.actual
}
});
}
this._inspectPort = portData.actual;
// Help in case we fail to start it
let startupTimeoutHandle: number;
@@ -260,26 +270,27 @@ export class ExtensionHostProcessWorker {
/**
* Find a free port if extension host debugging is enabled.
*/
private _tryFindDebugPort(): TPromise<number> {
const extensionHostPort = this._environmentService.debugExtensionHost.port;
if (typeof extensionHostPort !== 'number') {
return TPromise.wrap<number>(0);
private _tryFindDebugPort(): TPromise<{ expected: number; actual: number }> {
let expected: number;
let startPort = 9333;
if (typeof this._environmentService.debugExtensionHost.port === 'number') {
startPort = expected = this._environmentService.debugExtensionHost.port;
}
return new TPromise<number>((c, e) => {
findFreePort(extensionHostPort, 10 /* try 10 ports */, 5000 /* try up to 5 seconds */, (port) => {
return new TPromise((c, e) => {
return findFreePort(startPort, 10 /* try 10 ports */, 5000 /* try up to 5 seconds */).then(port => {
if (!port) {
console.warn('%c[Extension Host] %cCould not find a free port for debugging', 'color: blue', 'color: black');
return c(void 0);
}
if (port !== extensionHostPort) {
console.warn(`%c[Extension Host] %cProvided debugging port ${extensionHostPort} is not free, using ${port} instead.`, 'color: blue', 'color: black');
}
if (this._isExtensionDevDebugBrk) {
console.warn(`%c[Extension Host] %cSTOPPED on first line for debugging on port ${port}`, 'color: blue', 'color: black');
} else {
console.info(`%c[Extension Host] %cdebugger listening on port ${port}`, 'color: blue', 'color: black');
if (expected && port !== expected) {
console.warn(`%c[Extension Host] %cProvided debugging port ${expected} is not free, using ${port} instead.`, 'color: blue', 'color: black');
}
if (this._isExtensionDevDebugBrk) {
console.warn(`%c[Extension Host] %cSTOPPED on first line for debugging on port ${port}`, 'color: blue', 'color: black');
} else {
console.info(`%c[Extension Host] %cdebugger listening on port ${port}`, 'color: blue', 'color: black');
}
}
return c(port);
return c({ expected, actual: port });
});
});
}
@@ -364,7 +375,10 @@ export class ExtensionHostProcessWorker {
extensions: extensionDescriptions,
// Send configurations scopes only in development mode.
configuration: !this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment ? { ...configurationData, configurationScopes: getScopes(this._configurationService.keys().default) } : configurationData,
telemetryInfo
telemetryInfo,
args: this._environmentService.args,
execPath: this._environmentService.execPath,
windowId: this._windowService.getCurrentWindowId()
};
return r;
});
@@ -427,6 +441,10 @@ export class ExtensionHostProcessWorker {
}
}
public getInspectPort(): number {
return this._inspectPort;
}
public terminate(): void {
if (this._terminating) {
return;

View File

@@ -0,0 +1,154 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IExtensionService, IExtensionDescription, ProfileSession, IExtensionHostProfile, ProfileSegmentId } from 'vs/platform/extensions/common/extensions';
import { TPromise } from 'vs/base/common/winjs.base';
import { localize } from 'vs/nls';
import { TernarySearchTree } from 'vs/base/common/map';
import { realpathSync } from 'vs/base/node/extfs';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { IStatusbarService, StatusbarAlignment } from 'vs/platform/statusbar/common/statusbar';
import { writeFile } from 'vs/base/node/pfs';
import * as path from 'path';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { setTimeout } from 'timers';
import { Profile, ProfileNode } from 'v8-inspect-profiler';
export class ExtensionHostProfiler {
constructor(private readonly _port: number, @IExtensionService private readonly _extensionService: IExtensionService) {
}
public start(): TPromise<ProfileSession> {
return TPromise.wrap(import('v8-inspect-profiler')).then(profiler => {
return profiler.startProfiling({ port: this._port }).then(session => {
return {
stop: () => {
return TPromise.wrap(session.stop()).then(profile => {
return this._extensionService.getExtensions().then(extensions => {
return this.distill(profile.profile, extensions);
});
});
}
};
});
});
}
private distill(profile: Profile, extensions: IExtensionDescription[]): IExtensionHostProfile {
let searchTree = TernarySearchTree.forPaths<IExtensionDescription>();
for (let extension of extensions) {
searchTree.set(realpathSync(extension.extensionFolderPath), extension);
}
let nodes = profile.nodes;
let idsToNodes = new Map<number, ProfileNode>();
let idsToSegmentId = new Map<number, ProfileSegmentId>();
for (let node of nodes) {
idsToNodes.set(node.id, node);
}
function visit(node: ProfileNode, segmentId: ProfileSegmentId) {
if (!segmentId) {
switch (node.callFrame.functionName) {
case '(root)':
break;
case '(program)':
segmentId = 'program';
break;
case '(garbage collector)':
segmentId = 'gc';
break;
default:
segmentId = 'self';
break;
}
} else if (segmentId === 'self' && node.callFrame.url) {
let extension = searchTree.findSubstr(node.callFrame.url);
if (extension) {
segmentId = extension.id;
}
}
idsToSegmentId.set(node.id, segmentId);
if (node.children) {
for (let child of node.children) {
visit(idsToNodes.get(child), segmentId);
}
}
}
visit(nodes[0], null);
let samples = profile.samples;
let timeDeltas = profile.timeDeltas;
let distilledDeltas: number[] = [];
let distilledIds: ProfileSegmentId[] = [];
let currSegmentTime = 0;
let currSegmentId = void 0;
for (let i = 0; i < samples.length; i++) {
let id = samples[i];
let segmentId = idsToSegmentId.get(id);
if (segmentId !== currSegmentId) {
if (currSegmentId) {
distilledIds.push(currSegmentId);
distilledDeltas.push(currSegmentTime);
}
currSegmentId = segmentId;
currSegmentTime = 0;
}
currSegmentTime += timeDeltas[i];
}
if (currSegmentId) {
distilledIds.push(currSegmentId);
distilledDeltas.push(currSegmentTime);
}
idsToNodes = null;
idsToSegmentId = null;
searchTree = null;
return {
startTime: profile.startTime,
endTime: profile.endTime,
deltas: distilledDeltas,
ids: distilledIds,
data: profile,
getAggregatedTimes: () => {
let segmentsToTime = new Map<ProfileSegmentId, number>();
for (let i = 0; i < distilledIds.length; i++) {
let id = distilledIds[i];
segmentsToTime.set(id, (segmentsToTime.get(id) || 0) + distilledDeltas[i]);
}
return segmentsToTime;
}
};
}
}
CommandsRegistry.registerCommand('exthost.profile.start', async accessor => {
const statusbarService = accessor.get(IStatusbarService);
const extensionService = accessor.get(IExtensionService);
const environmentService = accessor.get(IEnvironmentService);
const handle = statusbarService.addEntry({ text: localize('message', "$(zap) Profiling Extension Host...") }, StatusbarAlignment.LEFT);
extensionService.startExtensionHostProfile().then(session => {
setTimeout(() => {
session.stop().then(result => {
result.getAggregatedTimes().forEach((val, index) => {
console.log(`${index} : ${Math.round(val / 1000)} ms`);
});
let profilePath = path.join(environmentService.userHome, 'extHostProfile.cpuprofile');
console.log(`Saving profile at ${profilePath}`);
return writeFile(profilePath, JSON.stringify(result.data));
}).then(() => {
handle.dispose();
});
}, 5000);
});
});

View File

@@ -6,30 +6,25 @@
'use strict';
import * as nls from 'vs/nls';
import * as Platform from 'vs/base/common/platform';
import pfs = require('vs/base/node/pfs');
import * as pfs from 'vs/base/node/pfs';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { TPromise } from 'vs/base/common/winjs.base';
import { groupBy, values } from 'vs/base/common/collections';
import { join, normalize, extname } from 'path';
import json = require('vs/base/common/json');
import Types = require('vs/base/common/types');
import * as json from 'vs/base/common/json';
import * as types from 'vs/base/common/types';
import { isValidExtensionDescription } from 'vs/platform/extensions/node/extensionValidator';
import * as semver from 'semver';
import { getIdAndVersionFromLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { getIdAndVersionFromLocalExtensionId } from 'vs/platform/extensionManagement/node/extensionManagementUtil';
import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages';
const MANIFEST_FILE = 'package.json';
const devMode = !!process.env['VSCODE_DEV'];
interface NlsConfiguration {
locale: string;
pseudo: boolean;
export interface NlsConfiguration {
readonly devMode: boolean;
readonly locale: string;
readonly pseudo: boolean;
}
const nlsConfig: NlsConfiguration = {
locale: Platform.locale,
pseudo: Platform.locale === 'pseudo'
};
export interface ILog {
error(source: string, message: string): void;
@@ -39,11 +34,11 @@ export interface ILog {
abstract class ExtensionManifestHandler {
protected _ourVersion: string;
protected _log: ILog;
protected _absoluteFolderPath: string;
protected _isBuiltin: boolean;
protected _absoluteManifestPath: string;
protected readonly _ourVersion: string;
protected readonly _log: ILog;
protected readonly _absoluteFolderPath: string;
protected readonly _isBuiltin: boolean;
protected readonly _absoluteManifestPath: string;
constructor(ourVersion: string, log: ILog, absoluteFolderPath: string, isBuiltin: boolean) {
this._ourVersion = ourVersion;
@@ -82,6 +77,13 @@ class ExtensionManifestParser extends ExtensionManifestHandler {
class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
private readonly _nlsConfig: NlsConfiguration;
constructor(ourVersion: string, log: ILog, absoluteFolderPath: string, isBuiltin: boolean, nlsConfig: NlsConfiguration) {
super(ourVersion, log, absoluteFolderPath, isBuiltin);
this._nlsConfig = nlsConfig;
}
public replaceNLS(extensionDescription: IExtensionDescription): TPromise<IExtensionDescription> {
let extension = extname(this._absoluteManifestPath);
let basename = this._absoluteManifestPath.substr(0, this._absoluteManifestPath.length - extension.length);
@@ -90,7 +92,7 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
if (!exists) {
return extensionDescription;
}
return ExtensionManifestNLSReplacer.findMessageBundles(basename).then((messageBundle) => {
return ExtensionManifestNLSReplacer.findMessageBundles(this._nlsConfig, basename).then((messageBundle) => {
if (!messageBundle.localized) {
return extensionDescription;
}
@@ -106,7 +108,7 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
return extensionDescription;
}
ExtensionManifestNLSReplacer._replaceNLStrings(extensionDescription, messages, originalMessages, this._log, this._absoluteFolderPath);
ExtensionManifestNLSReplacer._replaceNLStrings(this._nlsConfig, extensionDescription, messages, originalMessages, this._log, this._absoluteFolderPath);
return extensionDescription;
});
}, (err) => {
@@ -136,7 +138,7 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
* Finds localized message bundle and the original (unlocalized) one.
* If the localized file is not present, returns null for the original and marks original as localized.
*/
private static findMessageBundles(basename: string): TPromise<{ localized: string, original: string }> {
private static findMessageBundles(nlsConfig: NlsConfiguration, basename: string): TPromise<{ localized: string, original: string }> {
return new TPromise<{ localized: string, original: string }>((c, e, p) => {
function loop(basename: string, locale: string): void {
let toCheck = `${basename}.nls.${locale}.json`;
@@ -154,7 +156,7 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
});
}
if (devMode || nlsConfig.pseudo || !nlsConfig.locale) {
if (nlsConfig.devMode || nlsConfig.pseudo || !nlsConfig.locale) {
return c({ localized: basename + '.nls.json', original: null });
}
loop(basename, nlsConfig.locale);
@@ -165,10 +167,10 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
* This routine makes the following assumptions:
* The root element is an object literal
*/
private static _replaceNLStrings<T>(literal: T, messages: { [key: string]: string; }, originalMessages: { [key: string]: string }, log: ILog, messageScope: string): void {
private static _replaceNLStrings<T>(nlsConfig: NlsConfiguration, literal: T, messages: { [key: string]: string; }, originalMessages: { [key: string]: string }, log: ILog, messageScope: string): void {
function processEntry(obj: any, key: string | number, command?: boolean) {
let value = obj[key];
if (Types.isString(value)) {
if (types.isString(value)) {
let str = <string>value;
let length = str.length;
if (length > 1 && str[0] === '%' && str[length - 1] === '%') {
@@ -184,13 +186,13 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
log.warn(messageScope, nls.localize('missingNLSKey', "Couldn't find message for key {0}.", messageKey));
}
}
} else if (Types.isObject(value)) {
} else if (types.isObject(value)) {
for (let k in value) {
if (value.hasOwnProperty(k)) {
k === 'commands' ? processEntry(value, k, true) : processEntry(value, k, command);
}
}
} else if (Types.isArray(value)) {
} else if (types.isArray(value)) {
for (let i = 0; i < value.length; i++) {
processEntry(value, i, command);
}
@@ -201,7 +203,7 @@ class ExtensionManifestNLSReplacer extends ExtensionManifestHandler {
if (literal.hasOwnProperty(key)) {
processEntry(literal, key);
}
};
}
}
}
@@ -251,17 +253,48 @@ class ExtensionManifestValidator extends ExtensionManifestHandler {
}
}
export class ExtensionScannerInput {
public mtime: number;
constructor(
public readonly ourVersion: string,
public readonly commit: string,
public readonly locale: string,
public readonly devMode: boolean,
public readonly absoluteFolderPath: string,
public readonly isBuiltin: boolean
) {
// Keep empty!! (JSON.parse)
}
public static createNLSConfig(input: ExtensionScannerInput): NlsConfiguration {
return {
devMode: input.devMode,
locale: input.locale,
pseudo: input.locale === 'pseudo'
};
}
public static equals(a: ExtensionScannerInput, b: ExtensionScannerInput): boolean {
return (
a.ourVersion === b.ourVersion
&& a.commit === b.commit
&& a.locale === b.locale
&& a.devMode === b.devMode
&& a.absoluteFolderPath === b.absoluteFolderPath
&& a.isBuiltin === b.isBuiltin
&& a.mtime === b.mtime
);
}
}
export class ExtensionScanner {
/**
* Read the extension defined in `absoluteFolderPath`
*/
public static scanExtension(
version: string,
log: ILog,
absoluteFolderPath: string,
isBuiltin: boolean
): TPromise<IExtensionDescription> {
private 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);
@@ -270,7 +303,7 @@ export class ExtensionScanner {
return null;
}
let nlsReplacer = new ExtensionManifestNLSReplacer(version, log, absoluteFolderPath, isBuiltin);
let nlsReplacer = new ExtensionManifestNLSReplacer(version, log, absoluteFolderPath, isBuiltin, nlsConfig);
return nlsReplacer.replaceNLS(extensionDescription);
}).then((extensionDescription) => {
if (extensionDescription === null) {
@@ -285,81 +318,87 @@ export class ExtensionScanner {
/**
* Scan a list of extensions defined in `absoluteFolderPath`
*/
public static scanExtensions(
version: string,
log: ILog,
absoluteFolderPath: string,
isBuiltin: boolean
): TPromise<IExtensionDescription[]> {
let obsolete = TPromise.as({});
public static async scanExtensions(input: ExtensionScannerInput, log: ILog): TPromise<IExtensionDescription[]> {
const absoluteFolderPath = input.absoluteFolderPath;
const isBuiltin = input.isBuiltin;
if (!isBuiltin) {
obsolete = pfs.readFile(join(absoluteFolderPath, '.obsolete'), 'utf8')
.then(raw => JSON.parse(raw))
.then(null, err => ({}));
}
try {
let obsolete: { [folderName: string]: boolean; } = {};
if (!isBuiltin) {
try {
const obsoleteFileContents = await pfs.readFile(join(absoluteFolderPath, '.obsolete'), 'utf8');
obsolete = JSON.parse(obsoleteFileContents);
} catch (err) {
// Don't care
}
}
return obsolete.then(obsolete => {
return pfs.readDirsInDir(absoluteFolderPath)
.then(folders => {
if (isBuiltin) {
return folders;
const rawFolders = await pfs.readDirsInDir(absoluteFolderPath);
let folders: string[] = null;
if (isBuiltin) {
folders = rawFolders;
} else {
// TODO: align with extensionsService
const nonGallery: string[] = [];
const gallery: { folder: string; id: string; version: string; }[] = [];
rawFolders.forEach(folder => {
if (obsolete[folder]) {
return;
}
// TODO: align with extensionsService
const nonGallery: string[] = [];
const gallery: { folder: string; id: string; version: string; }[] = [];
const { id, version } = getIdAndVersionFromLocalExtensionId(folder);
folders.forEach(folder => {
if (obsolete[folder]) {
return;
}
if (!id || !version) {
nonGallery.push(folder);
return;
}
const { id, version } = getIdAndVersionFromLocalExtensionId(folder);
if (!id || !version) {
nonGallery.push(folder);
return;
}
gallery.push({ folder, id, version });
});
const byId = values(groupBy(gallery, p => p.id));
const latest = byId.map(p => p.sort((a, b) => semver.rcompare(a.version, b.version))[0])
.map(a => a.folder);
return [...nonGallery, ...latest];
})
.then(folders => TPromise.join(folders.map(f => this.scanExtension(version, log, join(absoluteFolderPath, f), isBuiltin))))
.then(extensionDescriptions => extensionDescriptions.filter(item => item !== null))
.then(null, err => {
log.error(absoluteFolderPath, err);
return [];
gallery.push({ folder, id, version });
});
});
const byId = values(groupBy(gallery, p => p.id));
const latest = byId.map(p => p.sort((a, b) => semver.rcompare(a.version, b.version))[0])
.map(a => a.folder);
folders = [...nonGallery, ...latest];
}
const nlsConfig = ExtensionScannerInput.createNLSConfig(input);
let extensionDescriptions = await TPromise.join(folders.map(f => this.scanExtension(input.ourVersion, log, join(absoluteFolderPath, f), isBuiltin, nlsConfig)));
extensionDescriptions = extensionDescriptions.filter(item => item !== null);
extensionDescriptions.sort((a, b) => {
if (a.extensionFolderPath < b.extensionFolderPath) {
return -1;
}
return 1;
});
return extensionDescriptions;
} catch (err) {
log.error(absoluteFolderPath, err);
return [];
}
}
/**
* Combination of scanExtension and scanExtensions: If an extension manifest is found at root, we load just this extension,
* otherwise we assume the folder contains multiple extensions.
*/
public static scanOneOrMultipleExtensions(
version: string,
log: ILog,
absoluteFolderPath: string,
isBuiltin: boolean
): TPromise<IExtensionDescription[]> {
public static scanOneOrMultipleExtensions(input: ExtensionScannerInput, log: ILog): TPromise<IExtensionDescription[]> {
const absoluteFolderPath = input.absoluteFolderPath;
const isBuiltin = input.isBuiltin;
return pfs.fileExists(join(absoluteFolderPath, MANIFEST_FILE)).then((exists) => {
if (exists) {
return this.scanExtension(version, log, absoluteFolderPath, isBuiltin).then((extensionDescription) => {
const nlsConfig = ExtensionScannerInput.createNLSConfig(input);
return this.scanExtension(input.ourVersion, log, absoluteFolderPath, isBuiltin, nlsConfig).then((extensionDescription) => {
if (extensionDescription === null) {
return [];
}
return [extensionDescription];
});
}
return this.scanExtensions(version, log, absoluteFolderPath, isBuiltin);
return this.scanExtensions(input, log);
}, (err) => {
log.error(absoluteFolderPath, err);
return [];

View File

@@ -6,18 +6,21 @@
import * as nls from 'vs/nls';
import * as errors from 'vs/base/common/errors';
import * as objects from 'vs/base/common/objects';
import Severity from 'vs/base/common/severity';
import { TPromise } from 'vs/base/common/winjs.base';
import pkg from 'vs/platform/node/package';
import * as path from 'path';
import * as pfs from 'vs/base/node/pfs';
import URI from 'vs/base/common/uri';
import * as platform from 'vs/base/common/platform';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
import { IMessage, IExtensionDescription, IExtensionsStatus, IExtensionService, ExtensionPointContribution, ActivationTimes } from 'vs/platform/extensions/common/extensions';
import { IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions, getGloballyDisabledExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IMessage, IExtensionDescription, IExtensionsStatus, IExtensionService, ExtensionPointContribution, ActivationTimes, IExtensionHostInformation, ProfileSession, 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 { ExtensionsRegistry, ExtensionPoint, IExtensionPointUser, ExtensionMessageCollector, IExtensionPoint } from 'vs/platform/extensions/common/extensionsRegistry';
import { ExtensionScanner, ILog } from 'vs/workbench/services/extensions/electron-browser/extensionPoints';
import { IMessageService } from 'vs/platform/message/common/message';
import { ExtensionScanner, ILog, ExtensionScannerInput } from 'vs/workbench/services/extensions/electron-browser/extensionPoints';
import { IMessageService, CloseAction } from 'vs/platform/message/common/message';
import { ProxyIdentifier } from 'vs/workbench/services/thread/common/threadService';
import { ExtHostContext, ExtHostExtensionServiceShape, IExtHostContext, MainContext } from 'vs/workbench/api/node/extHost.protocol';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
@@ -26,13 +29,18 @@ import { IStorageService } from 'vs/platform/storage/common/storage';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ExtensionHostProcessWorker } from 'vs/workbench/services/extensions/electron-browser/extensionHost';
import { MainThreadService } from 'vs/workbench/services/thread/electron-browser/threadService';
import { Barrier } from 'vs/workbench/services/extensions/node/barrier';
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc';
import { ExtHostCustomersRegistry } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { Action } from 'vs/base/common/actions';
import { IDisposable } from 'vs/base/common/lifecycle';
import { startTimer } from 'vs/base/node/startupTimers';
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 { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler';
import { ReloadWindowAction } from 'vs/workbench/electron-browser/actions';
import product from 'vs/platform/node/product';
const SystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', 'extensions'));
@@ -48,17 +56,21 @@ function messageWithSource2(source: string, message: string): string {
}
const hasOwnProperty = Object.hasOwnProperty;
const NO_OP_VOID_PROMISE = TPromise.as<void>(void 0);
const NO_OP_VOID_PROMISE = TPromise.wrap<void>(void 0);
export class ExtensionService implements IExtensionService {
export class ExtensionService extends Disposable implements IExtensionService {
public _serviceBrand: any;
private _onDidRegisterExtensions: Emitter<IExtensionDescription[]>;
private _registry: ExtensionDescriptionRegistry;
private readonly _barrier: Barrier;
private readonly _installedExtensionsReady: Barrier;
private readonly _isDev: boolean;
private readonly _extensionsStatus: { [id: string]: IExtensionsStatus };
private readonly _extensionsMessages: { [id: string]: IMessage[] };
private _allRequestedActivateEvents: { [activationEvent: string]: boolean; };
private readonly _onDidChangeExtensionsStatus: Emitter<string[]> = this._register(new Emitter<string[]>());
public readonly onDidChangeExtensionsStatus: Event<string[]> = this._onDidChangeExtensionsStatus.event;
// --- Members used per extension host process
@@ -67,6 +79,7 @@ export class ExtensionService implements IExtensionService {
*/
private _extensionHostProcessFinishedActivateEvents: { [activationEvent: string]: boolean; };
private _extensionHostProcessActivationTimes: { [id: string]: ActivationTimes; };
private _extensionHostExtensionRuntimeErrors: { [id: string]: Error[]; };
private _extensionHostProcessWorker: ExtensionHostProcessWorker;
private _extensionHostProcessThreadService: MainThreadService;
private _extensionHostProcessCustomers: IDisposable[];
@@ -82,23 +95,49 @@ export class ExtensionService implements IExtensionService {
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@IExtensionEnablementService private readonly _extensionEnablementService: IExtensionEnablementService,
@IStorageService private readonly _storageService: IStorageService,
@IWindowService private readonly _windowService: IWindowService
@IWindowService private readonly _windowService: IWindowService,
@ILifecycleService lifecycleService: ILifecycleService
) {
super();
this._registry = null;
this._barrier = new Barrier();
this._installedExtensionsReady = new Barrier();
this._isDev = !this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment;
this._extensionsStatus = {};
this._extensionsMessages = {};
this._allRequestedActivateEvents = Object.create(null);
this._onDidRegisterExtensions = new Emitter<IExtensionDescription[]>();
this._extensionHostProcessFinishedActivateEvents = Object.create(null);
this._extensionHostProcessActivationTimes = Object.create(null);
this._extensionHostExtensionRuntimeErrors = Object.create(null);
this._extensionHostProcessWorker = null;
this._extensionHostProcessThreadService = null;
this._extensionHostProcessCustomers = [];
this._extensionHostProcessProxy = null;
this._startExtensionHostProcess([]);
this._scanAndHandleExtensions();
lifecycleService.when(LifecyclePhase.Running).then(() => {
// delay extension host creation and extension scanning
// until after workbench is running
this._startExtensionHostProcess([]);
this._scanAndHandleExtensions();
});
}
public dispose(): void {
super.dispose();
}
public get onDidRegisterExtensions(): Event<IExtensionDescription[]> {
return this._onDidRegisterExtensions.event;
}
public getExtensionHostInformation(): IExtensionHostInformation {
if (!this._extensionHostProcessWorker) {
throw errors.illegalState();
}
return <IExtensionHostInformation>{
inspectPort: this._extensionHostProcessWorker.getInspectPort()
};
}
public restartExtensionHost(): void {
@@ -115,8 +154,11 @@ export class ExtensionService implements IExtensionService {
}
private _stopExtensionHostProcess(): void {
const previouslyActivatedExtensionIds = Object.keys(this._extensionHostProcessActivationTimes);
this._extensionHostProcessFinishedActivateEvents = Object.create(null);
this._extensionHostProcessActivationTimes = Object.create(null);
this._extensionHostExtensionRuntimeErrors = Object.create(null);
if (this._extensionHostProcessWorker) {
this._extensionHostProcessWorker.dispose();
this._extensionHostProcessWorker = null;
@@ -135,6 +177,8 @@ export class ExtensionService implements IExtensionService {
}
this._extensionHostProcessCustomers = [];
this._extensionHostProcessProxy = null;
this._onDidChangeExtensionsStatus.fire(previouslyActivatedExtensionIds);
}
private _startExtensionHostProcess(initialActivationEvents: string[]): void {
@@ -216,7 +260,7 @@ export class ExtensionService implements IExtensionService {
// ---- begin IExtensionService
public activateByEvent(activationEvent: string): TPromise<void> {
if (this._barrier.isOpen()) {
if (this._installedExtensionsReady.isOpen()) {
// Extensions have been scanned and interpreted
if (!this._registry.containsActivationEvent(activationEvent)) {
@@ -234,7 +278,7 @@ export class ExtensionService implements IExtensionService {
// Record the fact that this activationEvent was requested (in case of a restart)
this._allRequestedActivateEvents[activationEvent] = true;
return this._barrier.wait().then(() => this._activateByEvent(activationEvent));
return this._installedExtensionsReady.wait().then(() => this._activateByEvent(activationEvent));
}
}
@@ -249,18 +293,18 @@ export class ExtensionService implements IExtensionService {
});
}
public onReady(): TPromise<boolean> {
return this._barrier.wait();
public whenInstalledExtensionsRegistered(): TPromise<boolean> {
return this._installedExtensionsReady.wait();
}
public getExtensions(): TPromise<IExtensionDescription[]> {
return this.onReady().then(() => {
return this._installedExtensionsReady.wait().then(() => {
return this._registry.getAllExtensionDescriptions();
});
}
public readExtensionPointContributions<T>(extPoint: IExtensionPoint<T>): TPromise<ExtensionPointContribution<T>[]> {
return this.onReady().then(() => {
return this._installedExtensionsReady.wait().then(() => {
let availableExtensions = this._registry.getAllExtensionDescriptions();
let result: ExtensionPointContribution<T>[] = [], resultLen = 0;
@@ -277,11 +321,30 @@ export class ExtensionService implements IExtensionService {
}
public getExtensionsStatus(): { [id: string]: IExtensionsStatus; } {
return this._extensionsStatus;
let result: { [id: string]: IExtensionsStatus; } = Object.create(null);
if (this._registry) {
const extensions = this._registry.getAllExtensionDescriptions();
for (let i = 0, len = extensions.length; i < len; i++) {
const extension = extensions[i];
const id = extension.id;
result[id] = {
messages: this._extensionsMessages[id],
activationTimes: this._extensionHostProcessActivationTimes[id],
runtimeErrors: this._extensionHostExtensionRuntimeErrors[id],
};
}
}
return result;
}
public getExtensionsActivationTimes(): { [id: string]: ActivationTimes; } {
return this._extensionHostProcessActivationTimes;
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');
}
// ---- end IExtensionService
@@ -290,63 +353,100 @@ export class ExtensionService implements IExtensionService {
private _scanAndHandleExtensions(): void {
this._getRuntimeExtension()
.then(runtimeExtensons => {
this._registry = new ExtensionDescriptionRegistry(runtimeExtensons);
let availableExtensions = this._registry.getAllExtensionDescriptions();
let extensionPoints = ExtensionsRegistry.getExtensionPoints();
let messageHandler = (msg: IMessage) => this._handleExtensionPointMessage(msg);
for (let i = 0, len = extensionPoints.length; i < len; i++) {
const clock = time(`handleExtensionPoint:${extensionPoints[i].name}`);
try {
ExtensionService._handleExtensionPoint(extensionPoints[i], availableExtensions, messageHandler);
} finally {
clock.stop();
}
}
mark('extensionHostReady');
this._installedExtensionsReady.open();
this._onDidRegisterExtensions.fire(availableExtensions);
this._onDidChangeExtensionsStatus.fire(availableExtensions.map(e => e.id));
});
}
private _getRuntimeExtension(): TPromise<IExtensionDescription[]> {
const log = new Logger((severity, source, message) => {
this._logOrShowMessage(severity, this._isDev ? messageWithSource2(source, message) : message);
});
ExtensionService._scanInstalledExtensions(this._environmentService, log).then((installedExtensions) => {
return ExtensionService._scanInstalledExtensions(this._instantiationService, this._messageService, 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; } = {};
let extensionsToDisable: IExtensionIdentifier[] = [];
let userMigratedSystemExtensions: IExtensionIdentifier[] = [{ id: BetterMergeId }];
// Migrate enablement service to use identifiers
this._extensionEnablementService.migrateToIdentifiers(installedExtensions);
system.forEach((systemExtension) => {
// Disabling system extensions is not supported
result[systemExtension.id] = systemExtension;
});
const disabledExtensions = [
...getGloballyDisabledExtensions(this._extensionEnablementService, this._storageService, installedExtensions),
...this._extensionEnablementService.getWorkspaceDisabledExtensions()
];
user.forEach((userExtension) => {
if (result.hasOwnProperty(userExtension.id)) {
log.warn(userExtension.extensionFolderPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result[userExtension.id].extensionFolderPath, userExtension.extensionFolderPath));
}
if (disabledExtensions.every(disabled => !areSameExtensions(disabled, userExtension))) {
// Check if the extension is changed to system extension
let userMigratedSystemExtension = userMigratedSystemExtensions.filter(userMigratedSystemExtension => areSameExtensions(userMigratedSystemExtension, { id: userExtension.id }))[0];
if (userMigratedSystemExtension) {
extensionsToDisable.push(userMigratedSystemExtension);
} else {
result[userExtension.id] = userExtension;
}
}
});
/* __GDPR__
"extensionsScanned" : {
"totalCount" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
"disabledCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }
}
*/
this._telemetryService.publicLog('extensionsScanned', {
totalCount: installedExtensions.length,
disabledCount: disabledExtensions.length
development.forEach(developedExtension => {
log.info('', nls.localize('extensionUnderDevelopment', "Loading development extension at {0}", developedExtension.extensionFolderPath));
if (result.hasOwnProperty(developedExtension.id)) {
log.warn(developedExtension.extensionFolderPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result[developedExtension.id].extensionFolderPath, developedExtension.extensionFolderPath));
}
// Do not disable extensions under development
result[developedExtension.id] = developedExtension;
});
const runtimeExtensions = Object.keys(result).map(name => result[name]);
this._telemetryService.publicLog('extensionsScanned', {
totalCount: runtimeExtensions.length,
disabledCount: disabledExtensions.length
});
if (extensionsToDisable.length) {
return TPromise.join(extensionsToDisable.map(e => this._extensionEnablementService.setEnablement(e, EnablementState.Disabled)))
.then(() => {
this._storageService.store(BetterMergeDisabledNowKey, true);
return runtimeExtensions;
});
} else {
return runtimeExtensions;
}
});
});
if (disabledExtensions.length === 0) {
return installedExtensions;
}
return installedExtensions.filter(e => disabledExtensions.every(disabled => !areSameExtensions(disabled, e)));
}).then((extensionDescriptions) => {
this._registry = new ExtensionDescriptionRegistry(extensionDescriptions);
let availableExtensions = this._registry.getAllExtensionDescriptions();
let extensionPoints = ExtensionsRegistry.getExtensionPoints();
let messageHandler = (msg: IMessage) => this._handleExtensionPointMessage(msg);
for (let i = 0, len = extensionPoints.length; i < len; i++) {
const clock = startTimer(`handleExtensionPoint:${extensionPoints[i].name}`);
try {
ExtensionService._handleExtensionPoint(extensionPoints[i], availableExtensions, messageHandler);
} finally {
clock.stop();
}
}
this._barrier.open();
});
}
private _handleExtensionPointMessage(msg: IMessage) {
if (!this._extensionsStatus[msg.source]) {
this._extensionsStatus[msg.source] = { messages: [] };
if (!this._extensionsMessages[msg.source]) {
this._extensionsMessages[msg.source] = [];
}
this._extensionsStatus[msg.source].messages.push(msg);
this._extensionsMessages[msg.source].push(msg);
if (msg.source === this._environmentService.extensionDevelopmentPath) {
// This message is about the extension currently being developed
@@ -371,42 +471,157 @@ export class ExtensionService implements IExtensionService {
}
}
private static _scanInstalledExtensions(environmentService: IEnvironmentService, log: ILog): TPromise<IExtensionDescription[]> {
const version = pkg.version;
const builtinExtensions = ExtensionScanner.scanExtensions(version, log, SystemExtensionsRoot, true);
const userExtensions = environmentService.disableExtensions || !environmentService.extensionsPath ? TPromise.as([]) : ExtensionScanner.scanExtensions(version, log, environmentService.extensionsPath, false);
const developedExtensions = environmentService.disableExtensions || !environmentService.isExtensionDevelopment ? TPromise.as([]) : ExtensionScanner.scanOneOrMultipleExtensions(version, log, environmentService.extensionDevelopmentPath, false);
private static async _validateExtensionsCache(instantiationService: IInstantiationService, messageService: IMessageService, environmentService: IEnvironmentService, cacheKey: string, input: ExtensionScannerInput): TPromise<void> {
const cacheFolder = path.join(environmentService.userDataPath, MANIFEST_CACHE_FOLDER);
const cacheFile = path.join(cacheFolder, cacheKey);
return TPromise.join([builtinExtensions, userExtensions, developedExtensions]).then<IExtensionDescription[]>((extensionDescriptions: IExtensionDescription[][]) => {
const builtinExtensions = extensionDescriptions[0];
const userExtensions = extensionDescriptions[1];
const developedExtensions = extensionDescriptions[2];
const expected = await ExtensionScanner.scanExtensions(input, new NullLogger());
let result: { [extensionId: string]: IExtensionDescription; } = {};
builtinExtensions.forEach((builtinExtension) => {
result[builtinExtension.id] = builtinExtension;
});
userExtensions.forEach((userExtension) => {
if (result.hasOwnProperty(userExtension.id)) {
log.warn(userExtension.extensionFolderPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result[userExtension.id].extensionFolderPath, userExtension.extensionFolderPath));
}
result[userExtension.id] = userExtension;
});
developedExtensions.forEach(developedExtension => {
log.info('', nls.localize('extensionUnderDevelopment', "Loading development extension at {0}", developedExtension.extensionFolderPath));
if (result.hasOwnProperty(developedExtension.id)) {
log.warn(developedExtension.extensionFolderPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result[developedExtension.id].extensionFolderPath, developedExtension.extensionFolderPath));
}
result[developedExtension.id] = developedExtension;
});
const cacheContents = await this._readExtensionCache(environmentService, cacheKey);
const actual = cacheContents.result;
return Object.keys(result).map(name => result[name]);
}).then(null, err => {
log.error('', err);
return [];
if (objects.equals(expected, actual)) {
// Cache is valid and running with it is perfectly fine...
return;
}
try {
await pfs.del(cacheFile);
} catch (err) {
errors.onUnexpectedError(err);
console.error(err);
}
let message = nls.localize('extensionCache.invalid', "Extensions have been modified on disk. Please reload the window.");
messageService.show(Severity.Info, {
message: message,
actions: [
instantiationService.createInstance(ReloadWindowAction, ReloadWindowAction.ID, ReloadWindowAction.LABEL),
CloseAction
]
});
}
private static async _readExtensionCache(environmentService: IEnvironmentService, cacheKey: string): TPromise<IExtensionCacheData> {
const cacheFolder = path.join(environmentService.userDataPath, MANIFEST_CACHE_FOLDER);
const cacheFile = path.join(cacheFolder, cacheKey);
try {
const cacheRawContents = await pfs.readFile(cacheFile, 'utf8');
return JSON.parse(cacheRawContents);
} catch (err) {
// That's ok...
}
return null;
}
private static async _writeExtensionCache(environmentService: IEnvironmentService, cacheKey: string, cacheContents: IExtensionCacheData): TPromise<void> {
const cacheFolder = path.join(environmentService.userDataPath, MANIFEST_CACHE_FOLDER);
const cacheFile = path.join(cacheFolder, cacheKey);
try {
await pfs.mkdirp(cacheFolder);
} catch (err) {
// That's ok...
}
try {
await pfs.writeFile(cacheFile, JSON.stringify(cacheContents));
} catch (err) {
// That's ok...
}
}
private static async _scanExtensionsWithCache(instantiationService: IInstantiationService, messageService: IMessageService, 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);
}
try {
const folderStat = await pfs.stat(input.absoluteFolderPath);
input.mtime = folderStat.mtime.getTime();
} catch (err) {
// That's ok...
}
const cacheContents = await this._readExtensionCache(environmentService, cacheKey);
if (cacheContents && cacheContents.input && ExtensionScannerInput.equals(cacheContents.input, input)) {
// Validate the cache asynchronously after 5s
setTimeout(async () => {
try {
await this._validateExtensionsCache(instantiationService, messageService, environmentService, cacheKey, input);
} catch (err) {
errors.onUnexpectedError(err);
}
}, 5000);
return cacheContents.result;
}
const counterLogger = new CounterLogger(log);
const result = await ExtensionScanner.scanExtensions(input, counterLogger);
if (counterLogger.errorCnt === 0) {
// Nothing bad happened => cache the result
const cacheContents: IExtensionCacheData = {
input: input,
result: result
};
await this._writeExtensionCache(environmentService, cacheKey, cacheContents);
}
return result;
}
private static _scanInstalledExtensions(instantiationService: IInstantiationService, messageService: IMessageService, environmentService: IEnvironmentService, log: ILog): TPromise<{ system: IExtensionDescription[], user: IExtensionDescription[], development: IExtensionDescription[] }> {
const version = pkg.version;
const commit = product.commit;
const devMode = !!process.env['VSCODE_DEV'];
const locale = platform.locale;
const builtinExtensions = this._scanExtensionsWithCache(
instantiationService,
messageService,
environmentService,
BUILTIN_MANIFEST_CACHE_FILE,
new ExtensionScannerInput(version, commit, locale, devMode, SystemExtensionsRoot, true),
log
);
const userExtensions = (
environmentService.disableExtensions || !environmentService.extensionsPath
? TPromise.as([])
: this._scanExtensionsWithCache(
instantiationService,
messageService,
environmentService,
USER_MANIFEST_CACHE_FILE,
new ExtensionScannerInput(version, commit, locale, devMode, environmentService.extensionsPath, false),
log
)
);
// Always load developed extensions while extensions development
const developedExtensions = (
environmentService.isExtensionDevelopment
? ExtensionScanner.scanOneOrMultipleExtensions(
new ExtensionScannerInput(version, commit, locale, devMode, environmentService.extensionDevelopmentPath, false), log
)
: TPromise.as([])
);
return TPromise.join([builtinExtensions, userExtensions, developedExtensions])
.then((extensionDescriptions: IExtensionDescription[][]) => {
const system = extensionDescriptions[0];
const user = extensionDescriptions[1];
const development = extensionDescriptions[2];
return { system, user, development };
}).then(null, err => {
log.error('', err);
return { system: [], user: [], development: [] };
});
}
private static _handleExtensionPoint<T>(extensionPoint: ExtensionPoint<T>, availableExtensions: IExtensionDescription[], messageHandler: (msg: IMessage) => void): void {
let users: IExtensionPointUser<T>[] = [], usersLen = 0;
for (let i = 0, len = availableExtensions.length; i < len; i++) {
@@ -452,9 +667,37 @@ export class ExtensionService implements IExtensionService {
}
}
public _onExtensionActivated(extensionId: string, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number): void {
this._extensionHostProcessActivationTimes[extensionId] = new ActivationTimes(startup, codeLoadingTime, activateCallTime, activateResolvedTime);
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._onDidChangeExtensionsStatus.fire([extensionId]);
}
public _onExtensionRuntimeError(extensionId: string, err: Error): void {
if (!this._extensionHostExtensionRuntimeErrors[extensionId]) {
this._extensionHostExtensionRuntimeErrors[extensionId] = [];
}
this._extensionHostExtensionRuntimeErrors[extensionId].push(err);
this._onDidChangeExtensionsStatus.fire([extensionId]);
}
public _addMessage(extensionId: string, severity: Severity, message: string): void {
if (!this._extensionsMessages[extensionId]) {
this._extensionsMessages[extensionId] = [];
}
this._extensionsMessages[extensionId].push({
type: severity,
message: message,
source: null,
extensionId: null,
extensionPointId: null
});
this._onDidChangeExtensionsStatus.fire([extensionId]);
}
}
interface IExtensionCacheData {
input: ExtensionScannerInput;
result: IExtensionDescription[];
}
export class Logger implements ILog {
@@ -479,3 +722,34 @@ export class Logger implements ILog {
this._messageHandler(Severity.Info, source, message);
}
}
class CounterLogger implements ILog {
public errorCnt = 0;
public warnCnt = 0;
public infoCnt = 0;
constructor(private readonly _actual: ILog) {
}
public error(source: string, message: string): void {
this._actual.error(source, message);
}
public warn(source: string, message: string): void {
this._actual.warn(source, message);
}
public info(source: string, message: string): void {
this._actual.info(source, message);
}
}
class NullLogger implements ILog {
public error(source: string, message: string): void {
}
public warn(source: string, message: string): void {
}
public info(source: string, message: string): void {
}
}

View File

@@ -1,39 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
/**
* A barrier that is initially closed and then becomes opened permanently.
*/
export class Barrier {
private _isOpen: boolean;
private _promise: TPromise<boolean>;
private _completePromise: (v: boolean) => void;
constructor() {
this._isOpen = false;
this._promise = new TPromise<boolean>((c, e, p) => {
this._completePromise = c;
}, () => {
console.warn('You should really not try to cancel this ready promise!');
});
}
public isOpen(): boolean {
return this._isOpen;
}
public open(): void {
this._isOpen = true;
this._completePromise(true);
}
public wait(): TPromise<boolean> {
return this._promise;
}
}

View File

@@ -7,7 +7,7 @@
import { TPromise, ValueCallback, ErrorCallback } from 'vs/base/common/winjs.base';
import { onUnexpectedError } from 'vs/base/common/errors';
export class LazyPromise {
export class LazyPromise implements TPromise<any> {
private _onCancel: () => void;