Skip to content

Commit 75e8afe

Browse files
author
Benjamin Pasero
committed
wip
1 parent dff890a commit 75e8afe

8 files changed

Lines changed: 211 additions & 77 deletions

File tree

src/vs/base/node/storage.ts

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,11 @@ enum StorageState {
4343

4444
export interface IStorage extends IDisposable {
4545

46+
readonly items: Map<string, string>;
4647
readonly size: number;
4748
readonly onDidChangeStorage: Event<string>;
4849

49-
init(): Promise<void>;
50+
init(): Thenable<void>;
5051

5152
get(key: string, fallbackValue: string): string;
5253
get(key: string, fallbackValue?: string): string | undefined;
@@ -62,8 +63,16 @@ export interface IStorage extends IDisposable {
6263

6364
close(): Thenable<void>;
6465

65-
getItems(): Promise<Map<string, string>>;
66-
checkIntegrity(full: boolean): Promise<string>;
66+
checkIntegrity(full: boolean): Thenable<string>;
67+
}
68+
69+
export interface IStorageDatabase {
70+
getItems(): Thenable<Map<string, string>>;
71+
updateItems(request: IUpdateRequest): Thenable<void>;
72+
73+
close(): Thenable<void>;
74+
75+
checkIntegrity(full: boolean): Thenable<string>;
6776
}
6877

6978
export class Storage extends Disposable implements IStorage {
@@ -76,29 +85,34 @@ export class Storage extends Disposable implements IStorage {
7685
private _onDidChangeStorage: Emitter<string> = this._register(new Emitter<string>());
7786
get onDidChangeStorage(): Event<string> { return this._onDidChangeStorage.event; }
7887

88+
protected storage: IStorageDatabase;
89+
7990
private state = StorageState.None;
8091

81-
private storage: SQLiteStorageImpl;
8292
private cache: Map<string, string> = new Map<string, string>();
8393

8494
private flushDelayer: ThrottledDelayer<void>;
8595

8696
private pendingDeletes: Set<string> = new Set<string>();
8797
private pendingInserts: Map<string, string> = new Map();
8898

89-
constructor(private options: IStorageOptions) {
99+
constructor(private options: IStorageOptions, storage?: IStorageDatabase) {
90100
super();
91101

92-
this.storage = new SQLiteStorageImpl(options);
102+
this.storage = storage || new SQLiteStorageImpl(options);
93103

94104
this.flushDelayer = this._register(new ThrottledDelayer(Storage.FLUSH_DELAY));
95105
}
96106

107+
get items(): Map<string, string> {
108+
return this.cache;
109+
}
110+
97111
get size(): number {
98112
return this.cache.size;
99113
}
100114

101-
init(): Promise<void> {
115+
init(): Thenable<void> {
102116
if (this.state !== StorageState.None) {
103117
return Promise.resolve(); // either closed or already initialized
104118
}
@@ -236,11 +250,7 @@ export class Storage extends Disposable implements IStorage {
236250
return this.storage.updateItems(updateRequest);
237251
}
238252

239-
getItems(): Promise<Map<string, string>> {
240-
return this.storage.getItems();
241-
}
242-
243-
checkIntegrity(full: boolean): Promise<string> {
253+
checkIntegrity(full: boolean): Thenable<string> {
244254
return this.storage.checkIntegrity(full);
245255
}
246256
}
@@ -255,7 +265,7 @@ interface IOpenDatabaseResult {
255265
path: string;
256266
}
257267

258-
export class SQLiteStorageImpl {
268+
export class SQLiteStorageImpl implements IStorageDatabase {
259269

260270
private static measuredRequireDuration: boolean; // TODO@Ben remove me after a while
261271

@@ -604,11 +614,10 @@ class SQLiteStorageLogger {
604614

605615
export class NullStorage extends Disposable implements IStorage {
606616

617+
readonly items = new Map<string, string>();
607618
readonly size = 0;
608619
readonly onDidChangeStorage = Event.None;
609620

610-
private items = new Map<string, string>();
611-
612621
init(): Promise<void> { return Promise.resolve(); }
613622

614623
get(key: string, fallbackValue: string): string;
@@ -641,10 +650,6 @@ export class NullStorage extends Disposable implements IStorage {
641650
return Promise.resolve();
642651
}
643652

644-
getItems(): Promise<Map<string, string>> {
645-
return Promise.resolve(this.items);
646-
}
647-
648653
checkIntegrity(full: boolean): Promise<string> {
649654
return Promise.resolve('ok');
650655
}

src/vs/base/test/node/storage/storage.test.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { Storage, SQLiteStorageImpl, IStorageOptions } from 'vs/base/node/storage';
6+
import { Storage, SQLiteStorageImpl, IStorageOptions, IStorageDatabase } from 'vs/base/node/storage';
77
import { generateUuid } from 'vs/base/common/uuid';
88
import { join } from 'path';
99
import { tmpdir } from 'os';
@@ -519,7 +519,13 @@ suite('SQLite Storage Library', () => {
519519
const storageDir = uniqueStorageDir();
520520
await mkdirp(storageDir);
521521

522-
const storage = new Storage({ path: join(storageDir, 'storage.db') });
522+
class TestStorage extends Storage {
523+
getStorage(): IStorageDatabase {
524+
return this.storage;
525+
}
526+
}
527+
528+
const storage = new TestStorage({ path: join(storageDir, 'storage.db') });
523529

524530
await storage.init();
525531

@@ -551,7 +557,7 @@ suite('SQLite Storage Library', () => {
551557
storage.set('foo3', 'bar');
552558
await storage.set('some/foo3/path', 'some/bar/path');
553559

554-
const items = await storage.getItems();
560+
const items = await storage.getStorage().getItems();
555561
equal(items.get('foo'), 'bar');
556562
equal(items.get('some/foo/path'), 'some/bar/path');
557563
equal(items.has('foo1'), false);

src/vs/code/electron-main/app.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
7373
import { REMOTE_FILE_SYSTEM_CHANNEL_NAME } from 'vs/platform/remote/node/remoteAgentFileSystemChannel';
7474
import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver';
7575
import { SnapUpdateService } from 'vs/platform/update/electron-main/updateService.snap';
76-
import { IStorageMainService, StorageMainService } from 'vs/platform/storage/electron-main/storageMainService';
76+
import { IStorageMainService, StorageMainService } from 'vs/platform/storage/node/storageMainService';
77+
import { GlobalStorageDatabaseChannel } from 'vs/platform/storage/node/storageIpc';
7778

7879
export class CodeApplication extends Disposable {
7980

@@ -591,6 +592,10 @@ export class CodeApplication extends Disposable {
591592
const urlChannel = new URLServiceChannel(urlService);
592593
this.electronIpcServer.registerChannel('url', urlChannel);
593594

595+
const storageService = accessor.get(IStorageMainService);
596+
const storageChannel = new GlobalStorageDatabaseChannel(storageService);
597+
this.electronIpcServer.registerChannel('storage', storageChannel);
598+
594599
// Log level management
595600
const logLevelChannel = new LogLevelSetterChannel(accessor.get(ILogService));
596601
this.electronIpcServer.registerChannel('loglevel', logLevelChannel);

src/vs/platform/storage/common/storage.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,11 @@ export const enum StorageScope {
8888
WORKSPACE
8989
}
9090

91-
export interface IWorkspaceStorageChangeEvent {
91+
export interface IStorageChangeEvent {
9292
key: string;
93+
}
94+
95+
export interface IWorkspaceStorageChangeEvent extends IStorageChangeEvent {
9396
scope: StorageScope;
9497
}
9598

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc';
7+
import { Event, buffer } from 'vs/base/common/event';
8+
import { IStorageChangeEvent } from 'vs/platform/storage/common/storage';
9+
import { IStorageMainService } from 'vs/platform/storage/node/storageMainService';
10+
import { IUpdateRequest, IStorageDatabase } from 'vs/base/node/storage';
11+
12+
export class GlobalStorageDatabaseChannel implements IServerChannel {
13+
14+
onDidChangeStorage: Event<IStorageChangeEvent>;
15+
16+
constructor(private service: IStorageMainService) {
17+
this.onDidChangeStorage = buffer(service.onDidChangeStorage, true);
18+
}
19+
20+
listen(_, event: string): Event<any> {
21+
switch (event) {
22+
case 'onDidChangeStorage': return this.onDidChangeStorage;
23+
}
24+
25+
throw new Error(`Event not found: ${event}`);
26+
}
27+
28+
call(_, command: string, arg?: any): Thenable<any> {
29+
switch (command) {
30+
case 'getItems': {
31+
return Promise.resolve(itemsToSerializable(this.service.items));
32+
}
33+
34+
case 'updateItems': {
35+
const items = arg as IUpdateRequest;
36+
if (Array.isArray(items.insert)) {
37+
items.insert.forEach((value, key) => this.service.store(key, value));
38+
}
39+
40+
if (Array.isArray(items.delete)) {
41+
items.delete.forEach(key => this.service.remove(key));
42+
}
43+
44+
return Promise.resolve(); // do not wait for modifications to complete
45+
}
46+
47+
case 'checkIntegrity': {
48+
return this.service.checkIntegrity(arg);
49+
}
50+
}
51+
52+
throw new Error(`Call not found: ${command}`);
53+
}
54+
}
55+
56+
export class GlobalStorageDatabaseChannelClient implements IStorageDatabase {
57+
58+
_serviceBrand: any;
59+
60+
constructor(private channel: IChannel) { }
61+
62+
get onDidChangeStorage(): Event<IStorageChangeEvent> {
63+
return this.channel.listen('onDidChangeStorage');
64+
}
65+
66+
getItems(): Thenable<Map<string, string>> {
67+
return this.channel.call('getItems').then((data: [string, string][]) => serializableToItems(data));
68+
}
69+
70+
updateItems(request: IUpdateRequest): Thenable<void> {
71+
return this.channel.call('updateItems', request);
72+
}
73+
74+
checkIntegrity(full: boolean): Thenable<string> {
75+
return this.channel.call('checkIntegrity', full);
76+
}
77+
78+
close(): Thenable<void> {
79+
return Promise.resolve(); // global storage is closed on the main side
80+
}
81+
}
82+
83+
function itemsToSerializable(items: Map<string, string>): [string, string][] {
84+
const data: [string, string][] = [];
85+
86+
items.forEach((value, key) => {
87+
data.push([key, value]);
88+
});
89+
90+
return data;
91+
}
92+
93+
function serializableToItems(data: [string, string][]): Map<string, string> {
94+
const items = new Map<string, string>();
95+
96+
for (const [key, value] of data) {
97+
items.set(key, value);
98+
}
99+
100+
return items;
101+
}

src/vs/platform/storage/electron-main/storageMainService.ts renamed to src/vs/platform/storage/node/storageMainService.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { IStorage, Storage, IStorageLoggingOptions } from 'vs/base/node/storage'
1212
import { join } from 'path';
1313
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
1414
import { mark } from 'vs/base/common/performance';
15+
import { IStorageChangeEvent } from 'vs/platform/storage/common/storage';
1516

1617
export const IStorageMainService = createDecorator<IStorageMainService>('storageMainService');
1718

@@ -31,6 +32,11 @@ export interface IStorageMainService {
3132
*/
3233
readonly onWillSaveState: Event<void>;
3334

35+
/**
36+
* Retrieve all elements stored in storage.
37+
*/
38+
readonly items: Map<string, string>;
39+
3440
/**
3541
* Retrieve an element stored with the given key from storage. Use
3642
* the provided defaultValue if the element is null or undefined.
@@ -61,10 +67,11 @@ export interface IStorageMainService {
6167
* Delete an element stored under the provided key from storage.
6268
*/
6369
remove(key: string): void;
64-
}
6570

66-
export interface IStorageChangeEvent {
67-
key: string;
71+
/**
72+
* Check the integrity of the underlying database.
73+
*/
74+
checkIntegrity(full: boolean): Thenable<string>;
6875
}
6976

7077
export class StorageMainService extends Disposable implements IStorageMainService {
@@ -79,6 +86,8 @@ export class StorageMainService extends Disposable implements IStorageMainServic
7986
private _onWillSaveState: Emitter<void> = this._register(new Emitter<void>());
8087
get onWillSaveState(): Event<void> { return this._onWillSaveState.event; }
8188

89+
get items(): Map<string, string> { return this.storage.items; }
90+
8291
private storage: IStorage;
8392

8493
constructor(
@@ -167,4 +176,8 @@ export class StorageMainService extends Disposable implements IStorageMainServic
167176
// Do it
168177
return this.storage.close().then(() => this.logService.trace('StorageMainService#close() - finished'));
169178
}
179+
180+
checkIntegrity(full: boolean): Thenable<string> {
181+
return this.storage.checkIntegrity(full);
182+
}
170183
}

0 commit comments

Comments
 (0)