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

#100346 fix opening diffs and accept preview

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