mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge VS Code 1.21 source code (#1067)
* Initial VS Code 1.21 file copy with patches * A few more merges * Post npm install * Fix batch of build breaks * Fix more build breaks * Fix more build errors * Fix more build breaks * Runtime fixes 1 * Get connection dialog working with some todos * Fix a few packaging issues * Copy several node_modules to package build to fix loader issues * Fix breaks from master * A few more fixes * Make tests pass * First pass of license header updates * Second pass of license header updates * Fix restore dialog issues * Remove add additional themes menu items * fix select box issues where the list doesn't show up * formatting * Fix editor dispose issue * Copy over node modules to correct location on all platforms
This commit is contained in:
@@ -7,13 +7,11 @@
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { toErrorMessage } from 'vs/base/common/errorMessage';
|
||||
import { stringify } from 'vs/base/common/marshalling';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { isWindows, isLinux } from 'vs/base/common/platform';
|
||||
import { findFreePort } from 'vs/base/node/ports';
|
||||
import { IMessageService, Severity } from 'vs/platform/message/common/message';
|
||||
import { ILifecycleService, ShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
@@ -27,7 +25,7 @@ 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 { IInitData, IWorkspaceData, IConfigurationInitData } from 'vs/workbench/api/node/extHost.protocol';
|
||||
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
|
||||
import { ICrashReporterService } from 'vs/workbench/services/crashReporter/electron-browser/crashReporterService';
|
||||
import { IBroadcastService, IBroadcast } from 'vs/platform/broadcast/electron-browser/broadcastService';
|
||||
@@ -36,6 +34,9 @@ import { EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, EXTENSION_RELOAD_BROADCAST_C
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
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 {
|
||||
|
||||
@@ -63,7 +64,7 @@ export class ExtensionHostProcessWorker {
|
||||
constructor(
|
||||
/* intentionally not injected */private readonly _extensionService: IExtensionService,
|
||||
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,
|
||||
@IMessageService private readonly _messageService: IMessageService,
|
||||
@INotificationService private readonly _notificationService: INotificationService,
|
||||
@IWindowsService private readonly _windowsService: IWindowsService,
|
||||
@IWindowService private readonly _windowService: IWindowService,
|
||||
@IBroadcastService private readonly _broadcastService: IBroadcastService,
|
||||
@@ -71,7 +72,9 @@ export class ExtensionHostProcessWorker {
|
||||
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
|
||||
@IWorkspaceConfigurationService private readonly _configurationService: IWorkspaceConfigurationService,
|
||||
@ITelemetryService private readonly _telemetryService: ITelemetryService,
|
||||
@ICrashReporterService private readonly _crashReporterService: ICrashReporterService
|
||||
@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
|
||||
this._isExtensionDevHost = this._environmentService.isExtensionDevelopment;
|
||||
@@ -143,7 +146,6 @@ export class ExtensionHostProcessWorker {
|
||||
VSCODE_WINDOW_ID: String(this._windowService.getCurrentWindowId()),
|
||||
VSCODE_IPC_HOOK_EXTHOST: pipeName,
|
||||
VSCODE_HANDLES_UNCAUGHT_ERRORS: true,
|
||||
ELECTRON_NO_ASAR: '1',
|
||||
VSCODE_LOG_STACK: !this._isExtensionDevTestFromCli && (this._isExtensionDevHost || !this._environmentService.isBuilt || product.quality !== 'stable' || this._environmentService.verbose)
|
||||
}),
|
||||
// We only detach the extension host on windows. Linux and Mac orphan by default
|
||||
@@ -236,7 +238,11 @@ 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._messageService.show(Severity.Warning, msg);
|
||||
this._choiceService.choose(Severity.Warning, msg, [nls.localize('reloadWindow', "Reload Window")]).then(choice => {
|
||||
if (choice === 0) {
|
||||
this._windowService.reloadWindow();
|
||||
}
|
||||
});
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
@@ -275,6 +281,8 @@ export class ExtensionHostProcessWorker {
|
||||
let startPort = 9333;
|
||||
if (typeof this._environmentService.debugExtensionHost.port === 'number') {
|
||||
startPort = expected = this._environmentService.debugExtensionHost.port;
|
||||
} else {
|
||||
return TPromise.as({ expected: undefined, actual: 0 });
|
||||
}
|
||||
return new TPromise((c, e) => {
|
||||
return findFreePort(startPort, 10 /* try 10 ports */, 5000 /* try up to 5 seconds */).then(port => {
|
||||
@@ -329,7 +337,7 @@ export class ExtensionHostProcessWorker {
|
||||
|
||||
if (msg === 'ready') {
|
||||
// 1) Extension Host is ready to receive messages, initialize it
|
||||
this._createExtHostInitData().then(data => protocol.send(stringify(data)));
|
||||
this._createExtHostInitData().then(data => protocol.send(JSON.stringify(data)));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -356,7 +364,7 @@ export class ExtensionHostProcessWorker {
|
||||
|
||||
private _createExtHostInitData(): TPromise<IInitData> {
|
||||
return TPromise.join<any>([this._telemetryService.getTelemetryInfo(), this._extensionService.getExtensions()]).then(([telemetryInfo, extensionDescriptions]) => {
|
||||
const configurationData: IConfigurationInitData = { ...this._configurationService.getConfigurationData(), configurationScopes: [] };
|
||||
const configurationData: IConfigurationInitData = { ...this._configurationService.getConfigurationData(), configurationScopes: {} };
|
||||
const r: IInitData = {
|
||||
parentPid: process.pid,
|
||||
environment: {
|
||||
@@ -374,11 +382,12 @@ export class ExtensionHostProcessWorker {
|
||||
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(this._configurationService.keys().default) } : configurationData,
|
||||
configuration: !this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment ? { ...configurationData, configurationScopes: getScopes() } : configurationData,
|
||||
telemetryInfo,
|
||||
args: this._environmentService.args,
|
||||
execPath: this._environmentService.execPath,
|
||||
windowId: this._windowService.getCurrentWindowId()
|
||||
windowId: this._windowService.getCurrentWindowId(),
|
||||
logLevel: this._logService.getLevel()
|
||||
};
|
||||
return r;
|
||||
});
|
||||
@@ -416,7 +425,7 @@ export class ExtensionHostProcessWorker {
|
||||
|
||||
this._lastExtensionHostError = errorMessage;
|
||||
|
||||
this._messageService.show(Severity.Error, nls.localize('extensionHostProcess.error', "Error from the extension host: {0}", errorMessage));
|
||||
this._notificationService.error(nls.localize('extensionHostProcess.error', "Error from the extension host: {0}", errorMessage));
|
||||
}
|
||||
|
||||
private _onExtHostProcessExit(code: number, signal: string): void {
|
||||
|
||||
@@ -5,17 +5,10 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import { IExtensionService, IExtensionDescription, ProfileSession, IExtensionHostProfile, ProfileSegmentId } from 'vs/platform/extensions/common/extensions';
|
||||
import { IExtensionService, IExtensionDescription, ProfileSession, IExtensionHostProfile, ProfileSegmentId } from 'vs/workbench/services/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 {
|
||||
@@ -128,27 +121,3 @@ export class ExtensionHostProfiler {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,411 +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 * as nls from 'vs/nls';
|
||||
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 * 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/node/extensionManagementUtil';
|
||||
import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages';
|
||||
|
||||
const MANIFEST_FILE = 'package.json';
|
||||
|
||||
export interface NlsConfiguration {
|
||||
readonly devMode: boolean;
|
||||
readonly locale: string;
|
||||
readonly pseudo: boolean;
|
||||
}
|
||||
|
||||
export interface ILog {
|
||||
error(source: string, message: string): void;
|
||||
warn(source: string, message: string): void;
|
||||
info(source: string, message: string): void;
|
||||
}
|
||||
|
||||
abstract class ExtensionManifestHandler {
|
||||
|
||||
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;
|
||||
this._log = log;
|
||||
this._absoluteFolderPath = absoluteFolderPath;
|
||||
this._isBuiltin = isBuiltin;
|
||||
this._absoluteManifestPath = join(absoluteFolderPath, MANIFEST_FILE);
|
||||
}
|
||||
}
|
||||
|
||||
class ExtensionManifestParser extends ExtensionManifestHandler {
|
||||
|
||||
public parse(): TPromise<IExtensionDescription> {
|
||||
return pfs.readFile(this._absoluteManifestPath).then((manifestContents) => {
|
||||
try {
|
||||
const manifest = JSON.parse(manifestContents.toString());
|
||||
if (manifest.__metadata) {
|
||||
manifest.uuid = manifest.__metadata.id;
|
||||
}
|
||||
// {{SQL CARBON EDIT}}
|
||||
if (manifest.engines && !manifest.engines.sqlops) {
|
||||
manifest.engines.sqlops = '*';
|
||||
}
|
||||
delete manifest.__metadata;
|
||||
return manifest;
|
||||
} catch (e) {
|
||||
this._log.error(this._absoluteFolderPath, nls.localize('jsonParseFail', "Failed to parse {0}: {1}.", this._absoluteManifestPath, getParseErrorMessage(e.message)));
|
||||
}
|
||||
return null;
|
||||
}, (err) => {
|
||||
if (err.code === 'ENOENT') {
|
||||
return null;
|
||||
}
|
||||
|
||||
this._log.error(this._absoluteFolderPath, nls.localize('fileReadFail', "Cannot read file {0}: {1}.", this._absoluteManifestPath, err.message));
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return pfs.fileExists(basename + '.nls' + extension).then(exists => {
|
||||
if (!exists) {
|
||||
return extensionDescription;
|
||||
}
|
||||
return ExtensionManifestNLSReplacer.findMessageBundles(this._nlsConfig, basename).then((messageBundle) => {
|
||||
if (!messageBundle.localized) {
|
||||
return extensionDescription;
|
||||
}
|
||||
return pfs.readFile(messageBundle.localized).then(messageBundleContent => {
|
||||
let errors: json.ParseError[] = [];
|
||||
let messages: { [key: string]: string; } = json.parse(messageBundleContent.toString(), errors);
|
||||
|
||||
return ExtensionManifestNLSReplacer.resolveOriginalMessageBundle(messageBundle.original, errors).then(originalMessages => {
|
||||
if (errors.length > 0) {
|
||||
errors.forEach((error) => {
|
||||
this._log.error(this._absoluteFolderPath, nls.localize('jsonsParseFail', "Failed to parse {0} or {1}: {2}.", messageBundle.localized, messageBundle.original, getParseErrorMessage(error.error)));
|
||||
});
|
||||
return extensionDescription;
|
||||
}
|
||||
|
||||
ExtensionManifestNLSReplacer._replaceNLStrings(this._nlsConfig, extensionDescription, messages, originalMessages, this._log, this._absoluteFolderPath);
|
||||
return extensionDescription;
|
||||
});
|
||||
}, (err) => {
|
||||
this._log.error(this._absoluteFolderPath, nls.localize('fileReadFail', "Cannot read file {0}: {1}.", messageBundle.localized, err.message));
|
||||
return null;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses original message bundle, returns null if the original message bundle is null.
|
||||
*/
|
||||
private static resolveOriginalMessageBundle(originalMessageBundle: string, errors: json.ParseError[]) {
|
||||
return new TPromise<{ [key: string]: string; }>((c, e, p) => {
|
||||
if (originalMessageBundle) {
|
||||
pfs.readFile(originalMessageBundle).then(originalBundleContent => {
|
||||
c(json.parse(originalBundleContent.toString(), errors));
|
||||
});
|
||||
} else {
|
||||
c(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(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`;
|
||||
pfs.fileExists(toCheck).then(exists => {
|
||||
if (exists) {
|
||||
c({ localized: toCheck, original: `${basename}.nls.json` });
|
||||
}
|
||||
let index = locale.lastIndexOf('-');
|
||||
if (index === -1) {
|
||||
c({ localized: `${basename}.nls.json`, original: null });
|
||||
} else {
|
||||
locale = locale.substring(0, index);
|
||||
loop(basename, locale);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (nlsConfig.devMode || nlsConfig.pseudo || !nlsConfig.locale) {
|
||||
return c({ localized: basename + '.nls.json', original: null });
|
||||
}
|
||||
loop(basename, nlsConfig.locale);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This routine makes the following assumptions:
|
||||
* The root element is an object literal
|
||||
*/
|
||||
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)) {
|
||||
let str = <string>value;
|
||||
let length = str.length;
|
||||
if (length > 1 && str[0] === '%' && str[length - 1] === '%') {
|
||||
let messageKey = str.substr(1, length - 2);
|
||||
let message = messages[messageKey];
|
||||
if (message) {
|
||||
if (nlsConfig.pseudo) {
|
||||
// FF3B and FF3D is the Unicode zenkaku representation for [ and ]
|
||||
message = '\uFF3B' + message.replace(/[aouei]/g, '$&$&') + '\uFF3D';
|
||||
}
|
||||
obj[key] = command && (key === 'title' || key === 'category') && originalMessages ? { value: message, original: originalMessages[messageKey] } : message;
|
||||
} else {
|
||||
log.warn(messageScope, nls.localize('missingNLSKey', "Couldn't find message for key {0}.", messageKey));
|
||||
}
|
||||
}
|
||||
} 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)) {
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
processEntry(value, i, command);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let key in literal) {
|
||||
if (literal.hasOwnProperty(key)) {
|
||||
processEntry(literal, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ExtensionManifestValidator extends ExtensionManifestHandler {
|
||||
validate(_extensionDescription: IExtensionDescription): IExtensionDescription {
|
||||
// Relax the readonly properties here, it is the one place where we check and normalize values
|
||||
interface IRelaxedExtensionDescription {
|
||||
id: string;
|
||||
name: string;
|
||||
version: string;
|
||||
publisher: string;
|
||||
isBuiltin: boolean;
|
||||
extensionFolderPath: string;
|
||||
engines: {
|
||||
vscode: string;
|
||||
};
|
||||
main?: string;
|
||||
enableProposedApi?: boolean;
|
||||
}
|
||||
let extensionDescription = <IRelaxedExtensionDescription>_extensionDescription;
|
||||
extensionDescription.isBuiltin = this._isBuiltin;
|
||||
|
||||
let notices: string[] = [];
|
||||
if (!isValidExtensionDescription(this._ourVersion, this._absoluteFolderPath, extensionDescription, notices)) {
|
||||
notices.forEach((error) => {
|
||||
this._log.error(this._absoluteFolderPath, error);
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
// in this case the notices are warnings
|
||||
notices.forEach((error) => {
|
||||
this._log.warn(this._absoluteFolderPath, error);
|
||||
});
|
||||
|
||||
// id := `publisher.name`
|
||||
extensionDescription.id = `${extensionDescription.publisher}.${extensionDescription.name}`;
|
||||
|
||||
// main := absolutePath(`main`)
|
||||
if (extensionDescription.main) {
|
||||
extensionDescription.main = join(this._absoluteFolderPath, extensionDescription.main);
|
||||
}
|
||||
|
||||
extensionDescription.extensionFolderPath = this._absoluteFolderPath;
|
||||
|
||||
return extensionDescription;
|
||||
}
|
||||
}
|
||||
|
||||
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`
|
||||
*/
|
||||
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);
|
||||
return parser.parse().then((extensionDescription) => {
|
||||
if (extensionDescription === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let nlsReplacer = new ExtensionManifestNLSReplacer(version, log, absoluteFolderPath, isBuiltin, nlsConfig);
|
||||
return nlsReplacer.replaceNLS(extensionDescription);
|
||||
}).then((extensionDescription) => {
|
||||
if (extensionDescription === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let validator = new ExtensionManifestValidator(version, log, absoluteFolderPath, isBuiltin);
|
||||
return validator.validate(extensionDescription);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan a list of extensions defined in `absoluteFolderPath`
|
||||
*/
|
||||
public static async scanExtensions(input: ExtensionScannerInput, log: ILog): TPromise<IExtensionDescription[]> {
|
||||
const absoluteFolderPath = input.absoluteFolderPath;
|
||||
const isBuiltin = input.isBuiltin;
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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(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) {
|
||||
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(input, log);
|
||||
}, (err) => {
|
||||
log.error(absoluteFolderPath, err);
|
||||
return [];
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -7,42 +7,95 @@
|
||||
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 os from 'os';
|
||||
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, IExtensionHostInformation, ProfileSession, USER_MANIFEST_CACHE_FILE, BUILTIN_MANIFEST_CACHE_FILE, MANIFEST_CACHE_FOLDER } from 'vs/platform/extensions/common/extensions';
|
||||
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 { ExtensionsRegistry, ExtensionPoint, IExtensionPointUser, ExtensionMessageCollector, IExtensionPoint } from 'vs/platform/extensions/common/extensionsRegistry';
|
||||
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 { 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';
|
||||
import { ExtHostContext, ExtHostExtensionServiceShape, IExtHostContext, MainContext } from 'vs/workbench/api/node/extHost.protocol';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
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 { 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, 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';
|
||||
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';
|
||||
|
||||
const SystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', 'extensions'));
|
||||
let _SystemExtensionsRoot: string = null;
|
||||
function getSystemExtensionsRoot(): string {
|
||||
if (!_SystemExtensionsRoot) {
|
||||
_SystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', 'extensions'));
|
||||
}
|
||||
return _SystemExtensionsRoot;
|
||||
}
|
||||
let _ExtraDevSystemExtensionsRoot: string = null;
|
||||
function getExtraDevSystemExtensionsRoot(): string {
|
||||
if (!_ExtraDevSystemExtensionsRoot) {
|
||||
_ExtraDevSystemExtensionsRoot = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', '.build', 'builtInExtensions'));
|
||||
}
|
||||
return _ExtraDevSystemExtensionsRoot;
|
||||
}
|
||||
|
||||
interface IBuiltInExtension {
|
||||
name: string;
|
||||
version: string;
|
||||
repo: string;
|
||||
}
|
||||
|
||||
interface IBuiltInExtensionControl {
|
||||
[name: string]: 'marketplace' | 'disabled' | string;
|
||||
}
|
||||
|
||||
class ExtraBuiltInExtensionResolver implements IExtensionResolver {
|
||||
|
||||
constructor(private builtInExtensions: IBuiltInExtension[], private control: IBuiltInExtensionControl) { }
|
||||
|
||||
resolveExtensions(): TPromise<IExtensionReference[]> {
|
||||
const result: IExtensionReference[] = [];
|
||||
|
||||
for (const ext of this.builtInExtensions) {
|
||||
const controlState = this.control[ext.name] || 'marketplace';
|
||||
|
||||
switch (controlState) {
|
||||
case 'disabled':
|
||||
break;
|
||||
case 'marketplace':
|
||||
result.push({ name: ext.name, path: path.join(getExtraDevSystemExtensionsRoot(), ext.name) });
|
||||
break;
|
||||
default:
|
||||
result.push({ name: ext.name, path: controlState });
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return TPromise.as(result);
|
||||
}
|
||||
}
|
||||
|
||||
// Enable to see detailed message communication between window and extension host
|
||||
const logExtensionHostCommunication = false;
|
||||
|
||||
function messageWithSource(msg: IMessage): string {
|
||||
return messageWithSource2(msg.source, msg.message);
|
||||
@@ -61,7 +114,7 @@ const NO_OP_VOID_PROMISE = TPromise.wrap<void>(void 0);
|
||||
export class ExtensionService extends Disposable implements IExtensionService {
|
||||
public _serviceBrand: any;
|
||||
|
||||
private _onDidRegisterExtensions: Emitter<IExtensionDescription[]>;
|
||||
private _onDidRegisterExtensions: Emitter<void>;
|
||||
|
||||
private _registry: ExtensionDescriptionRegistry;
|
||||
private readonly _installedExtensionsReady: Barrier;
|
||||
@@ -81,7 +134,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
private _extensionHostProcessActivationTimes: { [id: string]: ActivationTimes; };
|
||||
private _extensionHostExtensionRuntimeErrors: { [id: string]: Error[]; };
|
||||
private _extensionHostProcessWorker: ExtensionHostProcessWorker;
|
||||
private _extensionHostProcessThreadService: MainThreadService;
|
||||
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.
|
||||
@@ -90,7 +143,8 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@IMessageService private readonly _messageService: IMessageService,
|
||||
@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,
|
||||
@@ -105,41 +159,56 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
this._extensionsMessages = {};
|
||||
this._allRequestedActivateEvents = Object.create(null);
|
||||
|
||||
this._onDidRegisterExtensions = new Emitter<IExtensionDescription[]>();
|
||||
this._onDidRegisterExtensions = new Emitter<void>();
|
||||
|
||||
this._extensionHostProcessFinishedActivateEvents = Object.create(null);
|
||||
this._extensionHostProcessActivationTimes = Object.create(null);
|
||||
this._extensionHostExtensionRuntimeErrors = Object.create(null);
|
||||
this._extensionHostProcessWorker = null;
|
||||
this._extensionHostProcessThreadService = null;
|
||||
this._extensionHostProcessRPCProtocol = null;
|
||||
this._extensionHostProcessCustomers = [];
|
||||
this._extensionHostProcessProxy = null;
|
||||
|
||||
lifecycleService.when(LifecyclePhase.Running).then(() => {
|
||||
// delay extension host creation and extension scanning
|
||||
// until after workbench is running
|
||||
this._startExtensionHostProcess([]);
|
||||
this._scanAndHandleExtensions();
|
||||
this.startDelayed(lifecycleService);
|
||||
}
|
||||
|
||||
private startDelayed(lifecycleService: ILifecycleService): void {
|
||||
let started = false;
|
||||
const startOnce = () => {
|
||||
if (!started) {
|
||||
started = true;
|
||||
|
||||
this._startExtensionHostProcess([]);
|
||||
this._scanAndHandleExtensions();
|
||||
}
|
||||
};
|
||||
|
||||
// delay extension host creation and extension scanning
|
||||
// until the workbench is restoring. we cannot defer the
|
||||
// extension host more (LifecyclePhase.Running) because
|
||||
// some editors require the extension host to restore
|
||||
// and this would result in a deadlock
|
||||
// see https://github.com/Microsoft/vscode/issues/41322
|
||||
lifecycleService.when(LifecyclePhase.Restoring).then(() => {
|
||||
// we add an additional delay of 800ms because the extension host
|
||||
// starting is a potential expensive operation and we do no want
|
||||
// to fight with editors, viewlets and panels restoring.
|
||||
setTimeout(() => startOnce(), 800);
|
||||
});
|
||||
|
||||
// if we are running before the 800ms delay, make sure to start
|
||||
// the extension host right away though.
|
||||
lifecycleService.when(LifecyclePhase.Running).then(() => startOnce());
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
public get onDidRegisterExtensions(): Event<IExtensionDescription[]> {
|
||||
public get onDidRegisterExtensions(): Event<void> {
|
||||
return this._onDidRegisterExtensions.event;
|
||||
}
|
||||
|
||||
public getExtensionHostInformation(): IExtensionHostInformation {
|
||||
if (!this._extensionHostProcessWorker) {
|
||||
throw errors.illegalState();
|
||||
}
|
||||
return <IExtensionHostInformation>{
|
||||
inspectPort: this._extensionHostProcessWorker.getInspectPort()
|
||||
};
|
||||
}
|
||||
|
||||
public restartExtensionHost(): void {
|
||||
this._stopExtensionHostProcess();
|
||||
this._startExtensionHostProcess(Object.keys(this._allRequestedActivateEvents));
|
||||
@@ -163,9 +232,9 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
this._extensionHostProcessWorker.dispose();
|
||||
this._extensionHostProcessWorker = null;
|
||||
}
|
||||
if (this._extensionHostProcessThreadService) {
|
||||
this._extensionHostProcessThreadService.dispose();
|
||||
this._extensionHostProcessThreadService = 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];
|
||||
@@ -202,16 +271,6 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
}
|
||||
|
||||
private _onExtensionHostCrashed(code: number, signal: string): void {
|
||||
const openDevTools = new Action('openDevTools', nls.localize('devTools', "Developer Tools"), '', true, (): TPromise<boolean> => {
|
||||
return this._windowService.openDevTools().then(() => false);
|
||||
});
|
||||
|
||||
const restart = new Action('restart', nls.localize('restart', "Restart Extension Host"), '', true, (): TPromise<boolean> => {
|
||||
this._messageService.hideAll();
|
||||
this._startExtensionHostProcess(Object.keys(this._allRequestedActivateEvents));
|
||||
return TPromise.as(true);
|
||||
});
|
||||
|
||||
console.error('Extension host terminated unexpectedly. Code: ', code, ' Signal: ', signal);
|
||||
this._stopExtensionHostProcess();
|
||||
|
||||
@@ -219,19 +278,27 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
if (code === 87) {
|
||||
message = nls.localize('extensionHostProcess.unresponsiveCrash', "Extension host terminated because it was not responsive.");
|
||||
}
|
||||
this._messageService.show(Severity.Error, {
|
||||
message: message,
|
||||
actions: [
|
||||
openDevTools,
|
||||
restart
|
||||
]
|
||||
|
||||
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 {
|
||||
|
||||
this._extensionHostProcessThreadService = this._instantiationService.createInstance(MainThreadService, protocol);
|
||||
const extHostContext: IExtHostContext = this._extensionHostProcessThreadService;
|
||||
if (logExtensionHostCommunication || this._environmentService.logExtensionHostCommunication) {
|
||||
protocol = asLoggingProtocol(protocol);
|
||||
}
|
||||
|
||||
this._extensionHostProcessRPCProtocol = new RPCProtocol(protocol);
|
||||
const extHostContext: IExtHostContext = this._extensionHostProcessRPCProtocol;
|
||||
|
||||
// Named customers
|
||||
const namedCustomers = ExtHostCustomersRegistry.getNamedCustomers();
|
||||
@@ -239,7 +306,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
const [id, ctor] = namedCustomers[i];
|
||||
const instance = this._instantiationService.createInstance(ctor, extHostContext);
|
||||
this._extensionHostProcessCustomers.push(instance);
|
||||
this._extensionHostProcessThreadService.set(id, instance);
|
||||
this._extensionHostProcessRPCProtocol.set(id, instance);
|
||||
}
|
||||
|
||||
// Customers
|
||||
@@ -252,9 +319,9 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
|
||||
// Check that no named customers are missing
|
||||
const expected: ProxyIdentifier<any>[] = Object.keys(MainContext).map((key) => MainContext[key]);
|
||||
this._extensionHostProcessThreadService.assertRegistered(expected);
|
||||
this._extensionHostProcessRPCProtocol.assertRegistered(expected);
|
||||
|
||||
return this._extensionHostProcessThreadService.get(ExtHostContext.ExtHostExtensionService);
|
||||
return this._extensionHostProcessRPCProtocol.getProxy(ExtHostContext.ExtHostExtensionService);
|
||||
}
|
||||
|
||||
// ---- begin IExtensionService
|
||||
@@ -337,6 +404,10 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
return result;
|
||||
}
|
||||
|
||||
public canProfileExtensionHost(): boolean {
|
||||
return this._extensionHostProcessWorker && Boolean(this._extensionHostProcessWorker.getInspectPort());
|
||||
}
|
||||
|
||||
public startExtensionHostProfile(): TPromise<ProfileSession> {
|
||||
if (this._extensionHostProcessWorker) {
|
||||
let port = this._extensionHostProcessWorker.getInspectPort();
|
||||
@@ -373,7 +444,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
|
||||
mark('extensionHostReady');
|
||||
this._installedExtensionsReady.open();
|
||||
this._onDidRegisterExtensions.fire(availableExtensions);
|
||||
this._onDidRegisterExtensions.fire(void 0);
|
||||
this._onDidChangeExtensionsStatus.fire(availableExtensions.map(e => e.id));
|
||||
});
|
||||
}
|
||||
@@ -383,7 +454,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
this._logOrShowMessage(severity, this._isDev ? messageWithSource2(source, message) : message);
|
||||
});
|
||||
|
||||
return ExtensionService._scanInstalledExtensions(this._instantiationService, this._messageService, this._environmentService, log)
|
||||
return ExtensionService._scanInstalledExtensions(this._windowService, this._choiceService, this._environmentService, log)
|
||||
.then(({ system, user, development }) => {
|
||||
this._extensionEnablementService.migrateToIdentifiers(user); // TODO: @sandy Remove it after couple of milestones
|
||||
return this._extensionEnablementService.getDisabledExtensions()
|
||||
@@ -393,8 +464,9 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
let userMigratedSystemExtensions: IExtensionIdentifier[] = [{ id: BetterMergeId }];
|
||||
|
||||
system.forEach((systemExtension) => {
|
||||
// Disabling system extensions is not supported
|
||||
result[systemExtension.id] = systemExtension;
|
||||
if (disabledExtensions.every(disabled => !areSameExtensions(disabled, systemExtension))) {
|
||||
result[systemExtension.id] = systemExtension;
|
||||
}
|
||||
});
|
||||
|
||||
user.forEach((userExtension) => {
|
||||
@@ -471,13 +543,17 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
}
|
||||
}
|
||||
|
||||
private static async _validateExtensionsCache(instantiationService: IInstantiationService, messageService: IMessageService, environmentService: IEnvironmentService, cacheKey: string, input: ExtensionScannerInput): TPromise<void> {
|
||||
private static async _validateExtensionsCache(windowService: IWindowService, choiceService: IChoiceService, environmentService: IEnvironmentService, cacheKey: string, input: ExtensionScannerInput): TPromise<void> {
|
||||
const cacheFolder = path.join(environmentService.userDataPath, MANIFEST_CACHE_FOLDER);
|
||||
const cacheFile = path.join(cacheFolder, cacheKey);
|
||||
|
||||
const expected = await ExtensionScanner.scanExtensions(input, new NullLogger());
|
||||
|
||||
const cacheContents = await this._readExtensionCache(environmentService, cacheKey);
|
||||
if (!cacheContents) {
|
||||
// Cache has been deleted by someone else, which is perfectly fine...
|
||||
return;
|
||||
}
|
||||
const actual = cacheContents.result;
|
||||
|
||||
if (objects.equals(expected, actual)) {
|
||||
@@ -492,13 +568,10 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
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
|
||||
]
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -533,7 +606,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
}
|
||||
}
|
||||
|
||||
private static async _scanExtensionsWithCache(instantiationService: IInstantiationService, messageService: IMessageService, environmentService: IEnvironmentService, cacheKey: string, input: ExtensionScannerInput, log: ILog): TPromise<IExtensionDescription[]> {
|
||||
private static async _scanExtensionsWithCache(windowService: IWindowService, choiceService: IChoiceService, 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);
|
||||
@@ -551,7 +624,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
// Validate the cache asynchronously after 5s
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
await this._validateExtensionsCache(instantiationService, messageService, environmentService, cacheKey, input);
|
||||
await this._validateExtensionsCache(windowService, choiceService, environmentService, cacheKey, input);
|
||||
} catch (err) {
|
||||
errors.onUnexpectedError(err);
|
||||
}
|
||||
@@ -573,45 +646,100 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
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;
|
||||
private static _scanInstalledExtensions(windowService: IWindowService, choiceService: IChoiceService, environmentService: IEnvironmentService, log: ILog): TPromise<{ system: IExtensionDescription[], user: IExtensionDescription[], development: IExtensionDescription[] }> {
|
||||
|
||||
const builtinExtensions = this._scanExtensionsWithCache(
|
||||
instantiationService,
|
||||
messageService,
|
||||
environmentService,
|
||||
BUILTIN_MANIFEST_CACHE_FILE,
|
||||
new ExtensionScannerInput(version, commit, locale, devMode, SystemExtensionsRoot, true),
|
||||
log
|
||||
);
|
||||
const translationConfig: TPromise<Translations> = platform.translationsConfigFile
|
||||
? pfs.readFile(platform.translationsConfigFile, 'utf8').then((content) => {
|
||||
try {
|
||||
return JSON.parse(content) as Translations;
|
||||
} catch (err) {
|
||||
return Object.create(null);
|
||||
}
|
||||
}, (err) => {
|
||||
return Object.create(null);
|
||||
})
|
||||
: TPromise.as(Object.create(null));
|
||||
|
||||
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
|
||||
)
|
||||
);
|
||||
return translationConfig.then((translations) => {
|
||||
const version = pkg.version;
|
||||
const commit = product.commit;
|
||||
const devMode = !!process.env['VSCODE_DEV'];
|
||||
const locale = platform.locale;
|
||||
|
||||
// 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([])
|
||||
);
|
||||
const builtinExtensions = this._scanExtensionsWithCache(
|
||||
windowService,
|
||||
choiceService,
|
||||
environmentService,
|
||||
BUILTIN_MANIFEST_CACHE_FILE,
|
||||
new ExtensionScannerInput(version, commit, locale, devMode, getSystemExtensionsRoot(), true, translations),
|
||||
log
|
||||
);
|
||||
|
||||
return TPromise.join([builtinExtensions, userExtensions, developedExtensions])
|
||||
.then((extensionDescriptions: IExtensionDescription[][]) => {
|
||||
let finalBuiltinExtensions: TPromise<IExtensionDescription[]> = builtinExtensions;
|
||||
|
||||
if (devMode) {
|
||||
const builtInExtensionsFilePath = path.normalize(path.join(URI.parse(require.toUrl('')).fsPath, '..', 'build', 'builtInExtensions.json'));
|
||||
const builtInExtensions = pfs.readFile(builtInExtensionsFilePath, 'utf8')
|
||||
.then<IBuiltInExtension[]>(raw => JSON.parse(raw));
|
||||
|
||||
const controlFilePath = path.join(os.homedir(), '.vscode-oss-dev', 'extensions', 'control.json');
|
||||
const controlFile = pfs.readFile(controlFilePath, 'utf8')
|
||||
.then<IBuiltInExtensionControl>(raw => JSON.parse(raw), () => ({} as any));
|
||||
|
||||
const input = new ExtensionScannerInput(version, commit, locale, devMode, getExtraDevSystemExtensionsRoot(), true, translations);
|
||||
const extraBuiltinExtensions = TPromise.join([builtInExtensions, controlFile])
|
||||
.then(([builtInExtensions, control]) => new ExtraBuiltInExtensionResolver(builtInExtensions, control))
|
||||
.then(resolver => ExtensionScanner.scanExtensions(input, log, resolver));
|
||||
|
||||
finalBuiltinExtensions = TPromise.join([builtinExtensions, extraBuiltinExtensions]).then(([builtinExtensions, extraBuiltinExtensions]) => {
|
||||
let resultMap: { [id: string]: IExtensionDescription; } = Object.create(null);
|
||||
for (let i = 0, len = builtinExtensions.length; i < len; i++) {
|
||||
resultMap[builtinExtensions[i].id] = builtinExtensions[i];
|
||||
}
|
||||
// Overwrite with extensions found in extra
|
||||
for (let i = 0, len = extraBuiltinExtensions.length; i < len; i++) {
|
||||
resultMap[extraBuiltinExtensions[i].id] = extraBuiltinExtensions[i];
|
||||
}
|
||||
|
||||
let resultArr = Object.keys(resultMap).map((id) => resultMap[id]);
|
||||
resultArr.sort((a, b) => {
|
||||
const aLastSegment = path.basename(a.extensionFolderPath);
|
||||
const bLastSegment = path.basename(b.extensionFolderPath);
|
||||
if (aLastSegment < bLastSegment) {
|
||||
return -1;
|
||||
}
|
||||
if (aLastSegment > bLastSegment) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
return resultArr;
|
||||
});
|
||||
}
|
||||
|
||||
const userExtensions = (
|
||||
environmentService.disableExtensions || !environmentService.extensionsPath
|
||||
? TPromise.as([])
|
||||
: this._scanExtensionsWithCache(
|
||||
windowService,
|
||||
choiceService,
|
||||
environmentService,
|
||||
USER_MANIFEST_CACHE_FILE,
|
||||
new ExtensionScannerInput(version, commit, locale, devMode, environmentService.extensionsPath, false, translations),
|
||||
log
|
||||
)
|
||||
);
|
||||
|
||||
// Always load developed extensions while extensions development
|
||||
const developedExtensions = (
|
||||
environmentService.isExtensionDevelopment
|
||||
? ExtensionScanner.scanOneOrMultipleExtensions(
|
||||
new ExtensionScannerInput(version, commit, locale, devMode, environmentService.extensionDevelopmentPath, false, translations), log
|
||||
)
|
||||
: TPromise.as([])
|
||||
);
|
||||
|
||||
return TPromise.join([finalBuiltinExtensions, userExtensions, developedExtensions]).then((extensionDescriptions: IExtensionDescription[][]) => {
|
||||
const system = extensionDescriptions[0];
|
||||
const user = extensionDescriptions[1];
|
||||
const development = extensionDescriptions[2];
|
||||
@@ -620,6 +748,8 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
log.error('', err);
|
||||
return { system: [], user: [], development: [] };
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private static _handleExtensionPoint<T>(extensionPoint: ExtensionPoint<T>, availableExtensions: IExtensionDescription[], messageHandler: (msg: IMessage) => void): void {
|
||||
@@ -641,7 +771,7 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
|
||||
private _showMessageToUser(severity: Severity, msg: string): void {
|
||||
if (severity === Severity.Error || severity === Severity.Warning) {
|
||||
this._messageService.show(severity, msg);
|
||||
this._notificationService.notify({ severity, message: msg });
|
||||
} else {
|
||||
this._logMessageInConsole(severity, msg);
|
||||
}
|
||||
@@ -695,6 +825,22 @@ export class ExtensionService extends Disposable implements IExtensionService {
|
||||
}
|
||||
}
|
||||
|
||||
function asLoggingProtocol(protocol: IMessagePassingProtocol): IMessagePassingProtocol {
|
||||
|
||||
protocol.onMessage(msg => {
|
||||
console.log('%c[Extension \u2192 Window]%c[len: ' + strings.pad(msg.length, 5, ' ') + ']', 'color: darkgreen', 'color: grey', msg);
|
||||
});
|
||||
|
||||
return {
|
||||
onMessage: protocol.onMessage,
|
||||
|
||||
send(msg: any) {
|
||||
protocol.send(msg);
|
||||
console.log('%c[Window \u2192 Extension]%c[len: ' + strings.pad(msg.length, 5, ' ') + ']', 'color: darkgreen', 'color: grey', msg);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
interface IExtensionCacheData {
|
||||
input: ExtensionScannerInput;
|
||||
result: IExtensionDescription[];
|
||||
|
||||
Reference in New Issue
Block a user