Merge from vscode cfbd1999769f4f08dce29629fb92fdc0fac53829

This commit is contained in:
ADS Merger
2020-08-06 07:08:52 +00:00
parent 9c67832880
commit 540046ba00
362 changed files with 7588 additions and 6584 deletions

View File

@@ -5,7 +5,7 @@
import {
IUserDataSyncService, SyncStatus, IUserDataSyncStoreService, SyncResource, IUserDataSyncLogService, IUserDataSynchroniser, UserDataSyncErrorCode,
UserDataSyncError, ISyncResourceHandle, IUserDataManifest, ISyncTask, IResourcePreview, IManualSyncTask, ISyncResourcePreview, HEADER_EXECUTION_ID, MergeState, Change
UserDataSyncError, ISyncResourceHandle, IUserDataManifest, ISyncTask, IResourcePreview, IManualSyncTask, ISyncResourcePreview, HEADER_EXECUTION_ID, MergeState, Change, IUserDataSyncStoreManagementService
} from 'vs/platform/userDataSync/common/userDataSync';
import { Disposable } from 'vs/base/common/lifecycle';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -28,6 +28,8 @@ import { createCancelablePromise, CancelablePromise } from 'vs/base/common/async
import { isPromiseCanceledError } from 'vs/base/common/errors';
type SyncErrorClassification = {
code: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
service: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
resource?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
executionId?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
};
@@ -67,6 +69,11 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
private _onDidChangeLastSyncTime: Emitter<number> = this._register(new Emitter<number>());
readonly onDidChangeLastSyncTime: Event<number> = this._onDidChangeLastSyncTime.event;
private _onDidResetLocal = this._register(new Emitter<void>());
readonly onDidResetLocal = this._onDidResetLocal.event;
private _onDidResetRemote = this._register(new Emitter<void>());
readonly onDidResetRemote = this._onDidResetRemote.event;
private readonly settingsSynchroniser: SettingsSynchroniser;
private readonly keybindingsSynchroniser: KeybindingsSynchroniser;
private readonly snippetsSynchroniser: SnippetsSynchroniser;
@@ -75,6 +82,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
constructor(
@IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService,
@IUserDataSyncStoreManagementService private readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@@ -89,7 +97,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
this.synchronisers = [this.settingsSynchroniser, this.keybindingsSynchroniser, this.snippetsSynchroniser, this.globalStateSynchroniser, this.extensionsSynchroniser];
this.updateStatus();
if (this.userDataSyncStoreService.userDataSyncStore) {
if (this.userDataSyncStoreManagementService.userDataSyncStore) {
this._register(Event.any(...this.synchronisers.map(s => Event.map(s.onDidChangeStatus, () => undefined)))(() => this.updateStatus()));
this._register(Event.any(...this.synchronisers.map(s => Event.map(s.onDidChangeConflicts, () => undefined)))(() => this.updateConflicts()));
}
@@ -98,36 +106,6 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
this.onDidChangeLocal = Event.any(...this.synchronisers.map(s => Event.map(s.onDidChangeLocal, () => s.resource)));
}
async pull(): Promise<void> {
await this.checkEnablement();
try {
for (const synchroniser of this.synchronisers) {
await synchroniser.pull();
}
this.updateLastSyncTime();
} catch (error) {
if (error instanceof UserDataSyncError) {
this.telemetryService.publicLog2<{ resource?: string, executionId?: string }, SyncErrorClassification>(`sync/error/${error.code}`, { resource: error.resource });
}
throw error;
}
}
async push(): Promise<void> {
await this.checkEnablement();
try {
for (const synchroniser of this.synchronisers) {
await synchroniser.push();
}
this.updateLastSyncTime();
} catch (error) {
if (error instanceof UserDataSyncError) {
this.telemetryService.publicLog2<{ resource?: string, executionId?: string }, SyncErrorClassification>(`sync/error/${error.code}`, { resource: error.resource });
}
throw error;
}
}
async createSyncTask(): Promise<ISyncTask> {
await this.checkEnablement();
@@ -136,9 +114,8 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
try {
manifest = await this.userDataSyncStoreService.manifest(createSyncHeaders(executionId));
} catch (error) {
if (error instanceof UserDataSyncError) {
this.telemetryService.publicLog2<{ resource?: string, executionId?: string }, SyncErrorClassification>(`sync/error/${error.code}`, { resource: error.resource, executionId });
}
error = UserDataSyncError.toUserDataSyncError(error);
this.telemetryService.publicLog2<{ code: string, service: string, resource?: string, executionId?: string }, SyncErrorClassification>('sync/error', { code: error.code, resource: error.resource, executionId, service: this.userDataSyncStoreManagementService.userDataSyncStore!.url.toString() });
throw error;
}
@@ -173,9 +150,8 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
try {
manifest = await this.userDataSyncStoreService.manifest(syncHeaders);
} catch (error) {
if (error instanceof UserDataSyncError) {
this.telemetryService.publicLog2<{ resource?: string, executionId?: string }, SyncErrorClassification>(`sync/error/${error.code}`, { resource: error.resource, executionId });
}
error = UserDataSyncError.toUserDataSyncError(error);
this.telemetryService.publicLog2<{ code: string, service: string, resource?: string, executionId?: string }, SyncErrorClassification>('sync/error', { code: error.code, resource: error.resource, executionId, service: this.userDataSyncStoreManagementService.userDataSyncStore!.url.toString() });
throw error;
}
@@ -220,9 +196,8 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
this.logService.info(`Sync done. Took ${new Date().getTime() - startTime}ms`);
this.updateLastSyncTime();
} catch (error) {
if (error instanceof UserDataSyncError) {
this.telemetryService.publicLog2<{ resource?: string, executionId?: string }, SyncErrorClassification>(`sync/error/${error.code}`, { resource: error.resource, executionId });
}
error = UserDataSyncError.toUserDataSyncError(error);
this.telemetryService.publicLog2<{ code: string, service: string, resource?: string, executionId?: string }, SyncErrorClassification>('sync/error', { code: error.code, resource: error.resource, executionId, service: this.userDataSyncStoreManagementService.userDataSyncStore!.url.toString() });
throw error;
} finally {
this.updateStatus();
@@ -256,7 +231,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
}
}
async accept(syncResource: SyncResource, resource: URI, content: string | null, apply: boolean): Promise<void> {
async accept(syncResource: SyncResource, resource: URI, content: string | null | undefined, apply: boolean): Promise<void> {
await this.checkEnablement();
const synchroniser = this.getSynchroniser(syncResource);
await synchroniser.accept(resource, content);
@@ -283,7 +258,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
return this.getSynchroniser(resource).getLocalSyncResourceHandles();
}
getAssociatedResources(resource: SyncResource, syncResourceHandle: ISyncResourceHandle): Promise<{ resource: URI, comparableResource?: URI }[]> {
getAssociatedResources(resource: SyncResource, syncResourceHandle: ISyncResourceHandle): Promise<{ resource: URI, comparableResource: URI }[]> {
return this.getSynchroniser(resource).getAssociatedResources(syncResourceHandle);
}
@@ -316,6 +291,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
} catch (e) {
this.logService.error(e);
}
this._onDidResetRemote.fire();
}
async resetLocal(): Promise<void> {
@@ -329,6 +305,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
this.logService.error(e);
}
}
this._onDidResetLocal.fire();
this.logService.info('Did reset the local sync state.');
}
@@ -367,7 +344,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
}
private computeStatus(): SyncStatus {
if (!this.userDataSyncStoreService.userDataSyncStore) {
if (!this.userDataSyncStoreManagementService.userDataSyncStore) {
return SyncStatus.Uninitialized;
}
if (this.synchronisers.some(s => s.status === SyncStatus.HasConflicts)) {
@@ -417,7 +394,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
}
private async checkEnablement(): Promise<void> {
if (!this.userDataSyncStoreService.userDataSyncStore) {
if (!this.userDataSyncStoreManagementService.userDataSyncStore) {
throw new Error('Not enabled');
}
}
@@ -435,6 +412,16 @@ class ManualSyncTask extends Disposable implements IManualSyncTask {
private isDisposed: boolean = false;
get status(): SyncStatus {
if (this.synchronisers.some(s => s.status === SyncStatus.HasConflicts)) {
return SyncStatus.HasConflicts;
}
if (this.synchronisers.some(s => s.status === SyncStatus.Syncing)) {
return SyncStatus.Syncing;
}
return SyncStatus.Idle;
}
constructor(
readonly id: string,
readonly manifest: IUserDataManifest | null,
@@ -452,60 +439,48 @@ class ManualSyncTask extends Disposable implements IManualSyncTask {
if (!this.previewsPromise) {
this.previewsPromise = createCancelablePromise(token => this.getPreviews(token));
}
this.previews = await this.previewsPromise;
if (!this.previews) {
this.previews = await this.previewsPromise;
}
return this.previews;
}
async accept(resource: URI, content: string | null): Promise<[SyncResource, ISyncResourcePreview][]> {
async accept(resource: URI, content?: string | null): Promise<[SyncResource, ISyncResourcePreview][]> {
return this.performAction(resource, sychronizer => sychronizer.accept(resource, content));
}
async merge(resource: URI): Promise<[SyncResource, ISyncResourcePreview][]> {
return this.performAction(resource, sychronizer => sychronizer.merge(resource));
async merge(resource?: URI): Promise<[SyncResource, ISyncResourcePreview][]> {
if (resource) {
return this.performAction(resource, sychronizer => sychronizer.merge(resource));
} else {
return this.mergeAll();
}
}
async discard(resource: URI): Promise<[SyncResource, ISyncResourcePreview][]> {
return this.performAction(resource, sychronizer => sychronizer.discard(resource));
}
private async performAction(resource: URI, action: (synchroniser: IUserDataSynchroniser) => Promise<ISyncResourcePreview | null>): Promise<[SyncResource, ISyncResourcePreview][]> {
async discardConflicts(): Promise<[SyncResource, ISyncResourcePreview][]> {
if (!this.previews) {
throw new Error('Missing preview. Create preview and try again.');
}
const index = this.previews.findIndex(([, preview]) => preview.resourcePreviews.some(({ localResource, previewResource, remoteResource }) =>
isEqual(resource, localResource) || isEqual(resource, previewResource) || isEqual(resource, remoteResource)));
if (index === -1) {
return this.previews;
if (this.synchronizingResources.length) {
throw new Error('Cannot discard while synchronizing resources');
}
const [syncResource, previews] = this.previews[index];
const resourcePreview = previews.resourcePreviews.find(({ localResource, remoteResource, previewResource }) => isEqual(localResource, resource) || isEqual(remoteResource, resource) || isEqual(previewResource, resource));
if (!resourcePreview) {
return this.previews;
const conflictResources: URI[] = [];
for (const [, syncResourcePreview] of this.previews) {
for (const resourcePreview of syncResourcePreview.resourcePreviews) {
if (resourcePreview.mergeState === MergeState.Conflict) {
conflictResources.push(resourcePreview.previewResource);
}
}
}
let synchronizingResources = this.synchronizingResources.find(s => s[0] === syncResource);
if (!synchronizingResources) {
synchronizingResources = [syncResource, []];
this.synchronizingResources.push(synchronizingResources);
for (const resource of conflictResources) {
await this.discard(resource);
}
if (!synchronizingResources[1].some(s => isEqual(s, resourcePreview.localResource))) {
synchronizingResources[1].push(resourcePreview.localResource);
this._onSynchronizeResources.fire(this.synchronizingResources);
}
const synchroniser = this.synchronisers.find(s => s.resource === this.previews![index][0])!;
const preview = await action(synchroniser);
preview ? this.previews.splice(index, 1, this.toSyncResourcePreview(synchroniser.resource, preview)) : this.previews.splice(index, 1);
const i = this.synchronizingResources.findIndex(s => s[0] === syncResource);
this.synchronizingResources[i][1].splice(synchronizingResources[1].findIndex(r => isEqual(r, resourcePreview.localResource)), 1);
if (!synchronizingResources[1].length) {
this.synchronizingResources.splice(i, 1);
this._onSynchronizeResources.fire(this.synchronizingResources);
}
return this.previews;
}
@@ -555,8 +530,7 @@ class ManualSyncTask extends Disposable implements IManualSyncTask {
this._onSynchronizeResources.fire(this.synchronizingResources);
const synchroniser = this.synchronisers.find(s => s.resource === syncResource)!;
for (const resourcePreview of preview.resourcePreviews) {
const content = await synchroniser.resolveContent(resourcePreview.remoteResource);
await synchroniser.accept(resourcePreview.remoteResource, content);
await synchroniser.accept(resourcePreview.remoteResource);
}
await synchroniser.apply(true, this.syncHeaders);
this.synchronizingResources.splice(this.synchronizingResources.findIndex(s => s[0] === syncResource), 1);
@@ -577,8 +551,7 @@ class ManualSyncTask extends Disposable implements IManualSyncTask {
this._onSynchronizeResources.fire(this.synchronizingResources);
const synchroniser = this.synchronisers.find(s => s.resource === syncResource)!;
for (const resourcePreview of preview.resourcePreviews) {
const content = await synchroniser.resolveContent(resourcePreview.localResource);
await synchroniser.accept(resourcePreview.localResource, content);
await synchroniser.accept(resourcePreview.localResource);
}
await synchroniser.apply(true, this.syncHeaders);
this.synchronizingResources.splice(this.synchronizingResources.findIndex(s => s[0] === syncResource), 1);
@@ -600,6 +573,80 @@ class ManualSyncTask extends Disposable implements IManualSyncTask {
this.reset();
}
private async performAction(resource: URI, action: (synchroniser: IUserDataSynchroniser) => Promise<ISyncResourcePreview | null>): Promise<[SyncResource, ISyncResourcePreview][]> {
if (!this.previews) {
throw new Error('Missing preview. Create preview and try again.');
}
const index = this.previews.findIndex(([, preview]) => preview.resourcePreviews.some(({ localResource, previewResource, remoteResource }) =>
isEqual(resource, localResource) || isEqual(resource, previewResource) || isEqual(resource, remoteResource)));
if (index === -1) {
return this.previews;
}
const [syncResource, previews] = this.previews[index];
const resourcePreview = previews.resourcePreviews.find(({ localResource, remoteResource, previewResource }) => isEqual(localResource, resource) || isEqual(remoteResource, resource) || isEqual(previewResource, resource));
if (!resourcePreview) {
return this.previews;
}
let synchronizingResources = this.synchronizingResources.find(s => s[0] === syncResource);
if (!synchronizingResources) {
synchronizingResources = [syncResource, []];
this.synchronizingResources.push(synchronizingResources);
}
if (!synchronizingResources[1].some(s => isEqual(s, resourcePreview.localResource))) {
synchronizingResources[1].push(resourcePreview.localResource);
this._onSynchronizeResources.fire(this.synchronizingResources);
}
const synchroniser = this.synchronisers.find(s => s.resource === this.previews![index][0])!;
const preview = await action(synchroniser);
preview ? this.previews.splice(index, 1, this.toSyncResourcePreview(synchroniser.resource, preview)) : this.previews.splice(index, 1);
const i = this.synchronizingResources.findIndex(s => s[0] === syncResource);
this.synchronizingResources[i][1].splice(synchronizingResources[1].findIndex(r => isEqual(r, resourcePreview.localResource)), 1);
if (!synchronizingResources[1].length) {
this.synchronizingResources.splice(i, 1);
this._onSynchronizeResources.fire(this.synchronizingResources);
}
return this.previews;
}
private async mergeAll(): Promise<[SyncResource, ISyncResourcePreview][]> {
if (!this.previews) {
throw new Error('You need to create preview before merging or applying');
}
if (this.synchronizingResources.length) {
throw new Error('Cannot merge or apply while synchronizing resources');
}
const previews: [SyncResource, ISyncResourcePreview][] = [];
for (const [syncResource, preview] of this.previews) {
this.synchronizingResources.push([syncResource, preview.resourcePreviews.map(r => r.localResource)]);
this._onSynchronizeResources.fire(this.synchronizingResources);
const synchroniser = this.synchronisers.find(s => s.resource === syncResource)!;
/* merge those which are not yet merged */
let newPreview: ISyncResourcePreview | null = preview;
for (const resourcePreview of preview.resourcePreviews) {
if ((resourcePreview.localChange !== Change.None || resourcePreview.remoteChange !== Change.None) && resourcePreview.mergeState === MergeState.Preview) {
newPreview = await synchroniser.merge(resourcePreview.previewResource);
}
}
if (newPreview) {
previews.push(this.toSyncResourcePreview(synchroniser.resource, newPreview));
}
this.synchronizingResources.splice(this.synchronizingResources.findIndex(s => s[0] === syncResource), 1);
this._onSynchronizeResources.fire(this.synchronizingResources);
}
this.previews = previews;
return this.previews;
}
private async getPreviews(token: CancellationToken): Promise<[SyncResource, ISyncResourcePreview][]> {
const result: [SyncResource, ISyncResourcePreview][] = [];
for (const synchroniser of this.synchronisers) {