mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-15 10:58:31 -05:00
Merge from vscode cfbd1999769f4f08dce29629fb92fdc0fac53829
This commit is contained in:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user