提交 6443e8a0 编写于 作者: S Sandeep Somavarapu

Extract and create key value log provider

上级 17e31216
......@@ -3,33 +3,19 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { URI } from 'vs/base/common/uri';
import { IFileSystemProviderWithFileReadWriteCapability, FileSystemProviderCapabilities, IFileChange, IWatchOptions, IStat, FileOverwriteOptions, FileType, FileDeleteOptions, FileWriteOptions, FileChangeType, FileSystemProviderErrorCode } from 'vs/platform/files/common/files';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { Event, Emitter } from 'vs/base/common/event';
import { VSBuffer } from 'vs/base/common/buffer';
import { FileSystemError } from 'vs/workbench/api/common/extHostTypes';
import { isEqualOrParent, joinPath, relativePath } from 'vs/base/common/resources';
import { KeyValueLogProvider } from 'vs/workbench/services/log/common/keyValueLogProvider';
export const INDEXEDDB_LOG_SCHEME = 'vscode-logs-indexedbd';
export const INDEXEDDB_LOGS_DB = 'vscode-logs-db';
export const INDEXEDDB_LOGS_OBJECT_STORE = 'vscode-logs-store';
export class IndexedDBLogProvider extends Disposable implements IFileSystemProviderWithFileReadWriteCapability {
readonly capabilities: FileSystemProviderCapabilities = FileSystemProviderCapabilities.FileReadWrite;
readonly onDidChangeCapabilities: Event<void> = Event.None;
private readonly _onDidChangeFile: Emitter<IFileChange[]> = this._register(new Emitter<IFileChange[]>());
readonly onDidChangeFile: Event<IFileChange[]> = this._onDidChangeFile.event;
private readonly versions: Map<string, number> = new Map<string, number>();
export class IndexedDBLogProvider extends KeyValueLogProvider {
private readonly database: Promise<IDBDatabase>;
constructor(
) {
super();
super(INDEXEDDB_LOG_SCHEME);
this.database = this.openDatabase(1);
}
......@@ -53,147 +39,60 @@ export class IndexedDBLogProvider extends Disposable implements IFileSystemProvi
});
}
watch(resource: URI, opts: IWatchOptions): IDisposable {
return Disposable.None;
}
async mkdir(resource: URI): Promise<void> {
}
async stat(resource: URI): Promise<IStat> {
try {
const content = await this.readFile(resource);
return {
type: FileType.File,
ctime: 0,
mtime: this.versions.get(resource.toString()) || 0,
size: content.byteLength
};
} catch (e) {
}
const files = await this.readdir(resource);
if (files.length) {
return {
type: FileType.Directory,
ctime: 0,
mtime: 0,
size: 0
};
}
return Promise.reject(new FileSystemError(resource, FileSystemProviderErrorCode.FileNotFound));
}
async readdir(resource: URI): Promise<[string, FileType][]> {
const hasKey = await this.hasKey(resource.path);
if (hasKey) {
return Promise.reject(new FileSystemError(resource, FileSystemProviderErrorCode.FileNotADirectory));
}
protected async getAllKeys(): Promise<string[]> {
return new Promise(async (c, e) => {
const db = await this.database;
const transaction = db.transaction([INDEXEDDB_LOGS_OBJECT_STORE]);
const objectStore = transaction.objectStore(INDEXEDDB_LOGS_OBJECT_STORE);
const request = objectStore.getAllKeys();
request.onerror = () => e(request.error);
request.onsuccess = () => {
const files: [string, FileType][] = [];
for (const key of <string[]>request.result) {
const keyResource = this.toResource(key);
if (isEqualOrParent(keyResource, resource, false)) {
const path = relativePath(resource, keyResource, false);
if (path) {
const keySegments = path.split('/');
files.push([keySegments[0], keySegments.length === 1 ? FileType.File : FileType.Directory]);
}
}
}
c(files);
};
request.onsuccess = () => c(<string[]>request.result);
});
}
async readFile(resource: URI): Promise<Uint8Array> {
const hasKey = await this.hasKey(resource.path);
if (!hasKey) {
return Promise.reject(new FileSystemError(resource, FileSystemProviderErrorCode.FileNotFound));
}
return new Promise(async (c, e) => {
protected hasKey(key: string): Promise<boolean> {
return new Promise<boolean>(async (c, e) => {
const db = await this.database;
const transaction = db.transaction([INDEXEDDB_LOGS_OBJECT_STORE]);
const objectStore = transaction.objectStore(INDEXEDDB_LOGS_OBJECT_STORE);
const request = objectStore.get(resource.path);
const request = objectStore.getKey(key);
request.onerror = () => e(request.error);
request.onsuccess = () => c(VSBuffer.fromString(request.result || '').buffer);
request.onsuccess = () => {
c(!!request.result);
};
});
}
async writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise<void> {
const hasKey = await this.hasKey(resource.path);
if (!hasKey) {
const files = await this.readdir(resource);
if (files.length) {
return Promise.reject(new FileSystemError(resource, FileSystemProviderErrorCode.FileIsADirectory));
}
}
protected getValue(key: string): Promise<string> {
return new Promise(async (c, e) => {
const db = await this.database;
const transaction = db.transaction([INDEXEDDB_LOGS_OBJECT_STORE], 'readwrite');
const transaction = db.transaction([INDEXEDDB_LOGS_OBJECT_STORE]);
const objectStore = transaction.objectStore(INDEXEDDB_LOGS_OBJECT_STORE);
const request = objectStore.put(VSBuffer.wrap(content).toString(), resource.path);
const request = objectStore.get(key);
request.onerror = () => e(request.error);
request.onsuccess = () => {
this.versions.set(resource.toString(), (this.versions.get(resource.toString()) || 0) + 1);
this._onDidChangeFile.fire([{ resource, type: FileChangeType.UPDATED }]);
c();
};
request.onsuccess = () => c(request.result || '');
});
}
async delete(resource: URI, opts: FileDeleteOptions): Promise<void> {
const hasKey = await this.hasKey(resource.path);
if (hasKey) {
await this.deleteKey(resource.path);
return;
}
if (opts.recursive) {
const files = await this.readdir(resource);
await Promise.all(files.map(([key]) => this.delete(joinPath(resource, key), opts)));
}
}
rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise<void> {
return Promise.reject(new Error('Not Supported'));
}
private hasKey(key: string): Promise<boolean> {
return new Promise<boolean>(async (c, e) => {
protected setValue(key: string, value: string): Promise<void> {
return new Promise(async (c, e) => {
const db = await this.database;
const transaction = db.transaction([INDEXEDDB_LOGS_OBJECT_STORE]);
const transaction = db.transaction([INDEXEDDB_LOGS_OBJECT_STORE], 'readwrite');
const objectStore = transaction.objectStore(INDEXEDDB_LOGS_OBJECT_STORE);
const request = objectStore.getKey(key);
const request = objectStore.put(value, key);
request.onerror = () => e(request.error);
request.onsuccess = () => {
c(!!request.result);
};
request.onsuccess = () => c();
});
}
private deleteKey(key: string): Promise<void> {
protected deleteKey(key: string): Promise<void> {
return new Promise(async (c, e) => {
const db = await this.database;
const transaction = db.transaction([INDEXEDDB_LOGS_OBJECT_STORE], 'readwrite');
const objectStore = transaction.objectStore(INDEXEDDB_LOGS_OBJECT_STORE);
const request = objectStore.delete(key);
request.onerror = () => e(request.error);
request.onsuccess = () => {
this.versions.delete(key);
this._onDidChangeFile.fire([{ resource: this.toResource(key), type: FileChangeType.DELETED }]);
c();
};
request.onsuccess = () => c();
});
}
private toResource(key: string): URI {
return URI.file(key).with({ scheme: INDEXEDDB_LOG_SCHEME });
}
}
/*---------------------------------------------------------------------------------------------
* 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 { IFileSystemProviderWithFileReadWriteCapability, FileSystemProviderCapabilities, IFileChange, IWatchOptions, IStat, FileOverwriteOptions, FileType, FileDeleteOptions, FileWriteOptions, FileChangeType, FileSystemProviderErrorCode } from 'vs/platform/files/common/files';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { Event, Emitter } from 'vs/base/common/event';
import { VSBuffer } from 'vs/base/common/buffer';
import { FileSystemError } from 'vs/workbench/api/common/extHostTypes';
import { isEqualOrParent, joinPath, relativePath } from 'vs/base/common/resources';
export abstract class KeyValueLogProvider extends Disposable implements IFileSystemProviderWithFileReadWriteCapability {
readonly capabilities: FileSystemProviderCapabilities = FileSystemProviderCapabilities.FileReadWrite;
readonly onDidChangeCapabilities: Event<void> = Event.None;
private readonly _onDidChangeFile: Emitter<IFileChange[]> = this._register(new Emitter<IFileChange[]>());
readonly onDidChangeFile: Event<IFileChange[]> = this._onDidChangeFile.event;
private readonly versions: Map<string, number> = new Map<string, number>();
constructor(private readonly scheme: string) {
super();
}
watch(resource: URI, opts: IWatchOptions): IDisposable {
return Disposable.None;
}
async mkdir(resource: URI): Promise<void> {
}
async stat(resource: URI): Promise<IStat> {
try {
const content = await this.readFile(resource);
return {
type: FileType.File,
ctime: 0,
mtime: this.versions.get(resource.toString()) || 0,
size: content.byteLength
};
} catch (e) {
}
const files = await this.readdir(resource);
if (files.length) {
return {
type: FileType.Directory,
ctime: 0,
mtime: 0,
size: 0
};
}
return Promise.reject(new FileSystemError(resource, FileSystemProviderErrorCode.FileNotFound));
}
async readdir(resource: URI): Promise<[string, FileType][]> {
const hasKey = await this.hasKey(resource.path);
if (hasKey) {
return Promise.reject(new FileSystemError(resource, FileSystemProviderErrorCode.FileNotADirectory));
}
const keys = await this.getAllKeys();
const files: [string, FileType][] = [];
for (const key of keys) {
const keyResource = this.toResource(key);
if (isEqualOrParent(keyResource, resource, false)) {
const path = relativePath(resource, keyResource, false);
if (path) {
const keySegments = path.split('/');
files.push([keySegments[0], keySegments.length === 1 ? FileType.File : FileType.Directory]);
}
}
}
return files;
}
async readFile(resource: URI): Promise<Uint8Array> {
const hasKey = await this.hasKey(resource.path);
if (!hasKey) {
return Promise.reject(new FileSystemError(resource, FileSystemProviderErrorCode.FileNotFound));
}
const value = await this.getValue(resource.path);
return VSBuffer.fromString(value).buffer;
}
async writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise<void> {
const hasKey = await this.hasKey(resource.path);
if (!hasKey) {
const files = await this.readdir(resource);
if (files.length) {
return Promise.reject(new FileSystemError(resource, FileSystemProviderErrorCode.FileIsADirectory));
}
}
await this.setValue(resource.path, VSBuffer.wrap(content).toString());
this.versions.set(resource.toString(), (this.versions.get(resource.toString()) || 0) + 1);
this._onDidChangeFile.fire([{ resource, type: FileChangeType.UPDATED }]);
}
async delete(resource: URI, opts: FileDeleteOptions): Promise<void> {
const hasKey = await this.hasKey(resource.path);
if (hasKey) {
await this.deleteKey(resource.path);
this.versions.delete(resource.path);
this._onDidChangeFile.fire([{ resource, type: FileChangeType.DELETED }]);
return;
}
if (opts.recursive) {
const files = await this.readdir(resource);
await Promise.all(files.map(([key]) => this.delete(joinPath(resource, key), opts)));
}
}
rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise<void> {
return Promise.reject(new Error('Not Supported'));
}
private toResource(key: string): URI {
return URI.file(key).with({ scheme: this.scheme });
}
protected abstract getAllKeys(): Promise<string[]>;
protected abstract hasKey(key: string): Promise<boolean>;
protected abstract getValue(key: string): Promise<string>;
protected abstract setValue(key: string, value: string): Promise<void>;
protected abstract deleteKey(key: string): Promise<void>;
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册