diff --git a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts index a5c6050c00f..fa19ec45df4 100644 --- a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts +++ b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts @@ -9,7 +9,7 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { URI } from 'vs/base/common/uri'; import { SyncResource, SyncStatus, IUserData, IUserDataSyncStoreService, UserDataSyncErrorCode, UserDataSyncError, IUserDataSyncLogService, IUserDataSyncUtilService, - IUserDataSyncResourceEnablementService, IUserDataSyncBackupStoreService, Conflict, ISyncResourceHandle, USER_DATA_SYNC_SCHEME, ISyncPreview, IUserDataManifest, ISyncData, IRemoteUserData + IUserDataSyncResourceEnablementService, IUserDataSyncBackupStoreService, Conflict, ISyncResourceHandle, USER_DATA_SYNC_SCHEME, ISyncResourcePreview as IBaseSyncResourcePreview, IUserDataManifest, ISyncData, IRemoteUserData, PREVIEW_DIR_NAME } from 'vs/platform/userDataSync/common/userDataSync'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { joinPath, dirname, isEqual, basename } from 'vs/base/common/resources'; @@ -52,11 +52,17 @@ function isSyncData(thing: any): thing is ISyncData { return false; } +export interface ISyncResourcePreview extends IBaseSyncResourcePreview { + readonly remoteUserData: IRemoteUserData; + readonly lastSyncUserData: IRemoteUserData | null; +} + export abstract class AbstractSynchroniser extends Disposable { - private syncPreviewPromise: CancelablePromise | null = null; + private syncPreviewPromise: CancelablePromise | null = null; protected readonly syncFolder: URI; + protected readonly syncPreviewFolder: URI; private readonly currentMachineIdPromise: Promise; private _status: SyncStatus = SyncStatus.Idle; @@ -93,6 +99,7 @@ export abstract class AbstractSynchroniser extends Disposable { super(); this.syncResourceLogLabel = uppercaseFirstLetter(this.resource); this.syncFolder = joinPath(environmentService.userDataSyncHome, resource); + this.syncPreviewFolder = joinPath(this.syncFolder, PREVIEW_DIR_NAME); this.lastSyncResource = joinPath(this.syncFolder, `lastSync${this.resource}.json`); this.currentMachineIdPromise = getServiceMachineId(environmentService, fileService, storageService); } @@ -287,7 +294,7 @@ export abstract class AbstractSynchroniser extends Disposable { return this.getRemoteUserData(lastSyncUserData); } - async generateSyncPreview(): Promise { + async generateSyncPreview(): Promise { if (this.isEnabled()) { const lastSyncUserData = await this.getLastSyncUserData(); const remoteUserData = await this.getRemoteUserData(lastSyncUserData); @@ -360,7 +367,7 @@ export abstract class AbstractSynchroniser extends Disposable { } } - protected async getSyncPreviewInProgress(): Promise { + protected async getSyncPreviewInProgress(): Promise { return this.syncPreviewPromise ? this.syncPreviewPromise : null; } @@ -528,15 +535,15 @@ export abstract class AbstractSynchroniser extends Disposable { } protected abstract readonly version: number; - protected abstract generatePullPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, token: CancellationToken): Promise; - protected abstract generatePushPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, token: CancellationToken): Promise; - protected abstract generateReplacePreview(syncData: ISyncData, remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise; - protected abstract generatePreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, token: CancellationToken): Promise; - protected abstract updatePreviewWithConflict(preview: ISyncPreview, conflictResource: URI, content: string, token: CancellationToken): Promise; - protected abstract applyPreview(preview: ISyncPreview, forcePush: boolean): Promise; + protected abstract generatePullPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, token: CancellationToken): Promise; + protected abstract generatePushPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, token: CancellationToken): Promise; + protected abstract generateReplacePreview(syncData: ISyncData, remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise; + protected abstract generatePreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, token: CancellationToken): Promise; + protected abstract updatePreviewWithConflict(preview: ISyncResourcePreview, conflictResource: URI, content: string, token: CancellationToken): Promise; + protected abstract applyPreview(preview: ISyncResourcePreview, forcePush: boolean): Promise; } -export interface IFileSyncPreview extends ISyncPreview { +export interface IFileSyncPreview extends ISyncResourcePreview { readonly fileContent: IFileContent | null; readonly content: string | null; } @@ -568,7 +575,7 @@ export abstract class AbstractFileSynchroniser extends AbstractSynchroniser { } catch (e) { /* ignore */ } } - protected async getConflictContent(conflictResource: URI): Promise { + protected async resolvePreviewContent(conflictResource: URI): Promise { if (isEqual(this.remotePreviewResource, conflictResource) || isEqual(this.localPreviewResource, conflictResource)) { const syncPreview = await this.getSyncPreviewInProgress(); if (syncPreview) { diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts index 485fbcc685f..4cce10a6179 100644 --- a/src/vs/platform/userDataSync/common/extensionsSync.ts +++ b/src/vs/platform/userDataSync/common/extensionsSync.ts @@ -5,7 +5,7 @@ import { IUserDataSyncStoreService, ISyncExtension, IUserDataSyncLogService, IUserDataSynchroniser, SyncResource, IUserDataSyncResourceEnablementService, - IUserDataSyncBackupStoreService, ISyncResourceHandle, ISyncPreview, USER_DATA_SYNC_SCHEME, IRemoteUserData, ISyncData + IUserDataSyncBackupStoreService, ISyncResourceHandle, USER_DATA_SYNC_SCHEME, IRemoteUserData, ISyncData, IResourcePreview } from 'vs/platform/userDataSync/common/userDataSync'; import { Event } from 'vs/base/common/event'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -14,8 +14,8 @@ import { ExtensionType, IExtensionIdentifier } from 'vs/platform/extensions/comm import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IFileService } from 'vs/platform/files/common/files'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { merge, getIgnoredExtensions } from 'vs/platform/userDataSync/common/extensionsMerge'; -import { AbstractSynchroniser } from 'vs/platform/userDataSync/common/abstractSynchronizer'; +import { merge, getIgnoredExtensions, IMergeResult } from 'vs/platform/userDataSync/common/extensionsMerge'; +import { AbstractSynchroniser, ISyncResourcePreview } from 'vs/platform/userDataSync/common/abstractSynchronizer'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { URI } from 'vs/base/common/uri'; import { joinPath, dirname, basename, isEqual } from 'vs/base/common/resources'; @@ -25,9 +25,8 @@ import { compare } from 'vs/base/common/strings'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { CancellationToken } from 'vs/base/common/cancellation'; -interface IExtensionsSyncPreview extends ISyncPreview { +interface IExtensionsSyncPreview extends ISyncResourcePreview { readonly localExtensions: ISyncExtension[]; - readonly remoteUserData: IRemoteUserData; readonly lastSyncUserData: ILastSyncUserData | null; readonly added: ISyncExtension[]; readonly removed: IExtensionIdentifier[]; @@ -48,6 +47,8 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse */ protected readonly version: number = 3; protected isEnabled(): boolean { return super.isEnabled() && this.extensionGalleryService.isEnabled(); } + private readonly localPreviewResource: URI = joinPath(this.syncPreviewFolder, 'extensions.json'); + private readonly remotePreviewResource: URI = this.localPreviewResource.with({ scheme: USER_DATA_SYNC_SCHEME }); constructor( @IEnvironmentService environmentService: IEnvironmentService, @@ -79,14 +80,17 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse const ignoredExtensions = getIgnoredExtensions(installedExtensions, this.configurationService); if (remoteUserData.syncData !== null) { const remoteExtensions = await this.parseAndMigrateExtensions(remoteUserData.syncData); - const { added, updated, remote, removed } = merge(localExtensions, remoteExtensions, localExtensions, [], ignoredExtensions); + const mergeResult = merge(localExtensions, remoteExtensions, localExtensions, [], ignoredExtensions); + const { added, removed, updated, remote } = mergeResult; + const resourcePreviews: IResourcePreview[] = this.getResourcePreviews(mergeResult); return { remoteUserData, lastSyncUserData, added, removed, updated, remote, localExtensions, skippedExtensions: [], - hasLocalChanged: added.length > 0 || removed.length > 0 || updated.length > 0, - hasRemoteChanged: remote !== null, + hasLocalChanged: resourcePreviews.some(({ hasLocalChanged }) => hasLocalChanged), + hasRemoteChanged: resourcePreviews.some(({ hasRemoteChanged }) => hasRemoteChanged), hasConflicts: false, isLastSyncFromCurrentMachine: false, + resourcePreviews, }; } else { return { @@ -96,6 +100,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse hasRemoteChanged: false, hasConflicts: false, isLastSyncFromCurrentMachine: false, + resourcePreviews: [], }; } } @@ -104,13 +109,16 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse const installedExtensions = await this.extensionManagementService.getInstalled(); const localExtensions = this.getLocalExtensions(installedExtensions); const ignoredExtensions = getIgnoredExtensions(installedExtensions, this.configurationService); - const { added, removed, updated, remote } = merge(localExtensions, null, null, [], ignoredExtensions); + const mergeResult = merge(localExtensions, null, null, [], ignoredExtensions); + const { added, removed, updated, remote } = mergeResult; + const resourcePreviews: IResourcePreview[] = this.getResourcePreviews(mergeResult); return { added, removed, updated, remote, remoteUserData, localExtensions, skippedExtensions: [], lastSyncUserData, - hasLocalChanged: added.length > 0 || removed.length > 0 || updated.length > 0, - hasRemoteChanged: remote !== null, + hasLocalChanged: resourcePreviews.some(({ hasLocalChanged }) => hasLocalChanged), + hasRemoteChanged: resourcePreviews.some(({ hasRemoteChanged }) => hasRemoteChanged), isLastSyncFromCurrentMachine: false, hasConflicts: false, + resourcePreviews }; } @@ -119,14 +127,16 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse const localExtensions = this.getLocalExtensions(installedExtensions); const syncExtensions = await this.parseAndMigrateExtensions(syncData); const ignoredExtensions = getIgnoredExtensions(installedExtensions, this.configurationService); - const { added, updated, removed } = merge(localExtensions, syncExtensions, localExtensions, [], ignoredExtensions); - + const mergeResult = merge(localExtensions, syncExtensions, localExtensions, [], ignoredExtensions); + const { added, removed, updated } = mergeResult; + const resourcePreviews: IResourcePreview[] = this.getResourcePreviews(mergeResult); return { added, removed, updated, remote: syncExtensions, remoteUserData, localExtensions, skippedExtensions: [], lastSyncUserData, - hasLocalChanged: added.length > 0 || removed.length > 0 || updated.length > 0, + hasLocalChanged: resourcePreviews.some(({ hasLocalChanged }) => hasLocalChanged), hasRemoteChanged: true, isLastSyncFromCurrentMachine: false, hasConflicts: false, + resourcePreviews }; } @@ -153,7 +163,9 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse this.logService.trace(`${this.syncResourceLogLabel}: Remote extensions does not exist. Synchronizing extensions for the first time.`); } - const { added, removed, updated, remote } = merge(localExtensions, remoteExtensions, lastSyncExtensions, skippedExtensions, ignoredExtensions); + const mergeResult = merge(localExtensions, remoteExtensions, lastSyncExtensions, skippedExtensions, ignoredExtensions); + const { added, removed, updated, remote } = mergeResult; + const resourcePreviews: IResourcePreview[] = this.getResourcePreviews(mergeResult); return { added, @@ -164,10 +176,11 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse remoteUserData, localExtensions, lastSyncUserData, - hasLocalChanged: added.length > 0 || removed.length > 0 || updated.length > 0, - hasRemoteChanged: remote !== null, + hasLocalChanged: resourcePreviews.some(({ hasLocalChanged }) => hasLocalChanged), + hasRemoteChanged: resourcePreviews.some(({ hasRemoteChanged }) => hasRemoteChanged), isLastSyncFromCurrentMachine, - hasConflicts: false + hasConflicts: false, + resourcePreviews }; } @@ -202,6 +215,18 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse } } + private getResourcePreviews({ added, removed, updated, remote }: IMergeResult): IResourcePreview[] { + const hasLocalChanged = added.length > 0 || removed.length > 0 || updated.length > 0; + const hasRemoteChanged = remote !== null; + return [{ + hasLocalChanged, + hasConflicts: false, + hasRemoteChanged, + localResouce: ExtensionsSynchroniser.EXTENSIONS_DATA_URI, + remoteResource: this.remotePreviewResource + }]; + } + async getAssociatedResources({ uri }: ISyncResourceHandle): Promise<{ resource: URI, comparableResource?: URI }[]> { return [{ resource: joinPath(uri, 'extensions.json'), comparableResource: ExtensionsSynchroniser.EXTENSIONS_DATA_URI }]; } diff --git a/src/vs/platform/userDataSync/common/globalStateSync.ts b/src/vs/platform/userDataSync/common/globalStateSync.ts index bdc1617013d..77dc7dee3a0 100644 --- a/src/vs/platform/userDataSync/common/globalStateSync.ts +++ b/src/vs/platform/userDataSync/common/globalStateSync.ts @@ -5,7 +5,7 @@ import { IUserDataSyncStoreService, IUserDataSyncLogService, IGlobalState, SyncResource, IUserDataSynchroniser, IUserDataSyncResourceEnablementService, - IUserDataSyncBackupStoreService, ISyncResourceHandle, IStorageValue, ISyncPreview, USER_DATA_SYNC_SCHEME, IRemoteUserData, ISyncData + IUserDataSyncBackupStoreService, ISyncResourceHandle, IStorageValue, USER_DATA_SYNC_SCHEME, IRemoteUserData, ISyncData, IResourcePreview } from 'vs/platform/userDataSync/common/userDataSync'; import { VSBuffer } from 'vs/base/common/buffer'; import { Event } from 'vs/base/common/event'; @@ -14,9 +14,9 @@ import { dirname, joinPath, basename, isEqual } from 'vs/base/common/resources'; import { IFileService } from 'vs/platform/files/common/files'; import { IStringDictionary } from 'vs/base/common/collections'; import { edit } from 'vs/platform/userDataSync/common/content'; -import { merge } from 'vs/platform/userDataSync/common/globalStateMerge'; +import { merge, IMergeResult } from 'vs/platform/userDataSync/common/globalStateMerge'; import { parse } from 'vs/base/common/json'; -import { AbstractSynchroniser } from 'vs/platform/userDataSync/common/abstractSynchronizer'; +import { AbstractSynchroniser, ISyncResourcePreview } from 'vs/platform/userDataSync/common/abstractSynchronizer'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { URI } from 'vs/base/common/uri'; @@ -30,12 +30,11 @@ import { CancellationToken } from 'vs/base/common/cancellation'; const argvStoragePrefx = 'globalState.argv.'; const argvProperties: string[] = ['locale']; -interface IGlobalStateSyncPreview extends ISyncPreview { +interface IGlobalStateSyncPreview extends ISyncResourcePreview { readonly local: { added: IStringDictionary, removed: string[], updated: IStringDictionary }; readonly remote: IStringDictionary | null; readonly skippedStorageKeys: string[]; readonly localUserData: IGlobalState; - readonly remoteUserData: IRemoteUserData; readonly lastSyncUserData: ILastSyncUserData | null; } @@ -47,6 +46,8 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs private static readonly GLOBAL_STATE_DATA_URI = URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'globalState', path: `/current.json` }); protected readonly version: number = 1; + private readonly localPreviewResource: URI = joinPath(this.syncPreviewFolder, 'globalState.json'); + private readonly remotePreviewResource: URI = this.localPreviewResource.with({ scheme: USER_DATA_SYNC_SCHEME }); constructor( @IFileService fileService: IFileService, @@ -78,14 +79,17 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs const localGlobalState = await this.getLocalGlobalState(); if (remoteUserData.syncData !== null) { const remoteGlobalState: IGlobalState = JSON.parse(remoteUserData.syncData.content); - const { local, remote, skipped } = merge(localGlobalState.storage, remoteGlobalState.storage, null, this.getSyncStorageKeys(), lastSyncUserData?.skippedStorageKeys || [], this.logService); + const mergeResult = merge(localGlobalState.storage, remoteGlobalState.storage, null, this.getSyncStorageKeys(), lastSyncUserData?.skippedStorageKeys || [], this.logService); + const { local, remote, skipped } = mergeResult; + const resourcePreviews: IResourcePreview[] = this.getResourcePreviews(mergeResult); return { remoteUserData, lastSyncUserData, local, remote, localUserData: localGlobalState, skippedStorageKeys: skipped, - hasLocalChanged: Object.keys(local.added).length > 0 || Object.keys(local.updated).length > 0 || local.removed.length > 0, - hasRemoteChanged: remote !== null, + hasLocalChanged: resourcePreviews.some(({ hasLocalChanged }) => hasLocalChanged), + hasRemoteChanged: resourcePreviews.some(({ hasRemoteChanged }) => hasRemoteChanged), hasConflicts: false, isLastSyncFromCurrentMachine: false, + resourcePreviews }; } else { return { @@ -95,6 +99,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs hasRemoteChanged: false, hasConflicts: false, isLastSyncFromCurrentMachine: false, + resourcePreviews: [] }; } } @@ -107,21 +112,25 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs hasLocalChanged: false, hasRemoteChanged: true, isLastSyncFromCurrentMachine: false, - hasConflicts: false + hasConflicts: false, + resourcePreviews: this.getResourcePreviews({ local: { added: {}, removed: [], updated: {} }, remote: localUserData.storage, skipped: [] }) }; } protected async generateReplacePreview(syncData: ISyncData, remoteUserData: IRemoteUserData, lastSyncUserData: ILastSyncUserData | null): Promise { const localUserData = await this.getLocalGlobalState(); const syncGlobalState: IGlobalState = JSON.parse(syncData.content); - const { local, skipped } = merge(localUserData.storage, syncGlobalState.storage, localUserData.storage, this.getSyncStorageKeys(), lastSyncUserData?.skippedStorageKeys || [], this.logService); + const mergeResult = merge(localUserData.storage, syncGlobalState.storage, localUserData.storage, this.getSyncStorageKeys(), lastSyncUserData?.skippedStorageKeys || [], this.logService); + const { local, skipped } = mergeResult; + const resourcePreviews: IResourcePreview[] = this.getResourcePreviews(mergeResult); return { local, remote: syncGlobalState.storage, remoteUserData, localUserData, lastSyncUserData, skippedStorageKeys: skipped, - hasLocalChanged: Object.keys(local.added).length > 0 || Object.keys(local.updated).length > 0 || local.removed.length > 0, + hasLocalChanged: resourcePreviews.some(({ hasLocalChanged }) => hasLocalChanged), hasRemoteChanged: true, isLastSyncFromCurrentMachine: false, - hasConflicts: false + hasConflicts: false, + resourcePreviews: [], }; } @@ -145,15 +154,18 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs this.logService.trace(`${this.syncResourceLogLabel}: Remote ui state does not exist. Synchronizing ui state for the first time.`); } - const { local, remote, skipped } = merge(localGloablState.storage, remoteGlobalState ? remoteGlobalState.storage : null, lastSyncGlobalState ? lastSyncGlobalState.storage : null, this.getSyncStorageKeys(), lastSyncUserData?.skippedStorageKeys || [], this.logService); + const mergeResult = merge(localGloablState.storage, remoteGlobalState ? remoteGlobalState.storage : null, lastSyncGlobalState ? lastSyncGlobalState.storage : null, this.getSyncStorageKeys(), lastSyncUserData?.skippedStorageKeys || [], this.logService); + const { local, remote, skipped } = mergeResult; + const resourcePreviews: IResourcePreview[] = this.getResourcePreviews(mergeResult); return { local, remote, remoteUserData, localUserData: localGloablState, lastSyncUserData, skippedStorageKeys: skipped, - hasLocalChanged: Object.keys(local.added).length > 0 || Object.keys(local.updated).length > 0 || local.removed.length > 0, - hasRemoteChanged: remote !== null, + hasLocalChanged: resourcePreviews.some(({ hasLocalChanged }) => hasLocalChanged), + hasRemoteChanged: resourcePreviews.some(({ hasRemoteChanged }) => hasRemoteChanged), isLastSyncFromCurrentMachine, - hasConflicts: false + hasConflicts: false, + resourcePreviews }; } @@ -191,6 +203,18 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs } } + private getResourcePreviews({ local, remote }: IMergeResult): IResourcePreview[] { + const hasLocalChanged = Object.keys(local.added).length > 0 || Object.keys(local.updated).length > 0 || local.removed.length > 0; + const hasRemoteChanged = remote !== null; + return [{ + hasLocalChanged, + hasConflicts: false, + hasRemoteChanged, + localResouce: GlobalStateSynchroniser.GLOBAL_STATE_DATA_URI, + remoteResource: this.remotePreviewResource + }]; + } + async getAssociatedResources({ uri }: ISyncResourceHandle): Promise<{ resource: URI, comparableResource?: URI }[]> { return [{ resource: joinPath(uri, 'globalState.json'), comparableResource: GlobalStateSynchroniser.GLOBAL_STATE_DATA_URI }]; } diff --git a/src/vs/platform/userDataSync/common/keybindingsSync.ts b/src/vs/platform/userDataSync/common/keybindingsSync.ts index a7d2d1ad7fc..ba5c85f34c1 100644 --- a/src/vs/platform/userDataSync/common/keybindingsSync.ts +++ b/src/vs/platform/userDataSync/common/keybindingsSync.ts @@ -6,8 +6,8 @@ import { IFileService, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; import { UserDataSyncError, UserDataSyncErrorCode, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, SyncResource, - IUserDataSynchroniser, IUserDataSyncResourceEnablementService, IUserDataSyncBackupStoreService, USER_DATA_SYNC_SCHEME, PREVIEW_DIR_NAME, ISyncResourceHandle, - IRemoteUserData, ISyncData + IUserDataSynchroniser, IUserDataSyncResourceEnablementService, IUserDataSyncBackupStoreService, USER_DATA_SYNC_SCHEME, ISyncResourceHandle, + IRemoteUserData, ISyncData, IResourcePreview } from 'vs/platform/userDataSync/common/userDataSync'; import { merge } from 'vs/platform/userDataSync/common/keybindingsMerge'; import { VSBuffer } from 'vs/base/common/buffer'; @@ -35,7 +35,7 @@ interface ISyncContent { export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implements IUserDataSynchroniser { protected readonly version: number = 1; - protected readonly localPreviewResource: URI = joinPath(this.syncFolder, PREVIEW_DIR_NAME, 'keybindings.json'); + protected readonly localPreviewResource: URI = joinPath(this.syncPreviewFolder, 'keybindings.json'); protected readonly remotePreviewResource: URI = this.localPreviewResource.with({ scheme: USER_DATA_SYNC_SCHEME }); constructor( @@ -57,45 +57,81 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem const fileContent = await this.getLocalFileContent(); const content = remoteUserData.syncData !== null ? this.getKeybindingsContentFromSyncContent(remoteUserData.syncData.content) : null; const hasLocalChanged = content !== null; + const hasRemoteChanged = false; + const hasConflicts = false; + + const resourcePreviews: IResourcePreview[] = [{ + hasConflicts, + hasLocalChanged, + hasRemoteChanged, + localResouce: this.file, + remoteResource: this.remotePreviewResource, + }]; + return { fileContent, remoteUserData, lastSyncUserData, content, - hasConflicts: false, + hasConflicts, hasLocalChanged, - hasRemoteChanged: false, - isLastSyncFromCurrentMachine: false + hasRemoteChanged, + isLastSyncFromCurrentMachine: false, + resourcePreviews }; } protected async generatePushPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, token: CancellationToken): Promise { const fileContent = await this.getLocalFileContent(); const content: string | null = fileContent ? fileContent.value.toString() : null; + const hasLocalChanged = false; + const hasRemoteChanged = content !== null; + const hasConflicts = false; + + const resourcePreviews: IResourcePreview[] = [{ + hasConflicts, + hasLocalChanged, + hasRemoteChanged, + localResouce: this.file, + remoteResource: this.remotePreviewResource, + }]; return { fileContent, remoteUserData, lastSyncUserData, content, - hasLocalChanged: false, - hasRemoteChanged: content !== null, - hasConflicts: false, - isLastSyncFromCurrentMachine: false + hasLocalChanged, + hasRemoteChanged, + hasConflicts, + isLastSyncFromCurrentMachine: false, + resourcePreviews }; } protected async generateReplacePreview(syncData: ISyncData, remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise { const fileContent = await this.getLocalFileContent(); const content = this.getKeybindingsContentFromSyncContent(syncData.content); + const hasLocalChanged = content !== null; + const hasRemoteChanged = content !== null; + const hasConflicts = false; + + const resourcePreviews: IResourcePreview[] = [{ + hasConflicts, + hasLocalChanged, + hasRemoteChanged, + localResouce: this.file, + remoteResource: this.remotePreviewResource, + }]; return { fileContent, remoteUserData, lastSyncUserData, content, - hasConflicts: false, - hasLocalChanged: content !== null, - hasRemoteChanged: content !== null, - isLastSyncFromCurrentMachine: false + hasConflicts, + hasLocalChanged, + hasRemoteChanged, + isLastSyncFromCurrentMachine: false, + resourcePreviews }; } @@ -154,8 +190,16 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem } this.setConflicts(hasConflicts && !token.isCancellationRequested ? [{ local: this.localPreviewResource, remote: this.remotePreviewResource }] : []); + const resourcePreviews: IResourcePreview[] = [{ + hasConflicts, + hasLocalChanged, + hasRemoteChanged, + localResouce: this.file, + remoteResource: this.remotePreviewResource, + previewResource: this.localPreviewResource + }]; - return { fileContent, remoteUserData, lastSyncUserData, content, hasLocalChanged, hasRemoteChanged, hasConflicts, isLastSyncFromCurrentMachine }; + return { fileContent, remoteUserData, lastSyncUserData, content, hasLocalChanged, hasRemoteChanged, hasConflicts, isLastSyncFromCurrentMachine, resourcePreviews }; } protected async updatePreviewWithConflict(preview: IFileSyncPreview, conflictResource: URI, conflictContent: string, token: CancellationToken): Promise { @@ -229,7 +273,7 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem async resolveContent(uri: URI): Promise { if (isEqual(this.remotePreviewResource, uri)) { - return this.getConflictContent(uri); + return this.resolvePreviewContent(uri); } let content = await super.resolveContent(uri); if (content) { @@ -248,8 +292,8 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem return null; } - protected async getConflictContent(conflictResource: URI): Promise { - const content = await super.getConflictContent(conflictResource); + protected async resolvePreviewContent(conflictResource: URI): Promise { + const content = await super.resolvePreviewContent(conflictResource); return content !== null ? this.getKeybindingsContentFromSyncContent(content) : null; } diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts index e5f9146a198..f5dea9f8686 100644 --- a/src/vs/platform/userDataSync/common/settingsSync.ts +++ b/src/vs/platform/userDataSync/common/settingsSync.ts @@ -6,8 +6,8 @@ import { IFileService, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; import { UserDataSyncError, UserDataSyncErrorCode, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, CONFIGURATION_SYNC_STORE_KEY, - SyncResource, IUserDataSyncResourceEnablementService, IUserDataSyncBackupStoreService, USER_DATA_SYNC_SCHEME, PREVIEW_DIR_NAME, ISyncResourceHandle, IUserDataSynchroniser, - IRemoteUserData, ISyncData + SyncResource, IUserDataSyncResourceEnablementService, IUserDataSyncBackupStoreService, USER_DATA_SYNC_SCHEME, ISyncResourceHandle, IUserDataSynchroniser, + IRemoteUserData, ISyncData, IResourcePreview } from 'vs/platform/userDataSync/common/userDataSync'; import { VSBuffer } from 'vs/base/common/buffer'; import { localize } from 'vs/nls'; @@ -39,7 +39,7 @@ function isSettingsSyncContent(thing: any): thing is ISettingsSyncContent { export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implements IUserDataSynchroniser { protected readonly version: number = 1; - protected readonly localPreviewResource: URI = joinPath(this.syncFolder, PREVIEW_DIR_NAME, 'settings.json'); + protected readonly localPreviewResource: URI = joinPath(this.syncPreviewFolder, 'settings.json'); protected readonly remotePreviewResource: URI = this.localPreviewResource.with({ scheme: USER_DATA_SYNC_SCHEME }); constructor( @@ -71,15 +71,28 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement content = updateIgnoredSettings(remoteSettingsSyncContent.settings, fileContent ? fileContent.value.toString() : '{}', ignoredSettings, formatUtils); } + const hasLocalChanged = content !== null; + const hasRemoteChanged = false; + const hasConflicts = false; + + const resourcePreviews: IResourcePreview[] = [{ + hasConflicts, + hasLocalChanged, + hasRemoteChanged, + localResouce: this.file, + remoteResource: this.remotePreviewResource, + }]; + return { fileContent, remoteUserData, lastSyncUserData, content, - hasLocalChanged: content !== null, - hasRemoteChanged: false, - hasConflicts: false, - isLastSyncFromCurrentMachine: false + hasLocalChanged, + hasRemoteChanged, + hasConflicts, + isLastSyncFromCurrentMachine: false, + resourcePreviews }; } @@ -95,15 +108,28 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement content = updateIgnoredSettings(fileContent.value.toString(), '{}', ignoredSettings, formatUtils); } + const hasLocalChanged = false; + const hasRemoteChanged = content !== null; + const hasConflicts = false; + + const resourcePreviews: IResourcePreview[] = [{ + hasConflicts, + hasLocalChanged, + hasRemoteChanged, + localResouce: this.file, + remoteResource: this.remotePreviewResource, + }]; + return { fileContent, remoteUserData, lastSyncUserData, content, - hasLocalChanged: false, - hasRemoteChanged: content !== null, - hasConflicts: false, - isLastSyncFromCurrentMachine: false + hasLocalChanged, + hasRemoteChanged, + hasConflicts, + isLastSyncFromCurrentMachine: false, + resourcePreviews }; } @@ -119,14 +145,27 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement content = updateIgnoredSettings(settingsSyncContent.settings, fileContent ? fileContent.value.toString() : '{}', ignoredSettings, formatUtils); } + const hasLocalChanged = content !== null; + const hasRemoteChanged = content !== null; + const hasConflicts = false; + + const resourcePreviews: IResourcePreview[] = [{ + hasConflicts, + hasLocalChanged, + hasRemoteChanged, + localResouce: this.file, + remoteResource: this.remotePreviewResource, + }]; + return { fileContent, remoteUserData, lastSyncUserData, content, - hasLocalChanged: content !== null, - hasRemoteChanged: content !== null, - hasConflicts: false, + hasLocalChanged, + hasRemoteChanged, + hasConflicts, + resourcePreviews, isLastSyncFromCurrentMachine: false }; } @@ -177,8 +216,16 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement } this.setConflicts(hasConflicts && !token.isCancellationRequested ? [{ local: this.localPreviewResource, remote: this.remotePreviewResource }] : []); + const resourcePreviews: IResourcePreview[] = [{ + hasConflicts, + hasLocalChanged, + hasRemoteChanged, + localResouce: this.file, + remoteResource: this.remotePreviewResource, + previewResource: this.localPreviewResource + }]; - return { fileContent, remoteUserData, lastSyncUserData, content, hasLocalChanged, hasRemoteChanged, hasConflicts, isLastSyncFromCurrentMachine }; + return { fileContent, remoteUserData, lastSyncUserData, content, hasLocalChanged, hasRemoteChanged, hasConflicts, isLastSyncFromCurrentMachine, resourcePreviews }; } protected async updatePreviewWithConflict(preview: IFileSyncPreview, conflictResource: URI, conflictContent: string, token: CancellationToken): Promise { @@ -256,7 +303,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement async resolveContent(uri: URI): Promise { if (isEqual(this.remotePreviewResource, uri)) { - return this.getConflictContent(uri); + return this.resolvePreviewContent(uri); } let content = await super.resolveContent(uri); if (content) { @@ -278,8 +325,8 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement return null; } - protected async getConflictContent(conflictResource: URI): Promise { - let content = await super.getConflictContent(conflictResource); + protected async resolvePreviewContent(conflictResource: URI): Promise { + let content = await super.resolvePreviewContent(conflictResource); if (content !== null) { const settingsSyncContent = this.parseSettingsSyncContent(content); content = settingsSyncContent ? settingsSyncContent.settings : null; diff --git a/src/vs/platform/userDataSync/common/snippetsSync.ts b/src/vs/platform/userDataSync/common/snippetsSync.ts index 623175fafdb..e73a65f5411 100644 --- a/src/vs/platform/userDataSync/common/snippetsSync.ts +++ b/src/vs/platform/userDataSync/common/snippetsSync.ts @@ -5,22 +5,22 @@ import { IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSynchroniser, SyncResource, IUserDataSyncResourceEnablementService, IUserDataSyncBackupStoreService, - Conflict, USER_DATA_SYNC_SCHEME, PREVIEW_DIR_NAME, ISyncResourceHandle, IRemoteUserData, ISyncData, ISyncPreview + Conflict, USER_DATA_SYNC_SCHEME, ISyncResourceHandle, IRemoteUserData, ISyncData, IResourcePreview } from 'vs/platform/userDataSync/common/userDataSync'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService, FileChangesEvent, IFileStat, IFileContent, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { AbstractSynchroniser } from 'vs/platform/userDataSync/common/abstractSynchronizer'; +import { AbstractSynchroniser, ISyncResourcePreview } from 'vs/platform/userDataSync/common/abstractSynchronizer'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IStringDictionary } from 'vs/base/common/collections'; import { URI } from 'vs/base/common/uri'; import { joinPath, extname, relativePath, isEqualOrParent, isEqual, basename, dirname } from 'vs/base/common/resources'; import { VSBuffer } from 'vs/base/common/buffer'; -import { merge } from 'vs/platform/userDataSync/common/snippetsMerge'; +import { merge, IMergeResult } from 'vs/platform/userDataSync/common/snippetsMerge'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IStorageService } from 'vs/platform/storage/common/storage'; -interface ISinppetsSyncPreview extends ISyncPreview { +interface ISinppetsSyncPreview extends ISyncResourcePreview { readonly local: IStringDictionary; readonly added: IStringDictionary; readonly updated: IStringDictionary; @@ -34,7 +34,6 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD protected readonly version: number = 1; private readonly snippetsFolder: URI; - private readonly snippetsPreviewFolder: URI; constructor( @IEnvironmentService environmentService: IEnvironmentService, @@ -49,7 +48,6 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD ) { super(SyncResource.Snippets, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncResourceEnablementService, telemetryService, logService, configurationService); this.snippetsFolder = environmentService.snippetsHome; - this.snippetsPreviewFolder = joinPath(this.syncFolder, PREVIEW_DIR_NAME); this._register(this.fileService.watch(environmentService.userRoamingDataHome)); this._register(this.fileService.watch(this.snippetsFolder)); this._register(this.fileService.onDidFilesChange(e => this.onFileChanges(e))); @@ -67,7 +65,8 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD const local = await this.getSnippetsFileContents(); const localSnippets = this.toSnippetsContents(local); const remoteSnippets = this.parseSnippets(remoteUserData.syncData); - const { added, updated, remote, removed } = merge(localSnippets, remoteSnippets, localSnippets); + const mergeResult = merge(localSnippets, remoteSnippets, localSnippets); + const { added, updated, remote, removed } = mergeResult; return { remoteUserData, lastSyncUserData, added, removed, updated, remote, local, @@ -75,6 +74,7 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD hasRemoteChanged: remote !== null, conflicts: [], resolvedConflicts: {}, hasConflicts: false, isLastSyncFromCurrentMachine: false, + resourcePreviews: this.getResourcePreviews(mergeResult) }; } else { return { @@ -84,6 +84,7 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD hasRemoteChanged: false, conflicts: [], resolvedConflicts: {}, hasConflicts: false, isLastSyncFromCurrentMachine: false, + resourcePreviews: [] }; } } @@ -91,13 +92,15 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD protected async generatePushPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, token: CancellationToken): Promise { const local = await this.getSnippetsFileContents(); const localSnippets = this.toSnippetsContents(local); - const { added, removed, updated, remote } = merge(localSnippets, null, null); + const mergeResult = merge(localSnippets, null, null); + const { added, updated, remote, removed } = mergeResult; return { added, removed, updated, remote, remoteUserData, local, lastSyncUserData, conflicts: [], resolvedConflicts: {}, hasLocalChanged: Object.keys(added).length > 0 || removed.length > 0 || Object.keys(updated).length > 0, hasRemoteChanged: remote !== null, isLastSyncFromCurrentMachine: false, hasConflicts: false, + resourcePreviews: this.getResourcePreviews(mergeResult) }; } @@ -105,12 +108,14 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD const local = await this.getSnippetsFileContents(); const localSnippets = this.toSnippetsContents(local); const snippets = this.parseSnippets(syncData); - const { added, updated, removed } = merge(localSnippets, snippets, localSnippets); + const mergeResult = merge(localSnippets, snippets, localSnippets); + const { added, updated, removed } = mergeResult; return { added, removed, updated, remote: snippets, remoteUserData, local, lastSyncUserData, conflicts: [], resolvedConflicts: {}, hasConflicts: false, hasLocalChanged: Object.keys(added).length > 0 || removed.length > 0 || Object.keys(updated).length > 0, hasRemoteChanged: true, isLastSyncFromCurrentMachine: false, + resourcePreviews: this.getResourcePreviews(mergeResult) }; } @@ -140,10 +145,11 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD } const mergeResult = merge(localSnippets, remoteSnippets, lastSyncSnippets, resolvedConflicts); + const resourcePreviews = this.getResourcePreviews(mergeResult); const conflicts: Conflict[] = []; for (const key of mergeResult.conflicts) { - const localPreview = joinPath(this.snippetsPreviewFolder, key); + const localPreview = joinPath(this.syncPreviewFolder, key); conflicts.push({ local: localPreview, remote: localPreview.with({ scheme: USER_DATA_SYNC_SCHEME }) }); const content = local[key]; if (!token.isCancellationRequested) { @@ -177,14 +183,15 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD resolvedConflicts, hasLocalChanged: Object.keys(mergeResult.added).length > 0 || mergeResult.removed.length > 0 || Object.keys(mergeResult.updated).length > 0, hasRemoteChanged: mergeResult.remote !== null, - isLastSyncFromCurrentMachine + isLastSyncFromCurrentMachine, + resourcePreviews }; } protected async updatePreviewWithConflict(preview: ISinppetsSyncPreview, conflictResource: URI, content: string, token: CancellationToken): Promise { const conflict = this.conflicts.filter(({ local, remote }) => isEqual(local, conflictResource) || isEqual(remote, conflictResource))[0]; if (conflict) { - const key = relativePath(this.snippetsPreviewFolder, conflict.local)!; + const key = relativePath(this.syncPreviewFolder, conflict.local)!; preview.resolvedConflicts[key] = content || null; preview = await this.doGeneratePreview(preview.local, preview.remoteUserData, preview.lastSyncUserData, preview.resolvedConflicts, token); } @@ -221,6 +228,47 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD } + private getResourcePreviews(mergeResult: IMergeResult): IResourcePreview[] { + const resourcePreviews: IResourcePreview[] = []; + for (const key of Object.keys(mergeResult.added)) { + resourcePreviews.push({ + remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME }), + hasConflicts: false, + hasLocalChanged: true, + hasRemoteChanged: false + }); + } + for (const key of Object.keys(mergeResult.updated)) { + resourcePreviews.push({ + remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME }), + localResouce: joinPath(this.snippetsFolder, key), + hasConflicts: false, + hasLocalChanged: true, + hasRemoteChanged: true + }); + } + for (const key of mergeResult.removed) { + resourcePreviews.push({ + localResouce: joinPath(this.snippetsFolder, key), + hasConflicts: false, + hasLocalChanged: true, + hasRemoteChanged: false + }); + } + for (const key of mergeResult.conflicts) { + resourcePreviews.push({ + localResouce: joinPath(this.snippetsFolder, key), + remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME }), + previewResource: joinPath(this.syncPreviewFolder, key), + hasConflicts: true, + hasLocalChanged: true, + hasRemoteChanged: true + }); + } + + return resourcePreviews; + } + async stop(): Promise { await this.clearConflicts(); return super.stop(); @@ -246,8 +294,8 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD } async resolveContent(uri: URI): Promise { - if (isEqualOrParent(uri.with({ scheme: this.syncFolder.scheme }), this.snippetsPreviewFolder)) { - return this.getConflictContent(uri); + if (isEqualOrParent(uri.with({ scheme: this.syncFolder.scheme }), this.syncPreviewFolder)) { + return this.resolvePreviewContent(uri); } let content = await super.resolveContent(uri); if (content) { @@ -264,11 +312,11 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD return null; } - private async getConflictContent(conflictResource: URI): Promise { + private async resolvePreviewContent(conflictResource: URI): Promise { const syncPreview = await this.getSyncPreviewInProgress(); if (syncPreview) { - const key = relativePath(this.snippetsPreviewFolder, conflictResource.with({ scheme: this.snippetsPreviewFolder.scheme }))!; - if (conflictResource.scheme === this.snippetsPreviewFolder.scheme) { + const key = relativePath(this.syncPreviewFolder, conflictResource.with({ scheme: this.syncPreviewFolder.scheme }))!; + if (conflictResource.scheme === this.syncPreviewFolder.scheme) { return (syncPreview as ISinppetsSyncPreview).local[key] ? (syncPreview as ISinppetsSyncPreview).local[key].value.toString() : null; } else if (syncPreview.remoteUserData && syncPreview.remoteUserData.syncData) { const snippets = this.parseSnippets(syncPreview.remoteUserData.syncData); diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index 82ac0b7c177..2183252b50d 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -293,13 +293,21 @@ export interface ISyncData { content: string; } -export interface ISyncPreview { - readonly remoteUserData: IRemoteUserData; - readonly lastSyncUserData: IRemoteUserData | null; +export interface IResourcePreview { + readonly remoteResource?: URI; + readonly localResouce?: URI; + readonly previewResource?: URI; + readonly hasLocalChanged: boolean; + readonly hasRemoteChanged: boolean; + readonly hasConflicts: boolean; +} + +export interface ISyncResourcePreview { readonly isLastSyncFromCurrentMachine: boolean; readonly hasLocalChanged: boolean; readonly hasRemoteChanged: boolean; readonly hasConflicts: boolean; + readonly resourcePreviews: IResourcePreview[]; } export interface IUserDataSynchroniser { @@ -317,7 +325,7 @@ export interface IUserDataSynchroniser { replace(uri: URI): Promise; stop(): Promise; - generateSyncPreview(): Promise + generateSyncPreview(): Promise hasPreviouslySynced(): Promise hasLocalData(): Promise; resetLocal(): Promise; diff --git a/src/vs/platform/userDataSync/test/common/synchronizer.test.ts b/src/vs/platform/userDataSync/test/common/synchronizer.test.ts index f9eb51db8eb..df32970dea9 100644 --- a/src/vs/platform/userDataSync/test/common/synchronizer.test.ts +++ b/src/vs/platform/userDataSync/test/common/synchronizer.test.ts @@ -4,16 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { IUserDataSyncStoreService, SyncResource, SyncStatus, IUserDataSyncResourceEnablementService, IRemoteUserData, ISyncData, ISyncPreview } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncStoreService, SyncResource, SyncStatus, IUserDataSyncResourceEnablementService, IRemoteUserData, ISyncData } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncClient, UserDataSyncTestServer } from 'vs/platform/userDataSync/test/common/userDataSyncClient'; import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; -import { AbstractSynchroniser } from 'vs/platform/userDataSync/common/abstractSynchronizer'; +import { AbstractSynchroniser, ISyncResourcePreview } from 'vs/platform/userDataSync/common/abstractSynchronizer'; import { Barrier } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { CancellationToken } from 'vs/base/common/cancellation'; import { URI } from 'vs/base/common/uri'; -interface ITestSyncPreview extends ISyncPreview { +interface ITestSyncPreview extends ISyncResourcePreview { ref?: string; } @@ -41,25 +41,25 @@ class TestSynchroniser extends AbstractSynchroniser { } protected async generatePullPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, token: CancellationToken): Promise { - return { hasLocalChanged: false, hasRemoteChanged: false, isLastSyncFromCurrentMachine: false, hasConflicts: this.syncResult.hasConflicts, remoteUserData, lastSyncUserData }; + return { hasLocalChanged: false, hasRemoteChanged: false, isLastSyncFromCurrentMachine: false, hasConflicts: this.syncResult.hasConflicts, remoteUserData, lastSyncUserData, resourcePreviews: [] }; } protected async generatePushPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, token: CancellationToken): Promise { - return { hasLocalChanged: false, hasRemoteChanged: false, isLastSyncFromCurrentMachine: false, hasConflicts: this.syncResult.hasConflicts, remoteUserData, lastSyncUserData }; + return { hasLocalChanged: false, hasRemoteChanged: false, isLastSyncFromCurrentMachine: false, hasConflicts: this.syncResult.hasConflicts, remoteUserData, lastSyncUserData, resourcePreviews: [] }; } protected async generateReplacePreview(syncData: ISyncData, remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise { - return { hasLocalChanged: false, hasRemoteChanged: false, isLastSyncFromCurrentMachine: false, hasConflicts: this.syncResult.hasConflicts, remoteUserData, lastSyncUserData }; + return { hasLocalChanged: false, hasRemoteChanged: false, isLastSyncFromCurrentMachine: false, hasConflicts: this.syncResult.hasConflicts, remoteUserData, lastSyncUserData, resourcePreviews: [] }; } protected async generatePreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, token: CancellationToken): Promise { if (this.syncResult.hasError) { throw new Error('failed'); } - return { ref: remoteUserData.ref, hasLocalChanged: false, hasRemoteChanged: false, isLastSyncFromCurrentMachine: false, hasConflicts: this.syncResult.hasConflicts, remoteUserData, lastSyncUserData }; + return { ref: remoteUserData.ref, hasLocalChanged: false, hasRemoteChanged: false, isLastSyncFromCurrentMachine: false, hasConflicts: this.syncResult.hasConflicts, remoteUserData, lastSyncUserData, resourcePreviews: [] }; } - protected async updatePreviewWithConflict(preview: ISyncPreview, conflictResource: URI, conflictContent: string): Promise { + protected async updatePreviewWithConflict(preview: ISyncResourcePreview, conflictResource: URI, conflictContent: string): Promise { return preview; }