Skip to content

Commit 26cbe9d

Browse files
committed
microsoft#93960 Ability to downlod and replace from cloud
1 parent 645f585 commit 26cbe9d

13 files changed

Lines changed: 178 additions & 25 deletions

File tree

src/vs/platform/userDataSync/common/abstractSynchronizer.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,33 @@ export abstract class AbstractSynchroniser extends Disposable {
144144
}
145145
}
146146

147+
async replace(uri: URI): Promise<boolean> {
148+
const content = await this.resolveContent(uri);
149+
if (!content) {
150+
return false;
151+
}
152+
153+
const syncData = this.parseSyncData(content);
154+
if (!syncData) {
155+
return false;
156+
}
157+
158+
await this.stop();
159+
160+
try {
161+
this.logService.trace(`${this.syncResourceLogLabel}: Started resetting ${this.resource.toLowerCase()}...`);
162+
this.setStatus(SyncStatus.Syncing);
163+
const lastSyncUserData = await this.getLastSyncUserData();
164+
const remoteUserData = await this.getLatestRemoteUserData(null, lastSyncUserData);
165+
await this.performReplace(syncData, remoteUserData, lastSyncUserData);
166+
this.logService.info(`${this.syncResourceLogLabel}: Finished resetting ${this.resource.toLowerCase()}.`);
167+
} finally {
168+
this.setStatus(SyncStatus.Idle);
169+
}
170+
171+
return true;
172+
}
173+
147174
private async getLatestRemoteUserData(manifest: IUserDataManifest | null, lastSyncUserData: IRemoteUserData | null): Promise<IRemoteUserData> {
148175
if (lastSyncUserData) {
149176

@@ -323,6 +350,7 @@ export abstract class AbstractSynchroniser extends Disposable {
323350

324351
protected abstract readonly version: number;
325352
protected abstract performSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise<SyncStatus>;
353+
protected abstract performReplace(syncData: ISyncData, remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise<void>;
326354
protected abstract generatePreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise<ISyncPreviewResult>;
327355
}
328356

src/vs/platform/userDataSync/common/extensionsSync.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,18 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
200200
return SyncStatus.Idle;
201201
}
202202

203+
protected async performReplace(syncData: ISyncData, remoteUserData: IRemoteUserData, lastSyncUserData: ILastSyncUserData | null): Promise<void> {
204+
const localExtensions = await this.getLocalExtensions();
205+
const syncExtensions = this.parseExtensions(syncData);
206+
const { added, updated, removed } = merge(localExtensions, syncExtensions, localExtensions, [], this.getIgnoredExtensions());
207+
208+
await this.apply({
209+
added, removed, updated, remote: syncExtensions, remoteUserData, localExtensions, skippedExtensions: [], lastSyncUserData,
210+
hasLocalChanged: added.length > 0 || removed.length > 0 || updated.length > 0,
211+
hasRemoteChanged: true
212+
});
213+
}
214+
203215
protected async generatePreview(remoteUserData: IRemoteUserData, lastSyncUserData: ILastSyncUserData | null): Promise<IExtensionsSyncPreviewResult> {
204216
const remoteExtensions: ISyncExtension[] | null = remoteUserData.syncData ? this.parseExtensions(remoteUserData.syncData) : null;
205217
const lastSyncExtensions: ISyncExtension[] | null = lastSyncUserData ? this.parseExtensions(lastSyncUserData.syncData!) : null;

src/vs/platform/userDataSync/common/globalStateSync.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { IStringDictionary } from 'vs/base/common/collections';
1313
import { edit } from 'vs/platform/userDataSync/common/content';
1414
import { merge } from 'vs/platform/userDataSync/common/globalStateMerge';
1515
import { parse } from 'vs/base/common/json';
16-
import { AbstractSynchroniser, IRemoteUserData } from 'vs/platform/userDataSync/common/abstractSynchronizer';
16+
import { AbstractSynchroniser, IRemoteUserData, ISyncData } from 'vs/platform/userDataSync/common/abstractSynchronizer';
1717
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
1818
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
1919
import { URI } from 'vs/base/common/uri';
@@ -200,6 +200,18 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
200200
return SyncStatus.Idle;
201201
}
202202

203+
protected async performReplace(syncData: ISyncData, remoteUserData: IRemoteUserData, lastSyncUserData: ILastSyncUserData | null): Promise<void> {
204+
const localUserData = await this.getLocalGlobalState();
205+
const syncGlobalState: IGlobalState = JSON.parse(syncData.content);
206+
const { local, skipped } = merge(localUserData.storage, syncGlobalState.storage, localUserData.storage, this.getSyncStorageKeys(), lastSyncUserData?.skippedStorageKeys || [], this.logService);
207+
await this.apply({
208+
local, remote: syncGlobalState.storage, remoteUserData, localUserData, lastSyncUserData,
209+
skippedStorageKeys: skipped,
210+
hasLocalChanged: Object.keys(local.added).length > 0 || Object.keys(local.updated).length > 0 || local.removed.length > 0,
211+
hasRemoteChanged: true
212+
});
213+
}
214+
203215
protected async generatePreview(remoteUserData: IRemoteUserData, lastSyncUserData: ILastSyncUserData | null): Promise<IGlobalSyncPreviewResult> {
204216
const remoteGlobalState: IGlobalState = remoteUserData.syncData ? JSON.parse(remoteUserData.syncData.content) : null;
205217
const lastSyncGlobalState: IGlobalState = lastSyncUserData && lastSyncUserData.syncData ? JSON.parse(lastSyncUserData.syncData.content) : null;

src/vs/platform/userDataSync/common/keybindingsSync.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { CancellationToken } from 'vs/base/common/cancellation';
1616
import { OS, OperatingSystem } from 'vs/base/common/platform';
1717
import { isUndefined } from 'vs/base/common/types';
1818
import { isNonEmptyArray } from 'vs/base/common/arrays';
19-
import { IFileSyncPreviewResult, AbstractJsonFileSynchroniser, IRemoteUserData } from 'vs/platform/userDataSync/common/abstractSynchronizer';
19+
import { IFileSyncPreviewResult, AbstractJsonFileSynchroniser, IRemoteUserData, ISyncData } from 'vs/platform/userDataSync/common/abstractSynchronizer';
2020
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
2121
import { URI } from 'vs/base/common/uri';
2222
import { joinPath, isEqual, dirname, basename } from 'vs/base/common/resources';
@@ -212,6 +212,24 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
212212
}
213213
}
214214

215+
protected async performReplace(syncData: ISyncData, remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise<void> {
216+
const content = this.getKeybindingsContentFromSyncContent(syncData.content);
217+
218+
if (content !== null) {
219+
const fileContent = await this.getLocalFileContent();
220+
this.syncPreviewResultPromise = createCancelablePromise(() => Promise.resolve<IFileSyncPreviewResult>({
221+
fileContent,
222+
remoteUserData,
223+
lastSyncUserData,
224+
content,
225+
hasConflicts: false,
226+
hasLocalChanged: true,
227+
hasRemoteChanged: true,
228+
}));
229+
await this.apply();
230+
}
231+
}
232+
215233
private async apply(forcePush?: boolean): Promise<void> {
216234
if (!this.syncPreviewResultPromise) {
217235
return;

src/vs/platform/userDataSync/common/settingsSync.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
1414
import { CancellationToken } from 'vs/base/common/cancellation';
1515
import { updateIgnoredSettings, merge, getIgnoredSettings, isEmpty } from 'vs/platform/userDataSync/common/settingsMerge';
1616
import { edit } from 'vs/platform/userDataSync/common/content';
17-
import { IFileSyncPreviewResult, AbstractJsonFileSynchroniser, IRemoteUserData } from 'vs/platform/userDataSync/common/abstractSynchronizer';
17+
import { IFileSyncPreviewResult, AbstractJsonFileSynchroniser, IRemoteUserData, ISyncData } from 'vs/platform/userDataSync/common/abstractSynchronizer';
1818
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
1919
import { URI } from 'vs/base/common/uri';
2020
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
@@ -257,6 +257,27 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser {
257257
}
258258
}
259259

260+
protected async performReplace(syncData: ISyncData, remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise<void> {
261+
const settingsSyncContent = this.parseSettingsSyncContent(syncData.content);
262+
if (settingsSyncContent) {
263+
const fileContent = await this.getLocalFileContent();
264+
const formatUtils = await this.getFormattingOptions();
265+
const ignoredSettings = await this.getIgnoredSettings();
266+
const content = updateIgnoredSettings(settingsSyncContent.settings, fileContent ? fileContent.value.toString() : '{}', ignoredSettings, formatUtils);
267+
this.syncPreviewResultPromise = createCancelablePromise(() => Promise.resolve<IFileSyncPreviewResult>({
268+
fileContent,
269+
remoteUserData,
270+
lastSyncUserData,
271+
content,
272+
hasLocalChanged: true,
273+
hasRemoteChanged: true,
274+
hasConflicts: false,
275+
}));
276+
277+
await this.apply();
278+
}
279+
}
280+
260281
private async apply(forcePush?: boolean): Promise<void> {
261282
if (!this.syncPreviewResultPromise) {
262283
return;

src/vs/platform/userDataSync/common/snippetsSync.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,19 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
256256
}
257257
}
258258

259+
protected async performReplace(syncData: ISyncData, remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise<void> {
260+
const local = await this.getSnippetsFileContents();
261+
const localSnippets = this.toSnippetsContents(local);
262+
const snippets = this.parseSnippets(syncData);
263+
const { added, updated, removed } = merge(localSnippets, snippets, localSnippets);
264+
this.syncPreviewResultPromise = createCancelablePromise(() => Promise.resolve<ISinppetsSyncPreviewResult>({
265+
added, removed, updated, remote: snippets, remoteUserData, local, lastSyncUserData, conflicts: [], resolvedConflicts: {},
266+
hasLocalChanged: Object.keys(added).length > 0 || removed.length > 0 || Object.keys(updated).length > 0,
267+
hasRemoteChanged: true
268+
}));
269+
await this.apply();
270+
}
271+
259272
protected getPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise<ISinppetsSyncPreviewResult> {
260273
if (!this.syncPreviewResultPromise) {
261274
this.syncPreviewResultPromise = createCancelablePromise(token => this.generatePreview(remoteUserData, lastSyncUserData, token));

src/vs/platform/userDataSync/common/userDataSync.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ export interface IUserDataSynchroniser {
275275
pull(): Promise<void>;
276276
push(): Promise<void>;
277277
sync(manifest: IUserDataManifest | null): Promise<void>;
278+
replace(uri: URI): Promise<boolean>;
278279
stop(): Promise<void>;
279280

280281
getSyncPreview(): Promise<ISyncPreviewResult>
@@ -330,6 +331,7 @@ export interface IUserDataSyncService {
330331
pull(): Promise<void>;
331332
sync(): Promise<void>;
332333
stop(): Promise<void>;
334+
replace(uri: URI): Promise<void>;
333335
reset(): Promise<void>;
334336
resetLocal(): Promise<void>;
335337

@@ -380,3 +382,13 @@ export function getSyncResourceFromLocalPreview(localPreview: URI, environmentSe
380382
localPreview = localPreview.with({ scheme: environmentService.userDataSyncHome.scheme });
381383
return ALL_SYNC_RESOURCES.filter(syncResource => isEqualOrParent(localPreview, joinPath(environmentService.userDataSyncHome, syncResource, PREVIEW_DIR_NAME)))[0];
382384
}
385+
386+
export function getSyncAreaLabel(source: SyncResource): string {
387+
switch (source) {
388+
case SyncResource.Settings: return localize('settings', "Settings");
389+
case SyncResource.Keybindings: return localize('keybindings', "Keyboard Shortcuts");
390+
case SyncResource.Snippets: return localize('snippets', "User Snippets");
391+
case SyncResource.Extensions: return localize('extensions', "Extensions");
392+
case SyncResource.GlobalState: return localize('ui state label', "UI State");
393+
}
394+
}

src/vs/platform/userDataSync/common/userDataSyncIpc.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export class UserDataSyncChannel implements IServerChannel {
3333
case 'pull': return this.service.pull();
3434
case 'sync': return this.service.sync();
3535
case 'stop': this.service.stop(); return Promise.resolve();
36+
case 'replace': return this.service.replace(URI.revive(args[0]));
3637
case 'reset': return this.service.reset();
3738
case 'resetLocal': return this.service.resetLocal();
3839
case 'isFirstTimeSyncWithMerge': return this.service.isFirstTimeSyncWithMerge();

src/vs/platform/userDataSync/common/userDataSyncService.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,15 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
162162
}
163163
}
164164

165+
async replace(uri: URI): Promise<void> {
166+
await this.checkEnablement();
167+
for (const synchroniser of this.synchronisers) {
168+
if (await synchroniser.replace(uri)) {
169+
return;
170+
}
171+
}
172+
}
173+
165174
async stop(): Promise<void> {
166175
await this.checkEnablement();
167176
if (this.status === SyncStatus.Idle) {

src/vs/platform/userDataSync/test/common/synchronizer.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import * as assert from 'assert';
77
import { IUserDataSyncStoreService, SyncResource, SyncStatus, IUserDataSyncEnablementService, ISyncPreviewResult } from 'vs/platform/userDataSync/common/userDataSync';
88
import { UserDataSyncClient, UserDataSyncTestServer } from 'vs/platform/userDataSync/test/common/userDataSyncClient';
99
import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
10-
import { AbstractSynchroniser, IRemoteUserData } from 'vs/platform/userDataSync/common/abstractSynchronizer';
10+
import { AbstractSynchroniser, IRemoteUserData, ISyncData } from 'vs/platform/userDataSync/common/abstractSynchronizer';
1111
import { Barrier } from 'vs/base/common/async';
1212
import { Emitter, Event } from 'vs/base/common/event';
1313

@@ -39,6 +39,8 @@ class TestSynchroniser extends AbstractSynchroniser {
3939
return this.syncResult.status || SyncStatus.Idle;
4040
}
4141

42+
protected async performReplace(syncData: ISyncData, remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise<void> { }
43+
4244
async apply(ref: string): Promise<void> {
4345
ref = await this.userDataSyncStoreService.write(this.resource, '', ref);
4446
await this.updateLastSyncUserData({ ref, syncData: { content: '', version: this.version } });

0 commit comments

Comments
 (0)