diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataManualSyncView.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataManualSyncView.ts index eda4147076c..2870fa4a60f 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataManualSyncView.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataManualSyncView.ts @@ -22,6 +22,7 @@ import { IUserDataSyncWorkbenchService, getSyncAreaLabel, CONTEXT_ENABLE_MANUAL_ import { TreeView } from 'vs/workbench/contrib/views/browser/treeView'; import { isEqual, basename } from 'vs/base/common/resources'; import { IDecorationsProvider, IDecorationData, IDecorationsService } from 'vs/workbench/services/decorations/browser/decorations'; +import { IProgressService } from 'vs/platform/progress/common/progress'; const viewName = localize('manual sync', "Sync Manually"); @@ -33,6 +34,9 @@ export class UserDataManualSyncView extends Disposable { constructor( container: ViewContainer, @IInstantiationService private readonly instantiationService: IInstantiationService, + @IEditorService private readonly editorService: IEditorService, + @IProgressService private readonly progressService: IProgressService, + @IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService, @IUserDataSyncWorkbenchService userDataSyncWorkbenchService: IUserDataSyncWorkbenchService, @IDecorationsService decorationsService: IDecorationsService, ) { @@ -83,6 +87,7 @@ export class UserDataManualSyncView extends Disposable { const localActionOrder = 1; const remoteActionOrder = 1; const mergeActionOrder = 1; + const that = this; /* accept all local */ registerAction2(class extends Action2 { @@ -99,9 +104,8 @@ export class UserDataManualSyncView extends Disposable { }, }); } - async run(accessor: ServicesAccessor): Promise { - const userDataSyncWorkbenchService = accessor.get(IUserDataSyncWorkbenchService); - await userDataSyncWorkbenchService.userDataSyncPreview.push(); + run(accessor: ServicesAccessor): Promise { + return that.push(); } }); @@ -121,8 +125,7 @@ export class UserDataManualSyncView extends Disposable { }); } async run(accessor: ServicesAccessor): Promise { - const userDataSyncWorkbenchService = accessor.get(IUserDataSyncWorkbenchService); - await userDataSyncWorkbenchService.userDataSyncPreview.pull(); + return that.pull(); } }); @@ -142,8 +145,7 @@ export class UserDataManualSyncView extends Disposable { }); } async run(accessor: ServicesAccessor): Promise { - const userDataSyncWorkbenchService = accessor.get(IUserDataSyncWorkbenchService); - await userDataSyncWorkbenchService.userDataSyncPreview.merge(); + return that.merge(); } }); @@ -163,13 +165,7 @@ export class UserDataManualSyncView extends Disposable { }); } async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise { - const userDataSyncWorkbenchService = accessor.get(IUserDataSyncWorkbenchService); - const userDataSyncService = accessor.get(IUserDataSyncService); - const previewResource: IUserDataSyncResourceGroup = ManualSyncViewDataProvider.toUserDataSyncResourceGroup(handle.$treeItemHandle); - const isConflict = userDataSyncWorkbenchService.userDataSyncPreview.conflicts.some(({ local }) => isEqual(local, previewResource.local)); - const localResource = isConflict ? previewResource.preview : previewResource.local; - const content = await userDataSyncService.resolveContent(localResource); - await userDataSyncWorkbenchService.userDataSyncPreview.accept(previewResource.syncResource, localResource, content || ''); + return that.acceptLocal(ManualSyncViewDataProvider.toUserDataSyncResourceGroup(handle.$treeItemHandle)); } }); @@ -189,11 +185,7 @@ export class UserDataManualSyncView extends Disposable { }); } async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise { - const userDataSyncWorkbenchService = accessor.get(IUserDataSyncWorkbenchService); - const userDataSyncService = accessor.get(IUserDataSyncService); - const previewResource: IUserDataSyncResourceGroup = ManualSyncViewDataProvider.toUserDataSyncResourceGroup(handle.$treeItemHandle); - const content = await userDataSyncService.resolveContent(previewResource.remote); - await userDataSyncWorkbenchService.userDataSyncPreview.accept(previewResource.syncResource, previewResource.remote, content || ''); + return that.acceptRemote(ManualSyncViewDataProvider.toUserDataSyncResourceGroup(handle.$treeItemHandle)); } }); @@ -213,9 +205,7 @@ export class UserDataManualSyncView extends Disposable { }); } async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise { - const userDataSyncWorkbenchService = accessor.get(IUserDataSyncWorkbenchService); - const previewResource: IUserDataSyncResourceGroup = ManualSyncViewDataProvider.toUserDataSyncResourceGroup(handle.$treeItemHandle); - await userDataSyncWorkbenchService.userDataSyncPreview.merge(previewResource.preview); + return that.mergeResource(ManualSyncViewDataProvider.toUserDataSyncResourceGroup(handle.$treeItemHandle)); } }); @@ -234,12 +224,7 @@ export class UserDataManualSyncView extends Disposable { }); } async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise { - const userDataSyncWorkbenchService = accessor.get(IUserDataSyncWorkbenchService); - const userDataSyncService = accessor.get(IUserDataSyncService); - const previewResource: IUserDataSyncResourceGroup = ManualSyncViewDataProvider.toUserDataSyncResourceGroup(handle.$treeItemHandle); - const resource = previewResource.remoteChange === Change.Deleted || previewResource.localChange === Change.Added ? previewResource.local : previewResource.remote; - const content = await userDataSyncService.resolveContent(resource); - await userDataSyncWorkbenchService.userDataSyncPreview.accept(previewResource.syncResource, resource, content || ''); + return that.deleteResource(ManualSyncViewDataProvider.toUserDataSyncResourceGroup(handle.$treeItemHandle)); } }); @@ -258,12 +243,7 @@ export class UserDataManualSyncView extends Disposable { }); } async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise { - const userDataSyncWorkbenchService = accessor.get(IUserDataSyncWorkbenchService); - const userDataSyncService = accessor.get(IUserDataSyncService); - const previewResource: IUserDataSyncResourceGroup = ManualSyncViewDataProvider.toUserDataSyncResourceGroup(handle.$treeItemHandle); - const resource = previewResource.remoteChange === Change.Added || previewResource.localChange === Change.Deleted ? previewResource.local : previewResource.remote; - const content = await userDataSyncService.resolveContent(resource); - await userDataSyncWorkbenchService.userDataSyncPreview.accept(previewResource.syncResource, resource, content || ''); + return that.addResource(ManualSyncViewDataProvider.toUserDataSyncResourceGroup(handle.$treeItemHandle)); } }); @@ -275,31 +255,85 @@ export class UserDataManualSyncView extends Disposable { }); } async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise { - const editorService = accessor.get(IEditorService); - const userDataSyncWorkbenchService = accessor.get(IUserDataSyncWorkbenchService); const previewResource: IUserDataSyncResourceGroup = ManualSyncViewDataProvider.toUserDataSyncResourceGroup(handle.$treeItemHandle); - const isConflict = userDataSyncWorkbenchService.userDataSyncPreview.conflicts.some(({ local }) => isEqual(local, previewResource.local)); - if (previewResource.localChange === Change.Added || previewResource.remoteChange === Change.Deleted) { - await editorService.openEditor({ resource: URI.revive(previewResource.remote), label: localize({ key: 'resourceLabel', comment: ['remote as in file in cloud'] }, "{0} (Remote)", basename(previewResource.remote)) }); - } else { - const leftResource = URI.revive(previewResource.remote); - const rightResource = isConflict ? URI.revive(previewResource.preview) : URI.revive(previewResource.local); - const leftResourceName = localize({ key: 'leftResourceName', comment: ['remote as in file in cloud'] }, "{0} (Remote)", basename(leftResource)); - const rightResourceName = localize({ key: 'rightResourceName', comment: ['local as in file in disk'] }, "{0} (Local)", basename(rightResource)); - await editorService.openEditor({ - leftResource, - rightResource, - label: localize('sideBySideLabels', "{0} ↔ {1}", leftResourceName, rightResourceName), - options: { - preserveFocus: true, - revealIfVisible: true, - }, - }); - } + return that.showChanges(previewResource); } }); } + private async push(): Promise { + return this.withProgress(() => this.userDataSyncPreview.push()); + } + + private async pull(): Promise { + return this.withProgress(() => this.userDataSyncPreview.pull()); + } + + private async merge(): Promise { + return this.withProgress(() => this.userDataSyncPreview.merge()); + } + + private async acceptLocal(previewResource: IUserDataSyncResourceGroup): Promise { + const isConflict = this.userDataSyncPreview.conflicts.some(({ local }) => isEqual(local, previewResource.local)); + const localResource = isConflict ? previewResource.preview : previewResource.local; + return this.withProgress(async () => { + const content = await this.userDataSyncService.resolveContent(localResource); + await this.userDataSyncPreview.accept(previewResource.syncResource, localResource, content || ''); + }); + } + + private async acceptRemote(previewResource: IUserDataSyncResourceGroup): Promise { + return this.withProgress(async () => { + const content = await this.userDataSyncService.resolveContent(previewResource.remote); + await this.userDataSyncPreview.accept(previewResource.syncResource, previewResource.remote, content || ''); + }); + } + + private async mergeResource(previewResource: IUserDataSyncResourceGroup): Promise { + return this.withProgress(() => this.userDataSyncPreview.merge(previewResource.preview)); + } + + private async deleteResource(previewResource: IUserDataSyncResourceGroup): Promise { + const resource = previewResource.remoteChange === Change.Deleted || previewResource.localChange === Change.Added ? previewResource.local : previewResource.remote; + return this.withProgress(async () => { + const content = await this.userDataSyncService.resolveContent(resource); + await this.userDataSyncPreview.accept(previewResource.syncResource, resource, content || ''); + }); + } + + private async addResource(previewResource: IUserDataSyncResourceGroup): Promise { + const resource = previewResource.remoteChange === Change.Added || previewResource.localChange === Change.Deleted ? previewResource.local : previewResource.remote; + return this.withProgress(async () => { + const content = await this.userDataSyncService.resolveContent(resource); + await this.userDataSyncPreview.accept(previewResource.syncResource, resource, content || ''); + }); + } + + private async showChanges(previewResource: IUserDataSyncResourceGroup): Promise { + const isConflict = this.userDataSyncPreview.conflicts.some(({ local }) => isEqual(local, previewResource.local)); + if (previewResource.localChange === Change.Added || previewResource.remoteChange === Change.Deleted) { + await this.editorService.openEditor({ resource: URI.revive(previewResource.remote), label: localize({ key: 'resourceLabel', comment: ['remote as in file in cloud'] }, "{0} (Remote)", basename(previewResource.remote)) }); + } else { + const leftResource = URI.revive(previewResource.remote); + const rightResource = isConflict ? URI.revive(previewResource.preview) : URI.revive(previewResource.local); + const leftResourceName = localize({ key: 'leftResourceName', comment: ['remote as in file in cloud'] }, "{0} (Remote)", basename(leftResource)); + const rightResourceName = localize({ key: 'rightResourceName', comment: ['local as in file in disk'] }, "{0} (Local)", basename(rightResource)); + await this.editorService.openEditor({ + leftResource, + rightResource, + label: localize('sideBySideLabels', "{0} ↔ {1}", leftResourceName, rightResourceName), + options: { + preserveFocus: true, + revealIfVisible: true, + }, + }); + } + } + + private withProgress(task: () => Promise): Promise { + return this.progressService.withProgress({ location: MANUAL_SYNC_VIEW_ID, delay: 500 }, task); + } + } class ManualSyncViewDataProvider implements ITreeViewDataProvider { @@ -344,27 +378,31 @@ class ManualSyncViewDataProvider implements ITreeViewDataProvider { } private getChanges(): ITreeItem[] { - return this.userDataSyncPreview.changes.map(change => ({ - handle: JSON.stringify(change), - resourceUri: change.remote, - themeIcon: FileThemeIcon, - description: getSyncAreaLabel(change.syncResource), - contextValue: `sync-resource-${change.localChange === Change.Added ? 'add-local' : change.localChange === Change.Deleted ? 'delete-local' : change.remoteChange === Change.Added ? 'add-remote' : change.remoteChange === Change.Deleted ? 'delete-remote' : 'modified'}-change`, - collapsibleState: TreeItemCollapsibleState.None, - command: { id: `workbench.actions.sync.showChanges`, title: '', arguments: [{ $treeViewId: '', $treeItemHandle: JSON.stringify(change) }] }, - })); + return this.userDataSyncPreview.changes.map(change => { + return { + handle: JSON.stringify(change), + resourceUri: change.remote, + themeIcon: FileThemeIcon, + description: getSyncAreaLabel(change.syncResource), + contextValue: `sync-resource-${change.localChange === Change.Added ? 'add-local' : change.localChange === Change.Deleted ? 'delete-local' : change.remoteChange === Change.Added ? 'add-remote' : change.remoteChange === Change.Deleted ? 'delete-remote' : 'modified'}-change`, + collapsibleState: TreeItemCollapsibleState.None, + command: { id: `workbench.actions.sync.showChanges`, title: '', arguments: [{ $treeViewId: '', $treeItemHandle: JSON.stringify(change) }] }, + }; + }); } private getConflicts(): ITreeItem[] { - return this.userDataSyncPreview.conflicts.map(conflict => ({ - handle: JSON.stringify(conflict), - resourceUri: conflict.remote, - themeIcon: FileThemeIcon, - description: getSyncAreaLabel(conflict.syncResource), - contextValue: `sync-resource-modified-conflict`, - collapsibleState: TreeItemCollapsibleState.None, - command: { id: `workbench.actions.sync.showChanges`, title: '', arguments: [{ $treeViewId: '', $treeItemHandle: JSON.stringify(conflict) }] }, - })); + return this.userDataSyncPreview.conflicts.map(conflict => { + return { + handle: JSON.stringify(conflict), + resourceUri: conflict.remote, + themeIcon: FileThemeIcon, + description: getSyncAreaLabel(conflict.syncResource), + contextValue: `sync-resource-modified-conflict`, + collapsibleState: TreeItemCollapsibleState.None, + command: { id: `workbench.actions.sync.showChanges`, title: '', arguments: [{ $treeViewId: '', $treeItemHandle: JSON.stringify(conflict) }] }, + }; + }); } static toUserDataSyncResourceGroup(handle: string): IUserDataSyncResourceGroup { diff --git a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts index cae0abd493b..8f5d4f6ecce 100644 --- a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts +++ b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts @@ -345,6 +345,8 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat /* Merge to sync globalState changes */ await task.merge(); + this.userDataSyncPreview.unsetManualSyncPreview(); + this.manualSyncViewEnablementContext.set(false); if (visibleViewContainer) { this.viewsService.openViewContainer(visibleViewContainer.id); @@ -558,19 +560,17 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat class UserDataSyncPreview extends Disposable implements IUserDataSyncPreview { + private _changes: ReadonlyArray = []; + get changes() { return Object.freeze(this._changes); } private _onDidChangeChanges = this._register(new Emitter>()); readonly onDidChangeChanges = this._onDidChangeChanges.event; + private _conflicts: ReadonlyArray = []; + get conflicts() { return Object.freeze(this._conflicts); } private _onDidChangeConflicts = this._register(new Emitter>()); readonly onDidChangeConflicts = this._onDidChangeConflicts.event; - private _changes: ReadonlyArray = []; - get changes() { return Object.freeze(this._changes); } - - private _conflicts: ReadonlyArray = []; - get conflicts() { return Object.freeze(this._conflicts); } - - private manualSync: { preview: [SyncResource, ISyncResourcePreview][], task: IManualSyncTask } | undefined; + private manualSync: { preview: [SyncResource, ISyncResourcePreview][], task: IManualSyncTask, disposables: DisposableStore } | undefined; constructor( private readonly userDataSyncService: IUserDataSyncService @@ -581,7 +581,16 @@ class UserDataSyncPreview extends Disposable implements IUserDataSyncPreview { } setManualSyncPreview(task: IManualSyncTask, preview: [SyncResource, ISyncResourcePreview][]): void { - this.manualSync = { task, preview }; + const disposables = new DisposableStore(); + this.manualSync = { task, preview, disposables }; + this.updateChanges(); + } + + unsetManualSyncPreview(): void { + if (this.manualSync) { + this.manualSync.disposables.dispose(); + this.manualSync = undefined; + } this.updateChanges(); }