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

#100346 fix opening diffs and accept preview

上级 28f3fa9e
......@@ -57,8 +57,8 @@ export interface IMergableResourcePreview extends IBaseResourcePreview {
readonly remoteContent: string | null;
readonly localContent: string | null;
readonly previewContent: string | null;
readonly acceptedContent: string | null;
readonly hasConflicts: boolean;
mergeState: MergeState;
}
export type IResourcePreview = Omit<IMergableResourcePreview, 'mergeState'>;
......@@ -385,48 +385,58 @@ export abstract class AbstractSynchroniser extends Disposable {
}
async accept(resource: URI, content: string): Promise<ISyncResourcePreview | null> {
if (!this.syncPreviewPromise) {
return null;
}
let preview = await this.syncPreviewPromise;
this.syncPreviewPromise = createCancelablePromise(token => this.updateSyncResourcePreviewContent(preview, resource, content, token));
preview = await this.syncPreviewPromise;
this.updateConflicts(preview.resourcePreviews);
if (preview.resourcePreviews.some(({ mergeState }) => mergeState === MergeState.Conflict)) {
this.setStatus(SyncStatus.HasConflicts);
} else {
this.setStatus(SyncStatus.Syncing);
}
return preview;
await this.updateSyncResourcePreview(resource, async (resourcePreview) => {
const updatedResourcePreview = await this.updateResourcePreview(resourcePreview, resource, content);
return {
...updatedResourcePreview,
mergeState: MergeState.Accepted
};
});
return this.syncPreviewPromise;
}
async merge(resource: URI): Promise<ISyncResourcePreview | null> {
await this.changeMergeState(resource, (resourcePreview) => resourcePreview.hasConflicts ? MergeState.Conflict : MergeState.Accepted);
await this.updateSyncResourcePreview(resource, async (resourcePreview) => ({
...resourcePreview,
mergeState: resourcePreview.hasConflicts ? MergeState.Conflict : MergeState.Accepted
}));
return this.syncPreviewPromise;
}
async discard(resource: URI): Promise<ISyncResourcePreview | null> {
await this.changeMergeState(resource, () => MergeState.Preview);
await this.updateSyncResourcePreview(resource, async (resourcePreview) => {
await this.fileService.writeFile(resourcePreview.previewResource, VSBuffer.fromString(resourcePreview.previewContent || ''));
const updatedPreview = await this.updateResourcePreview(resourcePreview, resourcePreview.previewResource, resourcePreview.previewContent || '');
return {
...updatedPreview,
mergeState: MergeState.Preview
};
});
return this.syncPreviewPromise;
}
private async changeMergeState(resource: URI, mergeState: (resourcePreview: IMergableResourcePreview) => MergeState): Promise<void> {
private async updateSyncResourcePreview(resource: URI, updateResourcePreview: (resourcePreview: IMergableResourcePreview) => Promise<IMergableResourcePreview>): Promise<void> {
if (!this.syncPreviewPromise) {
return;
}
const preview = await this.syncPreviewPromise;
const resourcePreview = preview.resourcePreviews.find(({ localResource, remoteResource, previewResource }) =>
let preview = await this.syncPreviewPromise;
const index = preview.resourcePreviews.findIndex(({ localResource, remoteResource, previewResource }) =>
isEqual(localResource, resource) || isEqual(remoteResource, resource) || isEqual(previewResource, resource));
if (!resourcePreview) {
if (index === -1) {
return;
}
resourcePreview.mergeState = mergeState(resourcePreview);
this.syncPreviewPromise = createCancelablePromise(async token => {
const resourcePreviews = [...preview.resourcePreviews];
resourcePreviews[index] = await updateResourcePreview(resourcePreviews[index]);
return {
...preview,
resourcePreviews
};
});
preview = await this.syncPreviewPromise;
this.updateConflicts(preview.resourcePreviews);
if (preview.resourcePreviews.some(({ mergeState }) => mergeState === MergeState.Conflict)) {
this.setStatus(SyncStatus.HasConflicts);
......@@ -435,6 +445,13 @@ export abstract class AbstractSynchroniser extends Disposable {
}
}
protected async updateResourcePreview(resourcePreview: IResourcePreview, resource: URI, acceptedContent: string): Promise<IResourcePreview> {
return {
...resourcePreview,
acceptedContent
};
}
private async doApply(force: boolean): Promise<SyncStatus> {
if (!this.syncPreviewPromise) {
return SyncStatus.Idle;
......@@ -464,32 +481,6 @@ export abstract class AbstractSynchroniser extends Disposable {
return SyncStatus.Idle;
}
private async updateSyncResourcePreviewContent(preview: ISyncResourcePreview, resource: URI, previewContent: string, token: CancellationToken): Promise<ISyncResourcePreview> {
const index = preview.resourcePreviews.findIndex(({ localResource, remoteResource, previewResource, localChange, remoteChange }) =>
(localChange !== Change.None || remoteChange !== Change.None)
&& (isEqual(localResource, resource) || isEqual(remoteResource, resource) || isEqual(previewResource, resource)));
if (index !== -1) {
const resourcePreviews = [...preview.resourcePreviews];
const resourcePreview = await this.updateResourcePreviewContent(resourcePreviews[index], resource, previewContent, token);
resourcePreviews[index] = { ...resourcePreview, mergeState: MergeState.Accepted };
preview = {
...preview,
resourcePreviews
};
}
return preview;
}
protected async updateResourcePreviewContent(resourcePreview: IResourcePreview, resource: URI, previewContent: string, token: CancellationToken): Promise<IResourcePreview> {
return {
...resourcePreview,
previewContent,
hasConflicts: false,
localChange: Change.Modified,
remoteChange: Change.Modified,
};
}
private async clearPreviewFolder(): Promise<void> {
try {
await this.fileService.del(this.syncPreviewFolder, { recursive: true });
......@@ -555,13 +546,13 @@ export abstract class AbstractSynchroniser extends Disposable {
const syncPreview = this.syncPreviewPromise ? await this.syncPreviewPromise : null;
if (syncPreview) {
for (const resourcePreview of syncPreview.resourcePreviews) {
if (resourcePreview.previewResource && isEqual(resourcePreview.previewResource, uri)) {
return resourcePreview.previewContent || '';
if (isEqual(resourcePreview.acceptedResource, uri)) {
return resourcePreview.acceptedContent || '';
}
if (resourcePreview.remoteResource && isEqual(resourcePreview.remoteResource, uri)) {
if (isEqual(resourcePreview.remoteResource, uri)) {
return resourcePreview.remoteContent || '';
}
if (resourcePreview.localResource && isEqual(resourcePreview.localResource, uri)) {
if (isEqual(resourcePreview.localResource, uri)) {
return resourcePreview.localContent || '';
}
}
......@@ -581,18 +572,25 @@ export abstract class AbstractSynchroniser extends Disposable {
// For preview, use remoteUserData if lastSyncUserData does not exists and last sync is from current machine
const lastSyncUserDataForPreview = lastSyncUserData === null && isLastSyncFromCurrentMachine ? remoteUserData : lastSyncUserData;
const resourcePreviews = await this.generateSyncPreview(remoteUserData, lastSyncUserDataForPreview, token);
// Mark merge
const mergableResourcePreviews = resourcePreviews.map<IMergableResourcePreview>(r => ({
...r,
mergeState: r.localChange === Change.None && r.remoteChange === Change.None ? MergeState.Accepted /* Mark previews with no changes as merged */
: apply ? (r.hasConflicts ? MergeState.Conflict : MergeState.Accepted)
: MergeState.Preview
const result = await this.generateSyncPreview(remoteUserData, lastSyncUserDataForPreview, token);
}));
const resourcePreviews: IMergableResourcePreview[] = [];
for (const resourcePreview of result) {
if (token.isCancellationRequested) {
break;
}
if (!apply) {
await this.fileService.writeFile(resourcePreview.previewResource, VSBuffer.fromString(resourcePreview.previewContent || ''));
}
resourcePreviews.push({
...resourcePreview,
mergeState: resourcePreview.localChange === Change.None && resourcePreview.remoteChange === Change.None ? MergeState.Accepted /* Mark previews with no changes as merged */
: apply ? (resourcePreview.hasConflicts ? MergeState.Conflict : MergeState.Accepted)
: MergeState.Preview
});
}
return { remoteUserData, lastSyncUserData, resourcePreviews: mergableResourcePreviews, isLastSyncFromCurrentMachine };
return { remoteUserData, lastSyncUserData, resourcePreviews, isLastSyncFromCurrentMachine };
}
async getLastSyncUserData<T extends IRemoteUserData>(): Promise<T | null> {
......
......@@ -47,7 +47,8 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
protected readonly version: number = 3;
protected isEnabled(): boolean { return super.isEnabled() && this.extensionGalleryService.isEnabled(); }
private readonly localPreviewResource: URI = joinPath(this.syncPreviewFolder, 'extensions.json');
private readonly remotePreviewResource: URI = this.localPreviewResource.with({ scheme: USER_DATA_SYNC_SCHEME });
private readonly remotePreviewResource: URI = this.localPreviewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' });
private readonly acceptedPreviewResource: URI = this.localPreviewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' });
constructor(
@IEnvironmentService environmentService: IEnvironmentService,
......@@ -100,6 +101,8 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
remoteContent: remoteExtensions ? this.format(remoteExtensions) : null,
previewResource: this.localPreviewResource,
previewContent: null,
acceptedResource: this.acceptedPreviewResource,
acceptedContent: null,
added,
removed,
updated,
......@@ -137,6 +140,8 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
remoteContent: remoteExtensions ? this.format(remoteExtensions) : null,
previewResource: this.localPreviewResource,
previewContent: null,
acceptedResource: this.acceptedPreviewResource,
acceptedContent: null,
added,
removed,
updated,
......@@ -177,14 +182,14 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
}
}
protected async updateResourcePreviewContent(resourcePreview: IExtensionResourcePreview, resource: URI, previewContent: string, token: CancellationToken): Promise<IExtensionResourcePreview> {
protected async updateResourcePreview(resourcePreview: IExtensionResourcePreview, resource: URI, acceptedContent: string): Promise<IExtensionResourcePreview> {
if (isEqual(resource, ExtensionsSynchroniser.EXTENSIONS_DATA_URI)) {
const remoteExtensions = resourcePreview.remoteContent ? JSON.parse(resourcePreview.remoteContent) : null;
return this.getPushPreview(remoteExtensions);
}
return {
...resourcePreview,
previewContent,
acceptedContent,
hasConflicts: false,
localChange: Change.Modified,
remoteChange: Change.Modified,
......@@ -199,6 +204,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
const localContent = this.format(localExtensions);
const remoteResource = this.remotePreviewResource;
const previewResource = this.localPreviewResource;
const acceptedResource = this.acceptedPreviewResource;
const previewContent = null;
if (remoteExtensions !== null) {
const mergeResult = merge(localExtensions, remoteExtensions, localExtensions, [], ignoredExtensions);
......@@ -210,6 +216,8 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
remoteContent: this.format(remoteExtensions),
previewResource,
previewContent,
acceptedResource,
acceptedContent: previewContent,
added,
removed,
updated,
......@@ -228,6 +236,8 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
remoteContent: null,
previewResource,
previewContent,
acceptedResource,
acceptedContent: previewContent,
added: [], removed: [], updated: [], remote: null, localExtensions, skippedExtensions: [],
localChange: Change.None,
remoteChange: Change.None,
......@@ -249,6 +259,8 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
remoteContent: remoteExtensions ? this.format(remoteExtensions) : null,
previewResource: this.localPreviewResource,
previewContent: null,
acceptedResource: this.acceptedPreviewResource,
acceptedContent: null,
added,
removed,
updated,
......@@ -272,7 +284,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
return this.format(localExtensions);
}
if (isEqual(this.remotePreviewResource, uri) || isEqual(this.localPreviewResource, uri)) {
if (isEqual(this.remotePreviewResource, uri) || isEqual(this.acceptedPreviewResource, uri)) {
return this.resolvePreviewContent(uri);
}
......
......@@ -46,7 +46,8 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
private static readonly GLOBAL_STATE_DATA_URI = URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'globalState', path: `/globalState.json` });
protected readonly version: number = 1;
private readonly localPreviewResource: URI = joinPath(this.syncPreviewFolder, 'globalState.json');
private readonly remotePreviewResource: URI = this.localPreviewResource.with({ scheme: USER_DATA_SYNC_SCHEME });
private readonly remotePreviewResource: URI = this.localPreviewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' });
private readonly acceptedPreviewResource: URI = this.localPreviewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' });
constructor(
@IFileService fileService: IFileService,
......@@ -99,6 +100,8 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
remoteContent: remoteGlobalState ? this.format(remoteGlobalState) : null,
previewResource: this.localPreviewResource,
previewContent: null,
acceptedResource: this.acceptedPreviewResource,
acceptedContent: null,
local,
remote: syncGlobalState.storage,
localUserData,
......@@ -131,6 +134,8 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
remoteContent: remoteGlobalState ? this.format(remoteGlobalState) : null,
previewResource: this.localPreviewResource,
previewContent: null,
acceptedResource: this.acceptedPreviewResource,
acceptedContent: null,
local,
remote,
localUserData: localGloablState,
......@@ -172,7 +177,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
}
}
protected async updateResourcePreviewContent(resourcePreview: IGlobalStateResourcePreview, resource: URI, previewContent: string, token: CancellationToken): Promise<IGlobalStateResourcePreview> {
protected async updateResourcePreview(resourcePreview: IGlobalStateResourcePreview, resource: URI, acceptedContent: string): Promise<IGlobalStateResourcePreview> {
if (GlobalStateSynchroniser.GLOBAL_STATE_DATA_URI, resource) {
return this.getPushPreview(resourcePreview.remoteContent);
}
......@@ -188,6 +193,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
const localContent = this.format(localGlobalState);
const remoteResource = this.remotePreviewResource;
const previewResource = this.localPreviewResource;
const acceptedResource = this.acceptedPreviewResource;
const previewContent = null;
if (remoteContent !== null) {
const remoteGlobalState: IGlobalState = JSON.parse(remoteContent);
......@@ -200,6 +206,8 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
remoteContent: this.format(remoteGlobalState),
previewResource,
previewContent,
acceptedResource,
acceptedContent: previewContent,
local,
remote,
localUserData: localGlobalState,
......@@ -216,6 +224,8 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
remoteContent: null,
previewResource,
previewContent,
acceptedResource,
acceptedContent: previewContent,
local: { added: {}, removed: [], updated: {} },
remote: null,
localUserData: localGlobalState,
......@@ -237,6 +247,8 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
remoteContent: remoteGlobalState ? this.format(remoteGlobalState) : null,
previewResource: this.localPreviewResource,
previewContent: null,
acceptedResource: this.acceptedPreviewResource,
acceptedContent: null,
local: { added: {}, removed: [], updated: {} },
remote: localUserData.storage,
localUserData,
......@@ -257,7 +269,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
return this.format(localGlobalState);
}
if (isEqual(this.remotePreviewResource, uri) || isEqual(this.localPreviewResource, uri)) {
if (isEqual(this.remotePreviewResource, uri) || isEqual(this.acceptedPreviewResource, uri)) {
return this.resolvePreviewContent(uri);
}
......
......@@ -36,7 +36,8 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
protected readonly version: number = 1;
protected readonly localPreviewResource: URI = joinPath(this.syncPreviewFolder, 'keybindings.json');
protected readonly remotePreviewResource: URI = this.localPreviewResource.with({ scheme: USER_DATA_SYNC_SCHEME });
protected readonly remotePreviewResource: URI = this.localPreviewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' });
private readonly acceptPreviewResource: URI = this.localPreviewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' });
constructor(
@IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService,
......@@ -65,6 +66,8 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
remoteContent: previewContent,
previewResource: this.localPreviewResource,
previewContent,
acceptedResource: this.acceptPreviewResource,
acceptedContent: previewContent,
localChange: previewContent !== null ? Change.Modified : Change.None,
remoteChange: Change.None,
hasConflicts: false,
......@@ -83,6 +86,8 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
remoteContent: remoteUserData.syncData !== null ? this.getKeybindingsContentFromSyncContent(remoteUserData.syncData.content) : null,
previewResource: this.localPreviewResource,
previewContent,
acceptedResource: this.acceptPreviewResource,
acceptedContent: previewContent,
localChange: Change.None,
remoteChange: previewContent !== null ? Change.Modified : Change.None,
hasConflicts: false,
......@@ -101,6 +106,8 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
remoteContent: remoteUserData.syncData !== null ? this.getKeybindingsContentFromSyncContent(remoteUserData.syncData.content) : null,
previewResource: this.localPreviewResource,
previewContent,
acceptedResource: this.acceptPreviewResource,
acceptedContent: previewContent,
localChange: previewContent !== null ? Change.Modified : Change.None,
remoteChange: previewContent !== null ? Change.Modified : Change.None,
hasConflicts: false,
......@@ -161,6 +168,8 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
remoteContent,
previewResource: this.localPreviewResource,
previewContent,
acceptedResource: this.acceptPreviewResource,
acceptedContent: previewContent,
hasConflicts,
localChange: hasLocalChanged ? fileContent ? Change.Modified : Change.Added : Change.None,
remoteChange: hasRemoteChanged ? Change.Modified : Change.None,
......@@ -168,7 +177,7 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
}
protected async applyPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, resourcePreviews: IFileResourcePreview[], force: boolean): Promise<void> {
let { fileContent, previewContent: content, localChange, remoteChange } = resourcePreviews[0];
let { fileContent, acceptedContent: content, localChange, remoteChange } = resourcePreviews[0];
if (content !== null) {
if (this.hasErrors(content)) {
......@@ -234,7 +243,7 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
const fileContent = await this.getLocalFileContent();
return fileContent ? fileContent.value.toString() : '';
}
if (isEqual(this.remotePreviewResource, uri) || isEqual(this.localPreviewResource, uri)) {
if (isEqual(this.remotePreviewResource, uri) || isEqual(this.acceptPreviewResource, uri)) {
return this.resolvePreviewContent(uri);
}
let content = await super.resolveContent(uri);
......
......@@ -39,8 +39,9 @@ function isSettingsSyncContent(thing: any): thing is ISettingsSyncContent {
export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implements IUserDataSynchroniser {
protected readonly version: number = 1;
protected readonly localPreviewResource: URI = joinPath(this.syncPreviewFolder, 'settings.json');
protected readonly remotePreviewResource: URI = this.localPreviewResource.with({ scheme: USER_DATA_SYNC_SCHEME });
private readonly localPreviewResource: URI = joinPath(this.syncPreviewFolder, 'settings.json');
private readonly remotePreviewResource: URI = this.localPreviewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' });
private readonly acceptPreviewResource: URI = this.localPreviewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' });
constructor(
@IFileService fileService: IFileService,
......@@ -79,6 +80,8 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
remoteContent: remoteSettingsSyncContent ? remoteSettingsSyncContent.settings : null,
previewResource: this.localPreviewResource,
previewContent,
acceptedResource: this.acceptPreviewResource,
acceptedContent: previewContent,
localChange: previewContent !== null ? Change.Modified : Change.None,
remoteChange: Change.None,
hasConflicts: false,
......@@ -106,6 +109,8 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
remoteContent: remoteSettingsSyncContent ? remoteSettingsSyncContent.settings : null,
previewResource: this.localPreviewResource,
previewContent,
acceptedResource: this.acceptPreviewResource,
acceptedContent: previewContent,
localChange: Change.None,
remoteChange: previewContent !== null ? Change.Modified : Change.None,
hasConflicts: false,
......@@ -133,6 +138,8 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
remoteContent: remoteSettingsSyncContent ? remoteSettingsSyncContent.settings : null,
previewResource: this.localPreviewResource,
previewContent,
acceptedResource: this.acceptPreviewResource,
acceptedContent: previewContent,
localChange: previewContent !== null ? Change.Modified : Change.None,
remoteChange: previewContent !== null ? Change.Modified : Change.None,
hasConflicts: false,
......@@ -146,6 +153,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
const lastSettingsSyncContent: ISettingsSyncContent | null = lastSyncUserData ? this.getSettingsSyncContent(lastSyncUserData) : null;
const ignoredSettings = await this.getIgnoredSettings();
let acceptedContent: string | null = null;
let previewContent: string | null = null;
let hasLocalChanged: boolean = false;
let hasRemoteChanged: boolean = false;
......@@ -156,7 +164,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
this.validateContent(localContent);
this.logService.trace(`${this.syncResourceLogLabel}: Merging remote settings with local settings...`);
const result = merge(localContent, remoteSettingsSyncContent.settings, lastSettingsSyncContent ? lastSettingsSyncContent.settings : null, ignoredSettings, [], formattingOptions);
previewContent = result.localContent || result.remoteContent;
acceptedContent = result.localContent || result.remoteContent;
hasLocalChanged = result.localContent !== null;
hasRemoteChanged = result.remoteContent !== null;
hasConflicts = result.hasConflicts;
......@@ -165,14 +173,13 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
// First time syncing to remote
else if (fileContent) {
this.logService.trace(`${this.syncResourceLogLabel}: Remote settings does not exist. Synchronizing settings for the first time.`);
previewContent = fileContent.value.toString();
acceptedContent = fileContent.value.toString();
hasRemoteChanged = true;
}
if (previewContent && !token.isCancellationRequested) {
if (acceptedContent && !token.isCancellationRequested) {
// Remove the ignored settings from the preview.
const content = updateIgnoredSettings(previewContent, '{}', ignoredSettings, formattingOptions);
await this.fileService.writeFile(this.localPreviewResource, VSBuffer.fromString(content));
previewContent = updateIgnoredSettings(acceptedContent, '{}', ignoredSettings, formattingOptions);
}
return [{
......@@ -183,22 +190,26 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
remoteContent: remoteSettingsSyncContent ? remoteSettingsSyncContent.settings : null,
previewResource: this.localPreviewResource,
previewContent,
acceptedResource: this.acceptPreviewResource,
acceptedContent,
localChange: hasLocalChanged ? fileContent ? Change.Modified : Change.Added : Change.None,
remoteChange: hasRemoteChanged ? Change.Modified : Change.None,
hasConflicts,
}];
}
protected async updateResourcePreviewContent(resourcePreview: IFileResourcePreview, resource: URI, previewContent: string, token: CancellationToken): Promise<IFileResourcePreview> {
const formatUtils = await this.getFormattingOptions();
// Add ignored settings from local file content
const ignoredSettings = await this.getIgnoredSettings();
previewContent = updateIgnoredSettings(previewContent, resourcePreview.fileContent ? resourcePreview.fileContent.value.toString() : '{}', ignoredSettings, formatUtils);
return super.updateResourcePreviewContent(resourcePreview, resource, previewContent, token) as Promise<IFileResourcePreview>;
protected async updateResourcePreview(resourcePreview: IFileResourcePreview, resource: URI, acceptedContent: string): Promise<IFileResourcePreview> {
if (isEqual(resource, this.localPreviewResource) || isEqual(resource, this.remotePreviewResource)) {
const formatUtils = await this.getFormattingOptions();
// Add ignored settings from local file content
const ignoredSettings = await this.getIgnoredSettings();
acceptedContent = updateIgnoredSettings(acceptedContent, resourcePreview.fileContent ? resourcePreview.fileContent.value.toString() : '{}', ignoredSettings, formatUtils);
}
return super.updateResourcePreview(resourcePreview, resource, acceptedContent) as Promise<IFileResourcePreview>;
}
protected async applyPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, resourcePreviews: IFileResourcePreview[], force: boolean): Promise<void> {
let { fileContent, previewContent: content, localChange, remoteChange } = resourcePreviews[0];
let { fileContent, acceptedContent: content, localChange, remoteChange } = resourcePreviews[0];
if (content !== null) {
......@@ -264,7 +275,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
const fileContent = await this.getLocalFileContent();
return fileContent ? fileContent.value.toString() : '';
}
if (isEqual(this.remotePreviewResource, uri) || isEqual(this.localPreviewResource, uri)) {
if (isEqual(this.remotePreviewResource, uri) || isEqual(this.acceptPreviewResource, uri)) {
return this.resolvePreviewContent(uri);
}
let content = await super.resolveContent(uri);
......
......@@ -5,7 +5,7 @@
import {
IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSynchroniser, SyncResource, IUserDataSyncResourceEnablementService, IUserDataSyncBackupStoreService,
USER_DATA_SYNC_SCHEME, ISyncResourceHandle, IRemoteUserData, ISyncData, UserDataSyncError, UserDataSyncErrorCode, Change
USER_DATA_SYNC_SCHEME, ISyncResourceHandle, IRemoteUserData, ISyncData, Change
} from 'vs/platform/userDataSync/common/userDataSync';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IFileService, FileChangesEvent, IFileStat, IFileContent, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
......@@ -20,7 +20,6 @@ import { merge, IMergeResult, areSame } from 'vs/platform/userDataSync/common/sn
import { CancellationToken } from 'vs/base/common/cancellation';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { deepClone } from 'vs/base/common/objects';
import { localize } from 'vs/nls';
export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserDataSynchroniser {
......@@ -94,34 +93,19 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
}
const mergeResult = merge(localSnippets, remoteSnippets, lastSyncSnippets);
const resourcePreviews = this.getResourcePreviews(mergeResult, local, remoteSnippets || {});
for (const resourcePreview of resourcePreviews) {
if (resourcePreview.hasConflicts) {
if (!token.isCancellationRequested) {
await this.fileService.writeFile(resourcePreview.previewResource!, VSBuffer.fromString(resourcePreview.previewContent || ''));
}
}
}
return resourcePreviews;
return this.getResourcePreviews(mergeResult, local, remoteSnippets || {});
}
protected async updateResourcePreviewContent(resourcePreview: IFileResourcePreview, resource: URI, previewContent: string, token: CancellationToken): Promise<IFileResourcePreview> {
protected async updateResourcePreview(resourcePreview: IFileResourcePreview, resource: URI, acceptedContent: string): Promise<IFileResourcePreview> {
return {
...resourcePreview,
previewContent: previewContent || null,
hasConflicts: false,
localChange: previewContent ? Change.Modified : Change.Deleted,
remoteChange: previewContent ? Change.Modified : Change.Deleted,
acceptedContent: acceptedContent || null,
localChange: acceptedContent ? Change.Modified : Change.Deleted,
remoteChange: acceptedContent ? Change.Modified : Change.Deleted,
};
}
protected async applyPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, resourcePreviews: IFileResourcePreview[], force: boolean): Promise<void> {
if (resourcePreviews.some(({ hasConflicts }) => hasConflicts)) {
throw new UserDataSyncError(localize('unresolved conflicts', "Error while syncing {0}. Please resolve conflicts first.", this.syncResourceLogLabel), UserDataSyncErrorCode.UnresolvedConflicts, this.resource);
}
if (resourcePreviews.every(({ localChange, remoteChange }) => localChange === Change.None && remoteChange === Change.None)) {
this.logService.info(`${this.syncResourceLogLabel}: No changes found during synchronizing snippets.`);
}
......@@ -161,10 +145,12 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
localResource: joinPath(this.snippetsFolder, key),
fileContent: null,
localContent: null,
remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME }),
remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }),
remoteContent: remoteSnippets[key],
previewResource: joinPath(this.syncPreviewFolder, key),
previewContent: mergeResult.local.added[key],
acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }),
acceptedContent: mergeResult.local.added[key],
hasConflicts: false,
localChange: Change.Added,
remoteChange: Change.None
......@@ -177,10 +163,12 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
localResource: joinPath(this.snippetsFolder, key),
fileContent: localFileContent[key],
localContent: localFileContent[key].value.toString(),
remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME }),
remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }),
remoteContent: remoteSnippets[key],
previewResource: joinPath(this.syncPreviewFolder, key),
previewContent: mergeResult.local.updated[key],
acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }),
acceptedContent: mergeResult.local.updated[key],
hasConflicts: false,
localChange: Change.Modified,
remoteChange: Change.None
......@@ -193,10 +181,12 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
localResource: joinPath(this.snippetsFolder, key),
fileContent: localFileContent[key],
localContent: localFileContent[key].value.toString(),
remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME }),
remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }),
remoteContent: null,
previewResource: joinPath(this.syncPreviewFolder, key),
previewContent: null,
acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }),
acceptedContent: null,
hasConflicts: false,
localChange: Change.Deleted,
remoteChange: Change.None
......@@ -209,10 +199,12 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
localResource: joinPath(this.snippetsFolder, key),
fileContent: localFileContent[key],
localContent: localFileContent[key].value.toString(),
remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME }),
remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }),
remoteContent: null,
previewResource: joinPath(this.syncPreviewFolder, key),
previewContent: mergeResult.remote.added[key],
acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }),
acceptedContent: mergeResult.remote.added[key],
hasConflicts: false,
localChange: Change.None,
remoteChange: Change.Added
......@@ -225,10 +217,12 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
localResource: joinPath(this.snippetsFolder, key),
fileContent: localFileContent[key],
localContent: localFileContent[key].value.toString(),
remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME }),
remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }),
remoteContent: remoteSnippets[key],
previewResource: joinPath(this.syncPreviewFolder, key),
previewContent: mergeResult.remote.updated[key],
acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }),
acceptedContent: mergeResult.remote.updated[key],
hasConflicts: false,
localChange: Change.None,
remoteChange: Change.Modified
......@@ -241,10 +235,12 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
localResource: joinPath(this.snippetsFolder, key),
fileContent: null,
localContent: null,
remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME }),
remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }),
remoteContent: remoteSnippets[key],
previewResource: joinPath(this.syncPreviewFolder, key),
previewContent: null,
acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }),
acceptedContent: null,
hasConflicts: false,
localChange: Change.None,
remoteChange: Change.Deleted
......@@ -257,10 +253,12 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
localResource: joinPath(this.snippetsFolder, key),
fileContent: localFileContent[key] || null,
localContent: localFileContent[key] ? localFileContent[key].value.toString() : null,
remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME }),
remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }),
remoteContent: remoteSnippets[key] || null,
previewResource: joinPath(this.syncPreviewFolder, key),
previewContent: localFileContent[key] ? localFileContent[key].value.toString() : null,
acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }),
acceptedContent: localFileContent[key] ? localFileContent[key].value.toString() : null,
hasConflicts: true,
localChange: localFileContent[key] ? Change.Modified : Change.Added,
remoteChange: remoteSnippets[key] ? Change.Modified : Change.Added
......@@ -274,10 +272,12 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
localResource: joinPath(this.snippetsFolder, key),
fileContent: localFileContent[key] || null,
localContent: localFileContent[key] ? localFileContent[key].value.toString() : null,
remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME }),
remoteResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }),
remoteContent: remoteSnippets[key] || null,
previewResource: joinPath(this.syncPreviewFolder, key),
previewContent: localFileContent[key] ? localFileContent[key].value.toString() : null,
acceptedResource: joinPath(this.syncPreviewFolder, key).with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }),
acceptedContent: localFileContent[key] ? localFileContent[key].value.toString() : null,
hasConflicts: false,
localChange: Change.None,
remoteChange: Change.None
......@@ -317,8 +317,8 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
}
}
if (isEqualOrParent(uri.with({ scheme: this.syncPreviewFolder.scheme }), this.syncPreviewFolder)
|| isEqualOrParent(uri, this.syncPreviewFolder.with({ scheme: USER_DATA_SYNC_SCHEME }))) {
if (isEqualOrParent(uri, this.syncPreviewFolder.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote' }))
|| isEqualOrParent(uri, this.syncPreviewFolder.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }))) {
return this.resolvePreviewContent(uri);
}
......@@ -362,12 +362,7 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
}
private async updateLocalSnippets(resourcePreviews: IFileResourcePreview[], force: boolean): Promise<void> {
if (resourcePreviews.some(({ hasConflicts }) => hasConflicts)) {
// Do not update if there are conflicts
return;
}
for (const { fileContent, previewContent: content, localResource, remoteResource, localChange } of resourcePreviews) {
for (const { fileContent, acceptedContent: content, localResource, remoteResource, localChange } of resourcePreviews) {
if (localChange !== Change.None) {
const key = remoteResource ? basename(remoteResource) : basename(localResource!);
const resource = joinPath(this.snippetsFolder, key);
......@@ -397,15 +392,10 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
}
private async updateRemoteSnippets(resourcePreviews: IFileResourcePreview[], remoteUserData: IRemoteUserData, forcePush: boolean): Promise<IRemoteUserData> {
if (resourcePreviews.some(({ hasConflicts }) => hasConflicts)) {
// Do not update if there are conflicts
return remoteUserData;
}
const currentSnippets: IStringDictionary<string> = remoteUserData.syncData ? this.parseSnippets(remoteUserData.syncData) : {};
const newSnippets: IStringDictionary<string> = deepClone(currentSnippets);
for (const { previewContent: content, localResource, remoteResource, remoteChange } of resourcePreviews) {
for (const { acceptedContent: content, localResource, remoteResource, remoteChange } of resourcePreviews) {
if (remoteChange !== Change.None) {
const key = localResource ? basename(localResource) : basename(remoteResource!);
if (remoteChange === Change.Deleted) {
......
......@@ -327,6 +327,7 @@ export interface IResourcePreview {
readonly remoteResource: URI;
readonly localResource: URI;
readonly previewResource: URI;
readonly acceptedResource: URI;
readonly localChange: Change;
readonly remoteChange: Change;
readonly mergeState: MergeState;
......
......@@ -641,6 +641,7 @@ function toStrictResourcePreview(resourcePreview: IResourcePreview): IResourcePr
localResource: resourcePreview.localResource,
previewResource: resourcePreview.previewResource,
remoteResource: resourcePreview.remoteResource,
acceptedResource: resourcePreview.acceptedResource,
localChange: resourcePreview.localChange,
remoteChange: resourcePreview.remoteChange,
mergeState: resourcePreview.mergeState,
......
......@@ -12,6 +12,8 @@ import { Barrier } from 'vs/base/common/async';
import { Emitter, Event } from 'vs/base/common/event';
import { CancellationToken } from 'vs/base/common/cancellation';
import { URI } from 'vs/base/common/uri';
import { IFileService } from 'vs/platform/files/common/files';
import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider';
const resource = URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'testResource', path: `/current.json` });
......@@ -47,27 +49,27 @@ class TestSynchroniser extends AbstractSynchroniser {
}
protected async generatePullPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, token: CancellationToken): Promise<IResourcePreview[]> {
return [{ localContent: null, localResource: resource, remoteContent: null, remoteResource: resource, previewContent: remoteUserData.ref, previewResource: resource, localChange: Change.Modified, remoteChange: Change.None, hasConflicts: this.syncResult.hasConflicts }];
return [{ localContent: null, localResource: resource, remoteContent: null, remoteResource: resource, acceptedContent: remoteUserData.ref, previewContent: remoteUserData.ref, previewResource: resource, acceptedResource: resource, localChange: Change.Modified, remoteChange: Change.None, hasConflicts: this.syncResult.hasConflicts }];
}
protected async generatePushPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, token: CancellationToken): Promise<IResourcePreview[]> {
return [{ localContent: null, localResource: resource, remoteContent: null, remoteResource: resource, previewContent: remoteUserData.ref, previewResource: resource, localChange: Change.Modified, remoteChange: Change.None, hasConflicts: this.syncResult.hasConflicts }];
return [{ localContent: null, localResource: resource, remoteContent: null, remoteResource: resource, acceptedContent: remoteUserData.ref, previewContent: remoteUserData.ref, previewResource: resource, acceptedResource: resource, localChange: Change.Modified, remoteChange: Change.None, hasConflicts: this.syncResult.hasConflicts }];
}
protected async generateReplacePreview(syncData: ISyncData, remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise<IResourcePreview[]> {
return [{ localContent: null, localResource: resource, remoteContent: null, remoteResource: resource, previewContent: remoteUserData.ref, previewResource: resource, localChange: Change.Modified, remoteChange: Change.None, hasConflicts: this.syncResult.hasConflicts }];
return [{ localContent: null, localResource: resource, remoteContent: null, remoteResource: resource, acceptedContent: remoteUserData.ref, previewContent: remoteUserData.ref, previewResource: resource, acceptedResource: resource, localChange: Change.Modified, remoteChange: Change.None, hasConflicts: this.syncResult.hasConflicts }];
}
protected async generateSyncPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, token: CancellationToken): Promise<IResourcePreview[]> {
if (this.syncResult.hasError) {
throw new Error('failed');
}
return [{ localContent: null, localResource: resource, remoteContent: null, remoteResource: resource, previewContent: remoteUserData.ref, previewResource: resource, localChange: Change.Modified, remoteChange: Change.None, hasConflicts: this.syncResult.hasConflicts }];
return [{ localContent: null, localResource: resource, remoteContent: null, remoteResource: resource, acceptedContent: remoteUserData.ref, previewContent: remoteUserData.ref, previewResource: resource, acceptedResource: resource, localChange: Change.Modified, remoteChange: Change.None, hasConflicts: this.syncResult.hasConflicts }];
}
protected async applyPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, preview: IResourcePreview[], forcePush: boolean): Promise<void> {
if (preview[0]?.previewContent) {
await this.applyRef(preview[0].previewContent);
if (preview[0]?.acceptedContent) {
await this.applyRef(preview[0].acceptedContent);
}
}
......@@ -106,6 +108,7 @@ suite('TestSynchronizer', () => {
await client.setUp();
userDataSyncStoreService = client.instantiationService.get(IUserDataSyncStoreService);
disposableStore.add(toDisposable(() => userDataSyncStoreService.clear()));
client.instantiationService.get(IFileService).registerProvider(USER_DATA_SYNC_SCHEME, new InMemoryFileSystemProvider());
});
teardown(() => disposableStore.clear());
......@@ -325,7 +328,7 @@ suite('TestSynchronizer', () => {
testObject.syncBarrier.open();
let preview = await testObject.preview(await client.manifest());
preview = await testObject.accept(preview!.resourcePreviews[0].previewResource, preview!.resourcePreviews[0].previewContent!);
preview = await testObject.accept(preview!.resourcePreviews[0].previewResource, preview!.resourcePreviews[0].acceptedContent!);
assert.deepEqual(testObject.status, SyncStatus.Syncing);
assertPreviews(preview!.resourcePreviews, [resource]);
......@@ -338,7 +341,7 @@ suite('TestSynchronizer', () => {
testObject.syncBarrier.open();
let preview = await testObject.preview(await client.manifest());
preview = await testObject.accept(preview!.resourcePreviews[0].previewResource, preview!.resourcePreviews[0].previewContent!);
preview = await testObject.accept(preview!.resourcePreviews[0].previewResource, preview!.resourcePreviews[0].acceptedContent!);
preview = await testObject.apply(false);
assert.deepEqual(testObject.status, SyncStatus.Idle);
......@@ -363,8 +366,8 @@ suite('TestSynchronizer', () => {
testObject.syncResult = { hasConflicts: true, hasError: false };
testObject.syncBarrier.open();
const preview = await testObject.preview(await client.manifest());
await testObject.merge(preview!.resourcePreviews[0].previewResource);
let preview = await testObject.preview(await client.manifest());
preview = await testObject.merge(preview!.resourcePreviews[0].previewResource);
assert.deepEqual(testObject.status, SyncStatus.HasConflicts);
assertPreviews(preview!.resourcePreviews, [resource]);
......@@ -394,7 +397,7 @@ suite('TestSynchronizer', () => {
let preview = await testObject.preview(await client.manifest());
await testObject.merge(preview!.resourcePreviews[0].previewResource);
preview = await testObject.accept(preview!.resourcePreviews[0].previewResource, preview!.resourcePreviews[0].previewContent!);
preview = await testObject.accept(preview!.resourcePreviews[0].previewResource, preview!.resourcePreviews[0].acceptedContent!);
assert.deepEqual(testObject.status, SyncStatus.Syncing);
assertPreviews(preview!.resourcePreviews, [resource]);
......@@ -408,7 +411,7 @@ suite('TestSynchronizer', () => {
let preview = await testObject.preview(await client.manifest());
await testObject.merge(preview!.resourcePreviews[0].previewResource);
preview = await testObject.accept(preview!.resourcePreviews[0].previewResource, preview!.resourcePreviews[0].previewContent!);
preview = await testObject.accept(preview!.resourcePreviews[0].previewResource, preview!.resourcePreviews[0].acceptedContent!);
preview = await testObject.apply(false);
assert.deepEqual(testObject.status, SyncStatus.Idle);
......@@ -422,7 +425,7 @@ suite('TestSynchronizer', () => {
testObject.syncBarrier.open();
let preview = await testObject.preview(await client.manifest());
preview = await testObject.accept(preview!.resourcePreviews[0].previewResource, preview!.resourcePreviews[0].previewContent!);
preview = await testObject.accept(preview!.resourcePreviews[0].previewResource, preview!.resourcePreviews[0].acceptedContent!);
assert.deepEqual(testObject.status, SyncStatus.Syncing);
assertPreviews(preview!.resourcePreviews, [resource]);
......@@ -435,7 +438,7 @@ suite('TestSynchronizer', () => {
testObject.syncBarrier.open();
let preview = await testObject.preview(await client.manifest());
preview = await testObject.accept(preview!.resourcePreviews[0].previewResource, preview!.resourcePreviews[0].previewContent!);
preview = await testObject.accept(preview!.resourcePreviews[0].previewResource, preview!.resourcePreviews[0].acceptedContent!);
preview = await testObject.apply(false);
assert.deepEqual(testObject.status, SyncStatus.Idle);
......
......@@ -31,6 +31,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { attachButtonStyler } from 'vs/platform/theme/common/styler';
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
export class UserDataManualSyncViewPane extends TreeViewPane {
......@@ -189,31 +190,35 @@ export class UserDataManualSyncViewPane extends TreeViewPane {
}
async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise<void> {
const previewResource: IUserDataSyncResource = ManualSyncViewDataProvider.toUserDataSyncResourceGroup(handle.$treeItemHandle);
return that.showChanges(previewResource);
return that.open(previewResource);
}
}));
}
private async acceptLocal(userDataSyncResource: IUserDataSyncResource): Promise<void> {
return this.withProgress(async () => {
await this.withProgress(async () => {
const content = await this.userDataSyncService.resolveContent(userDataSyncResource.local);
await this.userDataSyncPreview.accept(userDataSyncResource.syncResource, userDataSyncResource.local, content || '');
});
await this.reopen(userDataSyncResource);
}
private async acceptRemote(userDataSyncResource: IUserDataSyncResource): Promise<void> {
return this.withProgress(async () => {
await this.withProgress(async () => {
const content = await this.userDataSyncService.resolveContent(userDataSyncResource.remote);
await this.userDataSyncPreview.accept(userDataSyncResource.syncResource, userDataSyncResource.remote, content || '');
});
await this.reopen(userDataSyncResource);
}
private async mergeResource(previewResource: IUserDataSyncResource): Promise<void> {
return this.withProgress(() => this.userDataSyncPreview.merge(previewResource.preview));
await this.withProgress(() => this.userDataSyncPreview.merge(previewResource.merged));
await this.reopen(previewResource);
}
private async discardResource(previewResource: IUserDataSyncResource): Promise<void> {
return this.withProgress(() => this.userDataSyncPreview.discard(previewResource.preview));
this.close(previewResource);
return this.withProgress(() => this.userDataSyncPreview.discard(previewResource.merged));
}
private async apply(): Promise<void> {
......@@ -225,12 +230,12 @@ export class UserDataManualSyncViewPane extends TreeViewPane {
await this.userDataSyncPreview.apply();
}
private async showChanges(previewResource: IUserDataSyncResource): Promise<void> {
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)) });
private async open(previewResource: IUserDataSyncResource): Promise<void> {
if (previewResource.mergeState === MergeState.Accepted) {
await this.editorService.openEditor({ resource: previewResource.accepted, label: localize('preview', "{0} (Preview)", basename(previewResource.accepted)) });
} else {
const leftResource = URI.revive(previewResource.remote);
const rightResource = previewResource.mergeState === MergeState.Conflict ? URI.revive(previewResource.preview) : URI.revive(previewResource.local);
const leftResource = previewResource.remote;
const rightResource = previewResource.mergeState === MergeState.Conflict ? previewResource.merged : 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({
......@@ -245,6 +250,29 @@ export class UserDataManualSyncViewPane extends TreeViewPane {
}
}
private async reopen(previewResource: IUserDataSyncResource): Promise<void> {
this.close(previewResource);
const resource = this.userDataSyncPreview.resources.find(({ local }) => isEqual(local, previewResource.local));
if (resource) {
await this.open(resource);
}
}
private close(previewResource: IUserDataSyncResource) {
for (const input of this.editorService.editors) {
if (input instanceof DiffEditorInput) {
// Close all diff editors
if (isEqual(previewResource.remote, input.secondary.resource)) {
input.dispose();
}
}
// Close all preview editors
else if (isEqual(previewResource.accepted, input.resource)) {
input.dispose();
}
}
}
private withProgress(task: () => Promise<void>): Promise<void> {
return this.progressService.withProgress({ location: MANUAL_SYNC_VIEW_ID, delay: 500 }, task);
}
......@@ -281,8 +309,9 @@ class ManualSyncViewDataProvider implements ITreeViewDataProvider {
return {
syncResource: parsed.syncResource,
local: URI.revive(parsed.local),
preview: URI.revive(parsed.preview),
remote: URI.revive(parsed.remote),
merged: URI.revive(parsed.merged),
accepted: URI.revive(parsed.accepted),
localChange: parsed.localChange,
remoteChange: parsed.remoteChange,
mergeState: parsed.mergeState,
......
......@@ -688,8 +688,8 @@ class UserDataSyncPreview extends Disposable implements IUserDataSyncPreview {
private toUserDataSyncResourceGroups(syncResourcePreviews: [SyncResource, IResourcePreview[]][]): IUserDataSyncResource[] {
return flatten(
syncResourcePreviews.map(([syncResource, resourcePreviews]) =>
resourcePreviews.map<IUserDataSyncResource>(({ localResource, remoteResource, previewResource, localChange, remoteChange, mergeState }) =>
({ syncResource, local: localResource, remote: remoteResource, preview: previewResource, localChange, remoteChange, mergeState })))
resourcePreviews.map<IUserDataSyncResource>(({ localResource, remoteResource, previewResource, acceptedResource, localChange, remoteChange, mergeState }) =>
({ syncResource, local: localResource, remote: remoteResource, merged: previewResource, accepted: acceptedResource, localChange, remoteChange, mergeState })))
);
}
......
......@@ -33,7 +33,8 @@ export interface IUserDataSyncResource {
readonly syncResource: SyncResource;
readonly local: URI;
readonly remote: URI;
readonly preview: URI;
readonly merged: URI;
readonly accepted: URI;
readonly localChange: Change;
readonly remoteChange: Change;
readonly mergeState: MergeState;
......
......@@ -233,6 +233,7 @@ class ManualSyncTask implements IManualSyncTask {
localResource: URI.revive(r.localResource),
remoteResource: URI.revive(r.remoteResource),
previewResource: URI.revive(r.previewResource),
acceptedResource: URI.revive(r.acceptedResource),
}))
}
]));
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册