diff --git a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts index 4aa6495562c..466d0f81a84 100644 --- a/src/vs/platform/userDataSync/common/abstractSynchronizer.ts +++ b/src/vs/platform/userDataSync/common/abstractSynchronizer.ts @@ -384,7 +384,7 @@ export abstract class AbstractSynchroniser extends Disposable { } } - async accept(resource: URI, content: string): Promise { + async accept(resource: URI, content: string | null): Promise { await this.updateSyncResourcePreview(resource, async (resourcePreview) => { const updatedResourcePreview = await this.updateResourcePreview(resourcePreview, resource, content); return { @@ -397,7 +397,7 @@ export abstract class AbstractSynchroniser extends Disposable { async merge(resource: URI): Promise { await this.updateSyncResourcePreview(resource, async (resourcePreview) => { - const updatedResourcePreview = await this.updateResourcePreview(resourcePreview, resourcePreview.previewResource, resourcePreview.previewContent || ''); + const updatedResourcePreview = await this.updateResourcePreview(resourcePreview, resourcePreview.previewResource, resourcePreview.previewContent); return { ...updatedResourcePreview, mergeState: resourcePreview.hasConflicts ? MergeState.Conflict : MergeState.Accepted @@ -409,7 +409,7 @@ export abstract class AbstractSynchroniser extends Disposable { async discard(resource: URI): Promise { await this.updateSyncResourcePreview(resource, async (resourcePreview) => { await this.fileService.writeFile(resourcePreview.previewResource, VSBuffer.fromString(resourcePreview.previewContent || '')); - const updatedResourcePreview = await this.updateResourcePreview(resourcePreview, resourcePreview.previewResource, resourcePreview.previewContent || ''); + const updatedResourcePreview = await this.updateResourcePreview(resourcePreview, resourcePreview.previewResource, resourcePreview.previewContent); return { ...updatedResourcePreview, mergeState: MergeState.Preview @@ -448,7 +448,7 @@ export abstract class AbstractSynchroniser extends Disposable { } } - protected async updateResourcePreview(resourcePreview: IResourcePreview, resource: URI, acceptedContent: string): Promise { + protected async updateResourcePreview(resourcePreview: IResourcePreview, resource: URI, acceptedContent: string | null): Promise { return { ...resourcePreview, acceptedContent @@ -550,13 +550,13 @@ export abstract class AbstractSynchroniser extends Disposable { if (syncPreview) { for (const resourcePreview of syncPreview.resourcePreviews) { if (isEqual(resourcePreview.acceptedResource, uri)) { - return resourcePreview.acceptedContent || ''; + return resourcePreview.acceptedContent; } if (isEqual(resourcePreview.remoteResource, uri)) { - return resourcePreview.remoteContent || ''; + return resourcePreview.remoteContent; } if (isEqual(resourcePreview.localResource, uri)) { - return resourcePreview.localContent || ''; + return resourcePreview.localContent; } } } diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts index 5101b59bf25..7cc8738fdf9 100644 --- a/src/vs/platform/userDataSync/common/extensionsSync.ts +++ b/src/vs/platform/userDataSync/common/extensionsSync.ts @@ -183,7 +183,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse } } - protected async updateResourcePreview(resourcePreview: IExtensionResourcePreview, resource: URI, acceptedContent: string): Promise { + protected async updateResourcePreview(resourcePreview: IExtensionResourcePreview, resource: URI, acceptedContent: string | null): Promise { if (isEqual(resource, this.localResource)) { const remoteExtensions = resourcePreview.remoteContent ? JSON.parse(resourcePreview.remoteContent) : null; return this.getPushPreview(remoteExtensions); diff --git a/src/vs/platform/userDataSync/common/globalStateSync.ts b/src/vs/platform/userDataSync/common/globalStateSync.ts index f04ae509afb..de10325223a 100644 --- a/src/vs/platform/userDataSync/common/globalStateSync.ts +++ b/src/vs/platform/userDataSync/common/globalStateSync.ts @@ -178,7 +178,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs } } - protected async updateResourcePreview(resourcePreview: IGlobalStateResourcePreview, resource: URI, acceptedContent: string): Promise { + protected async updateResourcePreview(resourcePreview: IGlobalStateResourcePreview, resource: URI, acceptedContent: string | null): Promise { if (isEqual(this.localResource, resource)) { return this.getPushPreview(resourcePreview.remoteContent); } diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts index ac13e9650c9..87d352779bf 100644 --- a/src/vs/platform/userDataSync/common/settingsSync.ts +++ b/src/vs/platform/userDataSync/common/settingsSync.ts @@ -199,8 +199,8 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement }]; } - protected async updateResourcePreview(resourcePreview: IFileResourcePreview, resource: URI, acceptedContent: string): Promise { - if (acceptedContent && isEqual(resource, this.previewResource) || isEqual(resource, this.remoteResource)) { + protected async updateResourcePreview(resourcePreview: IFileResourcePreview, resource: URI, acceptedContent: string | null): Promise { + if (acceptedContent && (isEqual(resource, this.previewResource) || isEqual(resource, this.remoteResource))) { const formatUtils = await this.getFormattingOptions(); // Add ignored settings from local file content const ignoredSettings = await this.getIgnoredSettings(); diff --git a/src/vs/platform/userDataSync/common/snippetsSync.ts b/src/vs/platform/userDataSync/common/snippetsSync.ts index 3842f4513b6..b25c7d727bd 100644 --- a/src/vs/platform/userDataSync/common/snippetsSync.ts +++ b/src/vs/platform/userDataSync/common/snippetsSync.ts @@ -96,12 +96,12 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD return this.getResourcePreviews(mergeResult, local, remoteSnippets || {}); } - protected async updateResourcePreview(resourcePreview: IFileResourcePreview, resource: URI, acceptedContent: string): Promise { + protected async updateResourcePreview(resourcePreview: IFileResourcePreview, resource: URI, acceptedContent: string | null): Promise { return { ...resourcePreview, - acceptedContent: acceptedContent || null, - localChange: this.computeLocalChange(resourcePreview, resource, acceptedContent || null), - remoteChange: this.computeRemoteChange(resourcePreview, resource, acceptedContent || null), + acceptedContent, + localChange: this.computeLocalChange(resourcePreview, resource, acceptedContent), + remoteChange: this.computeRemoteChange(resourcePreview, resource, acceptedContent), }; } diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index 85b8dff20fa..782a2db69c5 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -360,7 +360,7 @@ export interface IUserDataSynchroniser { stop(): Promise; preview(manifest: IUserDataManifest | null, headers: IHeaders): Promise; - accept(resource: URI, content: string): Promise; + accept(resource: URI, content: string | null): Promise; merge(resource: URI): Promise; discard(resource: URI): Promise; apply(force: boolean, headers: IHeaders): Promise; @@ -400,7 +400,7 @@ export interface IManualSyncTask extends IDisposable { readonly manifest: IUserDataManifest | null; readonly onSynchronizeResources: Event<[SyncResource, URI[]][]>; preview(): Promise<[SyncResource, ISyncResourcePreview][]>; - accept(resource: URI, content: string): Promise<[SyncResource, ISyncResourcePreview][]>; + accept(resource: URI, content: string | null): Promise<[SyncResource, ISyncResourcePreview][]>; merge(resource: URI): Promise<[SyncResource, ISyncResourcePreview][]>; discard(resource: URI): Promise<[SyncResource, ISyncResourcePreview][]>; apply(): Promise<[SyncResource, ISyncResourcePreview][]>; @@ -437,7 +437,7 @@ export interface IUserDataSyncService { hasLocalData(): Promise; hasPreviouslySynced(): Promise; resolveContent(resource: URI): Promise; - accept(resource: SyncResource, conflictResource: URI, content: string, apply: boolean): Promise; + accept(resource: SyncResource, conflictResource: URI, content: string | null, apply: boolean): Promise; getLocalSyncResourceHandles(resource: SyncResource): Promise; getRemoteSyncResourceHandles(resource: SyncResource): Promise; diff --git a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts index 97789537313..015faac4543 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts @@ -65,7 +65,7 @@ export class UserDataSyncChannel implements IServerChannel { private async createManualSyncTask(): Promise<{ id: string, manifest: IUserDataManifest | null }> { const manualSyncTask = await this.service.createManualSyncTask(); - const manualSyncTaskChannel = new ManualSyncTaskChannel(manualSyncTask); + const manualSyncTaskChannel = new ManualSyncTaskChannel(manualSyncTask, this.logService); this.server.registerChannel(`manualSyncTask-${manualSyncTask.id}`, manualSyncTaskChannel); return { id: manualSyncTask.id, manifest: manualSyncTask.manifest }; } @@ -73,7 +73,10 @@ export class UserDataSyncChannel implements IServerChannel { class ManualSyncTaskChannel implements IServerChannel { - constructor(private readonly manualSyncTask: IManualSyncTask) { } + constructor( + private readonly manualSyncTask: IManualSyncTask, + private readonly logService: ILogService + ) { } listen(_: unknown, event: string): Event { switch (event) { @@ -83,6 +86,16 @@ class ManualSyncTaskChannel implements IServerChannel { } async call(context: any, command: string, args?: any): Promise { + try { + const result = await this._call(context, command, args); + return result; + } catch (e) { + this.logService.error(e); + throw e; + } + } + + private async _call(context: any, command: string, args?: any): Promise { switch (command) { case 'preview': return this.manualSyncTask.preview(); case 'accept': return this.manualSyncTask.accept(URI.revive(args[0]), args[1]); diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index 289427a0424..7d5dd64ce26 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -256,7 +256,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ } } - async accept(syncResource: SyncResource, resource: URI, content: string, apply: boolean): Promise { + async accept(syncResource: SyncResource, resource: URI, content: string | null, apply: boolean): Promise { await this.checkEnablement(); const synchroniser = this.getSynchroniser(syncResource); await synchroniser.accept(resource, content); @@ -456,7 +456,7 @@ class ManualSyncTask extends Disposable implements IManualSyncTask { return this.previews; } - async accept(resource: URI, content: string): Promise<[SyncResource, ISyncResourcePreview][]> { + async accept(resource: URI, content: string | null): Promise<[SyncResource, ISyncResourcePreview][]> { return this.performAction(resource, sychronizer => sychronizer.accept(resource, content)); } @@ -555,7 +555,7 @@ class ManualSyncTask extends Disposable implements IManualSyncTask { this._onSynchronizeResources.fire(this.synchronizingResources); const synchroniser = this.synchronisers.find(s => s.resource === syncResource)!; for (const resourcePreview of preview.resourcePreviews) { - const content = await synchroniser.resolveContent(resourcePreview.remoteResource) || ''; + const content = await synchroniser.resolveContent(resourcePreview.remoteResource); await synchroniser.accept(resourcePreview.remoteResource, content); } await synchroniser.apply(true, this.syncHeaders); @@ -577,7 +577,7 @@ class ManualSyncTask extends Disposable implements IManualSyncTask { this._onSynchronizeResources.fire(this.synchronizingResources); const synchroniser = this.synchronisers.find(s => s.resource === syncResource)!; for (const resourcePreview of preview.resourcePreviews) { - const content = await synchroniser.resolveContent(resourcePreview.localResource) || ''; + const content = await synchroniser.resolveContent(resourcePreview.localResource); await synchroniser.accept(resourcePreview.localResource, content); } await synchroniser.apply(true, this.syncHeaders); diff --git a/src/vs/platform/userDataSync/test/common/keybindingsSync.test.ts b/src/vs/platform/userDataSync/test/common/keybindingsSync.test.ts index 9fd790befce..49d439bfd60 100644 --- a/src/vs/platform/userDataSync/test/common/keybindingsSync.test.ts +++ b/src/vs/platform/userDataSync/test/common/keybindingsSync.test.ts @@ -83,4 +83,20 @@ suite('KeybindingsSync', () => { assert.equal(testObject.getKeybindingsContentFromSyncContent(lastSyncUserData!.syncData!.content!), '[]'); }); + test('test apply remote when keybindings file does not exist', async () => { + const fileService = client.instantiationService.get(IFileService); + const keybindingsResource = client.instantiationService.get(IEnvironmentService).keybindingsResource; + if (await fileService.exists(keybindingsResource)) { + await fileService.del(keybindingsResource); + } + + const preview = (await testObject.preview(await client.manifest()))!; + + server.reset(); + const content = await testObject.resolveContent(preview.resourcePreviews[0].remoteResource); + await testObject.accept(preview.resourcePreviews[0].remoteResource, content); + await testObject.apply(false); + assert.deepEqual(server.requests, []); + }); + }); diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataManualSyncView.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataManualSyncView.ts index 970c0e32331..5854248f34d 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataManualSyncView.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataManualSyncView.ts @@ -252,7 +252,7 @@ export class UserDataManualSyncViewPane extends TreeViewPane { private async acceptLocal(userDataSyncResource: IUserDataSyncResource): Promise { await this.withProgress(async () => { const content = await this.userDataSyncService.resolveContent(userDataSyncResource.local); - await this.userDataSyncPreview.accept(userDataSyncResource.syncResource, userDataSyncResource.local, content || ''); + await this.userDataSyncPreview.accept(userDataSyncResource.syncResource, userDataSyncResource.local, content); }); await this.reopen(userDataSyncResource); } @@ -260,7 +260,7 @@ export class UserDataManualSyncViewPane extends TreeViewPane { private async acceptRemote(userDataSyncResource: IUserDataSyncResource): Promise { await this.withProgress(async () => { const content = await this.userDataSyncService.resolveContent(userDataSyncResource.remote); - await this.userDataSyncPreview.accept(userDataSyncResource.syncResource, userDataSyncResource.remote, content || ''); + await this.userDataSyncPreview.accept(userDataSyncResource.syncResource, userDataSyncResource.remote, content); }); await this.reopen(userDataSyncResource); } diff --git a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts index 6d6701ebb64..6c446ffc535 100644 --- a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts +++ b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts @@ -593,7 +593,7 @@ class UserDataSyncPreview extends Disposable implements IUserDataSyncPreview { this.updateResources(); } - async accept(syncResource: SyncResource, resource: URI, content: string): Promise { + async accept(syncResource: SyncResource, resource: URI, content: string | null): Promise { if (this.manualSync) { const syncPreview = await this.manualSync.task.accept(resource, content); this.updatePreview(syncPreview); diff --git a/src/vs/workbench/services/userDataSync/common/userDataSync.ts b/src/vs/workbench/services/userDataSync/common/userDataSync.ts index 1bac7f1a6c4..f5d0e9b2d49 100644 --- a/src/vs/workbench/services/userDataSync/common/userDataSync.ts +++ b/src/vs/workbench/services/userDataSync/common/userDataSync.ts @@ -20,7 +20,7 @@ export interface IUserDataSyncPreview { readonly onDidChangeResources: Event>; readonly resources: ReadonlyArray; - accept(syncResource: SyncResource, resource: URI, content: string): Promise; + accept(syncResource: SyncResource, resource: URI, content: string | null): Promise; merge(resource?: URI): Promise; discard(resource?: URI): Promise; pull(): Promise; diff --git a/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts index dbe45d1ab02..8e74f8b5f48 100644 --- a/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts +++ b/src/vs/workbench/services/userDataSync/electron-browser/userDataSyncService.ts @@ -102,7 +102,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ return this.channel.call('hasLocalData'); } - accept(syncResource: SyncResource, resource: URI, content: string, apply: boolean): Promise { + accept(syncResource: SyncResource, resource: URI, content: string | null, apply: boolean): Promise { return this.channel.call('accept', [syncResource, resource, content, apply]); } @@ -186,7 +186,7 @@ class ManualSyncTask implements IManualSyncTask { return this.deserializePreviews(previews); } - async accept(resource: URI, content: string): Promise<[SyncResource, ISyncResourcePreview][]> { + async accept(resource: URI, content: string | null): Promise<[SyncResource, ISyncResourcePreview][]> { const previews = await this.channel.call<[SyncResource, ISyncResourcePreview][]>('accept', [resource, content]); return this.deserializePreviews(previews); }