提交 44596ac9 编写于 作者: J Johannes Rieken

first version of vscode.workspace.fs

上级 e4ca6137
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import * as vscode from 'vscode';
import { join } from 'path';
suite('workspace-fs', () => {
let root: vscode.Uri;
suiteSetup(function () {
root = vscode.workspace.workspaceFolders![0]!.uri;
});
test('fs.stat', async function () {
const stat = await vscode.workspace.fs.stat(root);
assert.equal(stat.type, vscode.FileType.Directory);
assert.equal(typeof stat.size, 'number');
assert.equal(typeof stat.mtime, 'number');
assert.equal(typeof stat.ctime, 'number');
const entries = await vscode.workspace.fs.readDirectory(root);
assert.ok(entries.length > 0);
// find far.js
const tuple = entries.find(tuple => tuple[0] === 'far.js')!;
assert.ok(tuple);
assert.equal(tuple[0], 'far.js');
assert.equal(tuple[1], vscode.FileType.File);
});
test('fs.stat - bad scheme', async function () {
try {
await vscode.workspace.fs.stat(vscode.Uri.parse('foo:/bar/baz/test.txt'));
assert.ok(false);
} catch {
assert.ok(true);
}
});
test('fs.stat - missing file', async function () {
try {
await vscode.workspace.fs.stat(root.with({ path: root.path + '.bad' }));
assert.ok(false);
} catch (e) {
assert.ok(true);
}
});
test('fs.write/stat/delete', async function () {
const uri = root.with({ path: join(root.path, 'new.file') });
await vscode.workspace.fs.writeFile(uri, Buffer.from('HELLO'));
const stat = await vscode.workspace.fs.stat(uri);
assert.equal(stat.type, vscode.FileType.File);
await vscode.workspace.fs.delete(uri);
try {
await vscode.workspace.fs.stat(uri);
assert.ok(false);
} catch {
assert.ok(true);
}
});
test('fs.delete folder', async function () {
const folder = root.with({ path: join(root.path, 'folder') });
const file = root.with({ path: join(root.path, 'folder/file') });
await vscode.workspace.fs.createDirectory(folder);
await vscode.workspace.fs.writeFile(file, Buffer.from('FOO'));
await vscode.workspace.fs.stat(folder);
await vscode.workspace.fs.stat(file);
// ensure non empty folder cannot be deleted
try {
await vscode.workspace.fs.delete(folder, { recursive: false });
assert.ok(false);
} catch {
await vscode.workspace.fs.stat(folder);
await vscode.workspace.fs.stat(file);
}
// ensure non empty folder cannot be deleted is DEFAULT
try {
await vscode.workspace.fs.delete(folder); // recursive: false as default
assert.ok(false);
} catch {
await vscode.workspace.fs.stat(folder);
await vscode.workspace.fs.stat(file);
}
// delete non empty folder with recursive-flag
await vscode.workspace.fs.delete(folder, { recursive: true });
// esnure folder/file are gone
try {
await vscode.workspace.fs.stat(folder);
assert.ok(false);
} catch {
assert.ok(true);
}
try {
await vscode.workspace.fs.stat(file);
assert.ok(false);
} catch {
assert.ok(true);
}
});
});
......@@ -1519,4 +1519,25 @@ declare module 'vscode' {
}
//#endregion
//#region Joh - read/write files of any scheme
export interface FileSystem {
stat(uri: Uri): Thenable<FileStat>;
readDirectory(uri: Uri): Thenable<[string, FileType][]>;
createDirectory(uri: Uri): Thenable<void>;
readFile(uri: Uri): Thenable<Uint8Array>;
writeFile(uri: Uri, content: Uint8Array, options?: { create: boolean, overwrite: boolean }): Thenable<void>;
delete(uri: Uri, options?: { recursive: boolean }): Thenable<void>;
rename(oldUri: Uri, newUri: Uri, options?: { overwrite: boolean }): Thenable<void>;
copy(source: Uri, destination: Uri, options?: { overwrite: boolean }): Thenable<void>;
}
export namespace workspace {
export const fs: FileSystem;
}
//#endregion
}
......@@ -5,8 +5,8 @@
import { Emitter, Event } from 'vs/base/common/event';
import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { FileWriteOptions, FileSystemProviderCapabilities, IFileChange, IFileService, IFileSystemProvider, IStat, IWatchOptions, FileType, FileOverwriteOptions, FileDeleteOptions, FileOpenOptions } from 'vs/platform/files/common/files';
import { URI, UriComponents } from 'vs/base/common/uri';
import { FileWriteOptions, FileSystemProviderCapabilities, IFileChange, IFileService, IFileSystemProvider, IStat, IWatchOptions, FileType, FileOverwriteOptions, FileDeleteOptions, FileOpenOptions, IFileStat } from 'vs/platform/files/common/files';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ExtHostContext, ExtHostFileSystemShape, IExtHostContext, IFileChangeDto, MainContext, MainThreadFileSystemShape } from '../common/extHost.protocol';
import { ResourceLabelFormatter, ILabelService } from 'vs/platform/label/common/label';
......@@ -60,6 +60,56 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape {
}
fileProvider.$onFileSystemChange(changes);
}
// ---
async $stat(uri: UriComponents): Promise<IStat> {
const stat = await this._fileService.resolve(URI.revive(uri), { resolveMetadata: true });
return {
ctime: 0,
mtime: stat.mtime,
size: stat.size,
type: MainThreadFileSystem._getFileType(stat)
};
}
async $readdir(uri: UriComponents): Promise<[string, FileType][]> {
const stat = await this._fileService.resolve(URI.revive(uri), { resolveMetadata: false });
if (!stat.children) {
throw new Error('not a folder');
}
return stat.children.map(child => [child.name, MainThreadFileSystem._getFileType(child)]);
}
private static _getFileType(stat: IFileStat): FileType {
return (stat.isDirectory ? FileType.Directory : FileType.File) + (stat.isSymbolicLink ? FileType.SymbolicLink : 0);
}
async $readFile(resource: UriComponents): Promise<VSBuffer> {
return (await this._fileService.readFile(URI.revive(resource))).value;
}
async $writeFile(resource: UriComponents, content: VSBuffer, opts: FileWriteOptions): Promise<void> {
//todo@joh honor opts
await this._fileService.writeFile(URI.revive(resource), content, {});
}
async $rename(resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): Promise<void> {
this._fileService.move(URI.revive(resource), URI.revive(target), opts.overwrite);
}
async $copy(resource: UriComponents, target: UriComponents, opts: FileOverwriteOptions): Promise<void> {
this._fileService.copy(URI.revive(resource), URI.revive(target), opts.overwrite);
}
async $mkdir(resource: UriComponents): Promise<void> {
this._fileService.createFolder(URI.revive(resource));
}
async $delete(resource: UriComponents, opts: FileDeleteOptions): Promise<void> {
this._fileService.del(URI.revive(resource), opts);
}
}
class RemoteFileSystemProvider implements IFileSystemProvider {
......
......@@ -592,6 +592,15 @@ export interface MainThreadFileSystemShape extends IDisposable {
$registerResourceLabelFormatter(handle: number, formatter: ResourceLabelFormatter): void;
$unregisterResourceLabelFormatter(handle: number): void;
$onFileSystemChange(handle: number, resource: IFileChangeDto[]): void;
$stat(uri: UriComponents): Promise<files.IStat>;
$readdir(resource: UriComponents): Promise<[string, files.FileType][]>;
$readFile(resource: UriComponents): Promise<VSBuffer>;
$writeFile(resource: UriComponents, content: VSBuffer, opts: files.FileWriteOptions): Promise<void>;
$rename(resource: UriComponents, target: UriComponents, opts: files.FileOverwriteOptions): Promise<void>;
$copy(resource: UriComponents, target: UriComponents, opts: files.FileOverwriteOptions): Promise<void>;
$mkdir(resource: UriComponents): Promise<void>;
$delete(resource: UriComponents, opts: files.FileDeleteOptions): Promise<void>;
}
export interface MainThreadSearchShape extends IDisposable {
......
......@@ -104,6 +104,36 @@ class FsLinkProvider {
}
}
class ConsumerFileSystem implements vscode.FileSystem {
constructor(private _proxy: MainThreadFileSystemShape) { }
stat(uri: vscode.Uri): Promise<vscode.FileStat> {
return this._proxy.$stat(uri);
}
readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> {
return this._proxy.$readdir(uri);
}
createDirectory(uri: vscode.Uri): Promise<void> {
return this._proxy.$mkdir(uri);
}
async readFile(uri: vscode.Uri): Promise<Uint8Array> {
return (await this._proxy.$readFile(uri)).buffer;
}
writeFile(uri: vscode.Uri, content: Uint8Array, options: { create: boolean; overwrite: boolean; } = { create: true, overwrite: true }): Promise<void> {
return this._proxy.$writeFile(uri, VSBuffer.wrap(content), options);
}
delete(uri: vscode.Uri, options: { recursive: boolean; } = { recursive: false }): Promise<void> {
return this._proxy.$delete(uri, { ...options, useTrash: true }); //todo@joh useTrash
}
rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean; } = { overwrite: false }): Promise<void> {
return this._proxy.$rename(oldUri, newUri, options);
}
copy(source: vscode.Uri, destination: vscode.Uri, options: { overwrite: boolean } = { overwrite: false }): Promise<void> {
return this._proxy.$copy(source, destination, options);
}
}
export class ExtHostFileSystem implements ExtHostFileSystemShape {
private readonly _proxy: MainThreadFileSystemShape;
......@@ -115,6 +145,8 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
private _linkProviderRegistration: IDisposable;
private _handlePool: number = 0;
readonly fileSystem: vscode.FileSystem;
constructor(mainContext: IMainContext, private _extHostLanguageFeatures: ExtHostLanguageFeatures) {
this._proxy = mainContext.getProxy(MainContext.MainThreadFileSystem);
this._usedSchemes.add(Schemas.file);
......@@ -127,6 +159,8 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
this._usedSchemes.add(Schemas.mailto);
this._usedSchemes.add(Schemas.data);
this._usedSchemes.add(Schemas.command);
this.fileSystem = new ConsumerFileSystem(this._proxy);
}
dispose(): void {
......
......@@ -673,6 +673,10 @@ export function createApiFactory(
registerFileSystemProvider(scheme, provider, options) {
return extHostFileSystem.registerFileSystemProvider(scheme, provider, options);
},
get fs() {
checkProposedApiEnabled(extension);
return extHostFileSystem.fileSystem;
},
registerFileSearchProvider: proposedApiFunction(extension, (scheme: string, provider: vscode.FileSearchProvider) => {
return extHostSearch.registerFileSearchProvider(scheme, provider);
}),
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册