Merge from master

This commit is contained in:
Raj Musuku
2019-02-21 17:56:04 -08:00
parent 5a146e34fa
commit 666ae11639
11482 changed files with 119352 additions and 255574 deletions

View File

@@ -2,28 +2,31 @@
* 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 { dispose } from 'vs/base/common/lifecycle';
import { join } from 'path';
import { mkdirp, dirExists, realpath, writeFile } from 'vs/base/node/pfs';
import Severity from 'vs/base/common/severity';
import { TPromise } from 'vs/base/common/winjs.base';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions';
import { ExtHostStorage } from 'vs/workbench/api/node/extHostStorage';
import * as nls from 'vs/nls';
import * as path from 'path';
// {{SQL CARBON EDIT}}
import { createApiFactory, initializeExtensionApi } from 'sql/workbench/api/node/sqlExtHost.api.impl';
import { checkProposedApiEnabled } from 'vs/workbench/api/node/extHost.api.impl';
import { MainContext, MainThreadExtensionServiceShape, IWorkspaceData, IEnvironment, IInitData, ExtHostExtensionServiceShape, MainThreadTelemetryShape, IMainContext } from './extHost.protocol';
import { IExtensionMemento, ExtensionsActivator, ActivatedExtension, IExtensionAPI, IExtensionContext, EmptyExtension, IExtensionModule, ExtensionActivationTimesBuilder, ExtensionActivationTimes, ExtensionActivationReason, ExtensionActivatedByEvent } from 'vs/workbench/api/node/extHostExtensionActivator';
import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { TernarySearchTree } from 'vs/base/common/map';
import { createApiFactory, initializeExtensionApi, ISqlExtensionApiFactory } from 'sql/workbench/api/node/sqlExtHost.api.impl';
import { Barrier } from 'vs/base/common/async';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { TernarySearchTree } from 'vs/base/common/map';
import Severity from 'vs/base/common/severity';
import { URI } from 'vs/base/common/uri';
import * as pfs from 'vs/base/node/pfs';
import { ILogService } from 'vs/platform/log/common/log';
// {{SQL CARBON EDIT}} - Remove createApiFactory initializeExtensionApi, and IExtensionApiFactory imports
import { ExtHostExtensionServiceShape, IEnvironment, IInitData, IMainContext, IWorkspaceData, MainContext, MainThreadExtensionServiceShape, MainThreadTelemetryShape, MainThreadWorkspaceShape } from 'vs/workbench/api/node/extHost.protocol';
import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration';
import { ActivatedExtension, EmptyExtension, ExtensionActivatedByAPI, ExtensionActivatedByEvent, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionContext, IExtensionMemento, IExtensionModule } from 'vs/workbench/api/node/extHostExtensionActivator';
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
import URI from 'vs/base/common/uri';
import { ExtHostStorage } from 'vs/workbench/api/node/extHostStorage';
import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { IExtensionDescription, checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry';
import { connectProxyResolver } from 'vs/workbench/node/proxyResolver';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import * as errors from 'vs/base/common/errors';
import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver';
class ExtensionMemento implements IExtensionMemento {
@@ -31,8 +34,9 @@ class ExtensionMemento implements IExtensionMemento {
private readonly _shared: boolean;
private readonly _storage: ExtHostStorage;
private readonly _init: TPromise<ExtensionMemento>;
private readonly _init: Thenable<ExtensionMemento>;
private _value: { [n: string]: any; };
private readonly _storageListener: IDisposable;
constructor(id: string, global: boolean, storage: ExtHostStorage) {
this._id = id;
@@ -43,9 +47,15 @@ class ExtensionMemento implements IExtensionMemento {
this._value = value;
return this;
});
this._storageListener = this._storage.onDidChangeStorage(e => {
if (e.shared === this._shared && e.key === this._id) {
this._value = e.value;
}
});
}
get whenReady(): TPromise<ExtensionMemento> {
get whenReady(): Thenable<ExtensionMemento> {
return this._init;
}
@@ -63,6 +73,10 @@ class ExtensionMemento implements IExtensionMemento {
.setValue(this._shared, this._id, this._value)
.then(() => true);
}
dispose(): void {
this._storageListener.dispose();
}
}
class ExtensionStoragePath {
@@ -83,31 +97,35 @@ class ExtensionStoragePath {
return this._ready;
}
value(extension: IExtensionDescription): string {
workspaceValue(extension: IExtensionDescription): string {
if (this._value) {
return join(this._value, extension.id);
return path.join(this._value, extension.id);
}
return undefined;
}
globalValue(extension: IExtensionDescription): string {
return path.join(this._environment.globalStorageHome.fsPath, extension.id);
}
private async _getOrCreateWorkspaceStoragePath(): Promise<string> {
if (!this._workspace) {
return TPromise.as(undefined);
return Promise.resolve(undefined);
}
const storageName = this._workspace.id;
const storagePath = join(this._environment.appSettingsHome, 'workspaceStorage', storageName);
const storagePath = path.join(this._environment.appSettingsHome.fsPath, 'workspaceStorage', storageName);
const exists = await dirExists(storagePath);
const exists = await pfs.dirExists(storagePath);
if (exists) {
return storagePath;
}
try {
await mkdirp(storagePath);
await writeFile(
join(storagePath, 'meta.json'),
await pfs.mkdirp(storagePath);
await pfs.writeFile(
path.join(storagePath, 'meta.json'),
JSON.stringify({
id: this._workspace.id,
configuration: this._workspace.configuration && URI.revive(this._workspace.configuration).toString(),
@@ -123,67 +141,112 @@ class ExtensionStoragePath {
}
}
interface ITestRunner {
run(testsRoot: string, clb: (error: Error, failures?: number) => void): void;
}
export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
private static readonly WORKSPACE_CONTAINS_TIMEOUT = 7000;
private readonly _nativeExit: (code?: number) => void;
private readonly _initData: IInitData;
private readonly _extHostContext: IMainContext;
private readonly _extHostWorkspace: ExtHostWorkspace;
private readonly _extHostConfiguration: ExtHostConfiguration;
private readonly _extHostLogService: ExtHostLogService;
private readonly _mainThreadWorkspaceProxy: MainThreadWorkspaceShape;
private readonly _mainThreadTelemetryProxy: MainThreadTelemetryShape;
private readonly _mainThreadExtensionsProxy: MainThreadExtensionServiceShape;
private readonly _barrier: Barrier;
private readonly _registry: ExtensionDescriptionRegistry;
private readonly _mainThreadTelemetry: MainThreadTelemetryShape;
private readonly _storage: ExtHostStorage;
private readonly _storagePath: ExtensionStoragePath;
private readonly _proxy: MainThreadExtensionServiceShape;
private readonly _extHostLogService: ExtHostLogService;
private _activator: ExtensionsActivator;
private _extensionPathIndex: TPromise<TernarySearchTree<IExtensionDescription>>;
/**
* This class is constructed manually because it is a service, so it doesn't use any ctor injection
*/
constructor(initData: IInitData,
private readonly _activator: ExtensionsActivator;
private _extensionPathIndex: Promise<TernarySearchTree<IExtensionDescription>>;
// {{SQL CARBON EDIT}}
private readonly _extensionApiFactory: ISqlExtensionApiFactory;
private _started: boolean;
constructor(
nativeExit: (code?: number) => void,
initData: IInitData,
extHostContext: IMainContext,
extHostWorkspace: ExtHostWorkspace,
extHostConfiguration: ExtHostConfiguration,
extHostLogService: ExtHostLogService
) {
this._nativeExit = nativeExit;
this._initData = initData;
this._extHostContext = extHostContext;
this._extHostWorkspace = extHostWorkspace;
this._extHostConfiguration = extHostConfiguration;
this._extHostLogService = extHostLogService;
this._mainThreadWorkspaceProxy = this._extHostContext.getProxy(MainContext.MainThreadWorkspace);
this._mainThreadTelemetryProxy = this._extHostContext.getProxy(MainContext.MainThreadTelemetry);
this._mainThreadExtensionsProxy = this._extHostContext.getProxy(MainContext.MainThreadExtensionService);
this._barrier = new Barrier();
this._registry = new ExtensionDescriptionRegistry(initData.extensions);
this._extHostLogService = extHostLogService;
this._mainThreadTelemetry = extHostContext.getProxy(MainContext.MainThreadTelemetry);
this._storage = new ExtHostStorage(extHostContext);
this._storage = new ExtHostStorage(this._extHostContext);
this._storagePath = new ExtensionStoragePath(initData.workspace, initData.environment);
this._proxy = extHostContext.getProxy(MainContext.MainThreadExtensionService);
this._activator = null;
this._activator = new ExtensionsActivator(this._registry, {
showMessage: (severity: Severity, message: string): void => {
this._mainThreadExtensionsProxy.$localShowMessage(severity, message);
switch (severity) {
case Severity.Error:
console.error(message);
break;
case Severity.Warning:
console.warn(message);
break;
default:
console.log(message);
}
},
actualActivateExtension: (extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension> => {
return this._activateExtension(extensionDescription, reason);
}
});
this._extensionPathIndex = null;
// initialize API first (i.e. do not release barrier until the API is initialized)
const apiFactory = createApiFactory(initData, extHostContext, extHostWorkspace, extHostConfiguration, this, this._extHostLogService);
this._extensionApiFactory = createApiFactory(this._initData, this._extHostContext, this._extHostWorkspace, this._extHostConfiguration, this, this._extHostLogService, this._storage);
initializeExtensionApi(this, apiFactory).then(() => {
this._activator = new ExtensionsActivator(this._registry, {
showMessage: (severity: Severity, message: string): void => {
this._proxy.$localShowMessage(severity, message);
switch (severity) {
case Severity.Error:
console.error(message);
break;
case Severity.Warning:
console.warn(message);
break;
default:
console.log(message);
}
},
actualActivateExtension: (extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): TPromise<ActivatedExtension> => {
return this._activateExtension(extensionDescription, reason);
}
});
this._started = false;
initializeExtensionApi(this, this._extensionApiFactory, this._registry).then(() => {
// Do this when extension service exists, but extensions are not being activated yet.
return connectProxyResolver(this._extHostWorkspace, this._extHostConfiguration, this, this._extHostLogService, this._mainThreadTelemetryProxy);
}).then(() => {
this._barrier.open();
});
if (this._initData.autoStart) {
this._startExtensionHost();
}
}
public onExtensionAPIReady(): TPromise<boolean> {
return this._barrier.wait();
public async deactivateAll(): Promise<void> {
let allPromises: Thenable<void>[] = [];
try {
const allExtensions = this._registry.getAllExtensionDescriptions();
const allExtensionsIds = allExtensions.map(ext => ext.id);
const activatedExtensions = allExtensionsIds.filter(id => this.isActivated(id));
allPromises = activatedExtensions.map((extensionId) => {
return this._deactivate(extensionId);
});
} catch (err) {
// TODO: write to log once we have one
}
await allPromises;
}
public isActivated(extensionId: string): boolean {
@@ -193,40 +256,28 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
return false;
}
public activateByEvent(activationEvent: string, startup: boolean): TPromise<void> {
private _activateByEvent(activationEvent: string, startup: boolean): Thenable<void> {
const reason = new ExtensionActivatedByEvent(startup, activationEvent);
if (this._barrier.isOpen()) {
return this._activator.activateByEvent(activationEvent, reason);
} else {
return this._barrier.wait().then(() => this._activator.activateByEvent(activationEvent, reason));
}
return this._activator.activateByEvent(activationEvent, reason);
}
public activateById(extensionId: string, reason: ExtensionActivationReason): TPromise<void> {
if (this._barrier.isOpen()) {
return this._activator.activateById(extensionId, reason);
} else {
return this._barrier.wait().then(() => this._activator.activateById(extensionId, reason));
}
private _activateById(extensionId: string, reason: ExtensionActivationReason): Thenable<void> {
return this._activator.activateById(extensionId, reason);
}
public activateByIdWithErrors(extensionId: string, reason: ExtensionActivationReason): TPromise<void> {
return this.activateById(extensionId, reason).then(() => {
public activateByIdWithErrors(extensionId: string, reason: ExtensionActivationReason): Thenable<void> {
return this._activateById(extensionId, reason).then(() => {
const extension = this._activator.getActivatedExtension(extensionId);
if (extension.activationFailed) {
// activation failed => bubble up the error as the promise result
return TPromise.wrapError(extension.activationFailedError);
return Promise.reject(extension.activationFailedError);
}
return void 0;
});
}
public getAllExtensionDescriptions(): IExtensionDescription[] {
return this._registry.getAllExtensionDescriptions();
}
public getExtensionDescription(extensionId: string): IExtensionDescription {
return this._registry.getExtensionDescription(extensionId);
public getExtensionRegistry(): Promise<ExtensionDescriptionRegistry> {
return this._barrier.wait().then(_ => this._registry);
}
public getExtensionExports(extensionId: string): IExtensionAPI {
@@ -238,23 +289,22 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
}
// create trie to enable fast 'filename -> extension id' look up
public getExtensionPathIndex(): TPromise<TernarySearchTree<IExtensionDescription>> {
public getExtensionPathIndex(): Promise<TernarySearchTree<IExtensionDescription>> {
if (!this._extensionPathIndex) {
const tree = TernarySearchTree.forPaths<IExtensionDescription>();
const extensions = this.getAllExtensionDescriptions().map(ext => {
const extensions = this._registry.getAllExtensionDescriptions().map(ext => {
if (!ext.main) {
return undefined;
}
return realpath(ext.extensionLocation.fsPath).then(value => tree.set(value, ext));
return pfs.realpath(ext.extensionLocation.fsPath).then(value => tree.set(URI.file(value).fsPath, ext));
});
this._extensionPathIndex = TPromise.join(extensions).then(() => tree);
this._extensionPathIndex = Promise.all(extensions).then(() => tree);
}
return this._extensionPathIndex;
}
public deactivate(extensionId: string): TPromise<void> {
let result: TPromise<void> = TPromise.as(void 0);
private _deactivate(extensionId: string): Thenable<void> {
let result = Promise.resolve(void 0);
if (!this._barrier.isOpen()) {
return result;
@@ -272,9 +322,9 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
// call deactivate if available
try {
if (typeof extension.module.deactivate === 'function') {
result = TPromise.wrap(extension.module.deactivate()).then(null, (err) => {
result = Promise.resolve(extension.module.deactivate()).then(null, (err) => {
// TODO: Do something with err if this is not the shutdown case
return TPromise.as(void 0);
return Promise.resolve(void 0);
});
}
} catch (err) {
@@ -292,25 +342,45 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
}
public addMessage(extensionId: string, severity: Severity, message: string): void {
this._proxy.$addMessage(extensionId, severity, message);
this._mainThreadExtensionsProxy.$addMessage(extensionId, severity, message);
}
// --- impl
private _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): TPromise<ActivatedExtension> {
private _activateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension> {
return this._doActivateExtension(extensionDescription, reason).then((activatedExtension) => {
const activationTimes = activatedExtension.activationTimes;
let activationEvent = (reason instanceof ExtensionActivatedByEvent ? reason.activationEvent : null);
this._proxy.$onExtensionActivated(extensionDescription.id, activationTimes.startup, activationTimes.codeLoadingTime, activationTimes.activateCallTime, activationTimes.activateResolvedTime, activationEvent);
this._mainThreadExtensionsProxy.$onExtensionActivated(extensionDescription.id, activationTimes.startup, activationTimes.codeLoadingTime, activationTimes.activateCallTime, activationTimes.activateResolvedTime, activationEvent);
this._logExtensionActivationTimes(extensionDescription, reason, 'success', activationTimes);
return activatedExtension;
}, (err) => {
this._proxy.$onExtensionActivationFailed(extensionDescription.id);
this._mainThreadExtensionsProxy.$onExtensionActivationFailed(extensionDescription.id);
this._logExtensionActivationTimes(extensionDescription, reason, 'failure');
throw err;
});
}
private _doActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): TPromise<ActivatedExtension> {
let event = getTelemetryActivationEvent(extensionDescription);
private _logExtensionActivationTimes(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason, outcome: string, activationTimes?: ExtensionActivationTimes) {
let event = getTelemetryActivationEvent(extensionDescription, reason);
/* __GDPR__
"extensionActivationTimes" : {
"${include}": [
"${TelemetryActivationEvent}",
"${ExtensionActivationTimes}"
],
"outcome" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this._mainThreadTelemetryProxy.$publicLog('extensionActivationTimes', {
...event,
...(activationTimes || {}),
outcome,
});
}
private _doActivateExtension(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): Promise<ActivatedExtension> {
let event = getTelemetryActivationEvent(extensionDescription, reason);
/* __GDPR__
"activatePlugin" : {
"${include}": [
@@ -318,39 +388,30 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
]
}
*/
this._mainThreadTelemetry.$publicLog('activatePlugin', event);
this._mainThreadTelemetryProxy.$publicLog('activatePlugin', event);
if (!extensionDescription.main) {
// Treat the extension as being empty => NOT AN ERROR CASE
return TPromise.as(new EmptyExtension(ExtensionActivationTimes.NONE));
return Promise.resolve(new EmptyExtension(ExtensionActivationTimes.NONE));
}
this._extHostLogService.info(`ExtensionService#_doActivateExtension ${extensionDescription.id} ${JSON.stringify(reason)}`);
const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup);
return TPromise.join<any>([
return Promise.all<any>([
loadCommonJSModule(this._extHostLogService, extensionDescription.main, activationTimesBuilder),
this._loadExtensionContext(extensionDescription)
]).then(values => {
return ExtHostExtensionService._callActivate(this._extHostLogService, extensionDescription.id, <IExtensionModule>values[0], <IExtensionContext>values[1], activationTimesBuilder);
}, (errors: any[]) => {
// Avoid failing with an array of errors, fail with a single error
if (errors[0]) {
return TPromise.wrapError<ActivatedExtension>(errors[0]);
}
if (errors[1]) {
return TPromise.wrapError<ActivatedExtension>(errors[1]);
}
return undefined;
});
}
private _loadExtensionContext(extensionDescription: IExtensionDescription): TPromise<IExtensionContext> {
private _loadExtensionContext(extensionDescription: IExtensionDescription): Promise<IExtensionContext> {
let globalState = new ExtensionMemento(extensionDescription.id, true, this._storage);
let workspaceState = new ExtensionMemento(extensionDescription.id, false, this._storage);
this._extHostLogService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.id}`);
return TPromise.join([
return Promise.all([
globalState.whenReady,
workspaceState.whenReady,
this._storagePath.whenReady
@@ -361,16 +422,10 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
workspaceState,
subscriptions: [],
get extensionPath() { return extensionDescription.extensionLocation.fsPath; },
storagePath: this._storagePath.value(extensionDescription),
asAbsolutePath: (relativePath: string) => { return join(extensionDescription.extensionLocation.fsPath, relativePath); },
get logger() {
checkProposedApiEnabled(extensionDescription);
return that._extHostLogService.getExtLogger(extensionDescription.id);
},
get logDirectory() {
checkProposedApiEnabled(extensionDescription);
return that._extHostLogService.getLogDirectory(extensionDescription.id);
}
storagePath: this._storagePath.workspaceValue(extensionDescription),
get globalStoragePath(): string { checkProposedApiEnabled(extensionDescription); return that._storagePath.globalValue(extensionDescription); },
asAbsolutePath: (relativePath: string) => { return path.join(extensionDescription.extensionLocation.fsPath, relativePath); },
logPath: that._extHostLogService.getLogDirectory(extensionDescription.id)
});
});
}
@@ -392,60 +447,246 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
try {
activationTimesBuilder.activateCallStart();
logService.trace(`ExtensionService#_callActivateOptional ${extensionId}`);
const activateResult: TPromise<IExtensionAPI> = extensionModule.activate.apply(global, [context]);
const activateResult: Thenable<IExtensionAPI> = extensionModule.activate.apply(global, [context]);
activationTimesBuilder.activateCallStop();
activationTimesBuilder.activateResolveStart();
return TPromise.as(activateResult).then((value) => {
return Promise.resolve(activateResult).then((value) => {
activationTimesBuilder.activateResolveStop();
return value;
});
} catch (err) {
return TPromise.wrapError(err);
return Promise.reject(err);
}
} else {
// No activate found => the module is the extension's exports
return TPromise.as<IExtensionAPI>(extensionModule);
return Promise.resolve<IExtensionAPI>(extensionModule);
}
}
// -- eager activation
// Handle "eager" activation extensions
private _handleEagerExtensions(): Promise<void> {
this._activateByEvent('*', true).then(null, (err) => {
console.error(err);
});
return this._handleWorkspaceContainsEagerExtensions(this._initData.workspace);
}
private _handleWorkspaceContainsEagerExtensions(workspace: IWorkspaceData): Promise<void> {
if (!workspace || workspace.folders.length === 0) {
return Promise.resolve(null);
}
return Promise.all(
this._registry.getAllExtensionDescriptions().map((desc) => {
return this._handleWorkspaceContainsEagerExtension(workspace, desc);
})
).then(() => { });
}
private _handleWorkspaceContainsEagerExtension(workspace: IWorkspaceData, desc: IExtensionDescription): Promise<void> {
const activationEvents = desc.activationEvents;
if (!activationEvents) {
return Promise.resolve(void 0);
}
const fileNames: string[] = [];
const globPatterns: string[] = [];
for (let i = 0; i < activationEvents.length; i++) {
if (/^workspaceContains:/.test(activationEvents[i])) {
const fileNameOrGlob = activationEvents[i].substr('workspaceContains:'.length);
if (fileNameOrGlob.indexOf('*') >= 0 || fileNameOrGlob.indexOf('?') >= 0) {
globPatterns.push(fileNameOrGlob);
} else {
fileNames.push(fileNameOrGlob);
}
}
}
if (fileNames.length === 0 && globPatterns.length === 0) {
return Promise.resolve(void 0);
}
const fileNamePromise = Promise.all(fileNames.map((fileName) => this._activateIfFileName(workspace, desc.id, fileName))).then(() => { });
const globPatternPromise = this._activateIfGlobPatterns(desc.id, globPatterns);
return Promise.all([fileNamePromise, globPatternPromise]).then(() => { });
}
private async _activateIfFileName(workspace: IWorkspaceData, extensionId: string, fileName: string): Promise<void> {
// find exact path
for (const { uri } of workspace.folders) {
if (await pfs.exists(path.join(URI.revive(uri).fsPath, fileName))) {
// the file was found
return (
this._activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContains:${fileName}`))
.then(null, err => console.error(err))
);
}
}
return undefined;
}
private async _activateIfGlobPatterns(extensionId: string, globPatterns: string[]): Promise<void> {
this._extHostLogService.trace(`extensionHostMain#activateIfGlobPatterns: fileSearch, extension: ${extensionId}, entryPoint: workspaceContains`);
if (globPatterns.length === 0) {
return Promise.resolve(void 0);
}
const tokenSource = new CancellationTokenSource();
const searchP = this._mainThreadWorkspaceProxy.$checkExists(globPatterns, tokenSource.token);
const timer = setTimeout(async () => {
tokenSource.cancel();
this._activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContainsTimeout:${globPatterns.join(',')}`))
.then(null, err => console.error(err));
}, ExtHostExtensionService.WORKSPACE_CONTAINS_TIMEOUT);
let exists: boolean;
try {
exists = await searchP;
} catch (err) {
if (!errors.isPromiseCanceledError(err)) {
console.error(err);
}
}
tokenSource.dispose();
clearTimeout(timer);
if (exists) {
// a file was found matching one of the glob patterns
return (
this._activateById(extensionId, new ExtensionActivatedByEvent(true, `workspaceContains:${globPatterns.join(',')}`))
.then(null, err => console.error(err))
);
}
return Promise.resolve(void 0);
}
private _handleExtensionTests(): Promise<void> {
if (!this._initData.environment.extensionTestsPath || !this._initData.environment.extensionDevelopmentLocationURI) {
return Promise.resolve(null);
}
// Require the test runner via node require from the provided path
let testRunner: ITestRunner;
let requireError: Error;
try {
testRunner = <any>require.__$__nodeRequire(this._initData.environment.extensionTestsPath);
} catch (error) {
requireError = error;
}
// Execute the runner if it follows our spec
if (testRunner && typeof testRunner.run === 'function') {
return new Promise<void>((c, e) => {
testRunner.run(this._initData.environment.extensionTestsPath, (error, failures) => {
if (error) {
e(error.toString());
} else {
c(null);
}
// after tests have run, we shutdown the host
this._gracefulExit(failures && failures > 0 ? 1 /* ERROR */ : 0 /* OK */);
});
});
}
// Otherwise make sure to shutdown anyway even in case of an error
else {
this._gracefulExit(1 /* ERROR */);
}
return Promise.reject(new Error(requireError ? requireError.toString() : nls.localize('extensionTestError', "Path {0} does not point to a valid extension test runner.", this._initData.environment.extensionTestsPath)));
}
private _gracefulExit(code: number): void {
// to give the PH process a chance to flush any outstanding console
// messages to the main process, we delay the exit() by some time
setTimeout(() => this._nativeExit(code), 500);
}
private _startExtensionHost(): Thenable<void> {
if (this._started) {
throw new Error(`Extension host is already started!`);
}
this._started = true;
return this._barrier.wait()
.then(() => this._handleEagerExtensions())
.then(() => this._handleExtensionTests())
.then(() => {
this._extHostLogService.info(`eager extensions activated`);
});
}
// -- called by main thread
public $activateByEvent(activationEvent: string): TPromise<void> {
return this.activateByEvent(activationEvent, false);
public async $resolveAuthority(remoteAuthority: string): Promise<ResolvedAuthority> {
throw new Error(`Not implemented`);
}
public $startExtensionHost(enabledExtensionIds: string[]): Thenable<void> {
this._registry.keepOnly(enabledExtensionIds);
return this._startExtensionHost();
}
public $activateByEvent(activationEvent: string): Thenable<void> {
return (
this._barrier.wait()
.then(_ => this._activateByEvent(activationEvent, false))
);
}
}
function loadCommonJSModule<T>(logService: ILogService, modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): TPromise<T> {
let r: T = null;
function loadCommonJSModule<T>(logService: ILogService, modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T> {
let r: T | null = null;
activationTimesBuilder.codeLoadingStart();
logService.info(`ExtensionService#loadCommonJSModule ${modulePath}`);
try {
r = require.__$__nodeRequire<T>(modulePath);
} catch (e) {
return TPromise.wrapError<T>(e);
return Promise.reject(e);
} finally {
activationTimesBuilder.codeLoadingStop();
}
return TPromise.as(r);
return Promise.resolve(r);
}
function getTelemetryActivationEvent(extensionDescription: IExtensionDescription): any {
function getTelemetryActivationEvent(extensionDescription: IExtensionDescription, reason: ExtensionActivationReason): any {
const reasonStr = reason instanceof ExtensionActivatedByEvent ? reason.activationEvent :
reason instanceof ExtensionActivatedByAPI ? 'api' :
'';
/* __GDPR__FRAGMENT__
"TelemetryActivationEvent" : {
"id": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
"name": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
"extensionVersion": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" },
"publisherDisplayName": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"activationEvents": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"isBuiltin": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
"isBuiltin": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"reason": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
let event = {
id: extensionDescription.id,
name: extensionDescription.name,
extensionVersion: extensionDescription.version,
publisherDisplayName: extensionDescription.publisher,
activationEvents: extensionDescription.activationEvents ? extensionDescription.activationEvents.join(',') : null,
isBuiltin: extensionDescription.isBuiltin
isBuiltin: extensionDescription.isBuiltin,
reason: reasonStr
};
return event;