fileUserDataService.ts 3.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import { Event, Emitter } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { IUserDataService, IUserDataChangesEvent } from './userDataService';
import { IFileService, FileChangesEvent } from 'vs/platform/files/common/files';
import { URI } from 'vs/base/common/uri';
import * as resources from 'vs/base/common/resources';
import { TernarySearchTree } from 'vs/base/common/map';
import { VSBuffer } from 'vs/base/common/buffer';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
15
import { Schemas } from 'vs/base/common/network';
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39

export class FileUserDataService extends Disposable implements IUserDataService {
	_serviceBrand: any;

	private readonly settingsHome: URI;

	private _onDidChange: Emitter<IUserDataChangesEvent> = this._register(new Emitter<IUserDataChangesEvent>());
	readonly onDidChange: Event<IUserDataChangesEvent> = this._onDidChange.event;

	constructor(
		@IEnvironmentService environmentService: IEnvironmentService,
		@IFileService private readonly fileService: IFileService
	) {
		super();
		// Assumption: This path always exists
		this.settingsHome = environmentService.appSettingsHome;
		this.fileService.watch(this.settingsHome);

		this._register(this.fileService.onFileChanges(e => this.handleFileChanges(e)));
	}

	private handleFileChanges(event: FileChangesEvent): void {
		const changedKeys: string[] = [];
		for (const change of event.changes) {
40 41 42 43 44
			if (change.resource.scheme !== Schemas.userData) {
				const key = this.toKey(change.resource.with({ scheme: Schemas.userData }));
				if (key) {
					changedKeys.push(key);
				}
45 46 47 48 49 50 51
			}
		}
		if (changedKeys.length) {
			this._onDidChange.fire(new UserDataChangesEvent(changedKeys));
		}
	}

S
Sandeep Somavarapu 已提交
52
	async read(key: string): Promise<string> {
53
		const resource = this.toFileResource(key);
S
Sandeep Somavarapu 已提交
54 55 56 57 58 59 60 61 62 63
		try {
			const content = await this.fileService.readFile(resource);
			return content.value.toString();
		} catch (e) {
			const exists = await this.fileService.exists(resource);
			if (exists) {
				throw e;
			}
		}
		return '';
64 65 66
	}

	write(key: string, value: string): Promise<void> {
67
		return this.fileService.writeFile(this.toFileResource(key), VSBuffer.fromString(value)).then(() => undefined);
68 69
	}

70
	private toFileResource(key: string): URI {
71 72 73
		return resources.joinPath(this.settingsHome, ...key.split('/'));
	}

74 75 76 77 78 79 80 81
	toResource(key: string): URI {
		return this.toFileResource(key).with({ scheme: Schemas.userData });
	}

	toKey(resource: URI): string | undefined {
		return resources.relativePath(this.settingsHome.with({ scheme: Schemas.userData }), resource);
	}

82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
}

class UserDataChangesEvent implements IUserDataChangesEvent {

	private _keysTree: TernarySearchTree<string> | undefined = undefined;

	constructor(readonly keys: string[]) { }

	private get keysTree(): TernarySearchTree<string> {
		if (!this._keysTree) {
			this._keysTree = TernarySearchTree.forPaths<string>();
			for (const key of this.keys) {
				this._keysTree.set(key, key);
			}
		}
		return this._keysTree;
	}

	contains(keyOrSegment: string): boolean {
		return this.keysTree.findSubstr(keyOrSegment) !== undefined;
	}

}