Merge from vscode 70dc55955d586ebd427658b43cdb344f2047f9c2 (#6789)

This commit is contained in:
Anthony Dresser
2019-08-16 21:47:46 -07:00
committed by GitHub
parent fb26126bcb
commit 41d8663b09
79 changed files with 1815 additions and 572 deletions

View File

@@ -22,10 +22,16 @@ import { WebWorkerExtensionHostStarter } from 'vs/workbench/services/extensions/
import { URI } from 'vs/base/common/uri';
import { isWebExtension } from 'vs/workbench/services/extensions/common/extensionsUtil';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { FetchFileSystemProvider } from 'vs/workbench/services/extensions/browser/webWorkerFileSystemProvider';
import { Schemas } from 'vs/base/common/network';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { IStaticExtensionsService } from 'vs/workbench/services/extensions/common/staticExtensions';
export class ExtensionService extends AbstractExtensionService implements IExtensionService {
private _remoteExtensionsEnvironmentData: IRemoteAgentEnvironment | null;
private _disposables = new DisposableStore();
private _remoteExtensionsEnvironmentData: IRemoteAgentEnvironment | null = null;
constructor(
@IInstantiationService instantiationService: IInstantiationService,
@@ -37,6 +43,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
@IProductService productService: IProductService,
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService,
@IConfigurationService private readonly _configService: IConfigurationService,
@IStaticExtensionsService private readonly _staticExtensions: IStaticExtensionsService,
) {
super(
instantiationService,
@@ -48,8 +55,19 @@ export class ExtensionService extends AbstractExtensionService implements IExten
productService,
);
this._remoteExtensionsEnvironmentData = null;
this._initialize();
this._initFetchFileSystem();
}
dispose(): void {
this._disposables.dispose();
super.dispose();
}
private _initFetchFileSystem(): void {
const provider = new FetchFileSystemProvider();
this._disposables.add(this._fileService.registerProvider(Schemas.http, provider));
this._disposables.add(this._fileService.registerProvider(Schemas.https, provider));
}
private _createProvider(remoteAuthority: string): IInitDataProvider {
@@ -84,23 +102,31 @@ export class ExtensionService extends AbstractExtensionService implements IExten
protected async _scanAndHandleExtensions(): Promise<void> {
// fetch the remote environment
const remoteEnv = (await this._remoteAgentService.getEnvironment())!;
let [remoteEnv, localExtensions] = await Promise.all([
<Promise<IRemoteAgentEnvironment>>this._remoteAgentService.getEnvironment(),
this._staticExtensions.getExtensions()
]);
// enable or disable proposed API per extension
// local: only enabled and web'ish extension
localExtensions = localExtensions.filter(ext => this._isEnabled(ext) && isWebExtension(ext, this._configService));
this._checkEnableProposedApi(localExtensions);
// remote: only enabled and none-web'ish extension
remoteEnv.extensions = remoteEnv.extensions.filter(extension => this._isEnabled(extension) && !isWebExtension(extension, this._configService));
this._checkEnableProposedApi(remoteEnv.extensions);
// remove disabled extensions
remoteEnv.extensions = remoteEnv.extensions.filter(extension => this._isEnabled(extension));
// in case of overlap, the remote wins
const isRemoteExtension = new Set<string>();
remoteEnv.extensions.forEach(extension => isRemoteExtension.add(ExtensionIdentifier.toKey(extension.identifier)));
localExtensions = localExtensions.filter(extension => !isRemoteExtension.has(ExtensionIdentifier.toKey(extension.identifier)));
// save for remote extension's init data
this._remoteExtensionsEnvironmentData = remoteEnv;
// this._handleExtensionPoints((<IExtensionDescription[]>[]).concat(remoteEnv.extensions).concat(localExtensions));
const result = this._registry.deltaExtensions(remoteEnv.extensions, []);
const result = this._registry.deltaExtensions(remoteEnv.extensions.concat(localExtensions), []);
if (result.removedDueToLooping.length > 0) {
this._logOrShowMessage(Severity.Error, nls.localize('looping', "The following extensions contain dependency loops and have been disabled: {0}", result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', ')));
}
this._doHandleExtensionPoints(this._registry.getAllExtensionDescriptions());
}

View File

@@ -65,8 +65,8 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter {
};
worker.onerror = (event) => {
console.error(event.error);
this._onDidExit.fire([81, event.error]);
console.error(event.message, event.error);
this._onDidExit.fire([81, event.message || event.error]);
};
// keep for cleanup

View File

@@ -0,0 +1,62 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IFileSystemProvider, FileSystemProviderCapabilities, IStat, FileType, FileDeleteOptions, FileOverwriteOptions, FileWriteOptions, FileSystemProviderError, FileSystemProviderErrorCode } from 'vs/platform/files/common/files';
import { Event } from 'vs/base/common/event';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { NotImplementedError } from 'vs/base/common/errors';
export class FetchFileSystemProvider implements IFileSystemProvider {
readonly capabilities = FileSystemProviderCapabilities.Readonly + FileSystemProviderCapabilities.FileReadWrite + FileSystemProviderCapabilities.PathCaseSensitive;
readonly onDidChangeCapabilities = Event.None;
readonly onDidChangeFile = Event.None;
// working implementations
async readFile(resource: URI): Promise<Uint8Array> {
try {
const res = await fetch(resource.toString(true));
if (res.status === 200) {
return new Uint8Array(await res.arrayBuffer());
}
throw new FileSystemProviderError(res.statusText, FileSystemProviderErrorCode.Unknown);
} catch (err) {
throw new FileSystemProviderError(err, FileSystemProviderErrorCode.Unknown);
}
}
// fake implementations
async stat(_resource: URI): Promise<IStat> {
return {
type: FileType.File,
size: 0,
mtime: 0,
ctime: 0
};
}
watch(): IDisposable {
return Disposable.None;
}
// error implementations
writeFile(_resource: URI, _content: Uint8Array, _opts: FileWriteOptions): Promise<void> {
throw new NotImplementedError();
}
readdir(_resource: URI): Promise<[string, FileType][]> {
throw new NotImplementedError();
}
mkdir(_resource: URI): Promise<void> {
throw new NotImplementedError();
}
delete(_resource: URI, _opts: FileDeleteOptions): Promise<void> {
throw new NotImplementedError();
}
rename(_from: URI, _to: URI, _opts: FileOverwriteOptions): Promise<void> {
throw new NotImplementedError();
}
}

View File

@@ -61,9 +61,7 @@ export interface IExtensionPointUser<T> {
collector: ExtensionMessageCollector;
}
export interface IExtensionPointHandler<T> {
(extensions: IExtensionPointUser<T>[], delta: ExtensionPointUserDelta<T>): void;
}
export type IExtensionPointHandler<T> = (extensions: readonly IExtensionPointUser<T>[], delta: ExtensionPointUserDelta<T>) => void;
export interface IExtensionPoint<T> {
name: string;
@@ -73,7 +71,7 @@ export interface IExtensionPoint<T> {
export class ExtensionPointUserDelta<T> {
private static _toSet<T>(arr: IExtensionPointUser<T>[]): Set<string> {
private static _toSet<T>(arr: readonly IExtensionPointUser<T>[]): Set<string> {
const result = new Set<string>();
for (let i = 0, len = arr.length; i < len; i++) {
result.add(ExtensionIdentifier.toKey(arr[i].description.identifier));
@@ -81,7 +79,7 @@ export class ExtensionPointUserDelta<T> {
return result;
}
public static compute<T>(previous: IExtensionPointUser<T>[] | null, current: IExtensionPointUser<T>[]): ExtensionPointUserDelta<T> {
public static compute<T>(previous: readonly IExtensionPointUser<T>[] | null, current: readonly IExtensionPointUser<T>[]): ExtensionPointUserDelta<T> {
if (!previous || !previous.length) {
return new ExtensionPointUserDelta<T>(current, []);
}
@@ -99,8 +97,8 @@ export class ExtensionPointUserDelta<T> {
}
constructor(
public readonly added: IExtensionPointUser<T>[],
public readonly removed: IExtensionPointUser<T>[],
public readonly added: readonly IExtensionPointUser<T>[],
public readonly removed: readonly IExtensionPointUser<T>[],
) { }
}

View File

@@ -0,0 +1,34 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtensionDescription, IExtensionManifest, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { UriComponents, URI } from 'vs/base/common/uri';
export const IStaticExtensionsService = createDecorator<IStaticExtensionsService>('IStaticExtensionsService');
export interface IStaticExtensionsService {
_serviceBrand: any;
getExtensions(): Promise<IExtensionDescription[]>;
}
export class StaticExtensionsService implements IStaticExtensionsService {
_serviceBrand: any;
private readonly _descriptions: IExtensionDescription[] = [];
constructor(staticExtensions: { packageJSON: IExtensionManifest, extensionLocation: UriComponents }[]) {
this._descriptions = staticExtensions.map(data => <IExtensionDescription>{
identifier: new ExtensionIdentifier(`${data.packageJSON.publisher}.${data.packageJSON.name}`),
extensionLocation: URI.revive(data.extensionLocation),
...data.packageJSON,
});
}
async getExtensions(): Promise<IExtensionDescription[]> {
return this._descriptions;
}
}

View File

@@ -34,6 +34,8 @@ import { IFileService } from 'vs/platform/files/common/files';
import { PersistentConnectionEventType } from 'vs/platform/remote/common/remoteAgentConnection';
import { IProductService } from 'vs/platform/product/common/product';
import { Logger } from 'vs/workbench/services/extensions/common/extensionPoints';
import { flatten } from 'vs/base/common/arrays';
import { IStaticExtensionsService } from 'vs/workbench/services/extensions/common/staticExtensions';
class DeltaExtensionsQueueItem {
constructor(
@@ -64,6 +66,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
@IConfigurationService private readonly _configurationService: IConfigurationService,
@ILifecycleService private readonly _lifecycleService: ILifecycleService,
@IWindowService protected readonly _windowService: IWindowService,
@IStaticExtensionsService private readonly _staticExtensions: IStaticExtensionsService,
) {
super(
instantiationService,
@@ -72,7 +75,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
telemetryService,
extensionEnablementService,
fileService,
productService,
productService
);
if (this._extensionEnablementService.allUserExtensionsDisabled) {
@@ -437,7 +440,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
const remoteAuthority = this._environmentService.configuration.remoteAuthority;
const extensionHost = this._extensionHostProcessManagers[0];
let localExtensions = await this._extensionScanner.scannedExtensions;
let localExtensions = flatten(await Promise.all([this._extensionScanner.scannedExtensions, this._staticExtensions.getExtensions()]));
// enable or disable proposed API per extension
this._checkEnableProposedApi(localExtensions);
@@ -463,7 +466,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
this._remoteAuthorityResolverService.setResolvedAuthorityError(remoteAuthority, err);
// Proceed with the local extension host
await this._startLocalExtensionHost(extensionHost, localExtensions);
await this._startLocalExtensionHost(extensionHost, localExtensions, localExtensions.map(extension => extension.identifier));
return;
}
@@ -508,20 +511,18 @@ export class ExtensionService extends AbstractExtensionService implements IExten
// save for remote extension's init data
this._remoteExtensionsEnvironmentData.set(remoteAuthority, remoteEnv);
this._handleExtensionPoints((<IExtensionDescription[]>[]).concat(remoteEnv.extensions).concat(localExtensions));
extensionHost.start(localExtensions.map(extension => extension.identifier));
await this._startLocalExtensionHost(extensionHost, remoteEnv.extensions.concat(localExtensions), localExtensions.map(extension => extension.identifier));
} else {
await this._startLocalExtensionHost(extensionHost, localExtensions);
await this._startLocalExtensionHost(extensionHost, localExtensions, localExtensions.map(extension => extension.identifier));
}
}
private async _startLocalExtensionHost(extensionHost: ExtensionHostProcessManager, localExtensions: IExtensionDescription[]): Promise<void> {
this._handleExtensionPoints(localExtensions);
extensionHost.start(localExtensions.map(extension => extension.identifier).filter(id => this._registry.containsExtension(id)));
private async _startLocalExtensionHost(extensionHost: ExtensionHostProcessManager, allExtensions: IExtensionDescription[], localExtensions: ExtensionIdentifier[]): Promise<void> {
this._registerAndHandleExtensions(allExtensions);
extensionHost.start(localExtensions.filter(id => this._registry.containsExtension(id)));
}
private _handleExtensionPoints(allExtensions: IExtensionDescription[]): void {
private _registerAndHandleExtensions(allExtensions: IExtensionDescription[]): void {
const result = this._registry.deltaExtensions(allExtensions, []);
if (result.removedDueToLooping.length > 0) {
this._logOrShowMessage(Severity.Error, nls.localize('looping', "The following extensions contain dependency loops and have been disabled: {0}", result.removedDueToLooping.map(e => `'${e.identifier.value}'`).join(', ')));