From 2aa549d5c24f74e0dccb00e98f54adc32e6b8810 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 10 Jul 2017 15:25:48 +0200 Subject: [PATCH] starting with remote file service --- .../workbench/electron-browser/workbench.ts | 5 +- .../files/electron-browser/fileService.ts | 4 +- .../electron-browser/remoteFileService.ts | 144 ++++++++++++++++++ 3 files changed, 149 insertions(+), 4 deletions(-) create mode 100644 src/vs/workbench/services/files/electron-browser/remoteFileService.ts diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index c89a3a7f421..a56a3bc70e5 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -57,7 +57,8 @@ import { ContextKeyExpr, RawContextKey, IContextKeyService, IContextKey } from ' import { IActivityBarService } from 'vs/workbench/services/activity/common/activityBarService'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ViewletService } from 'vs/workbench/services/viewlet/browser/viewletService'; -import { FileService } from 'vs/workbench/services/files/electron-browser/fileService'; +// import { FileService } from 'vs/workbench/services/files/electron-browser/fileService'; +import { RemoteFileService } from "vs/workbench/services/files/electron-browser/remoteFileService"; import { IFileService } from 'vs/platform/files/common/files'; import { IListService, ListService } from 'vs/platform/list/browser/listService'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; @@ -561,7 +562,7 @@ export class Workbench implements IPartService { serviceCollection.set(ITitleService, this.titlebarPart); // File Service - const fileService = this.instantiationService.createInstance(FileService); + const fileService = this.instantiationService.createInstance(RemoteFileService); serviceCollection.set(IFileService, fileService); this.toDispose.push(fileService.onFileChanges(e => this.configurationService.handleWorkspaceFileEvents(e))); diff --git a/src/vs/workbench/services/files/electron-browser/fileService.ts b/src/vs/workbench/services/files/electron-browser/fileService.ts index 991e116aebc..cffc61ed957 100644 --- a/src/vs/workbench/services/files/electron-browser/fileService.ts +++ b/src/vs/workbench/services/files/electron-browser/fileService.ts @@ -41,7 +41,7 @@ export class FileService implements IFileService { private toUnbind: IDisposable[]; private activeOutOfWorkspaceWatchers: ResourceMap; - private _onFileChanges: Emitter; + protected _onFileChanges: Emitter; private _onAfterOperation: Emitter; constructor( @@ -310,4 +310,4 @@ export class FileService implements IFileService { // Dispose service this.raw.dispose(); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/services/files/electron-browser/remoteFileService.ts b/src/vs/workbench/services/files/electron-browser/remoteFileService.ts new file mode 100644 index 00000000000..1fc46d3d38f --- /dev/null +++ b/src/vs/workbench/services/files/electron-browser/remoteFileService.ts @@ -0,0 +1,144 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +import URI from 'vs/base/common/uri'; +import { FileService } from 'vs/workbench/services/files/electron-browser/fileService'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IMessageService } from 'vs/platform/message/common/message'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IContent, IStreamContent, IFileStat, IResolveContentOptions, IUpdateContentOptions, FileChangesEvent, FileChangeType } from "vs/platform/files/common/files"; +import { TPromise } from "vs/base/common/winjs.base"; +import Event from "vs/base/common/event"; +import { EventEmitter } from "events"; +import { basename } from "path"; +import { IDisposable } from "vs/base/common/lifecycle"; + +export interface IRemoteFileProvider { + onDidChange: Event; + resolve(resource: URI): TPromise; + update(resource: URI, content: string): TPromise; +} + +export class RemoteFileService extends FileService { + + private readonly _remoteAuthority: string; + private _provider: IRemoteFileProvider; + + constructor( + @IConfigurationService configurationService: IConfigurationService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IWorkbenchEditorService editorService: IWorkbenchEditorService, + @IEnvironmentService environmentService: IEnvironmentService, + @IEditorGroupService editorGroupService: IEditorGroupService, + @ILifecycleService lifecycleService: ILifecycleService, + @IMessageService messageService: IMessageService, + @IStorageService storageService: IStorageService + ) { + super(configurationService, contextService, editorService, environmentService, editorGroupService, lifecycleService, messageService, storageService); + + this._remoteAuthority = environmentService.args['remote']; + + this.registerProvider(new class implements IRemoteFileProvider { + onDidChange: Event = Event.None; + resolve(resource: URI): TPromise { + return TPromise.as(JSON.stringify(resource, undefined, 4)); + } + update(resource: URI, content: string): TPromise { + return TPromise.as(undefined); + } + }); + } + + private _shouldIntercept(resource: URI): boolean { + return this._provider && resource.authority === this._remoteAuthority; + } + + registerProvider(provider: IRemoteFileProvider): IDisposable { + this._provider = provider; + const reg = this._provider.onDidChange(e => { + // forward change events + this._onFileChanges.fire(new FileChangesEvent([{ resource: e, type: FileChangeType.UPDATED }])); + }); + return { + dispose: () => { + reg.dispose(); + this._provider = undefined; + } + }; + } + + // --- resolve + + resolveContent(resource: URI, options?: IResolveContentOptions): TPromise { + if (this._shouldIntercept(resource)) { + return this._doResolveContent(resource); + } + + return super.resolveContent(resource, options); + } + + resolveStreamContent(resource: URI, options?: IResolveContentOptions): TPromise { + if (this._shouldIntercept(resource)) { + return this._doResolveContent(resource).then(RemoteFileService._asStreamContent); + } + + return super.resolveStreamContent(resource, options); + } + + private async _doResolveContent(resource: URI): TPromise { + + const stat = RemoteFileService._createFakeStat(resource); + const value = await this._provider.resolve(resource); + return { ...stat, value }; + } + + // --- saving + + updateContent(resource: URI, value: string, options?: IUpdateContentOptions): TPromise { + if (this._shouldIntercept(resource)) { + return this._doUpdateContent(resource, value).then(RemoteFileService._createFakeStat); + } + + return super.updateContent(resource, value, options); + } + + private async _doUpdateContent(resource: URI, content: string): TPromise { + await this._provider.update(resource, content); + return resource; + } + + // --- util + + private static _createFakeStat(resource: URI): IFileStat { + + return { + resource, + name: basename(resource.path), + encoding: 'utf8', + mtime: Date.now(), + etag: Date.now().toString(16), + isDirectory: false, + hasChildren: false + }; + } + + private static _asStreamContent(content: IContent): IStreamContent { + const emitter = new EventEmitter(); + const { value } = content; + const result = content; + result.value = emitter; + setTimeout(() => { + emitter.emit('data', value); + emitter.emit('end'); + }, 0); + return result; + } +} -- GitLab