提交 a8842026 编写于 作者: S Sandeep Somavarapu

Adopt InMemoryUserDataProvider to use FileSystemProvider

上级 d5fcdf39
......@@ -42,6 +42,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage';
import { getThemeTypeSelector, DARK, HIGH_CONTRAST, LIGHT } from 'vs/platform/theme/common/themeService';
import { IRequestService } from 'vs/platform/request/common/request';
import { RequestService } from 'vs/workbench/services/request/browser/requestService';
import { InMemoryUserDataProvider } from 'vs/workbench/services/userData/common/inMemoryUserDataProvider';
class CodeRendererMain extends Disposable {
......@@ -157,11 +158,13 @@ class CodeRendererMain extends Disposable {
}
}
// User Data Provider
if (userDataProvider) {
fileService.registerProvider(Schemas.userData, userDataProvider);
if (!userDataProvider) {
userDataProvider = this._register(new InMemoryUserDataProvider());
}
// User Data Provider
fileService.registerProvider(Schemas.userData, userDataProvider);
const [configurationService, storageService] = await Promise.all([
this.createWorkspaceService(payload, environmentService, fileService, remoteAgentService, logService).then(service => {
......
......@@ -4,48 +4,229 @@
*--------------------------------------------------------------------------------------------*/
import { Event, Emitter } from 'vs/base/common/event';
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { IUserDataProvider, FileChangeEvent } from 'vs/workbench/services/userData/common/userData';
import { VSBuffer } from 'vs/base/common/buffer';
import { FileChangeType } from 'vs/platform/files/common/files';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import * as resources from 'vs/base/common/resources';
import { FileChangeType, IFileSystemProvider, FileType, IWatchOptions, IStat, FileSystemProviderErrorCode, FileSystemProviderError, FileWriteOptions, IFileChange, FileDeleteOptions, FileSystemProviderCapabilities, FileOverwriteOptions } from 'vs/platform/files/common/files';
import { URI } from 'vs/base/common/uri';
export class InMemoryUserDataProvider extends Disposable implements IUserDataProvider {
_serviceBrand: any;
class File implements IStat {
private _onDidChangeFile: Emitter<FileChangeEvent[]> = this._register(new Emitter<FileChangeEvent[]>());
readonly onDidChangeFile: Event<FileChangeEvent[]> = this._onDidChangeFile.event;
type: FileType;
ctime: number;
mtime: number;
size: number;
private readonly store: Map<string, string> = new Map<string, string>();
name: string;
data?: Uint8Array;
constructor() {
super();
this._register(toDisposable(() => this.store.clear()));
constructor(name: string) {
this.type = FileType.File;
this.ctime = Date.now();
this.mtime = Date.now();
this.size = 0;
this.name = name;
}
}
async listFiles(path: string): Promise<string[]> {
return [];
class Directory implements IStat {
type: FileType;
ctime: number;
mtime: number;
size: number;
name: string;
entries: Map<string, File | Directory>;
constructor(name: string) {
this.type = FileType.Directory;
this.ctime = Date.now();
this.mtime = Date.now();
this.size = 0;
this.name = name;
this.entries = new Map();
}
}
export type Entry = File | Directory;
export class InMemoryUserDataProvider extends Disposable implements IFileSystemProvider {
readonly capabilities: FileSystemProviderCapabilities = FileSystemProviderCapabilities.FileReadWrite;
readonly onDidChangeCapabilities: Event<void> = Event.None;
root = new Directory('');
// --- manage file metadata
async stat(resource: URI): Promise<IStat> {
return this._lookup(resource, false);
}
async readdir(resource: URI): Promise<[string, FileType][]> {
const entry = this._lookupAsDirectory(resource, false);
let result: [string, FileType][] = [];
for (const [name, child] of entry.entries) {
result.push([name, child.type]);
}
return result;
}
// --- manage file contents
async readFile(resource: URI): Promise<Uint8Array> {
const data = this._lookupAsFile(resource, false).data;
if (data) {
return data;
}
throw new FileSystemProviderError('file not found', FileSystemProviderErrorCode.FileNotFound);
}
async writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise<void> {
let basename = resources.basename(resource);
let parent = this._lookupParentDirectory(resource);
let entry = parent.entries.get(basename);
if (entry instanceof Directory) {
throw new FileSystemProviderError('file is directory', FileSystemProviderErrorCode.FileIsADirectory);
}
if (!entry && !opts.create) {
throw new FileSystemProviderError('file not found', FileSystemProviderErrorCode.FileNotFound);
}
if (entry && opts.create && !opts.overwrite) {
throw new FileSystemProviderError('file exists already', FileSystemProviderErrorCode.FileExists);
}
if (!entry) {
entry = new File(basename);
parent.entries.set(basename, entry);
this._fireSoon({ type: FileChangeType.ADDED, resource });
}
entry.mtime = Date.now();
entry.size = content.byteLength;
entry.data = content;
this._fireSoon({ type: FileChangeType.UPDATED, resource });
}
// --- manage files/folders
async rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise<void> {
if (!opts.overwrite && this._lookup(to, true)) {
throw new FileSystemProviderError('file exists already', FileSystemProviderErrorCode.FileExists);
}
let entry = this._lookup(from, false);
let oldParent = this._lookupParentDirectory(from);
let newParent = this._lookupParentDirectory(to);
let newName = resources.basename(to);
oldParent.entries.delete(entry.name);
entry.name = newName;
newParent.entries.set(newName, entry);
this._fireSoon(
{ type: FileChangeType.DELETED, resource: from },
{ type: FileChangeType.ADDED, resource: to }
);
}
async delete(resource: URI, opts: FileDeleteOptions): Promise<void> {
let dirname = resources.dirname(resource);
let basename = resources.basename(resource);
let parent = this._lookupAsDirectory(dirname, false);
if (!parent.entries.has(basename)) {
throw new FileSystemProviderError('file not found', FileSystemProviderErrorCode.FileNotFound);
}
parent.entries.delete(basename);
parent.mtime = Date.now();
parent.size -= 1;
this._fireSoon({ type: FileChangeType.UPDATED, resource: dirname }, { resource, type: FileChangeType.DELETED });
}
async mkdir(resource: URI): Promise<void> {
let basename = resources.basename(resource);
let dirname = resources.dirname(resource);
let parent = this._lookupAsDirectory(dirname, false);
let entry = new Directory(basename);
parent.entries.set(entry.name, entry);
parent.mtime = Date.now();
parent.size += 1;
this._fireSoon({ type: FileChangeType.UPDATED, resource: dirname }, { type: FileChangeType.ADDED, resource });
}
// --- lookup
private _lookup(uri: URI, silent: false): Entry;
private _lookup(uri: URI, silent: boolean): Entry | undefined;
private _lookup(uri: URI, silent: boolean): Entry | undefined {
let parts = uri.path.split('/');
let entry: Entry = this.root;
for (const part of parts) {
if (!part) {
continue;
}
let child: Entry | undefined;
if (entry instanceof Directory) {
child = entry.entries.get(part);
}
if (!child) {
if (!silent) {
throw new FileSystemProviderError('file not found', FileSystemProviderErrorCode.FileNotFound);
} else {
return undefined;
}
}
entry = child;
}
return entry;
}
async readFile(path: string): Promise<Uint8Array> {
if (this.store.has(path)) {
return VSBuffer.fromString(this.store.get(path)!).buffer;
private _lookupAsDirectory(uri: URI, silent: boolean): Directory {
let entry = this._lookup(uri, silent);
if (entry instanceof Directory) {
return entry;
}
throw new Error(`Not Found: ${path}`);
throw new FileSystemProviderError('file not a directory', FileSystemProviderErrorCode.FileNotADirectory);
}
async writeFile(path: string, value: Uint8Array): Promise<void> {
const exists = this.store.has(path);
const content = VSBuffer.wrap(value).toString();
if (!exists || content !== this.store.get(path)) {
this.store.set(path, content);
this._onDidChangeFile.fire([{ path, type: exists ? FileChangeType.UPDATED : FileChangeType.ADDED }]);
private _lookupAsFile(uri: URI, silent: boolean): File {
let entry = this._lookup(uri, silent);
if (entry instanceof File) {
return entry;
}
throw new FileSystemProviderError('file is a directory', FileSystemProviderErrorCode.FileIsADirectory);
}
async deleteFile(path: string): Promise<void> {
if (this.store.has(path)) {
this.store.delete(path);
this._onDidChangeFile.fire([{ path, type: FileChangeType.DELETED }]);
private _lookupParentDirectory(uri: URI): Directory {
const dirname = resources.dirname(uri);
return this._lookupAsDirectory(dirname, false);
}
// --- manage file events
private readonly _onDidChangeFile: Emitter<IFileChange[]> = this._register(new Emitter<IFileChange[]>());
readonly onDidChangeFile: Event<IFileChange[]> = this._onDidChangeFile.event;
private _bufferedChanges: IFileChange[] = [];
private _fireSoonHandle?: NodeJS.Timer;
watch(resource: URI, opts: IWatchOptions): IDisposable {
// ignore, fires for all changes...
return Disposable.None;
}
private _fireSoon(...changes: IFileChange[]): void {
this._bufferedChanges.push(...changes);
if (this._fireSoonHandle) {
clearTimeout(this._fireSoonHandle);
}
this._fireSoonHandle = setTimeout(() => {
this._onDidChangeFile.fire(this._bufferedChanges);
this._bufferedChanges.length = 0;
}, 5);
}
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event } from 'vs/base/common/event';
import { FileChangeType } from 'vs/platform/files/common/files';
/**
* The event user data providers must use to signal a file change.
*/
export interface FileChangeEvent {
/**
* The type of change.
*/
readonly type: FileChangeType;
/**
* The path of the file that has changed.
*/
readonly path: string;
}
/**
* The userDataProvider is used to handle user specific application
* state like settings, keybindings, UI state (e.g. opened editors) and snippets.
*
* The API reflects a simple file system provider that comes with the notion of paths
* (UNIX slash separated) as well as files. Folders are not a top level concept (e.g. we
* do not require to create or delete them), however, files can be grouped beneath one path
* and also listed from that path.
*
* Example:
* ```ts
* await writeFile('snippets/global/markdown.json', <some data>);
* await writeFile('snippets/global/html.json', <some data>);
* await writeFile('snippets/global/javascript.json', <some data>);
*
* const files = await listFiles('snippets/global');
* console.log(files); // -> ['snippets/global/markdown.json', 'snippets/global/html.json', 'snippets/global/javascript.json']
* ```
*/
export interface IUserDataProvider {
/**
* An event to signal that a file has been created, changed, or deleted.
*/
readonly onDidChangeFile: Event<FileChangeEvent[]>;
/**
* Read the file contents of the given path.
*
* Throw an error if the path does not exist.
*/
readFile(path: string): Promise<Uint8Array>;
/**
* Writes the provided content to the file path overwriting any existing content on that path.
*
* If the path does not exist, it will be created.
*
* Throw an error if the path is a parent to existing files.
*/
writeFile(path: string, content: Uint8Array): Promise<void>;
/**
* Delete the file at the given path.
*
* Does NOT throw an error when the path does not exist.
*/
deleteFile(path: string): Promise<void>;
/**
* Returns an array of files at the given path.
*
* Throw an error if the path does not exist or points to a file.
*/
listFiles(path: string): Promise<string[]>;
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册