mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
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:
@@ -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 {
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user