#86681 Change API to access conflict settings in sync way

This commit is contained in:
Sandeep Somavarapu 2020-01-09 13:22:46 +01:00
parent 4cfb719aa1
commit cd4472ed5f
4 changed files with 47 additions and 23 deletions

View file

@ -19,6 +19,8 @@ import { startsWith } from 'vs/base/common/strings';
import { CancellationToken } from 'vs/base/common/cancellation';
import { computeRemoteContent, merge } from 'vs/platform/userDataSync/common/settingsMerge';
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
import * as arrays from 'vs/base/common/arrays';
import * as objects from 'vs/base/common/objects';
interface ISyncPreviewResult {
readonly fileContent: IFileContent | null;
@ -41,6 +43,11 @@ export class SettingsSynchroniser extends Disposable implements ISettingsSyncSer
private _onDidChangStatus: Emitter<SyncStatus> = this._register(new Emitter<SyncStatus>());
readonly onDidChangeStatus: Event<SyncStatus> = this._onDidChangStatus.event;
private _conflicts: IConflictSetting[] = [];
get conflicts(): IConflictSetting[] { return this._conflicts; }
private _onDidChangeConflicts: Emitter<IConflictSetting[]> = this._register(new Emitter<IConflictSetting[]>());
readonly onDidChangeConflicts: Event<IConflictSetting[]> = this._onDidChangeConflicts.event;
private _onDidChangeLocal: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChangeLocal: Event<void> = this._onDidChangeLocal.event;
@ -65,6 +72,18 @@ export class SettingsSynchroniser extends Disposable implements ISettingsSyncSer
this._status = status;
this._onDidChangStatus.fire(status);
}
if (this._status !== SyncStatus.HasConflicts) {
this.setConflicts([]);
}
}
private setConflicts(conflicts: IConflictSetting[]): void {
if (!arrays.equals(this.conflicts, conflicts,
(a, b) => a.key === b.key && objects.equals(a.localValue, b.localValue) && objects.equals(a.remoteValue, b.remoteValue))
) {
this._conflicts = conflicts;
this._onDidChangeConflicts.fire(conflicts);
}
}
async sync(_continue?: boolean): Promise<boolean> {
@ -83,6 +102,8 @@ export class SettingsSynchroniser extends Disposable implements ISettingsSyncSer
return false;
}
this.logService.trace('Settings: Started synchronizing settings...');
this.setStatus(SyncStatus.Syncing);
return this.doSync([]);
}
@ -96,28 +117,15 @@ export class SettingsSynchroniser extends Disposable implements ISettingsSyncSer
this.setStatus(SyncStatus.Idle);
}
async getConflicts(): Promise<IConflictSetting[]> {
if (!this.syncPreviewResultPromise) {
return [];
}
if (this.status !== SyncStatus.HasConflicts) {
return [];
}
const preview = await this.syncPreviewResultPromise;
return preview.conflicts;
}
async resolveConflicts(resolvedConflicts: { key: string, value: any | undefined }[]): Promise<void> {
if (this.status === SyncStatus.HasConflicts) {
this.stop();
this.syncPreviewResultPromise!.cancel();
this.syncPreviewResultPromise = null;
await this.doSync(resolvedConflicts);
}
}
private async doSync(resolvedConflicts: { key: string, value: any | undefined }[]): Promise<boolean> {
this.logService.trace('Settings: Started synchronizing settings...');
this.setStatus(SyncStatus.Syncing);
try {
const result = await this.getPreview(resolvedConflicts);
if (result.conflicts.length) {
@ -222,12 +230,13 @@ export class SettingsSynchroniser extends Disposable implements ISettingsSyncSer
if (remoteContent) {
const localContent: string = fileContent ? fileContent.value.toString() : '{}';
// No action when there are errors
if (this.hasErrors(localContent)) {
this.logService.error('Settings: Unable to sync settings as there are errors/warning in settings file.');
return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, conflicts };
}
if (!lastSyncData // First time sync
else if (!lastSyncData // First time sync
|| lastSyncData.content !== localContent // Local has forwarded
|| lastSyncData.content !== remoteContent // Remote has forwarded
) {
@ -255,6 +264,7 @@ export class SettingsSynchroniser extends Disposable implements ISettingsSyncSer
await this.fileService.writeFile(this.environmentService.settingsSyncPreviewResource, VSBuffer.fromString(previewContent));
}
this.setConflicts(conflicts);
return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, conflicts };
}

View file

@ -200,7 +200,8 @@ export interface IConflictSetting {
export const ISettingsSyncService = createDecorator<ISettingsSyncService>('ISettingsSyncService');
export interface ISettingsSyncService extends ISynchroniser {
_serviceBrand: any;
getConflicts(): Promise<IConflictSetting[]>;
readonly onDidChangeConflicts: Event<IConflictSetting[]>;
readonly conflicts: IConflictSetting[];
resolveConflicts(resolvedConflicts: { key: string, value: any | undefined }[]): Promise<void>;
}

View file

@ -42,6 +42,7 @@ export class SettingsSyncChannel implements IServerChannel {
switch (event) {
case 'onDidChangeStatus': return this.service.onDidChangeStatus;
case 'onDidChangeLocal': return this.service.onDidChangeLocal;
case 'onDidChangeConflicts': return this.service.onDidChangeConflicts;
}
throw new Error(`Event not found: ${event}`);
}
@ -50,8 +51,8 @@ export class SettingsSyncChannel implements IServerChannel {
switch (command) {
case 'sync': return this.service.sync(args[0]);
case '_getInitialStatus': return Promise.resolve(this.service.status);
case '_getInitialConflicts': return Promise.resolve(this.service.conflicts);
case 'stop': this.service.stop(); return Promise.resolve();
case 'getConflicts': return this.service.getConflicts();
case 'resolveConflicts': return this.service.resolveConflicts(args[0]);
}
throw new Error('Invalid call');

View file

@ -21,6 +21,11 @@ export class SettingsSyncService extends Disposable implements ISettingsSyncServ
private _onDidChangeStatus: Emitter<SyncStatus> = this._register(new Emitter<SyncStatus>());
readonly onDidChangeStatus: Event<SyncStatus> = this._onDidChangeStatus.event;
private _conflicts: IConflictSetting[] = [];
get conflicts(): IConflictSetting[] { return this._conflicts; }
private _onDidChangeConflicts: Emitter<IConflictSetting[]> = this._register(new Emitter<IConflictSetting[]>());
readonly onDidChangeConflicts: Event<IConflictSetting[]> = this._onDidChangeConflicts.event;
get onDidChangeLocal(): Event<void> { return this.channel.listen('onDidChangeLocal'); }
constructor(
@ -32,6 +37,12 @@ export class SettingsSyncService extends Disposable implements ISettingsSyncServ
this.updateStatus(status);
this._register(this.channel.listen<SyncStatus>('onDidChangeStatus')(status => this.updateStatus(status)));
});
this.channel.call<IConflictSetting[]>('_getInitialConflicts').then(conflicts => {
if (conflicts.length) {
this.updateConflicts(conflicts);
}
this._register(this.channel.listen<IConflictSetting[]>('onDidChangeConflicts')(conflicts => this.updateConflicts(conflicts)));
});
}
sync(_continue?: boolean): Promise<boolean> {
@ -42,10 +53,6 @@ export class SettingsSyncService extends Disposable implements ISettingsSyncServ
this.channel.call('stop');
}
getConflicts(): Promise<IConflictSetting[]> {
return this.channel.call<IConflictSetting[]>('getConflicts');
}
resolveConflicts(conflicts: { key: string, value: any | undefined }[]): Promise<void> {
return this.channel.call('resolveConflicts', [conflicts]);
}
@ -55,6 +62,11 @@ export class SettingsSyncService extends Disposable implements ISettingsSyncServ
this._onDidChangeStatus.fire(status);
}
private async updateConflicts(conflicts: IConflictSetting[]): Promise<void> {
this._conflicts = conflicts;
this._onDidChangeConflicts.fire(conflicts);
}
}
registerSingleton(ISettingsSyncService, SettingsSyncService);