diff --git a/src/vs/platform/download/node/downloadIpc.ts b/src/vs/platform/download/node/downloadIpc.ts new file mode 100644 index 0000000000000000000000000000000000000000..413f411c4750d9190dafe4f4e637af6d6ebe9c5f --- /dev/null +++ b/src/vs/platform/download/node/downloadIpc.ts @@ -0,0 +1,79 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { URI } from 'vs/base/common/uri'; +import * as path from 'path'; +import * as fs from 'fs'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { IChannel } from 'vs/base/parts/ipc/node/ipc'; +import { Event, Emitter, buffer } from 'vs/base/common/event'; +import { IDownloadService } from 'vs/platform/download/common/download'; +import { mkdirp } from 'vs/base/node/pfs'; +import { IURITransformer } from 'vs/base/common/uriIpc'; + +export type UploadResponse = Buffer | string | undefined; + +export function upload(uri: URI): Event { + const stream = new Emitter(); + const readstream = fs.createReadStream(uri.fsPath); + readstream.on('data', data => stream.fire(data)); + readstream.on('error', error => stream.fire(error.toString())); + readstream.on('close', () => stream.fire()); + return stream.event; +} + +export interface IDownloadServiceChannel extends IChannel { + listen(event: 'upload', uri: URI): Event; + listen(event: string, arg?: any): Event; +} + +export class DownloadServiceChannel implements IDownloadServiceChannel { + + constructor() { } + + listen(event: string, arg?: any): Event { + switch (event) { + case 'upload': return buffer(upload(URI.revive(arg))); + } + return undefined; + } + + call(command: string, arg?: any): TPromise { + throw new Error('No calls'); + } +} + +export class DownloadServiceChannelClient implements IDownloadService { + + _serviceBrand: any; + + constructor(private channel: IDownloadServiceChannel, private uriTransformer: IURITransformer) { } + + download(from: URI, to: string): Promise { + from = this.uriTransformer.transformOutgoing(from); + const dirName = path.dirname(to); + let out: fs.WriteStream; + return new Promise((c, e) => { + return mkdirp(dirName) + .then(() => { + out = fs.createWriteStream(to); + out.once('close', () => c(null)); + out.once('error', e); + const uploadStream = this.channel.listen('upload', from); + const disposable = uploadStream((result: UploadResponse) => { + if (result === void 0) { + disposable.dispose(); + out.end(c); + } else if (Buffer.isBuffer(result)) { + out.write(result); + } else if (typeof result === 'string') { + disposable.dispose(); + out.end(() => e(result)); + } + }); + }); + }); + } +} \ No newline at end of file diff --git a/src/vs/platform/extensionManagement/node/multiExtensionManagement.ts b/src/vs/platform/extensionManagement/node/multiExtensionManagement.ts new file mode 100644 index 0000000000000000000000000000000000000000..34002c24444a4862616c38d68d316bca6d5e62c5 --- /dev/null +++ b/src/vs/platform/extensionManagement/node/multiExtensionManagement.ts @@ -0,0 +1,101 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event, EventMultiplexer } from 'vs/base/common/event'; +import { + IExtensionManagementService, ILocalExtension, IGalleryExtension, LocalExtensionType, InstallExtensionEvent, DidInstallExtensionEvent, IExtensionIdentifier, DidUninstallExtensionEvent, IReportedExtension, IGalleryMetadata, + IExtensionManagementServerService, IExtensionManagementServer, IExtensionGalleryService +} from 'vs/platform/extensionManagement/common/extensionManagement'; +import { flatten } from 'vs/base/common/arrays'; +import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { isUIExtension } from 'vs/platform/extensions/common/extensions'; +import { URI } from 'vs/base/common/uri'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { CancellationToken } from 'vs/base/common/cancellation'; + +export class MulitExtensionManagementService extends Disposable implements IExtensionManagementService { + + _serviceBrand: any; + + readonly onInstallExtension: Event; + readonly onDidInstallExtension: Event; + readonly onUninstallExtension: Event; + readonly onDidUninstallExtension: Event; + + private readonly servers: IExtensionManagementServer[]; + + constructor( + @IExtensionManagementServerService private extensionManagementServerService: IExtensionManagementServerService, + @IExtensionGalleryService private extensionGalleryService: IExtensionGalleryService, + @IConfigurationService private configurationService: IConfigurationService + ) { + super(); + this.servers = this.extensionManagementServerService.otherExtensionManagementServer ? [this.extensionManagementServerService.localExtensionManagementServer, this.extensionManagementServerService.otherExtensionManagementServer] : [this.extensionManagementServerService.localExtensionManagementServer]; + + this.onInstallExtension = this._register(this.servers.reduce((emitter: EventMultiplexer, server) => { emitter.add(server.extensionManagementService.onInstallExtension); return emitter; }, new EventMultiplexer())).event; + this.onDidInstallExtension = this._register(this.servers.reduce((emitter: EventMultiplexer, server) => { emitter.add(server.extensionManagementService.onDidInstallExtension); return emitter; }, new EventMultiplexer())).event; + this.onUninstallExtension = this._register(this.servers.reduce((emitter: EventMultiplexer, server) => { emitter.add(server.extensionManagementService.onUninstallExtension); return emitter; }, new EventMultiplexer())).event; + this.onDidUninstallExtension = this._register(this.servers.reduce((emitter: EventMultiplexer, server) => { emitter.add(server.extensionManagementService.onDidUninstallExtension); return emitter; }, new EventMultiplexer())).event; + } + + getInstalled(type?: LocalExtensionType): Promise { + return Promise.all(this.servers.map(({ extensionManagementService }) => extensionManagementService.getInstalled(type))) + .then(result => flatten(result)); + } + + uninstall(extension: ILocalExtension, force?: boolean): Promise { + return this.getServer(extension).extensionManagementService.uninstall(extension, force); + } + + reinstallFromGallery(extension: ILocalExtension): Promise { + return this.getServer(extension).extensionManagementService.reinstallFromGallery(extension); + } + + updateMetadata(extension: ILocalExtension, metadata: IGalleryMetadata): Promise { + return this.getServer(extension).extensionManagementService.updateMetadata(extension, metadata); + } + + zip(extension: ILocalExtension): Promise { + throw new Error('Not Supported'); + } + + unzip(zipLocation: URI, type: LocalExtensionType): Promise { + return Promise.all(this.servers.map(({ extensionManagementService }) => extensionManagementService.unzip(zipLocation, type))).then(() => null); + } + + install(vsix: URI): Promise { + return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.install(vsix) + .then(extensionIdentifer => this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.getInstalled(LocalExtensionType.User) + .then(installed => { + const extension = installed.filter(i => areSameExtensions(i.identifier, extensionIdentifer))[0]; + if (this.extensionManagementServerService.otherExtensionManagementServer && extension && !isUIExtension(extension.manifest, this.configurationService)) { + return this.extensionManagementServerService.otherExtensionManagementServer.extensionManagementService.install(vsix) + .then(() => extensionIdentifer); + } + return extensionIdentifer; + })); + } + + installFromGallery(gallery: IGalleryExtension): Promise { + if (!this.extensionManagementServerService.otherExtensionManagementServer) { + return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.installFromGallery(gallery); + } + return this.extensionGalleryService.getManifest(gallery, CancellationToken.None) + .then(manifest => { + const servers = !isUIExtension(manifest, this.configurationService) ? this.servers : [this.extensionManagementServerService.localExtensionManagementServer]; + return Promise.all(servers.map(server => server.extensionManagementService.installFromGallery(gallery))) + .then(() => null); + }); + } + + getExtensionsReport(): Promise { + return this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.getExtensionsReport(); + } + + private getServer(extension: ILocalExtension): IExtensionManagementServer { + return this.extensionManagementServerService.getExtensionManagementServer(extension.location); + } +} \ No newline at end of file diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts index 156f323eed1f90f08a791b73f2787e8ba308af15..27bf7dbb337ec61d0c87e701cc0436cf0771ac46 100644 --- a/src/vs/workbench/electron-browser/shell.ts +++ b/src/vs/workbench/electron-browser/shell.ts @@ -96,11 +96,14 @@ import { SearchHistoryService } from 'vs/workbench/services/search/node/searchHi import { ExtensionManagementServerService } from 'vs/workbench/services/extensions/node/extensionManagementServerService'; import { DefaultURITransformer } from 'vs/base/common/uriIpc'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; +import { LogLevelSetterChannel } from 'vs/platform/log/node/logIpc'; import { ILabelService, LabelService } from 'vs/platform/label/common/label'; import { IDownloadService } from 'vs/platform/download/common/download'; import { DownloadService } from 'vs/platform/download/node/downloadService'; +import { DownloadServiceChannel } from 'vs/platform/download/node/downloadIpc'; import { runWhenIdle } from 'vs/base/common/async'; import { TextResourcePropertiesService } from 'vs/workbench/services/textfile/electron-browser/textResourcePropertiesService'; +import { MulitExtensionManagementService } from 'vs/platform/extensionManagement/node/multiExtensionManagement'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { RemoteAuthorityResolverChannelClient, IRemoteAuthorityResolverChannel } from 'vs/platform/remote/node/remoteAuthorityResolverChannel'; @@ -460,10 +463,17 @@ export class WorkbenchShell extends Disposable { const remoteAgentService = new RemoteAgentService(this.configuration, this.notificationService, this.environmentService, remoteAuthorityResolverService); serviceCollection.set(IRemoteAgentService, remoteAgentService); + const remoteAgentConnection = remoteAgentService.getConnection(); + if (remoteAgentConnection) { + remoteAgentConnection.registerChannel('dialog', instantiationService.createInstance(DialogChannel)); + remoteAgentConnection.registerChannel('download', new DownloadServiceChannel()); + remoteAgentConnection.registerChannel('loglevel', new LogLevelSetterChannel(this.logService)); + } + const extensionManagementChannel = getDelayedChannel(sharedProcess.then(c => c.getChannel('extensions'))); const extensionManagementChannelClient = new ExtensionManagementChannelClient(extensionManagementChannel, DefaultURITransformer); serviceCollection.set(IExtensionManagementServerService, new SyncDescriptor(ExtensionManagementServerService, [extensionManagementChannelClient])); - serviceCollection.set(IExtensionManagementService, extensionManagementChannelClient); + serviceCollection.set(IExtensionManagementService, new SyncDescriptor(MulitExtensionManagementService)); const extensionEnablementService = this._register(instantiationService.createInstance(ExtensionEnablementService)); serviceCollection.set(IExtensionEnablementService, extensionEnablementService);