提交 e9e726f6 编写于 作者: S Sandeep Somavarapu

handle conflicts outside synchroniser

上级 2d6ab9cb
...@@ -91,14 +91,13 @@ export const SETTINGS_PREVIEW_RESOURCE = URI.file('Settings-Preview').with({ sch ...@@ -91,14 +91,13 @@ export const SETTINGS_PREVIEW_RESOURCE = URI.file('Settings-Preview').with({ sch
export interface ISynchroniser { export interface ISynchroniser {
readonly conflicts: URI | null;
readonly status: SyncStatus; readonly status: SyncStatus;
readonly onDidChangeStatus: Event<SyncStatus>; readonly onDidChangeStatus: Event<SyncStatus>;
readonly onDidChangeLocal: Event<void>; readonly onDidChangeLocal: Event<void>;
sync(): Promise<boolean>; sync(): Promise<boolean>;
continueSync(): Promise<boolean>; continueSync(): Promise<boolean>;
handleConflicts(): boolean;
} }
export const IUserDataSyncService = createDecorator<IUserDataSyncService>('IUserDataSyncService'); export const IUserDataSyncService = createDecorator<IUserDataSyncService>('IUserDataSyncService');
......
...@@ -24,6 +24,7 @@ import { ResourceContextKey } from 'vs/workbench/common/resources'; ...@@ -24,6 +24,7 @@ import { ResourceContextKey } from 'vs/workbench/common/resources';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { Event } from 'vs/base/common/event'; 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); const CONTEXT_SYNC_STATE = new RawContextKey<string>('syncStatus', SyncStatus.Uninitialized);
...@@ -93,6 +94,7 @@ class SyncActionsContribution extends Disposable implements IWorkbenchContributi ...@@ -93,6 +94,7 @@ class SyncActionsContribution extends Disposable implements IWorkbenchContributi
@IConfigurationService private readonly configurationService: IConfigurationService, @IConfigurationService private readonly configurationService: IConfigurationService,
@IEditorService private readonly editorService: IEditorService, @IEditorService private readonly editorService: IEditorService,
@ITextFileService private readonly textFileService: ITextFileService, @ITextFileService private readonly textFileService: ITextFileService,
@IHistoryService private readonly historyService: IHistoryService,
) { ) {
super(); super();
this.syncEnablementContext = CONTEXT_SYNC_STATE.bindTo(contextKeyService); this.syncEnablementContext = CONTEXT_SYNC_STATE.bindTo(contextKeyService);
...@@ -126,7 +128,7 @@ class SyncActionsContribution extends Disposable implements IWorkbenchContributi ...@@ -126,7 +128,7 @@ class SyncActionsContribution extends Disposable implements IWorkbenchContributi
[ [
{ {
label: localize('resolve', "Resolve Conflicts"), label: localize('resolve', "Resolve Conflicts"),
run: () => this.userDataSyncService.handleConflicts() run: () => this.handleConflicts()
} }
]); ]);
this.conflictsWarningDisposable.value = toDisposable(() => handle.close()); this.conflictsWarningDisposable.value = toDisposable(() => handle.close());
...@@ -160,6 +162,22 @@ class SyncActionsContribution extends Disposable implements IWorkbenchContributi ...@@ -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 { private registerActions(): void {
const startSyncMenuItem: IMenuItem = { const startSyncMenuItem: IMenuItem = {
...@@ -194,7 +212,7 @@ class SyncActionsContribution extends Disposable implements IWorkbenchContributi ...@@ -194,7 +212,7 @@ class SyncActionsContribution extends Disposable implements IWorkbenchContributi
}, },
when: CONTEXT_SYNC_STATE.isEqualTo(SyncStatus.HasConflicts), 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.GlobalActivity, resolveConflictsMenuItem);
MenuRegistry.appendMenuItem(MenuId.CommandPalette, resolveConflictsMenuItem); MenuRegistry.appendMenuItem(MenuId.CommandPalette, resolveConflictsMenuItem);
......
...@@ -6,16 +6,15 @@ ...@@ -6,16 +6,15 @@
import { Disposable } from 'vs/base/common/lifecycle'; import { Disposable } from 'vs/base/common/lifecycle';
import { IFileService, FileSystemProviderErrorCode, FileSystemProviderError, IFileContent } from 'vs/platform/files/common/files'; import { IFileService, FileSystemProviderErrorCode, FileSystemProviderError, IFileContent } from 'vs/platform/files/common/files';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; 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 { VSBuffer } from 'vs/base/common/buffer';
import { parse, ParseError } from 'vs/base/common/json'; import { parse, ParseError } from 'vs/base/common/json';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { localize } from 'vs/nls'; import { localize } from 'vs/nls';
import { Emitter, Event } from 'vs/base/common/event'; import { Emitter, Event } from 'vs/base/common/event';
import { ILogService } from 'vs/platform/log/common/log'; 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 { CancelablePromise, createCancelablePromise, ThrottledDelayer } from 'vs/base/common/async';
import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { URI } from 'vs/base/common/uri';
interface ISyncPreviewResult { interface ISyncPreviewResult {
readonly fileContent: IFileContent | null; readonly fileContent: IFileContent | null;
...@@ -41,15 +40,15 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser { ...@@ -41,15 +40,15 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
private _onDidChangeLocal: Emitter<void> = this._register(new Emitter<void>()); private _onDidChangeLocal: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChangeLocal: Event<void> = this._onDidChangeLocal.event; readonly onDidChangeLocal: Event<void> = this._onDidChangeLocal.event;
readonly conflicts: URI = SETTINGS_PREVIEW_RESOURCE;
constructor( constructor(
@IFileService private readonly fileService: IFileService, @IFileService private readonly fileService: IFileService,
@IEnvironmentService private readonly environmentService: IEnvironmentService, @IEnvironmentService private readonly environmentService: IEnvironmentService,
@IStorageService private readonly storageService: IStorageService, @IStorageService private readonly storageService: IStorageService,
@IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService, @IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService,
@IEditorService private readonly editorService: IEditorService,
@ISettingsMergeService private readonly settingsMergeService: ISettingsMergeService, @ISettingsMergeService private readonly settingsMergeService: ISettingsMergeService,
@ILogService private readonly logService: ILogService, @ILogService private readonly logService: ILogService,
@IHistoryService private readonly historyService: IHistoryService,
) { ) {
super(); super();
this.throttledDelayer = this._register(new ThrottledDelayer<void>(500)); this.throttledDelayer = this._register(new ThrottledDelayer<void>(500));
...@@ -111,24 +110,6 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser { ...@@ -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> { async continueSync(): Promise<boolean> {
if (this.status !== SyncStatus.HasConflicts) { if (this.status !== SyncStatus.HasConflicts) {
return false; return false;
...@@ -142,8 +123,8 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser { ...@@ -142,8 +123,8 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
return; return;
} }
if (await this.fileService.exists(SETTINGS_PREVIEW_RESOURCE)) { if (await this.fileService.exists(this.conflicts)) {
const settingsPreivew = await this.fileService.readFile(SETTINGS_PREVIEW_RESOURCE); const settingsPreivew = await this.fileService.readFile(this.conflicts);
const content = settingsPreivew.value.toString(); const content = settingsPreivew.value.toString();
if (this.hasErrors(content)) { if (this.hasErrors(content)) {
return Promise.reject(localize('errorInvalidSettings', "Unable to sync settings. Please resolve conflicts without any errors/warnings and try again.")); return Promise.reject(localize('errorInvalidSettings', "Unable to sync settings. Please resolve conflicts without any errors/warnings and try again."));
...@@ -162,7 +143,7 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser { ...@@ -162,7 +143,7 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
} }
// Delete the preview // Delete the preview
await this.fileService.del(SETTINGS_PREVIEW_RESOURCE); await this.fileService.del(this.conflicts);
} }
this.syncPreviewResultPromise = null; this.syncPreviewResultPromise = null;
...@@ -194,7 +175,7 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser { ...@@ -194,7 +175,7 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
if (fileContent && !remoteUserData) { if (fileContent && !remoteUserData) {
this.logService.trace('Settings Sync: Remote contents does not exist. So sync with settings file.'); this.logService.trace('Settings Sync: Remote contents does not exist. So sync with settings file.');
hasRemoteChanged = true; 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 }; return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts };
} }
...@@ -202,7 +183,7 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser { ...@@ -202,7 +183,7 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
if (remoteUserData && !fileContent) { if (remoteUserData && !fileContent) {
this.logService.trace('Settings Sync: Settings file does not exist. So sync with remote contents'); this.logService.trace('Settings Sync: Settings file does not exist. So sync with remote contents');
hasLocalChanged = true; 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 }; return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts };
} }
...@@ -221,7 +202,7 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser { ...@@ -221,7 +202,7 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
if (hasLocalChanged || hasRemoteChanged) { if (hasLocalChanged || hasRemoteChanged) {
// Sync only if there are changes // Sync only if there are changes
hasConflicts = this.hasErrors(mergeContent); 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 }; return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts };
} }
} }
......
...@@ -11,6 +11,7 @@ import { SettingsSynchroniser } from 'vs/workbench/services/userData/common/sett ...@@ -11,6 +11,7 @@ import { SettingsSynchroniser } from 'vs/workbench/services/userData/common/sett
import { Emitter, Event } from 'vs/base/common/event'; import { Emitter, Event } from 'vs/base/common/event';
import { IFileService } from 'vs/platform/files/common/files'; import { IFileService } from 'vs/platform/files/common/files';
import { InMemoryFileSystemProvider } from 'vs/workbench/services/userData/common/inMemoryUserDataProvider'; import { InMemoryFileSystemProvider } from 'vs/workbench/services/userData/common/inMemoryUserDataProvider';
import { URI } from 'vs/base/common/uri';
export class UserDataSyncService extends Disposable implements IUserDataSyncService { export class UserDataSyncService extends Disposable implements IUserDataSyncService {
...@@ -40,6 +41,11 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ ...@@ -40,6 +41,11 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
this.onDidChangeLocal = Event.any(...this.synchronisers.map(s => s.onDidChangeLocal)); 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> { async sync(): Promise<boolean> {
if (!this.userDataSyncStoreService.enabled) { if (!this.userDataSyncStoreService.enabled) {
throw new Error('Not enabled'); throw new Error('Not enabled');
...@@ -64,18 +70,6 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ ...@@ -64,18 +70,6 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
return false; 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 { private updateStatus(): void {
this.setStatus(this.computeStatus()); this.setStatus(this.computeStatus());
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册