Skip to content

Commit 7f15423

Browse files
author
Benjamin Pasero
committed
storage - implement migration when entering workspace
1 parent e5f714c commit 7f15423

10 files changed

Lines changed: 79 additions & 170 deletions

File tree

src/vs/editor/contrib/suggest/suggestWidget.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ class Renderer implements IListRenderer<ICompletionItem, ISuggestionTemplateData
162162

163163
if (
164164
(suggestion.kind === CompletionItemKind.File)
165-
&& document.querySelector('.file-icons-enabled') // todo@ben move file icon knowledge to editor or platform
165+
&& document.querySelector('.file-icons-enabled') // todo@aeschli move file icon knowledge to editor or platform
166166
) {
167167
addClass(data.root, 'show-file-icons');
168168
data.icon.className = 'icon hide';

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

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import { StorageLegacyService, IStorageLegacy } from 'vs/platform/storage/common/storageLegacyService';
77
import { endsWith, startsWith, rtrim } from 'vs/base/common/strings';
88
import { URI } from 'vs/base/common/uri';
9-
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
109

1110
/**
1211
* We currently store local storage with the following format:
@@ -166,33 +165,4 @@ export function parseStorage(storage: IStorageLegacy): IParsedStorage {
166165
folder: folderWorkspacesStorage,
167166
empty: emptyWorkspacesStorage
168167
};
169-
}
170-
171-
export function migrateStorageToMultiRootWorkspace(fromWorkspaceId: string, toWorkspace: IWorkspaceIdentifier, storage: IStorageLegacy): string {
172-
const parsed = parseStorage(storage);
173-
174-
const newWorkspaceId = URI.from({ path: toWorkspace.id, scheme: 'root' }).toString();
175-
176-
// Find in which location the workspace storage is to be migrated from
177-
let storageForWorkspace: StorageObject;
178-
if (parsed.multiRoot.has(fromWorkspaceId)) {
179-
storageForWorkspace = parsed.multiRoot.get(fromWorkspaceId);
180-
} else if (parsed.empty.has(fromWorkspaceId)) {
181-
storageForWorkspace = parsed.empty.get(fromWorkspaceId);
182-
} else if (parsed.folder.has(fromWorkspaceId)) {
183-
storageForWorkspace = parsed.folder.get(fromWorkspaceId);
184-
}
185-
186-
// Migrate existing storage to new workspace id
187-
if (storageForWorkspace) {
188-
Object.keys(storageForWorkspace).forEach(key => {
189-
if (key === StorageLegacyService.WORKSPACE_IDENTIFIER) {
190-
return; // make sure to never migrate the workspace identifier
191-
}
192-
193-
storage.setItem(`${StorageLegacyService.WORKSPACE_PREFIX}${newWorkspaceId}/${key}`, storageForWorkspace[key]);
194-
});
195-
}
196-
197-
return newWorkspaceId;
198168
}

src/vs/platform/storage/electron-browser/storageService.ts renamed to src/vs/platform/storage/node/storageService.ts

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

6-
import { Disposable } from 'vs/base/common/lifecycle';
6+
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
77
import { Event, Emitter } from 'vs/base/common/event';
88
import { ILogService } from 'vs/platform/log/common/log';
99
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
1010
import { IWorkspaceStorageChangeEvent, IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
1111
import { Storage, IStorageLoggingOptions } from 'vs/base/node/storage';
1212
import { IStorageLegacyService, StorageLegacyScope } from 'vs/platform/storage/common/storageLegacyService';
13-
import { addDisposableListener } from 'vs/base/browser/dom';
1413
import { startsWith } from 'vs/base/common/strings';
1514
import { Action } from 'vs/base/common/actions';
1615
import { IWindowService } from 'vs/platform/windows/common/windows';
1716
import { localize } from 'vs/nls';
1817
import { mark, getDuration } from 'vs/base/common/performance';
18+
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
19+
import { join, basename } from 'path';
20+
import { mkdirp, copy } from 'vs/base/node/pfs';
1921

2022
export class StorageService extends Disposable implements IStorageService {
2123
_serviceBrand: any;
@@ -46,16 +48,21 @@ export class StorageService extends Disposable implements IStorageService {
4648
}
4749

4850
private globalStorage: Storage;
51+
52+
private workspaceStoragePath: string;
4953
private workspaceStorage: Storage;
54+
private workspaceStorageListener: IDisposable;
55+
56+
private loggingOptions: IStorageLoggingOptions;
5057

5158
constructor(
52-
workspaceDBPath: string,
59+
workspaceStoragePath: string,
5360
@ILogService logService: ILogService,
54-
@IEnvironmentService environmentService: IEnvironmentService
61+
@IEnvironmentService private environmentService: IEnvironmentService
5562
) {
5663
super();
5764

58-
const loggingOptions: IStorageLoggingOptions = {
65+
this.loggingOptions = {
5966
info: environmentService.verbose || environmentService.logStorage,
6067
infoLogger: msg => logService.info(msg),
6168
errorLogger: error => {
@@ -69,15 +76,22 @@ export class StorageService extends Disposable implements IStorageService {
6976
}
7077
};
7178

72-
this.globalStorage = new Storage({ path: workspaceDBPath === StorageService.IN_MEMORY_PATH ? StorageService.IN_MEMORY_PATH : StorageService.IN_MEMORY_PATH, logging: loggingOptions });
73-
this.workspaceStorage = new Storage({ path: workspaceDBPath, logging: loggingOptions });
79+
this.globalStorage = new Storage({ path: workspaceStoragePath === StorageService.IN_MEMORY_PATH ? StorageService.IN_MEMORY_PATH : StorageService.IN_MEMORY_PATH, logging: this.loggingOptions });
80+
this._register(this.globalStorage.onDidChangeStorage(key => this.handleDidChangeStorage(key, StorageScope.GLOBAL)));
7481

75-
this.registerListeners();
82+
this.createWorkspaceStorage(workspaceStoragePath);
7683
}
7784

78-
private registerListeners(): void {
79-
this._register(this.globalStorage.onDidChangeStorage(key => this.handleDidChangeStorage(key, StorageScope.GLOBAL)));
80-
this._register(this.workspaceStorage.onDidChangeStorage(key => this.handleDidChangeStorage(key, StorageScope.WORKSPACE)));
85+
private createWorkspaceStorage(workspaceStoragePath: string): void {
86+
87+
// Dispose old (if any)
88+
this.workspaceStorage = dispose(this.workspaceStorage);
89+
this.workspaceStorageListener = dispose(this.workspaceStorageListener);
90+
91+
// Create new
92+
this.workspaceStoragePath = workspaceStoragePath;
93+
this.workspaceStorage = new Storage({ path: workspaceStoragePath, logging: this.loggingOptions });
94+
this.workspaceStorageListener = this.workspaceStorage.onDidChangeStorage(key => this.handleDidChangeStorage(key, StorageScope.WORKSPACE));
8195
}
8296

8397
private handleDidChangeStorage(key: string, scope: StorageScope): void {
@@ -180,6 +194,30 @@ export class StorageService extends Disposable implements IStorageService {
180194
console.log(workspaceItemsParsed);
181195
});
182196
}
197+
198+
migrate(toWorkspace: IWorkspaceIdentifier): Promise<void> {
199+
if (this.workspaceStoragePath === StorageService.IN_MEMORY_PATH) {
200+
return Promise.resolve(); // no migration needed if running in memory
201+
}
202+
203+
// Compute new workspace storage path based on workspace identifier
204+
const newWorkspaceStorageHome = join(this.environmentService.workspaceStorageHome, toWorkspace.id);
205+
const newWorkspaceStoragePath = join(newWorkspaceStorageHome, basename(this.workspaceStoragePath));
206+
if (this.workspaceStoragePath === newWorkspaceStoragePath) {
207+
return Promise.resolve(); // guard against migrating to same path
208+
}
209+
210+
// Close workspace DB to be able to copy
211+
return this.workspaceStorage.close().then(() => {
212+
return mkdirp(newWorkspaceStorageHome).then(() => {
213+
return copy(this.workspaceStoragePath, newWorkspaceStoragePath).then(() => {
214+
this.createWorkspaceStorage(newWorkspaceStoragePath);
215+
216+
return this.workspaceStorage.init();
217+
});
218+
});
219+
});
220+
}
183221
}
184222

185223
export class LogStorageAction extends Action {
@@ -215,9 +253,9 @@ export class DelegatingStorageService extends Disposable implements IStorageServ
215253
private closed: boolean;
216254

217255
constructor(
218-
@IStorageService private storageService: StorageService,
219-
@IStorageLegacyService private storageLegacyService: IStorageLegacyService,
220-
@ILogService private logService: ILogService
256+
private storageService: IStorageService,
257+
private storageLegacyService: IStorageLegacyService,
258+
private logService: ILogService
221259
) {
222260
super();
223261

@@ -229,17 +267,18 @@ export class DelegatingStorageService extends Disposable implements IStorageServ
229267
this._register(this.storageService.onWillSaveState(() => this._onWillSaveState.fire()));
230268

231269
const globalKeyMarker = 'storage://global/';
232-
this._register(addDisposableListener(window, 'storage', (e: StorageEvent) => {
270+
271+
window.addEventListener('storage', e => {
233272
if (startsWith(e.key, globalKeyMarker)) {
234273
const key = e.key.substr(globalKeyMarker.length);
235274

236275
this._onDidChangeStorage.fire({ key, scope: StorageScope.GLOBAL });
237276
}
238-
}));
277+
});
239278
}
240279

241280
get storage(): StorageService {
242-
return this.storageService;
281+
return this.storageService as StorageService;
243282
}
244283

245284
get(key: string, scope: StorageScope, fallbackValue?: any): string {
@@ -291,7 +330,7 @@ export class DelegatingStorageService extends Disposable implements IStorageServ
291330
}
292331

293332
close(): Promise<void> {
294-
const promise = this.storageService.close();
333+
const promise = this.storage.close();
295334

296335
this.closed = true;
297336

src/vs/platform/storage/test/browser/storageLegacyMigration.test.ts

Lines changed: 1 addition & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import * as assert from 'assert';
77
import { StorageLegacyScope, StorageLegacyService } from 'vs/platform/storage/common/storageLegacyService';
8-
import { parseStorage, migrateStorageToMultiRootWorkspace } from 'vs/platform/storage/common/storageLegacyMigration';
8+
import { parseStorage } from 'vs/platform/storage/common/storageLegacyMigration';
99
import { URI } from 'vs/base/common/uri';
1010
import { startsWith } from 'vs/base/common/strings';
1111

@@ -154,96 +154,4 @@ suite('Storage Migration', () => {
154154

155155
return service;
156156
}
157-
158-
test('Migrate Storage (folder (Unix) => multi root)', () => {
159-
const workspaceToMigrateFrom = URI.file('/some/folder/folder1').toString();
160-
createService(workspaceToMigrateFrom);
161-
162-
const workspaceToMigrateTo = URI.from({ path: '1500007676869', scheme: 'root' }).toString();
163-
164-
migrateStorageToMultiRootWorkspace(workspaceToMigrateFrom, { id: '1500007676869', configPath: null }, storage);
165-
166-
const s2 = new StorageLegacyService(storage, storage, workspaceToMigrateTo);
167-
const parsed = parseStorage(storage);
168-
169-
assert.equal(parsed.empty.size, 0);
170-
assert.equal(parsed.folder.size, 1);
171-
assert.equal(parsed.multiRoot.size, 1);
172-
173-
const migratedStorage = parsed.multiRoot.get(workspaceToMigrateTo);
174-
assert.equal(Object.keys(migratedStorage).length, 4);
175-
assert.equal(migratedStorage['key1'], s2.get('key1', StorageLegacyScope.WORKSPACE));
176-
assert.equal(migratedStorage['key2.something'], s2.get('key2.something', StorageLegacyScope.WORKSPACE));
177-
assert.equal(migratedStorage['key3/special'], s2.get('key3/special', StorageLegacyScope.WORKSPACE));
178-
assert.equal(migratedStorage['key4 space'], s2.get('key4 space', StorageLegacyScope.WORKSPACE));
179-
});
180-
181-
test('Migrate Storage (folder (Windows) => multi root)', () => {
182-
const workspaceToMigrateFrom = URI.file('c:\\some\\folder\\folder1').toString();
183-
createService(workspaceToMigrateFrom);
184-
185-
const workspaceToMigrateTo = URI.from({ path: '1500007676869', scheme: 'root' }).toString();
186-
187-
migrateStorageToMultiRootWorkspace(workspaceToMigrateFrom, { id: '1500007676869', configPath: null }, storage);
188-
189-
const s2 = new StorageLegacyService(storage, storage, workspaceToMigrateTo);
190-
const parsed = parseStorage(storage);
191-
192-
assert.equal(parsed.empty.size, 0);
193-
assert.equal(parsed.folder.size, 1);
194-
assert.equal(parsed.multiRoot.size, 1);
195-
196-
const migratedStorage = parsed.multiRoot.get(workspaceToMigrateTo);
197-
assert.equal(Object.keys(migratedStorage).length, 4);
198-
assert.equal(migratedStorage['key1'], s2.get('key1', StorageLegacyScope.WORKSPACE));
199-
assert.equal(migratedStorage['key2.something'], s2.get('key2.something', StorageLegacyScope.WORKSPACE));
200-
assert.equal(migratedStorage['key3/special'], s2.get('key3/special', StorageLegacyScope.WORKSPACE));
201-
assert.equal(migratedStorage['key4 space'], s2.get('key4 space', StorageLegacyScope.WORKSPACE));
202-
});
203-
204-
test('Migrate Storage (folder (Windows UNC) => multi root)', () => {
205-
const workspaceToMigrateFrom = 'file://localhost/c%3A/some/folder/folder1';
206-
createService(workspaceToMigrateFrom);
207-
208-
const workspaceToMigrateTo = URI.from({ path: '1500007676869', scheme: 'root' }).toString();
209-
210-
migrateStorageToMultiRootWorkspace(workspaceToMigrateFrom, { id: '1500007676869', configPath: null }, storage);
211-
212-
const s2 = new StorageLegacyService(storage, storage, workspaceToMigrateTo);
213-
const parsed = parseStorage(storage);
214-
215-
assert.equal(parsed.empty.size, 0);
216-
assert.equal(parsed.folder.size, 1);
217-
assert.equal(parsed.multiRoot.size, 1);
218-
219-
const migratedStorage = parsed.multiRoot.get(workspaceToMigrateTo);
220-
assert.equal(Object.keys(migratedStorage).length, 4);
221-
assert.equal(migratedStorage['key1'], s2.get('key1', StorageLegacyScope.WORKSPACE));
222-
assert.equal(migratedStorage['key2.something'], s2.get('key2.something', StorageLegacyScope.WORKSPACE));
223-
assert.equal(migratedStorage['key3/special'], s2.get('key3/special', StorageLegacyScope.WORKSPACE));
224-
assert.equal(migratedStorage['key4 space'], s2.get('key4 space', StorageLegacyScope.WORKSPACE));
225-
});
226-
227-
test('Migrate Storage (empty => multi root)', () => {
228-
const workspaceToMigrateFrom = URI.from({ path: '1500007676869', scheme: 'empty' }).toString();
229-
createService(workspaceToMigrateFrom);
230-
231-
const workspaceToMigrateTo = URI.from({ path: '2500007676869', scheme: 'root' }).toString();
232-
233-
migrateStorageToMultiRootWorkspace(workspaceToMigrateFrom, { id: '2500007676869', configPath: null }, storage);
234-
235-
const s2 = new StorageLegacyService(storage, storage, workspaceToMigrateTo);
236-
const parsed = parseStorage(storage);
237-
238-
assert.equal(parsed.empty.size, 1);
239-
assert.equal(parsed.folder.size, 0);
240-
assert.equal(parsed.multiRoot.size, 1);
241-
242-
const migratedStorage = parsed.multiRoot.get(workspaceToMigrateTo);
243-
assert.equal(Object.keys(migratedStorage).length, 4);
244-
assert.equal(migratedStorage['key1'], s2.get('key1', StorageLegacyScope.WORKSPACE));
245-
assert.equal(migratedStorage['key2.something'], s2.get('key2.something', StorageLegacyScope.WORKSPACE));
246-
assert.equal(migratedStorage['key3/special'], s2.get('key3/special', StorageLegacyScope.WORKSPACE));
247-
assert.equal(migratedStorage['key4 space'], s2.get('key4 space', StorageLegacyScope.WORKSPACE));
248-
});
249157
});

src/vs/platform/telemetry/test/electron-browser/commonProperties.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import * as fs from 'fs';
99
import { resolveWorkbenchCommonProperties } from 'vs/platform/telemetry/node/workbenchCommonProperties';
1010
import { getRandomTestPath, TestEnvironmentService } from 'vs/workbench/test/workbenchTestServices';
1111
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
12-
import { StorageService } from 'vs/platform/storage/electron-browser/storageService';
12+
import { StorageService } from 'vs/platform/storage/node/storageService';
1313
import { NullLogService } from 'vs/platform/log/common/log';
1414
import { del } from 'vs/base/node/extfs';
1515
import { mkdirp } from 'vs/base/node/pfs';

src/vs/workbench/electron-browser/main.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import { IWorkspacesService, ISingleFolderWorkspaceIdentifier, isWorkspaceIdenti
3535
import { createSpdLogService } from 'vs/platform/log/node/spdlogService';
3636
import * as fs from 'fs';
3737
import { ConsoleLogService, MultiplexLogService, ILogService } from 'vs/platform/log/common/log';
38-
import { StorageService, DelegatingStorageService } from 'vs/platform/storage/electron-browser/storageService';
38+
import { StorageService, DelegatingStorageService } from 'vs/platform/storage/node/storageService';
3939
import { IssueChannelClient } from 'vs/platform/issue/node/issueIpc';
4040
import { IIssueService } from 'vs/platform/issue/common/issue';
4141
import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/node/logIpc';
@@ -116,8 +116,7 @@ function openWorkbench(configuration: IWindowConfiguration): Promise<void> {
116116
createStorageService(workspaceStoragePath, payload, environmentService, logService)
117117
]).then(services => {
118118
const workspaceService = services[0];
119-
const storageLegacyService = createStorageLegacyService(workspaceService, environmentService);
120-
const storageService = new DelegatingStorageService(services[1], storageLegacyService, logService);
119+
const storageService = new DelegatingStorageService(services[1], createStorageLegacyService(workspaceService, environmentService), logService);
121120

122121
return domContentLoaded().then(() => {
123122
perf.mark('willStartWorkbench');
@@ -128,7 +127,6 @@ function openWorkbench(configuration: IWindowConfiguration): Promise<void> {
128127
configurationService: workspaceService,
129128
environmentService,
130129
logService,
131-
storageLegacyService,
132130
storageService
133131
}, mainServices, mainProcessClient, configuration);
134132

src/vs/workbench/electron-browser/shell.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ import { IIntegrityService } from 'vs/platform/integrity/common/integrity';
3939
import { EditorWorkerServiceImpl } from 'vs/editor/common/services/editorWorkerServiceImpl';
4040
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
4141
import { ExtensionService } from 'vs/workbench/services/extensions/electron-browser/extensionService';
42-
import { IStorageLegacyService } from 'vs/platform/storage/common/storageLegacyService';
4342
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
4443
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
4544
import { InstantiationService } from 'vs/platform/instantiation/node/instantiationService';
@@ -76,7 +75,7 @@ import { HashService } from 'vs/workbench/services/hash/node/hashService';
7675
import { IHashService } from 'vs/workbench/services/hash/common/hashService';
7776
import { ILogService } from 'vs/platform/log/common/log';
7877
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
79-
import { DelegatingStorageService } from 'vs/platform/storage/electron-browser/storageService';
78+
import { DelegatingStorageService } from 'vs/platform/storage/node/storageService';
8079
import { Event, Emitter } from 'vs/base/common/event';
8180
import { WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme';
8281
import { ILocalizationsChannel, LocalizationsChannelClient } from 'vs/platform/localizations/node/localizationsIpc';
@@ -109,7 +108,6 @@ export interface ICoreServices {
109108
configurationService: IConfigurationService;
110109
environmentService: IEnvironmentService;
111110
logService: ILogService;
112-
storageLegacyService: IStorageLegacyService;
113111
storageService: DelegatingStorageService;
114112
}
115113

@@ -125,7 +123,6 @@ export class WorkbenchShell extends Disposable {
125123
private readonly _onRunning = this._register(new Emitter<void>());
126124
get onRunning(): Event<void> { return this._onRunning.event; }
127125

128-
private storageLegacyService: IStorageLegacyService;
129126
private storageService: DelegatingStorageService;
130127
private environmentService: IEnvironmentService;
131128
private logService: ILogService;
@@ -156,7 +153,6 @@ export class WorkbenchShell extends Disposable {
156153
this.configurationService = coreServices.configurationService;
157154
this.environmentService = coreServices.environmentService;
158155
this.logService = coreServices.logService;
159-
this.storageLegacyService = coreServices.storageLegacyService;
160156
this.storageService = coreServices.storageService;
161157

162158
this.mainProcessServices = mainProcessServices;
@@ -376,7 +372,6 @@ export class WorkbenchShell extends Disposable {
376372
serviceCollection.set(IEnvironmentService, this.environmentService);
377373
serviceCollection.set(ILabelService, new SyncDescriptor(LabelService));
378374
serviceCollection.set(ILogService, this._register(this.logService));
379-
serviceCollection.set(IStorageLegacyService, this.storageLegacyService);
380375
serviceCollection.set(IStorageService, this.storageService);
381376

382377
this.mainProcessServices.forEach((serviceIdentifier, serviceInstance) => {

0 commit comments

Comments
 (0)