Merge from vscode 8a997f7321ae6612fc0e6eb3eac4f358a6233bfb

This commit is contained in:
ADS Merger
2020-02-11 07:08:19 +00:00
parent 0f934081e1
commit 085752f111
217 changed files with 2561 additions and 2063 deletions

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IUserDataSyncService, SyncStatus, IUserDataSyncStoreService, SyncSource, ISettingsSyncService, IUserDataSyncLogService, IUserDataAuthTokenService, IUserDataSynchroniser, UserDataSyncStoreError, UserDataSyncErrorCode } from 'vs/platform/userDataSync/common/userDataSync';
import { IUserDataSyncService, SyncStatus, IUserDataSyncStoreService, SyncSource, ISettingsSyncService, IUserDataSyncLogService, IUserDataAuthTokenService, IUserDataSynchroniser, UserDataSyncStoreError, UserDataSyncErrorCode, UserDataSyncError } from 'vs/platform/userDataSync/common/userDataSync';
import { Disposable } from 'vs/base/common/lifecycle';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Emitter, Event } from 'vs/base/common/event';
@@ -11,12 +11,9 @@ import { ExtensionsSynchroniser } from 'vs/platform/userDataSync/common/extensio
import { KeybindingsSynchroniser } from 'vs/platform/userDataSync/common/keybindingsSync';
import { GlobalStateSynchroniser } from 'vs/platform/userDataSync/common/globalStateSync';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { localize } from 'vs/nls';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
type SyncConflictsClassification = {
source?: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
};
import { equals } from 'vs/base/common/arrays';
import { localize } from 'vs/nls';
type SyncErrorClassification = {
source: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
@@ -35,8 +32,10 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
readonly onDidChangeLocal: Event<void>;
private _conflictsSource: SyncSource | null = null;
get conflictsSource(): SyncSource | null { return this._conflictsSource; }
private _conflictsSources: SyncSource[] = [];
get conflictsSources(): SyncSource[] { return this._conflictsSources; }
private _onDidChangeConflicts: Emitter<SyncSource[]> = this._register(new Emitter<SyncSource[]>());
readonly onDidChangeConflicts: Event<SyncSource[]> = this._onDidChangeConflicts.event;
private readonly keybindingsSynchroniser: KeybindingsSynchroniser;
private readonly extensionsSynchroniser: ExtensionsSynchroniser;
@@ -66,12 +65,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
}
async pull(): Promise<void> {
if (!this.userDataSyncStoreService.userDataSyncStore) {
throw new Error('Not enabled');
}
if (!(await this.userDataAuthTokenService.getToken())) {
throw new Error('Not Authenticated. Please sign in to start sync.');
}
await this.checkEnablement();
for (const synchroniser of this.synchronisers) {
try {
await synchroniser.pull();
@@ -82,12 +76,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
}
async push(): Promise<void> {
if (!this.userDataSyncStoreService.userDataSyncStore) {
throw new Error('Not enabled');
}
if (!(await this.userDataAuthTokenService.getToken())) {
throw new Error('Not Authenticated. Please sign in to start sync.');
}
await this.checkEnablement();
for (const synchroniser of this.synchronisers) {
try {
await synchroniser.push();
@@ -98,105 +87,79 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
}
async sync(): Promise<void> {
if (!this.userDataSyncStoreService.userDataSyncStore) {
throw new Error('Not enabled');
}
if (!(await this.userDataAuthTokenService.getToken())) {
throw new Error('Not Authenticated. Please sign in to start sync.');
}
if (this.status === SyncStatus.HasConflicts) {
throw new Error(localize('resolve conflicts', "Please resolve conflicts before resuming sync."));
}
const startTime = new Date().getTime();
this.logService.trace('Started Syncing...');
for (const synchroniser of this.synchronisers) {
try {
await synchroniser.sync();
// do not continue if synchroniser has conflicts
if (synchroniser.status === SyncStatus.HasConflicts) {
break;
}
} catch (e) {
this.handleSyncError(e, synchroniser.source);
}
}
this.logService.trace(`Finished Syncing. Took ${new Date().getTime() - startTime}ms`);
}
await this.checkEnablement();
async accept(source: SyncSource, content: string): Promise<void> {
const synchroniser = this.getSynchroniser(source);
await synchroniser.accept(content);
if (synchroniser.status !== SyncStatus.HasConflicts) {
await this.sync();
const startTime = new Date().getTime();
try {
this.logService.trace('Sync started.');
if (this.status !== SyncStatus.HasConflicts) {
this.setStatus(SyncStatus.Syncing);
}
const manifest = await this.userDataSyncStoreService.manifest();
// Server has no data but this machine was synced before
if (manifest === null && await this.hasPreviouslySynced()) {
// Sync was turned off from other machine
throw new UserDataSyncError(localize('turned off', "Cannot sync because syncing is turned off in the cloud"), UserDataSyncErrorCode.TurnedOff);
}
for (const synchroniser of this.synchronisers) {
try {
await synchroniser.sync(manifest ? manifest[synchroniser.resourceKey] : undefined);
} catch (e) {
this.handleSyncError(e, synchroniser.source);
}
}
this.logService.info(`Sync done. Took ${new Date().getTime() - startTime}ms`);
} finally {
this.updateStatus();
}
}
async stop(): Promise<void> {
if (!this.userDataSyncStoreService.userDataSyncStore) {
throw new Error('Not enabled');
if (this.status === SyncStatus.Idle) {
return;
}
for (const synchroniser of this.synchronisers) {
await synchroniser.stop();
try {
if (synchroniser.status !== SyncStatus.Idle) {
await synchroniser.stop();
}
} catch (e) {
this.logService.error(e);
}
}
}
async restart(): Promise<void> {
const synchroniser = this.getSynchroniserInConflicts();
if (!synchroniser) {
throw new Error(localize('no synchroniser with conflicts', "No conflicts detected."));
}
await synchroniser.restart();
if (synchroniser.status !== SyncStatus.HasConflicts) {
await this.sync();
}
async accept(source: SyncSource, content: string): Promise<void> {
await this.checkEnablement();
const synchroniser = this.getSynchroniser(source);
return synchroniser.accept(content);
}
async hasPreviouslySynced(): Promise<boolean> {
if (!this.userDataSyncStoreService.userDataSyncStore) {
throw new Error('Not enabled');
}
if (!(await this.userDataAuthTokenService.getToken())) {
throw new Error('Not Authenticated. Please sign in to start sync.');
}
await this.checkEnablement();
for (const synchroniser of this.synchronisers) {
if (await synchroniser.hasPreviouslySynced()) {
return true;
}
}
return false;
}
async hasRemoteData(): Promise<boolean> {
if (!this.userDataSyncStoreService.userDataSyncStore) {
throw new Error('Not enabled');
}
if (!(await this.userDataAuthTokenService.getToken())) {
throw new Error('Not Authenticated. Please sign in to start sync.');
}
for (const synchroniser of this.synchronisers) {
if (await synchroniser.hasRemoteData()) {
return true;
}
}
return false;
}
async hasLocalData(): Promise<boolean> {
if (!this.userDataSyncStoreService.userDataSyncStore) {
throw new Error('Not enabled');
}
if (!(await this.userDataAuthTokenService.getToken())) {
throw new Error('Not Authenticated. Please sign in to start sync.');
}
private async hasLocalData(): Promise<boolean> {
await this.checkEnablement();
for (const synchroniser of this.synchronisers) {
if (await synchroniser.hasLocalData()) {
return true;
}
}
return false;
}
async getRemoteContent(source: SyncSource, preview: boolean): Promise<string | null> {
await this.checkEnablement();
for (const synchroniser of this.synchronisers) {
if (synchroniser.source === source) {
return synchroniser.getRemoteContent(preview);
@@ -205,12 +168,10 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
return null;
}
async isFirstTimeSyncAndHasUserData(): Promise<boolean> {
if (!this.userDataSyncStoreService.userDataSyncStore) {
throw new Error('Not enabled');
}
if (!(await this.userDataAuthTokenService.getToken())) {
throw new Error('Not Authenticated. Please sign in to start sync.');
async isFirstTimeSyncWithMerge(): Promise<boolean> {
await this.checkEnablement();
if (!await this.userDataSyncStoreService.manifest()) {
return false;
}
if (await this.hasPreviouslySynced()) {
return false;
@@ -219,60 +180,46 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
}
async reset(): Promise<void> {
await this.checkEnablement();
await this.resetRemote();
await this.resetLocal();
}
private async resetRemote(): Promise<void> {
if (!this.userDataSyncStoreService.userDataSyncStore) {
throw new Error('Not enabled');
}
if (!(await this.userDataAuthTokenService.getToken())) {
throw new Error('Not Authenticated. Please sign in to start sync.');
}
try {
await this.userDataSyncStoreService.clear();
this.logService.info('Completed clearing remote data');
} catch (e) {
this.logService.error(e);
}
}
async resetLocal(): Promise<void> {
if (!this.userDataSyncStoreService.userDataSyncStore) {
throw new Error('Not enabled');
}
if (!(await this.userDataAuthTokenService.getToken())) {
throw new Error('Not Authenticated. Please sign in to start sync.');
}
await this.checkEnablement();
for (const synchroniser of this.synchronisers) {
try {
await synchroniser.resetLocal();
} catch (e) {
this.logService.error(`${synchroniser.source}: ${toErrorMessage(e)}`);
this.logService.error(e);
}
}
this.logService.info('Completed resetting local cache');
}
private async resetRemote(): Promise<void> {
await this.checkEnablement();
try {
await this.userDataSyncStoreService.clear();
} catch (e) {
this.logService.error(e);
}
}
private setStatus(status: SyncStatus): void {
if (this._status !== status) {
this._status = status;
this._onDidChangeStatus.fire(status);
}
}
private updateStatus(): void {
const status = this.computeStatus();
if (this._status !== status) {
const oldStatus = this._status;
const oldConflictsSource = this._conflictsSource;
this._conflictsSource = this.computeConflictsSource();
this._status = status;
if (status === SyncStatus.HasConflicts) {
// Log to telemetry when there is a sync conflict
this.telemetryService.publicLog2<{ source: string }, SyncConflictsClassification>('sync/conflictsDetected', { source: this._conflictsSource! });
}
if (oldStatus === SyncStatus.HasConflicts && status === SyncStatus.Idle) {
// Log to telemetry when conflicts are resolved
this.telemetryService.publicLog2<{ source: string }, SyncConflictsClassification>('sync/conflictsResolved', { source: oldConflictsSource! });
}
this._onDidChangeStatus.fire(status);
const conflictsSources = this.computeConflictsSources();
if (!equals(this._conflictsSources, conflictsSources)) {
this._conflictsSources = this.computeConflictsSources();
this._onDidChangeConflicts.fire(conflictsSources);
}
const status = this.computeStatus();
this.setStatus(status);
}
private computeStatus(): SyncStatus {
@@ -300,14 +247,8 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
this.logService.error(`${source}: ${toErrorMessage(e)}`);
}
private computeConflictsSource(): SyncSource | null {
const synchroniser = this.synchronisers.filter(s => s.status === SyncStatus.HasConflicts)[0];
return synchroniser ? synchroniser.source : null;
}
private getSynchroniserInConflicts(): IUserDataSynchroniser | null {
const synchroniser = this.synchronisers.filter(s => s.status === SyncStatus.HasConflicts)[0];
return synchroniser || null;
private computeConflictsSources(): SyncSource[] {
return this.synchronisers.filter(s => s.status === SyncStatus.HasConflicts).map(s => s.source);
}
private getSynchroniser(source: SyncSource): IUserDataSynchroniser {
@@ -319,6 +260,15 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
}
}
private async checkEnablement(): Promise<void> {
if (!this.userDataSyncStoreService.userDataSyncStore) {
throw new Error('Not enabled');
}
if (!(await this.userDataAuthTokenService.getToken())) {
throw new UserDataSyncError('Not Authenticated. Please sign in to start sync.', UserDataSyncErrorCode.Unauthorized);
}
}
private onDidChangeAuthTokenStatus(token: string | undefined): void {
if (!token) {
this.stop();