Skip to content

Commit

Permalink
handle conflicts outside synchroniser
Browse files Browse the repository at this point in the history
  • Loading branch information
sandy081 committed Sep 16, 2019
1 parent 2d6ab9c commit e9e726f
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 45 deletions.
3 changes: 1 addition & 2 deletions src/vs/platform/userDataSync/common/userDataSync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,13 @@ export const SETTINGS_PREVIEW_RESOURCE = URI.file('Settings-Preview').with({ sch

export interface ISynchroniser {

readonly conflicts: URI | null;
readonly status: SyncStatus;
readonly onDidChangeStatus: Event<SyncStatus>;

readonly onDidChangeLocal: Event<void>;

sync(): Promise<boolean>;
continueSync(): Promise<boolean>;
handleConflicts(): boolean;
}

export const IUserDataSyncService = createDecorator<IUserDataSyncService>('IUserDataSyncService');
Expand Down
22 changes: 20 additions & 2 deletions src/vs/workbench/contrib/userData/browser/userData.contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { ResourceContextKey } from 'vs/workbench/common/resources';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { Event } from 'vs/base/common/event';
import { IHistoryService } from 'vs/workbench/services/history/common/history';

const CONTEXT_SYNC_STATE = new RawContextKey<string>('syncStatus', SyncStatus.Uninitialized);

Expand Down Expand Up @@ -93,6 +94,7 @@ class SyncActionsContribution extends Disposable implements IWorkbenchContributi
@IConfigurationService private readonly configurationService: IConfigurationService,
@IEditorService private readonly editorService: IEditorService,
@ITextFileService private readonly textFileService: ITextFileService,
@IHistoryService private readonly historyService: IHistoryService,
) {
super();
this.syncEnablementContext = CONTEXT_SYNC_STATE.bindTo(contextKeyService);
Expand Down Expand Up @@ -126,7 +128,7 @@ class SyncActionsContribution extends Disposable implements IWorkbenchContributi
[
{
label: localize('resolve', "Resolve Conflicts"),
run: () => this.userDataSyncService.handleConflicts()
run: () => this.handleConflicts()
}
]);
this.conflictsWarningDisposable.value = toDisposable(() => handle.close());
Expand Down Expand Up @@ -160,6 +162,22 @@ class SyncActionsContribution extends Disposable implements IWorkbenchContributi
}
}

private async handleConflicts(): Promise<void> {
const resource = this.userDataSyncService.conflicts;
if (resource) {
const resourceInput = {
resource,
options: {
preserveFocus: false,
pinned: false,
revealIfVisible: true,
},
mode: 'jsonc'
};
this.editorService.openEditor(resourceInput).then(() => this.historyService.remove(resourceInput));
}
}

private registerActions(): void {

const startSyncMenuItem: IMenuItem = {
Expand Down Expand Up @@ -194,7 +212,7 @@ class SyncActionsContribution extends Disposable implements IWorkbenchContributi
},
when: CONTEXT_SYNC_STATE.isEqualTo(SyncStatus.HasConflicts),
};
CommandsRegistry.registerCommand(resolveConflictsMenuItem.command.id, serviceAccessor => serviceAccessor.get(IUserDataSyncService).handleConflicts());
CommandsRegistry.registerCommand(resolveConflictsMenuItem.command.id, () => this.handleConflicts());
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, resolveConflictsMenuItem);
MenuRegistry.appendMenuItem(MenuId.CommandPalette, resolveConflictsMenuItem);

Expand Down
39 changes: 10 additions & 29 deletions src/vs/workbench/services/userData/common/settingsSync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,15 @@
import { Disposable } from 'vs/base/common/lifecycle';
import { IFileService, FileSystemProviderErrorCode, FileSystemProviderError, IFileContent } from 'vs/platform/files/common/files';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IUserData, UserDataSyncStoreError, UserDataSyncStoreErrorCode, ISynchroniser, SyncStatus, SETTINGS_PREVIEW_RESOURCE, ISettingsMergeService, IUserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { IUserData, UserDataSyncStoreError, UserDataSyncStoreErrorCode, ISynchroniser, SyncStatus, ISettingsMergeService, IUserDataSyncStoreService, SETTINGS_PREVIEW_RESOURCE } from 'vs/platform/userDataSync/common/userDataSync';
import { VSBuffer } from 'vs/base/common/buffer';
import { parse, ParseError } from 'vs/base/common/json';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { localize } from 'vs/nls';
import { Emitter, Event } from 'vs/base/common/event';
import { ILogService } from 'vs/platform/log/common/log';
import { IHistoryService } from 'vs/workbench/services/history/common/history';
import { CancelablePromise, createCancelablePromise, ThrottledDelayer } from 'vs/base/common/async';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { URI } from 'vs/base/common/uri';

interface ISyncPreviewResult {
readonly fileContent: IFileContent | null;
Expand All @@ -41,15 +40,15 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
private _onDidChangeLocal: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChangeLocal: Event<void> = this._onDidChangeLocal.event;

readonly conflicts: URI = SETTINGS_PREVIEW_RESOURCE;

constructor(
@IFileService private readonly fileService: IFileService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IStorageService private readonly storageService: IStorageService,
@IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService,
@IEditorService private readonly editorService: IEditorService,
@ISettingsMergeService private readonly settingsMergeService: ISettingsMergeService,
@ILogService private readonly logService: ILogService,
@IHistoryService private readonly historyService: IHistoryService,
) {
super();
this.throttledDelayer = this._register(new ThrottledDelayer<void>(500));
Expand Down Expand Up @@ -111,24 +110,6 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
}
}

handleConflicts(): boolean {
if (this.status !== SyncStatus.HasConflicts) {
return false;
}
const resourceInput = {
resource: SETTINGS_PREVIEW_RESOURCE,
label: localize('Settings Conflicts', "Local ↔ Remote (Settings Conflicts)"),
options: {
preserveFocus: false,
pinned: false,
revealIfVisible: true,
},
mode: 'jsonc'
};
this.editorService.openEditor(resourceInput).then(() => this.historyService.remove(resourceInput));
return true;
}

async continueSync(): Promise<boolean> {
if (this.status !== SyncStatus.HasConflicts) {
return false;
Expand All @@ -142,8 +123,8 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
return;
}

if (await this.fileService.exists(SETTINGS_PREVIEW_RESOURCE)) {
const settingsPreivew = await this.fileService.readFile(SETTINGS_PREVIEW_RESOURCE);
if (await this.fileService.exists(this.conflicts)) {
const settingsPreivew = await this.fileService.readFile(this.conflicts);
const content = settingsPreivew.value.toString();
if (this.hasErrors(content)) {
return Promise.reject(localize('errorInvalidSettings', "Unable to sync settings. Please resolve conflicts without any errors/warnings and try again."));
Expand All @@ -162,7 +143,7 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
}

// Delete the preview
await this.fileService.del(SETTINGS_PREVIEW_RESOURCE);
await this.fileService.del(this.conflicts);
}

this.syncPreviewResultPromise = null;
Expand Down Expand Up @@ -194,15 +175,15 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
if (fileContent && !remoteUserData) {
this.logService.trace('Settings Sync: Remote contents does not exist. So sync with settings file.');
hasRemoteChanged = true;
await this.fileService.writeFile(SETTINGS_PREVIEW_RESOURCE, VSBuffer.fromString(fileContent.value.toString()));
await this.fileService.writeFile(this.conflicts, VSBuffer.fromString(fileContent.value.toString()));
return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts };
}

// Settings file does not exist, so sync with remote contents.
if (remoteUserData && !fileContent) {
this.logService.trace('Settings Sync: Settings file does not exist. So sync with remote contents');
hasLocalChanged = true;
await this.fileService.writeFile(SETTINGS_PREVIEW_RESOURCE, VSBuffer.fromString(remoteUserData.content));
await this.fileService.writeFile(this.conflicts, VSBuffer.fromString(remoteUserData.content));
return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts };
}

Expand All @@ -221,7 +202,7 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
if (hasLocalChanged || hasRemoteChanged) {
// Sync only if there are changes
hasConflicts = this.hasErrors(mergeContent);
await this.fileService.writeFile(SETTINGS_PREVIEW_RESOURCE, VSBuffer.fromString(mergeContent));
await this.fileService.writeFile(this.conflicts, VSBuffer.fromString(mergeContent));
return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts };
}
}
Expand Down
18 changes: 6 additions & 12 deletions src/vs/workbench/services/userData/common/userDataSyncService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { SettingsSynchroniser } from 'vs/workbench/services/userData/common/sett
import { Emitter, Event } from 'vs/base/common/event';
import { IFileService } from 'vs/platform/files/common/files';
import { InMemoryFileSystemProvider } from 'vs/workbench/services/userData/common/inMemoryUserDataProvider';
import { URI } from 'vs/base/common/uri';

export class UserDataSyncService extends Disposable implements IUserDataSyncService {

Expand Down Expand Up @@ -40,6 +41,11 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
this.onDidChangeLocal = Event.any(...this.synchronisers.map(s => s.onDidChangeLocal));
}

get conflicts(): URI | null {
const synchroniser = this.synchronisers.filter(s => s.status === SyncStatus.HasConflicts)[0];
return synchroniser ? synchroniser.conflicts : null;
}

async sync(): Promise<boolean> {
if (!this.userDataSyncStoreService.enabled) {
throw new Error('Not enabled');
Expand All @@ -64,18 +70,6 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
return false;
}

handleConflicts(): boolean {
if (!this.userDataSyncStoreService.enabled) {
throw new Error('Not enabled');
}
for (const synchroniser of this.synchronisers) {
if (synchroniser.handleConflicts()) {
return true;
}
}
return false;
}

private updateStatus(): void {
this.setStatus(this.computeStatus());
}
Expand Down

0 comments on commit e9e726f

Please sign in to comment.