Merge pull request #34229 from Microsoft/joh/ftp

ftp file system provider
This commit is contained in:
Johannes Rieken
2017-09-20 15:07:23 +02:00
committed by GitHub
47 changed files with 975 additions and 414 deletions

View File

@@ -53,6 +53,8 @@ import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions';
import { ExtHostThreadService } from 'vs/workbench/services/thread/node/extHostThreadService';
import { ProxyIdentifier } from 'vs/workbench/services/thread/common/threadService';
import { ExtHostDialogs } from 'vs/workbench/api/node/extHostDialogs';
import { ExtHostFileSystem } from 'vs/workbench/api/node/extHostFileSystem';
import { FileChangeType, FileType } from 'vs/platform/files/common/files';
export interface IExtensionApiFactory {
(extension: IExtensionDescription): typeof vscode;
@@ -93,6 +95,7 @@ export function createApiFactory(
const extHostConfiguration = threadService.set(ExtHostContext.ExtHostConfiguration, new ExtHostConfiguration(threadService.get(MainContext.MainThreadConfiguration), extHostWorkspace, initData.configuration));
const extHostDiagnostics = threadService.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(threadService));
const languageFeatures = threadService.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(threadService, extHostDocuments, extHostCommands, extHostHeapService, extHostDiagnostics));
const extHostFileSystem = threadService.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(threadService));
const extHostFileSystemEvent = threadService.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService());
const extHostQuickOpen = threadService.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(threadService, extHostWorkspace, extHostCommands));
const extHostTerminalService = threadService.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(threadService));
@@ -480,7 +483,7 @@ export function createApiFactory(
return extHostTask.registerTaskProvider(extension, provider);
},
registerFileSystemProvider: proposedApiFunction(extension, (authority, provider) => {
return extHostWorkspace.registerFileSystemProvider(authority, provider);
return extHostFileSystem.registerFileSystemProvider(authority, provider);
})
};
@@ -604,7 +607,11 @@ export function createApiFactory(
ShellExecution: extHostTypes.ShellExecution,
TaskScope: extHostTypes.TaskScope,
Task: extHostTypes.Task,
ConfigurationTarget: extHostTypes.ConfigurationTarget
ConfigurationTarget: extHostTypes.ConfigurationTarget,
// TODO@JOH
FileChangeType: <any>FileChangeType,
FileType: <any>FileType
};
if (extension.enableProposedApi && extension.isBuiltin) {
api['credentials'] = credentials;

View File

@@ -48,6 +48,7 @@ import { ThemeColor } from 'vs/platform/theme/common/themeService';
import { IDisposable } from 'vs/base/common/lifecycle';
import { SerializedError } from 'vs/base/common/errors';
import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IStat, IFileChange } from 'vs/platform/files/common/files';
export interface IEnvironment {
isExtensionDevelopmentDebug: boolean;
@@ -311,12 +312,15 @@ export interface MainThreadWorkspaceShape extends IDisposable {
$startSearch(include: string, exclude: string, maxResults: number, requestId: number): Thenable<URI[]>;
$cancelSearch(requestId: number): Thenable<boolean>;
$saveAll(includeUntitled?: boolean): Thenable<boolean>;
}
$registerFileSystemProvider(handle: number, authority: string): void;
export interface MainThreadFileSystemShape extends IDisposable {
$registerFileSystemProvider(handle: number, scheme: string): void;
$unregisterFileSystemProvider(handle: number): void;
$onFileSystemChange(handle: number, resource: URI): void;
$updateSearchSession(session: number, data): void;
$finishSearchSession(session: number, err?: any): void;
$onDidAddFileSystemRoot(root: URI): void;
$onFileSystemChange(handle: number, resource: IFileChange[]): void;
$reportFileChunk(handle: number, resource: URI, chunk: number[] | null): void;
}
export interface MainThreadTaskShape extends IDisposable {
@@ -470,11 +474,18 @@ export interface ExtHostTreeViewsShape {
export interface ExtHostWorkspaceShape {
$acceptWorkspaceData(workspace: IWorkspaceData): void;
}
$resolveFile(handle: number, resource: URI): TPromise<string>;
$storeFile(handle: number, resource: URI, content: string): TPromise<any>;
$startSearch(handle: number, session: number, query: string): void;
$cancelSearch(handle: number, session: number): void;
export interface ExtHostFileSystemShape {
$utimes(handle: number, resource: URI, mtime: number): TPromise<IStat>;
$stat(handle: number, resource: URI): TPromise<IStat>;
$read(handle: number, resource: URI): TPromise<void>;
$write(handle: number, resource: URI, content: number[]): TPromise<void>;
$unlink(handle: number, resource: URI): TPromise<void>;
$rename(handle: number, resource: URI, target: URI): TPromise<void>;
$mkdir(handle: number, resource: URI): TPromise<void>;
$readdir(handle: number, resource: URI): TPromise<IStat[]>;
$rmdir(handle: number, resource: URI): TPromise<void>;
}
export interface ExtHostExtensionServiceShape {
@@ -611,6 +622,7 @@ export const MainContext = {
MainThreadTelemetry: createMainId<MainThreadTelemetryShape>('MainThreadTelemetry'),
MainThreadTerminalService: createMainId<MainThreadTerminalServiceShape>('MainThreadTerminalService'),
MainThreadWorkspace: createMainId<MainThreadWorkspaceShape>('MainThreadWorkspace'),
MainThreadFileSystem: createMainId<MainThreadFileSystemShape>('MainThreadFileSystem'),
MainThreadExtensionService: createMainId<MainThreadExtensionServiceShape>('MainThreadExtensionService'),
MainThreadSCM: createMainId<MainThreadSCMShape>('MainThreadSCM'),
MainThreadTask: createMainId<MainThreadTaskShape>('MainThreadTask'),
@@ -629,6 +641,7 @@ export const ExtHostContext = {
ExtHostDocumentSaveParticipant: createExtId<ExtHostDocumentSaveParticipantShape>('ExtHostDocumentSaveParticipant'),
ExtHostEditors: createExtId<ExtHostEditorsShape>('ExtHostEditors'),
ExtHostTreeViews: createExtId<ExtHostTreeViewsShape>('ExtHostTreeViews'),
ExtHostFileSystem: createExtId<ExtHostFileSystemShape>('ExtHostFileSystem'),
ExtHostFileSystemEventService: createExtId<ExtHostFileSystemEventServiceShape>('ExtHostFileSystemEventService'),
ExtHostHeapService: createExtId<ExtHostHeapServiceShape>('ExtHostHeapMonitor'),
ExtHostLanguageFeatures: createExtId<ExtHostLanguageFeaturesShape>('ExtHostLanguageFeatures'),

View File

@@ -0,0 +1,75 @@
/*---------------------------------------------------------------------------------------------
* 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 { TPromise } from 'vs/base/common/winjs.base';
import { MainContext, IMainContext, ExtHostFileSystemShape, MainThreadFileSystemShape } from './extHost.protocol';
import * as vscode from 'vscode';
import { IStat } from 'vs/platform/files/common/files';
import { IDisposable } from 'vs/base/common/lifecycle';
export class ExtHostFileSystem implements ExtHostFileSystemShape {
private readonly _proxy: MainThreadFileSystemShape;
private readonly _provider = new Map<number, vscode.FileSystemProvider>();
private _handlePool: number = 0;
constructor(mainContext: IMainContext) {
this._proxy = mainContext.get(MainContext.MainThreadFileSystem);
}
registerFileSystemProvider(scheme: string, provider: vscode.FileSystemProvider) {
const handle = this._handlePool++;
this._provider.set(handle, provider);
this._proxy.$registerFileSystemProvider(handle, scheme);
this._proxy.$onDidAddFileSystemRoot(<any>provider.root);
let reg: IDisposable;
if (provider.onDidChange) {
reg = provider.onDidChange(event => this._proxy.$onFileSystemChange(handle, <any>event));
}
return {
dispose: () => {
if (reg) {
reg.dispose();
}
this._provider.delete(handle);
this._proxy.$unregisterFileSystemProvider(handle);
}
};
}
$utimes(handle: number, resource: URI, mtime: number): TPromise<IStat, any> {
return TPromise.as<any>(this._provider.get(handle).utimes(resource, mtime));
}
$stat(handle: number, resource: URI): TPromise<IStat, any> {
return TPromise.as<any>(this._provider.get(handle).stat(resource));
}
$read(handle: number, resource: URI): TPromise<void> {
return TPromise.as<any>(this._provider.get(handle).read(resource, {
report: (chunk) => {
this._proxy.$reportFileChunk(handle, resource, [].slice.call(chunk));
}
}));
}
$write(handle: number, resource: URI, content: number[]): TPromise<void, any> {
return TPromise.as<any>(this._provider.get(handle).write(resource, Buffer.from(content)));
}
$unlink(handle: number, resource: URI): TPromise<void, any> {
return TPromise.as<any>(this._provider.get(handle).unlink(resource));
}
$rename(handle: number, resource: URI, target: URI): TPromise<void, any> {
return TPromise.as<any>(this._provider.get(handle).rename(resource, target));
}
$mkdir(handle: number, resource: URI): TPromise<void, any> {
return TPromise.as<any>(this._provider.get(handle).mkdir(resource));
}
$readdir(handle: number, resource: URI): TPromise<IStat[], any> {
return TPromise.as<any>(this._provider.get(handle).readdir(resource));
}
$rmdir(handle: number, resource: URI): TPromise<void, any> {
return TPromise.as<any>(this._provider.get(handle).rmdir(resource));
}
}

View File

@@ -10,15 +10,10 @@ import { normalize } from 'vs/base/common/paths';
import { delta } from 'vs/base/common/arrays';
import { relative } from 'path';
import { Workspace } from 'vs/platform/workspace/common/workspace';
import { TPromise } from 'vs/base/common/winjs.base';
import { IWorkspaceData, ExtHostWorkspaceShape, MainContext, MainThreadWorkspaceShape, IMainContext } from './extHost.protocol';
import * as vscode from 'vscode';
import { compare } from 'vs/base/common/strings';
import { asWinJsPromise } from 'vs/base/common/async';
import { Disposable } from 'vs/workbench/api/node/extHostTypes';
import { TrieMap } from 'vs/base/common/map';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { Progress } from 'vs/platform/progress/common/progress';
class Workspace2 extends Workspace {
@@ -179,52 +174,4 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape {
saveAll(includeUntitled?: boolean): Thenable<boolean> {
return this._proxy.$saveAll(includeUntitled);
}
// --- EXPERIMENT: workspace resolver
private _handlePool = 0;
private readonly _fsProvider = new Map<number, vscode.FileSystemProvider>();
private readonly _searchSession = new Map<number, CancellationTokenSource>();
registerFileSystemProvider(authority: string, provider: vscode.FileSystemProvider): vscode.Disposable {
const handle = ++this._handlePool;
this._fsProvider.set(handle, provider);
const reg = provider.onDidChange(e => this._proxy.$onFileSystemChange(handle, <URI>e));
this._proxy.$registerFileSystemProvider(handle, authority);
return new Disposable(() => {
this._fsProvider.delete(handle);
reg.dispose();
});
}
$resolveFile(handle: number, resource: URI): TPromise<string> {
const provider = this._fsProvider.get(handle);
return asWinJsPromise(token => provider.resolveContents(resource));
}
$storeFile(handle: number, resource: URI, content: string): TPromise<any> {
const provider = this._fsProvider.get(handle);
return asWinJsPromise(token => provider.writeContents(resource, content));
}
$startSearch(handle: number, session: number, query: string): void {
const provider = this._fsProvider.get(handle);
const source = new CancellationTokenSource();
const progress = new Progress<any>(chunk => this._proxy.$updateSearchSession(session, chunk));
this._searchSession.set(session, source);
TPromise.wrap(provider.findFiles(query, progress, source.token)).then(() => {
this._proxy.$finishSearchSession(session);
}, err => {
this._proxy.$finishSearchSession(session, err);
});
}
$cancelSearch(handle: number, session: number): void {
if (this._searchSession.has(session)) {
this._searchSession.get(session).cancel();
this._searchSession.delete(session);
}
}
}