From b089ff140a16195f4ff76be80e04ae892a671555 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 20 Mar 2020 16:31:37 +0100 Subject: [PATCH] #86678 Enhance back up views - Show individual resources --- .../sharedProcess/sharedProcessMain.ts | 10 +- .../common/abstractSynchronizer.ts | 41 ++++-- .../userDataSync/common/extensionsSync.ts | 39 +++--- .../userDataSync/common/globalStateSync.ts | 42 +++--- .../userDataSync/common/keybindingsSync.ts | 48 ++++--- .../userDataSync/common/settingsSync.ts | 64 +++++---- .../userDataSync/common/snippetsSync.ts | 77 ++++++----- .../userDataSync/common/userDataSync.ts | 37 ++---- .../userDataSync/common/userDataSyncIpc.ts | 46 +------ .../common/userDataSyncService.ts | 30 +++-- .../userDataSync/browser/userDataSync.ts | 4 +- .../userDataSync/browser/userDataSyncView.ts | 124 ++++++++---------- .../userDataSyncBackupStoreService.ts | 37 ------ .../electron-browser/userDataSyncService.ts | 29 +++- .../userDataSyncStoreService.ts | 58 -------- src/vs/workbench/workbench.desktop.main.ts | 2 - 16 files changed, 278 insertions(+), 410 deletions(-) delete mode 100644 src/vs/workbench/services/userDataSync/electron-browser/userDataSyncBackupStoreService.ts delete mode 100644 src/vs/workbench/services/userDataSync/electron-browser/userDataSyncStoreService.ts diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index e8532f91590..eaa08a1d044 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -52,7 +52,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; -import { UserDataSyncChannel, UserDataSyncUtilServiceClient, UserDataAutoSyncChannel, UserDataSyncStoreServiceChannel, UserDataSyncBackupStoreServiceChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc'; +import { UserDataSyncChannel, UserDataSyncUtilServiceClient, UserDataAutoSyncChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc'; import { IElectronService } from 'vs/platform/electron/node/electron'; import { LoggerService } from 'vs/platform/log/node/loggerService'; import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog'; @@ -219,14 +219,6 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat const authTokenChannel = new AuthenticationTokenServiceChannel(authTokenService); server.registerChannel('authToken', authTokenChannel); - const userDataSyncStoreService = accessor.get(IUserDataSyncStoreService); - const userDataSyncStoreServiceChannel = new UserDataSyncStoreServiceChannel(userDataSyncStoreService); - server.registerChannel('userDataSyncStoreService', userDataSyncStoreServiceChannel); - - const userDataSyncBackupStoreService = accessor.get(IUserDataSyncBackupStoreService); - const userDataSyncBackupStoreServiceChannel = new UserDataSyncBackupStoreServiceChannel(userDataSyncBackupStoreService); - server.registerChannel('userDataSyncBackupStoreService', userDataSyncBackupStoreServiceChannel); - const userDataSyncService = accessor.get(IUserDataSyncService); const userDataSyncChannel = new UserDataSyncChannel(userDataSyncService); server.registerChannel('userDataSync', userDataSyncChannel); diff --git a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts index f00644b7772..8ecbd09d7ac 100644 --- a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts +++ b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts @@ -7,9 +7,9 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IFileService, IFileContent, FileChangesEvent, FileOperationResult, FileOperationError } from 'vs/platform/files/common/files'; import { VSBuffer } from 'vs/base/common/buffer'; import { URI } from 'vs/base/common/uri'; -import { SyncResource, SyncStatus, IUserData, IUserDataSyncStoreService, UserDataSyncErrorCode, UserDataSyncError, IUserDataSyncLogService, IUserDataSyncUtilService, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, Conflict } from 'vs/platform/userDataSync/common/userDataSync'; +import { SyncResource, SyncStatus, IUserData, IUserDataSyncStoreService, UserDataSyncErrorCode, UserDataSyncError, IUserDataSyncLogService, IUserDataSyncUtilService, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, Conflict, ISyncResourceHandle, USER_DATA_SYNC_SCHEME } from 'vs/platform/userDataSync/common/userDataSync'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { joinPath, dirname, isEqual } from 'vs/base/common/resources'; +import { joinPath, dirname, isEqual, basename } from 'vs/base/common/resources'; import { CancelablePromise } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -173,18 +173,34 @@ export abstract class AbstractSynchroniser extends Disposable { return !!lastSyncData; } - async getConflictContent(conflictResource: URI): Promise { - return null; + async getRemoteSyncResourceHandles(): Promise { + const handles = await this.userDataSyncStoreService.getAllRefs(this.resource); + return handles.map(({ created, ref }) => ({ created, uri: this.toRemoteBackupResource(ref) })); + } + + async getLocalSyncResourceHandles(): Promise { + const handles = await this.userDataSyncBackupStoreService.getAllRefs(this.resource); + return handles.map(({ created, ref }) => ({ created, uri: this.toLocalBackupResource(ref) })); } - async getRemoteContent(ref?: string): Promise { - const refOrLastSyncUserData: string | IRemoteUserData | null = ref || await this.getLastSyncUserData(); - const { content } = await this.getUserData(refOrLastSyncUserData); - return content; + private toRemoteBackupResource(ref: string): URI { + return URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote-backup', path: `/${this.resource}/${ref}` }); } - async getLocalBackupContent(ref?: string): Promise { - return this.userDataSyncBackupStoreService.resolveContent(this.resource, ref); + private toLocalBackupResource(ref: string): URI { + return URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local-backup', path: `/${this.resource}/${ref}` }); + } + + async resolveContent(uri: URI): Promise { + const ref = basename(uri); + if (isEqual(uri, this.toRemoteBackupResource(ref))) { + const { content } = await this.getUserData(ref); + return content; + } + if (isEqual(uri, this.toLocalBackupResource(ref))) { + return this.userDataSyncBackupStoreService.resolveContent(this.resource, ref); + } + return null; } async resetLocal(): Promise { @@ -265,9 +281,10 @@ export abstract class AbstractSynchroniser extends Disposable { return this.userDataSyncBackupStoreService.backup(this.resource, JSON.stringify(syncData)); } + abstract stop(): Promise; + protected abstract readonly version: number; protected abstract performSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise; - abstract stop(): Promise; } export interface IFileSyncPreviewResult { @@ -310,7 +327,7 @@ export abstract class AbstractFileSynchroniser extends AbstractSynchroniser { this.setStatus(SyncStatus.Idle); } - async getConflictContent(conflictResource: URI): Promise { + protected async getConflictContent(conflictResource: URI): Promise { if (isEqual(this.remotePreviewResource, conflictResource) || isEqual(this.localPreviewResource, conflictResource)) { if (this.syncPreviewResultPromise) { const result = await this.syncPreviewResultPromise; diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts index 01ad8d03ab0..70b478c1828 100644 --- a/src/vs/platform/userDataSync/common/extensionsSync.ts +++ b/src/vs/platform/userDataSync/common/extensionsSync.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { SyncStatus, IUserDataSyncStoreService, ISyncExtension, IUserDataSyncLogService, IUserDataSynchroniser, SyncResource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync'; +import { SyncStatus, IUserDataSyncStoreService, ISyncExtension, IUserDataSyncLogService, IUserDataSynchroniser, SyncResource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, ISyncResourceHandle } from 'vs/platform/userDataSync/common/userDataSync'; import { Event } from 'vs/base/common/event'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IExtensionManagementService, IExtensionGalleryService, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; @@ -16,6 +16,9 @@ import { isNonEmptyArray } from 'vs/base/common/arrays'; import { AbstractSynchroniser, IRemoteUserData, ISyncData } 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 } from 'vs/base/common/resources'; +import { format } from 'vs/base/common/jsonFormatter'; +import { applyEdits } from 'vs/base/common/jsonEdit'; interface ISyncPreviewResult { readonly localExtensions: ISyncExtension[]; @@ -120,28 +123,24 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse async stop(): Promise { } - async getRemoteContent(ref?: string, fragment?: string): Promise { - const content = await super.getRemoteContent(ref); - if (content !== null && fragment) { - return this.getFragment(content, fragment); - } - return content; + async getAssociatedResources({ uri }: ISyncResourceHandle): Promise<{ resource: URI, comparableResource?: URI }[]> { + return [{ resource: joinPath(uri, 'extensions.json') }]; } - async getLocalBackupContent(ref?: string, fragment?: string): Promise { - let content = await super.getLocalBackupContent(ref); - if (content !== null && fragment) { - return this.getFragment(content, fragment); + async resolveContent(uri: URI): Promise { + let content = await super.resolveContent(uri); + if (content) { + return content; } - return content; - } - - private getFragment(content: string, fragment: string): string | null { - const syncData = this.parseSyncData(content); - if (syncData) { - switch (fragment) { - case 'extensions': - return syncData.content; + content = await super.resolveContent(dirname(uri)); + if (content) { + const syncData = this.parseSyncData(content); + if (syncData) { + switch (basename(uri)) { + case 'extensions.json': + const edits = format(syncData.content, undefined, {}); + return applyEdits(syncData.content, edits); + } } } return null; diff --git a/src/vs/platform/userDataSync/common/globalStateSync.ts b/src/vs/platform/userDataSync/common/globalStateSync.ts index 66939041a9d..5b0a7190d28 100644 --- a/src/vs/platform/userDataSync/common/globalStateSync.ts +++ b/src/vs/platform/userDataSync/common/globalStateSync.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IGlobalState, SyncResource, IUserDataSynchroniser, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync'; +import { SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IGlobalState, SyncResource, IUserDataSynchroniser, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, ISyncResourceHandle } from 'vs/platform/userDataSync/common/userDataSync'; import { VSBuffer } from 'vs/base/common/buffer'; import { Event } from 'vs/base/common/event'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { dirname } from 'vs/base/common/resources'; +import { dirname, joinPath, basename } 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'; @@ -17,6 +17,8 @@ import { AbstractSynchroniser, IRemoteUserData } from 'vs/platform/userDataSync/ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { URI } from 'vs/base/common/uri'; +import { format } from 'vs/base/common/jsonFormatter'; +import { applyEdits } from 'vs/base/common/jsonEdit'; const argvProperties: string[] = ['locale']; @@ -105,28 +107,24 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs async stop(): Promise { } - async getRemoteContent(ref?: string, fragment?: string): Promise { - let content = await super.getRemoteContent(ref); - if (content !== null && fragment) { - return this.getFragment(content, fragment); - } - return content; - } - - async getLocalBackupContent(ref?: string, fragment?: string): Promise { - let content = await super.getLocalBackupContent(ref); - if (content !== null && fragment) { - return this.getFragment(content, fragment); - } - return content; + async getAssociatedResources({ uri }: ISyncResourceHandle): Promise<{ resource: URI, comparableResource?: URI }[]> { + return [{ resource: joinPath(uri, 'globalState.json') }]; } - private getFragment(content: string, fragment: string): string | null { - const syncData = this.parseSyncData(content); - if (syncData) { - switch (fragment) { - case 'globalState': - return syncData.content; + async resolveContent(uri: URI): Promise { + let content = await super.resolveContent(uri); + if (content) { + return content; + } + content = await super.resolveContent(dirname(uri)); + if (content) { + const syncData = this.parseSyncData(content); + if (syncData) { + switch (basename(uri)) { + case 'globalState.json': + const edits = format(syncData.content, undefined, {}); + return applyEdits(syncData.content, edits); + } } } return null; diff --git a/src/vs/platform/userDataSync/common/keybindingsSync.ts b/src/vs/platform/userDataSync/common/keybindingsSync.ts index 0aa4c71114e..0ea9b58ccdd 100644 --- a/src/vs/platform/userDataSync/common/keybindingsSync.ts +++ b/src/vs/platform/userDataSync/common/keybindingsSync.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IFileService, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; -import { UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, SyncResource, IUserDataSynchroniser, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, USER_DATA_SYNC_SCHEME, PREVIEW_DIR_NAME } from 'vs/platform/userDataSync/common/userDataSync'; +import { UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, SyncResource, IUserDataSynchroniser, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, USER_DATA_SYNC_SCHEME, PREVIEW_DIR_NAME, ISyncResourceHandle } from 'vs/platform/userDataSync/common/userDataSync'; import { merge } from 'vs/platform/userDataSync/common/keybindingsMerge'; import { VSBuffer } from 'vs/base/common/buffer'; import { parse } from 'vs/base/common/json'; @@ -19,7 +19,7 @@ import { isNonEmptyArray } from 'vs/base/common/arrays'; import { IFileSyncPreviewResult, AbstractJsonFileSynchroniser, IRemoteUserData } from 'vs/platform/userDataSync/common/abstractSynchronizer'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { URI } from 'vs/base/common/uri'; -import { joinPath, isEqual } from 'vs/base/common/resources'; +import { joinPath, isEqual, dirname, basename } from 'vs/base/common/resources'; interface ISyncContent { mac?: string; @@ -160,38 +160,36 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem return false; } - async getConflictContent(conflictResource: URI): Promise { - const content = await super.getConflictContent(conflictResource); - return content !== null ? this.getKeybindingsContentFromSyncContent(content) : null; + async getAssociatedResources({ uri }: ISyncResourceHandle): Promise<{ resource: URI, comparableResource?: URI }[]> { + return [{ resource: joinPath(uri, 'keybindings.json'), comparableResource: this.file }]; } - async getRemoteContent(ref?: string, fragment?: string): Promise { - const content = await super.getRemoteContent(ref); - if (content !== null && fragment) { - return this.getFragment(content, fragment); + async resolveContent(uri: URI): Promise { + if (isEqual(this.remotePreviewResource, uri)) { + return this.getConflictContent(uri); } - return content; - } - - async getLocalBackupContent(ref?: string, fragment?: string): Promise { - let content = await super.getLocalBackupContent(ref); - if (content !== null && fragment) { - return this.getFragment(content, fragment); + let content = await super.resolveContent(uri); + if (content) { + return content; } - return content; - } - - private getFragment(content: string, fragment: string): string | null { - const syncData = this.parseSyncData(content); - if (syncData) { - switch (fragment) { - case 'keybindings': - return this.getKeybindingsContentFromSyncContent(syncData.content); + content = await super.resolveContent(dirname(uri)); + if (content) { + const syncData = this.parseSyncData(content); + if (syncData) { + switch (basename(uri)) { + case 'keybindings.json': + return this.getKeybindingsContentFromSyncContent(syncData.content); + } } } return null; } + protected async getConflictContent(conflictResource: URI): Promise { + const content = await super.getConflictContent(conflictResource); + return content !== null ? this.getKeybindingsContentFromSyncContent(content) : null; + } + protected async performSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise { try { const result = await this.getPreview(remoteUserData, lastSyncUserData); diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts index 234496f2f4b..9b4d848b5a6 100644 --- a/src/vs/platform/userDataSync/common/settingsSync.ts +++ b/src/vs/platform/userDataSync/common/settingsSync.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IFileService, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; -import { UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, CONFIGURATION_SYNC_STORE_KEY, SyncResource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, USER_DATA_SYNC_SCHEME, PREVIEW_DIR_NAME } from 'vs/platform/userDataSync/common/userDataSync'; +import { UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, CONFIGURATION_SYNC_STORE_KEY, SyncResource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, USER_DATA_SYNC_SCHEME, PREVIEW_DIR_NAME, ISyncResourceHandle } from 'vs/platform/userDataSync/common/userDataSync'; import { VSBuffer } from 'vs/base/common/buffer'; import { parse } from 'vs/base/common/json'; import { localize } from 'vs/nls'; @@ -20,7 +20,7 @@ import { IFileSyncPreviewResult, AbstractJsonFileSynchroniser, IRemoteUserData } import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { URI } from 'vs/base/common/uri'; import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { joinPath, isEqual } from 'vs/base/common/resources'; +import { joinPath, isEqual, dirname, basename } from 'vs/base/common/resources'; export interface ISettingsSyncContent { settings: string; @@ -173,7 +173,35 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser { return false; } - async getConflictContent(conflictResource: URI): Promise { + async getAssociatedResources({ uri }: ISyncResourceHandle): Promise<{ resource: URI, comparableResource?: URI }[]> { + return [{ resource: joinPath(uri, 'settings.json'), comparableResource: this.file }]; + } + + async resolveContent(uri: URI): Promise { + if (isEqual(this.remotePreviewResource, uri)) { + return this.getConflictContent(uri); + } + let content = await super.resolveContent(uri); + if (content) { + return content; + } + content = await super.resolveContent(dirname(uri)); + if (content) { + const syncData = this.parseSyncData(content); + if (syncData) { + const settingsSyncContent = this.parseSettingsSyncContent(syncData.content); + if (settingsSyncContent) { + switch (basename(uri)) { + case 'settings.json': + return settingsSyncContent.settings; + } + } + } + } + return null; + } + + protected async getConflictContent(conflictResource: URI): Promise { let content = await super.getConflictContent(conflictResource); if (content !== null) { const settingsSyncContent = this.parseSettingsSyncContent(content); @@ -188,36 +216,6 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser { return content; } - async getRemoteContent(ref?: string, fragment?: string): Promise { - let content = await super.getRemoteContent(ref); - if (content !== null && fragment) { - return this.getFragment(content, fragment); - } - return content; - } - - async getLocalBackupContent(ref?: string, fragment?: string): Promise { - let content = await super.getLocalBackupContent(ref); - if (content !== null && fragment) { - return this.getFragment(content, fragment); - } - return content; - } - - private getFragment(content: string, fragment: string): string | null { - const syncData = this.parseSyncData(content); - if (syncData) { - const settingsSyncContent = this.parseSettingsSyncContent(syncData.content); - if (settingsSyncContent) { - switch (fragment) { - case 'settings': - return settingsSyncContent.settings; - } - } - } - return null; - } - async acceptConflict(conflict: URI, content: string): Promise { if (this.status === SyncStatus.HasConflicts && (isEqual(this.localPreviewResource, conflict) || isEqual(this.remotePreviewResource, conflict)) diff --git a/src/vs/platform/userDataSync/common/snippetsSync.ts b/src/vs/platform/userDataSync/common/snippetsSync.ts index f9d70f18abc..e12cef7ce1f 100644 --- a/src/vs/platform/userDataSync/common/snippetsSync.ts +++ b/src/vs/platform/userDataSync/common/snippetsSync.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSynchroniser, SyncResource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, Conflict, USER_DATA_SYNC_SCHEME, PREVIEW_DIR_NAME, UserDataSyncError, UserDataSyncErrorCode } from 'vs/platform/userDataSync/common/userDataSync'; +import { SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSynchroniser, SyncResource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, Conflict, USER_DATA_SYNC_SCHEME, PREVIEW_DIR_NAME, UserDataSyncError, UserDataSyncErrorCode, ISyncResourceHandle } 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'; @@ -11,7 +11,7 @@ import { AbstractSynchroniser, IRemoteUserData, ISyncData } from 'vs/platform/us 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 } from 'vs/base/common/resources'; +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 { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; @@ -148,8 +148,46 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD this.setStatus(SyncStatus.Idle); } - async getConflictContent(conflictResource: URI): Promise { - if (isEqualOrParent(conflictResource.with({ scheme: this.syncFolder.scheme }), this.snippetsPreviewFolder) && this.syncPreviewResultPromise) { + async getAssociatedResources({ uri }: ISyncResourceHandle): Promise<{ resource: URI, comparableResource?: URI }[]> { + let content = await super.resolveContent(uri); + if (content) { + const syncData = this.parseSyncData(content); + if (syncData) { + const snippets = this.parseSnippets(syncData); + const result = []; + for (const snippet of Object.keys(snippets)) { + const resource = joinPath(uri, snippet); + const comparableResource = joinPath(this.snippetsFolder, snippet); + const exists = await this.fileService.exists(comparableResource); + result.push({ resource, comparableResource: exists ? comparableResource : undefined }); + } + return result; + } + } + return []; + } + + async resolveContent(uri: URI): Promise { + if (isEqualOrParent(uri.with({ scheme: this.syncFolder.scheme }), this.snippetsPreviewFolder)) { + return this.getConflictContent(uri); + } + let content = await super.resolveContent(uri); + if (content) { + return content; + } + content = await super.resolveContent(dirname(uri)); + if (content) { + const syncData = this.parseSyncData(content); + if (syncData) { + const snippets = this.parseSnippets(syncData); + return snippets[basename(uri)] || null; + } + } + return null; + } + + protected async getConflictContent(conflictResource: URI): Promise { + if (this.syncPreviewResultPromise) { const result = await this.syncPreviewResultPromise; const key = relativePath(this.snippetsPreviewFolder, conflictResource.with({ scheme: this.snippetsPreviewFolder.scheme }))!; if (conflictResource.scheme === this.snippetsPreviewFolder.scheme) { @@ -162,37 +200,6 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD return null; } - async getRemoteContent(ref?: string, fragment?: string): Promise { - const content = await super.getRemoteContent(ref); - if (content !== null && fragment) { - return this.getFragment(content, fragment); - } - return content; - } - - async getLocalBackupContent(ref?: string, fragment?: string): Promise { - let content = await super.getLocalBackupContent(ref); - if (content !== null && fragment) { - return this.getFragment(content, fragment); - } - return content; - } - - private getFragment(content: string, fragment: string): string | null { - const syncData = this.parseSyncData(content); - return syncData ? this.getFragmentFromSyncData(syncData, fragment) : null; - } - - private getFragmentFromSyncData(syncData: ISyncData, fragment: string): string | null { - switch (fragment) { - case 'snippets': - return syncData.content; - default: - const remoteSnippets = this.parseSnippets(syncData); - return remoteSnippets[fragment] || null; - } - } - async acceptConflict(conflictResource: URI, content: string): Promise { const conflict = this.conflicts.filter(({ local, remote }) => isEqual(local, conflictResource) || isEqual(remote, conflictResource))[0]; if (this.status === SyncStatus.HasConflicts && conflict) { diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index 553d241c96c..3d8b90f0598 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -18,7 +18,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IStringDictionary } from 'vs/base/common/collections'; import { FormattingOptions } from 'vs/base/common/jsonFormatter'; import { URI } from 'vs/base/common/uri'; -import { joinPath, dirname, basename, isEqualOrParent } from 'vs/base/common/resources'; +import { joinPath, isEqualOrParent } from 'vs/base/common/resources'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IProductService } from 'vs/platform/product/common/productService'; import { distinct } from 'vs/base/common/arrays'; @@ -243,6 +243,11 @@ export const enum SyncStatus { HasConflicts = 'hasConflicts', } +export interface ISyncResourceHandle { + created: number; + uri: URI; +} + export type Conflict = { remote: URI, local: URI }; export interface IUserDataSynchroniser { @@ -263,11 +268,12 @@ export interface IUserDataSynchroniser { hasLocalData(): Promise; resetLocal(): Promise; - getConflictContent(conflictResource: URI): Promise; + resolveContent(resource: URI): Promise; acceptConflict(conflictResource: URI, content: string): Promise; - getRemoteContent(ref?: string, fragment?: string): Promise; - getLocalBackupContent(ref?: string, fragment?: string): Promise; + getRemoteSyncResourceHandles(): Promise; + getLocalSyncResourceHandles(): Promise; + getAssociatedResources(syncResourceHandle: ISyncResourceHandle): Promise<{ resource: URI, comparableResource?: URI }[]>; } //#endregion @@ -315,6 +321,10 @@ export interface IUserDataSyncService { isFirstTimeSyncWithMerge(): Promise; resolveContent(resource: URI): Promise; acceptConflict(conflictResource: URI, content: string): Promise; + + getLocalSyncResourceHandles(resource: SyncResource): Promise; + getRemoteSyncResourceHandles(resource: SyncResource): Promise; + getAssociatedResources(resource: SyncResource, syncResourceHandle: ISyncResourceHandle): Promise<{ resource: URI, comparableResource?: URI }[]>; } export const IUserDataAutoSyncService = createDecorator('IUserDataAutoSyncService'); @@ -347,25 +357,6 @@ export const USER_DATA_SYNC_SCHEME = 'vscode-userdata-sync'; export const CONTEXT_SYNC_STATE = new RawContextKey('syncStatus', SyncStatus.Uninitialized); export const CONTEXT_SYNC_ENABLEMENT = new RawContextKey('syncEnabled', false); -export function toRemoteBackupSyncResource(resource: SyncResource, ref?: string): URI { - return URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote-backup', path: `/${resource}/${ref ? ref : 'latest'}` }); -} -export function toLocalBackupSyncResource(resource: SyncResource, ref?: string): URI { - return URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local-backup', path: `/${resource}/${ref ? ref : 'latest'}` }); -} -export function resolveBackupSyncResource(resource: URI): { remote: boolean, resource: SyncResource, path: string } | null { - if (resource.scheme === USER_DATA_SYNC_SCHEME - && resource.authority === 'remote-backup' || resource.authority === 'local-backup') { - const resourceKey: SyncResource = basename(dirname(resource)) as SyncResource; - const path = resource.path.substring(resourceKey.length + 1); - if (resourceKey && path) { - const remote = resource.authority === 'remote-backup'; - return { remote, resource: resourceKey, path }; - } - } - return null; -} - export const PREVIEW_DIR_NAME = 'preview'; export function getSyncResourceFromLocalPreview(localPreview: URI, environmentService: IEnvironmentService): SyncResource | undefined { if (localPreview.scheme === USER_DATA_SYNC_SCHEME) { diff --git a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts index ef68213366c..26c093498b7 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts @@ -5,7 +5,7 @@ import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc'; import { Event } from 'vs/base/common/event'; -import { IUserDataSyncService, IUserDataSyncUtilService, IUserDataAutoSyncService, IUserDataSyncStoreService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncService, IUserDataSyncUtilService, IUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataSync'; import { URI } from 'vs/base/common/uri'; import { IStringDictionary } from 'vs/base/common/collections'; import { FormattingOptions } from 'vs/base/common/jsonFormatter'; @@ -28,14 +28,17 @@ export class UserDataSyncChannel implements IServerChannel { call(context: any, command: string, args?: any): Promise { switch (command) { case '_getInitialData': return Promise.resolve([this.service.status, this.service.conflicts, this.service.lastSyncTime]); - case 'sync': return this.service.sync(); - case 'acceptConflict': return this.service.acceptConflict(URI.revive(args[0]), args[1]); case 'pull': return this.service.pull(); + case 'sync': return this.service.sync(); case 'stop': this.service.stop(); return Promise.resolve(); case 'reset': return this.service.reset(); case 'resetLocal': return this.service.resetLocal(); - case 'resolveContent': return this.service.resolveContent(URI.revive(args[0])); case 'isFirstTimeSyncWithMerge': return this.service.isFirstTimeSyncWithMerge(); + case 'acceptConflict': return this.service.acceptConflict(URI.revive(args[0]), args[1]); + case 'resolveContent': return this.service.resolveContent(URI.revive(args[0])); + case 'getLocalSyncResourceHandles': return this.service.getLocalSyncResourceHandles(args[0]); + case 'getRemoteSyncResourceHandles': return this.service.getRemoteSyncResourceHandles(args[0]); + case 'getAssociatedResources': return this.service.getAssociatedResources(args[0], { created: args[1].created, uri: URI.revive(args[1].uri) }); } throw new Error('Invalid call'); } @@ -98,38 +101,3 @@ export class UserDataSyncUtilServiceClient implements IUserDataSyncUtilService { } } - -export class UserDataSyncStoreServiceChannel implements IServerChannel { - - constructor(private readonly service: IUserDataSyncStoreService) { } - - listen(_: unknown, event: string): Event { - throw new Error(`Event not found: ${event}`); - } - - call(context: any, command: string, args?: any): Promise { - switch (command) { - case 'getAllRefs': return this.service.getAllRefs(args[0]); - case 'resolveContent': return this.service.resolveContent(args[0], args[1]); - case 'delete': return this.service.delete(args[0]); - } - throw new Error('Invalid call'); - } -} - -export class UserDataSyncBackupStoreServiceChannel implements IServerChannel { - - constructor(private readonly service: IUserDataSyncBackupStoreService) { } - - listen(_: unknown, event: string): Event { - throw new Error(`Event not found: ${event}`); - } - - call(context: any, command: string, args?: any): Promise { - switch (command) { - case 'getAllRefs': return this.service.getAllRefs(args[0]); - case 'resolveContent': return this.service.resolveContent(args[0], args[1]); - } - throw new Error('Invalid call'); - } -} diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index 92a79046411..d601761a271 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IUserDataSyncService, SyncStatus, IUserDataSyncStoreService, SyncResource, IUserDataSyncLogService, IUserDataSynchroniser, UserDataSyncStoreError, UserDataSyncErrorCode, UserDataSyncError, resolveBackupSyncResource, SyncResourceConflicts } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncService, SyncStatus, IUserDataSyncStoreService, SyncResource, IUserDataSyncLogService, IUserDataSynchroniser, UserDataSyncStoreError, UserDataSyncErrorCode, UserDataSyncError, SyncResourceConflicts, ISyncResourceHandle } from 'vs/platform/userDataSync/common/userDataSync'; import { Disposable } from 'vs/base/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Emitter, Event } from 'vs/base/common/event'; @@ -188,25 +188,27 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ } async resolveContent(resource: URI): Promise { - const result = resolveBackupSyncResource(resource); - if (result) { - const synchronizer = this.synchronisers.filter(s => s.resource === result.resource)[0]; - if (synchronizer) { - const ref = result.path !== 'latest' ? result.path : undefined; - return result.remote ? synchronizer.getRemoteContent(ref, resource.fragment) : synchronizer.getLocalBackupContent(ref, resource.fragment); - } - } - - for (const synchronizer of this.synchronisers) { - const content = await synchronizer.getConflictContent(resource); - if (content !== null) { + for (const synchroniser of this.synchronisers) { + const content = await synchroniser.resolveContent(resource); + if (content) { return content; } } - return null; } + getRemoteSyncResourceHandles(resource: SyncResource): Promise { + return this.getSynchroniser(resource).getRemoteSyncResourceHandles(); + } + + getLocalSyncResourceHandles(resource: SyncResource): Promise { + return this.getSynchroniser(resource).getLocalSyncResourceHandles(); + } + + getAssociatedResources(resource: SyncResource, syncResourceHandle: ISyncResourceHandle): Promise<{ resource: URI, comparableResource?: URI }[]> { + return this.getSynchroniser(resource).getAssociatedResources(syncResourceHandle); + } + async isFirstTimeSyncWithMerge(): Promise { await this.checkEnablement(); if (!await this.userDataSyncStoreService.manifest()) { diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 3f972a36c94..92ab2b8304e 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -750,14 +750,14 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo private getConflictsEditorInputs(syncResource: SyncResource): DiffEditorInput[] { return this.editorService.editors.filter(input => { const resource = input instanceof DiffEditorInput ? input.master.resource : input.resource; - return getSyncResourceFromLocalPreview(resource!, this.workbenchEnvironmentService) === syncResource; + return resource && getSyncResourceFromLocalPreview(resource!, this.workbenchEnvironmentService) === syncResource; }) as DiffEditorInput[]; } private getAllConflictsEditorInputs(): IEditorInput[] { return this.editorService.editors.filter(input => { const resource = input instanceof DiffEditorInput ? input.master.resource : input.resource; - return getSyncResourceFromLocalPreview(resource!, this.workbenchEnvironmentService) !== undefined; + return resource && getSyncResourceFromLocalPreview(resource!, this.workbenchEnvironmentService) !== undefined; }); } diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncView.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncView.ts index bc59eeaff1d..cd83abd8dda 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSyncView.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSyncView.ts @@ -10,13 +10,12 @@ import { localize } from 'vs/nls'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { TreeViewPane, TreeView } from 'vs/workbench/browser/parts/views/treeView'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { ALL_SYNC_RESOURCES, CONTEXT_SYNC_ENABLEMENT, IUserDataSyncStoreService, toRemoteBackupSyncResource, resolveBackupSyncResource, IUserDataSyncBackupStoreService, IResourceRefHandle, toLocalBackupSyncResource, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; +import { ALL_SYNC_RESOURCES, CONTEXT_SYNC_ENABLEMENT, SyncResource, IUserDataSyncService, ISyncResourceHandle } from 'vs/platform/userDataSync/common/userDataSync'; import { registerAction2, Action2, MenuId } from 'vs/platform/actions/common/actions'; import { IContextKeyService, RawContextKey, ContextKeyExpr, ContextKeyEqualsExpr } from 'vs/platform/contextkey/common/contextkey'; import { URI } from 'vs/base/common/uri'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { FolderThemeIcon, FileThemeIcon } from 'vs/platform/theme/common/themeService'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { FolderThemeIcon } from 'vs/platform/theme/common/themeService'; import { fromNow } from 'vs/base/common/date'; import { pad } from 'vs/base/common/strings'; import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; @@ -26,8 +25,7 @@ export class UserDataSyncViewContribution implements IWorkbenchContribution { constructor( @IInstantiationService private readonly instantiationService: IInstantiationService, @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService, - @IUserDataSyncBackupStoreService private readonly userDataSyncBackupStoreService: IUserDataSyncBackupStoreService, + @IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService, ) { const container = this.registerSyncViewContainer(); this.registerBackupView(container, true); @@ -59,9 +57,7 @@ export class UserDataSyncViewContribution implements IWorkbenchContribution { const disposable = treeView.onDidChangeVisibility(visible => { if (visible && !treeView.dataProvider) { disposable.dispose(); - treeView.dataProvider = this.instantiationService.createInstance(UserDataSyncHistoryViewDataProvider, id, - (resource: SyncResource) => remote ? this.userDataSyncStoreService.getAllRefs(resource) : this.userDataSyncBackupStoreService.getAllRefs(resource), - (resource: SyncResource, ref: string) => remote ? toRemoteBackupSyncResource(resource, ref) : toLocalBackupSyncResource(resource, ref)); + treeView.dataProvider = new UserDataSyncHistoryViewDataProvider(remote, this.userDataSyncService); } }); const viewsRegistry = Registry.as(Extensions.ViewsRegistry); @@ -104,28 +100,11 @@ export class UserDataSyncViewContribution implements IWorkbenchContribution { registerAction2(class extends Action2 { constructor() { super({ - id: `workbench.actions.sync.${viewId}.resolveResourceRef`, - title: localize('workbench.actions.sync.resolveResourceRef', "Resolve Resource Ref"), - }); - } - async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise { - const editorService = accessor.get(IEditorService); - let resource = URI.parse(handle.$treeItemHandle); - const result = resolveBackupSyncResource(resource); - if (result) { - resource = resource.with({ fragment: result.resource }); - await editorService.openEditor({ resource }); - } - } - }); - registerAction2(class extends Action2 { - constructor() { - super({ - id: `workbench.actions.sync.${viewId}.resolveResourceRefCompletely`, - title: localize('workbench.actions.sync.resolveResourceRefCompletely', "Show full content"), + id: `workbench.actions.sync.resolveResource`, + title: localize('workbench.actions.sync.resolveResourceRef', "Show full content"), menu: { id: MenuId.ViewItemContext, - when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', viewId), ContextKeyExpr.regex('viewItem', /syncref-.*/i)) + when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', viewId), ContextKeyExpr.regex('viewItem', /sync-resource-.*/i)) }, }); } @@ -134,34 +113,28 @@ export class UserDataSyncViewContribution implements IWorkbenchContribution { await editorService.openEditor({ resource: URI.parse(handle.$treeItemHandle) }); } }); + registerAction2(class extends Action2 { constructor() { super({ - id: `workbench.actions.${viewId}.commpareWithLocal`, - title: localize('workbench.action.deleteRef', "Open Changes"), - menu: { - id: MenuId.ViewItemContext, - when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', viewId), ContextKeyExpr.regex('viewItem', /syncref-(settings|keybindings).*/i)) - }, + id: `workbench.actions.sync.commpareWithLocal`, + title: localize('workbench.actions.sync.commpareWithLocal', "Open Changes"), }); } async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise { const editorService = accessor.get(IEditorService); - const environmentService = accessor.get(IEnvironmentService); - const resource = URI.parse(handle.$treeItemHandle); - const result = resolveBackupSyncResource(resource); - if (result) { - const leftResource: URI = resource.with({ fragment: result.resource }); - const rightResource: URI = result.resource === 'settings' ? environmentService.settingsResource : environmentService.keybindingsResource; + const { resource, comparableResource } = <{ resource: string, comparableResource?: string }>JSON.parse(handle.$treeItemHandle); + if (comparableResource) { await editorService.openEditor({ - leftResource, - rightResource, + leftResource: URI.parse(resource), + rightResource: URI.parse(comparableResource), options: { - preserveFocus: false, - pinned: true, + preserveFocus: true, revealIfVisible: true, }, }); + } else { + await editorService.openEditor({ resource: URI.parse(resource) }); } } }); @@ -169,48 +142,55 @@ export class UserDataSyncViewContribution implements IWorkbenchContribution { } +interface SyncResourceTreeItem extends ITreeItem { + resource: SyncResource; + resourceHandle: ISyncResourceHandle; +} + class UserDataSyncHistoryViewDataProvider implements ITreeViewDataProvider { - constructor( - private readonly viewId: string, - private getAllRefs: (resource: SyncResource) => Promise, - private toResource: (resource: SyncResource, ref: string) => URI - ) { - } + constructor(private readonly remote: boolean, private userDataSyncService: IUserDataSyncService) { } async getChildren(element?: ITreeItem): Promise { - if (element) { - return this.getResources(element.handle); + if (!element) { + return ALL_SYNC_RESOURCES.map(resourceKey => ({ + handle: resourceKey, + collapsibleState: TreeItemCollapsibleState.Collapsed, + label: { label: resourceKey }, + themeIcon: FolderThemeIcon, + })); } - return ALL_SYNC_RESOURCES.map(resourceKey => ({ - handle: resourceKey, - collapsibleState: TreeItemCollapsibleState.Collapsed, - label: { label: resourceKey }, - themeIcon: FolderThemeIcon, - contextValue: `sync-${resourceKey}` - })); - } - - private async getResources(handle: string): Promise { - const resourceKey = ALL_SYNC_RESOURCES.filter(key => key === handle)[0]; + const resourceKey = ALL_SYNC_RESOURCES.filter(key => key === element.handle)[0] as SyncResource; if (resourceKey) { - const refHandles = await this.getAllRefs(resourceKey); - return refHandles.map(({ ref, created }) => { - const handle = this.toResource(resourceKey, ref).toString(); + const refHandles = this.remote ? await this.userDataSyncService.getRemoteSyncResourceHandles(resourceKey) : await this.userDataSyncService.getLocalSyncResourceHandles(resourceKey); + return refHandles.map(({ uri, created }) => { + return { + handle: uri.toString(), + collapsibleState: TreeItemCollapsibleState.Collapsed, + label: { label: label(new Date(created)) }, + description: fromNow(created, true), + resourceUri: uri, + resource: resourceKey, + resourceHandle: { uri, created }, + contextValue: `sync-resource-${resourceKey}` + }; + }); + } + if ((element).resourceHandle) { + const associatedResources = await this.userDataSyncService.getAssociatedResources((element).resource, (element).resourceHandle); + return associatedResources.map(({ resource, comparableResource }) => { + const handle = JSON.stringify({ resource: resource.toString(), comparableResource: comparableResource?.toString() }); return { handle, collapsibleState: TreeItemCollapsibleState.None, - label: { label: label(new Date(created)) }, - description: fromNow(created, true), - command: { id: `workbench.actions.sync.${this.viewId}.resolveResourceRef`, title: '', arguments: [{ $treeItemHandle: handle, $treeViewId: '' }] }, - themeIcon: FileThemeIcon, - contextValue: `syncref-${resourceKey}` + resourceUri: resource, + command: { id: `workbench.actions.sync.commpareWithLocal`, title: '', arguments: [{ $treeViewId: '', $treeItemHandle: handle }] }, + contextValue: `sync-associatedResource-${(element).resource}` }; }); } return []; } - } function label(date: Date): string { diff --git a/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncBackupStoreService.ts b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncBackupStoreService.ts deleted file mode 100644 index 7ce287cdc13..00000000000 --- a/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncBackupStoreService.ts +++ /dev/null @@ -1,37 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { IResourceRefHandle, IUserDataSyncBackupStoreService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync'; -import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; -import { IChannel } from 'vs/base/parts/ipc/common/ipc'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; - -export class UserDataSyncBackupStoreService implements IUserDataSyncBackupStoreService { - - _serviceBrand: undefined; - private readonly channel: IChannel; - - constructor( - @ISharedProcessService sharedProcessService: ISharedProcessService, - ) { - this.channel = sharedProcessService.getChannel('userDataSyncBackupStoreService'); - } - - backup(key: SyncResource, content: string): Promise { - return this.channel.call('backup', [key, content]); - } - - - getAllRefs(key: SyncResource): Promise { - return this.channel.call('getAllRefs', [key]); - } - - resolveContent(key: SyncResource, ref: string): Promise { - return this.channel.call('resolveContent', [key, ref]); - } - -} - -registerSingleton(IUserDataSyncBackupStoreService, UserDataSyncBackupStoreService); diff --git a/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts index c33de4a7cde..3cfea3362a7 100644 --- a/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts +++ b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { SyncStatus, SyncResource, IUserDataSyncService, UserDataSyncError, SyncResourceConflicts } from 'vs/platform/userDataSync/common/userDataSync'; +import { SyncStatus, SyncResource, IUserDataSyncService, UserDataSyncError, SyncResourceConflicts, ISyncResourceHandle } from 'vs/platform/userDataSync/common/userDataSync'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { Disposable } from 'vs/base/common/lifecycle'; import { Emitter, Event } from 'vs/base/common/event'; @@ -73,8 +73,8 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ return this.channel.call('sync'); } - acceptConflict(conflict: URI, content: string): Promise { - return this.channel.call('acceptConflict', [conflict, content]); + stop(): Promise { + return this.channel.call('stop'); } reset(): Promise { @@ -85,16 +85,31 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ return this.channel.call('resetLocal'); } - stop(): Promise { - return this.channel.call('stop'); + isFirstTimeSyncWithMerge(): Promise { + return this.channel.call('isFirstTimeSyncWithMerge'); + } + + acceptConflict(conflict: URI, content: string): Promise { + return this.channel.call('acceptConflict', [conflict, content]); } resolveContent(resource: URI): Promise { return this.channel.call('resolveContent', [resource]); } - isFirstTimeSyncWithMerge(): Promise { - return this.channel.call('isFirstTimeSyncWithMerge'); + async getLocalSyncResourceHandles(resource: SyncResource): Promise { + const handles = await this.channel.call('getLocalSyncResourceHandles', [resource]); + return handles.map(({ created, uri }) => ({ created, uri: URI.revive(uri) })); + } + + async getRemoteSyncResourceHandles(resource: SyncResource): Promise { + const handles = await this.channel.call('getRemoteSyncResourceHandles', [resource]); + return handles.map(({ created, uri }) => ({ created, uri: URI.revive(uri) })); + } + + async getAssociatedResources(resource: SyncResource, syncResourceHandle: ISyncResourceHandle): Promise<{ resource: URI, comparableResource?: URI }[]> { + const result = await this.channel.call<{ resource: URI, comparableResource?: URI }[]>('getAssociatedResources', [resource, syncResourceHandle]); + return result.map(({ resource, comparableResource }) => ({ resource: URI.revive(resource), comparableResource: URI.revive(comparableResource) })); } private async updateStatus(status: SyncStatus): Promise { diff --git a/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncStoreService.ts b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncStoreService.ts deleted file mode 100644 index 21365024770..00000000000 --- a/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncStoreService.ts +++ /dev/null @@ -1,58 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { SyncResource, IUserDataSyncStoreService, IUserDataSyncStore, getUserDataSyncStore, IUserData, IUserDataManifest, IResourceRefHandle } from 'vs/platform/userDataSync/common/userDataSync'; -import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; -import { IChannel } from 'vs/base/parts/ipc/common/ipc'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IProductService } from 'vs/platform/product/common/productService'; - -export class UserDataSyncStoreService implements IUserDataSyncStoreService { - - _serviceBrand: undefined; - private readonly channel: IChannel; - readonly userDataSyncStore: IUserDataSyncStore | undefined; - - constructor( - @ISharedProcessService sharedProcessService: ISharedProcessService, - @IProductService productService: IProductService, - @IConfigurationService configurationService: IConfigurationService - ) { - this.channel = sharedProcessService.getChannel('userDataSyncStoreService'); - this.userDataSyncStore = getUserDataSyncStore(productService, configurationService); - } - - read(key: SyncResource, oldValue: IUserData | null, source?: SyncResource): Promise { - throw new Error('Not Supported'); - } - - write(key: SyncResource, content: string, ref: string | null, source?: SyncResource): Promise { - throw new Error('Not Supported'); - } - - manifest(): Promise { - throw new Error('Not Supported'); - } - - clear(): Promise { - throw new Error('Not Supported'); - } - - getAllRefs(key: SyncResource): Promise { - return this.channel.call('getAllRefs', [key]); - } - - resolveContent(key: SyncResource, ref: string): Promise { - return this.channel.call('resolveContent', [key, ref]); - } - - delete(key: SyncResource): Promise { - return this.channel.call('delete', [key]); - } - -} - -registerSingleton(IUserDataSyncStoreService, UserDataSyncStoreService); diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index d5ab3c6eb15..c8fc7ae282d 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -51,8 +51,6 @@ import 'vs/workbench/services/workspaces/electron-browser/workspacesService'; import 'vs/workbench/services/workspaces/electron-browser/workspaceEditingService'; import 'vs/workbench/services/userDataSync/electron-browser/userDataSyncService'; import 'vs/workbench/services/userDataSync/electron-browser/userDataAutoSyncService'; -import 'vs/workbench/services/userDataSync/electron-browser/userDataSyncStoreService'; -import 'vs/workbench/services/userDataSync/electron-browser/userDataSyncBackupStoreService'; import 'vs/workbench/services/authentication/electron-browser/authenticationTokenService'; import 'vs/workbench/services/authentication/browser/authenticationService'; import 'vs/workbench/services/host/electron-browser/desktopHostService'; -- GitLab