diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index bb0f578552b..ac12244ade0 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -995,6 +995,16 @@ declare module 'vscode' { } + export class UserDataError extends Error { + + static VersionExists(): FileSystemError; + + /** + * Creates a new userData error. + */ + constructor(); + } + export interface UserDataProvider { read(key: string): Promise<{ version: number, content: string } | null>; diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index e21f6563b02..c7c285a15e3 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -897,7 +897,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I // proposed CallHierarchyDirection: extHostTypes.CallHierarchyDirection, CallHierarchyItem: extHostTypes.CallHierarchyItem, - Decoration: extHostTypes.Decoration + Decoration: extHostTypes.Decoration, + UserDataError: extHostTypes.UserDataError }; }; } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 623297aa26d..6d19f8d7669 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -14,6 +14,7 @@ import { generateUuid } from 'vs/base/common/uuid'; import * as vscode from 'vscode'; import { FileSystemProviderErrorCode, markAsFileSystemProviderError } from 'vs/platform/files/common/files'; import { RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { RemoteUserDataErrorCode, markAsUserDataError } from 'vs/workbench/services/userData/common/userData'; function es5ClassCompat(target: Function): any { ///@ts-ignore @@ -2367,3 +2368,25 @@ export class Decoration { priority?: number; bubble?: boolean; } + +@es5ClassCompat +export class UserDataError extends Error { + + static VersionExists(message?: string): UserDataError { + return new UserDataError(message, RemoteUserDataErrorCode.VersionExists); + } + + constructor(message?: string, code: RemoteUserDataErrorCode = RemoteUserDataErrorCode.Unknown) { + super(message); + + // mark the error as user data provider error so that + // we can extract the error code on the receiving side + markAsUserDataError(this, code); + + // workaround when extending builtin objects and when compiling to ES5, see: + // https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work + if (typeof (Object).setPrototypeOf === 'function') { + (Object).setPrototypeOf(this, UserDataError.prototype); + } + } +} diff --git a/src/vs/workbench/services/userData/common/remoteUserDataService.ts b/src/vs/workbench/services/userData/common/remoteUserDataService.ts index 2e71e26f455..c244ae82529 100644 --- a/src/vs/workbench/services/userData/common/remoteUserDataService.ts +++ b/src/vs/workbench/services/userData/common/remoteUserDataService.ts @@ -6,7 +6,7 @@ import { Disposable, } from 'vs/base/common/lifecycle'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Emitter, Event } from 'vs/base/common/event'; -import { IRemoteUserDataService, IRemoteUserDataProvider, IUserData } from 'vs/workbench/services/userData/common/userData'; +import { IRemoteUserDataService, IRemoteUserDataProvider, IUserData, RemoteUserDataError, toUserDataErrorCode } from 'vs/workbench/services/userData/common/userData'; import { ILogService } from 'vs/platform/log/common/log'; export class RemoteUserDataService extends Disposable implements IRemoteUserDataService { @@ -53,14 +53,16 @@ export class RemoteUserDataService extends Disposable implements IRemoteUserData if (!this.remoteUserDataProvider) { throw new Error('No remote user data provider exists.'); } - return this.remoteUserDataProvider.read(key); + return this.remoteUserDataProvider.read(key) + .then(null, error => Promise.reject(new RemoteUserDataError(error.message, toUserDataErrorCode(error)))); } write(key: string, version: number, content: string): Promise { if (!this.remoteUserDataProvider) { throw new Error('No remote user data provider exists.'); } - return this.remoteUserDataProvider.write(key, version, content); + return this.remoteUserDataProvider.write(key, version, content) + .then(null, error => Promise.reject(new RemoteUserDataError(error.message, toUserDataErrorCode(error)))); } } diff --git a/src/vs/workbench/services/userData/common/settingsSync.ts b/src/vs/workbench/services/userData/common/settingsSync.ts index b59afff1b52..5a9cb0556d4 100644 --- a/src/vs/workbench/services/userData/common/settingsSync.ts +++ b/src/vs/workbench/services/userData/common/settingsSync.ts @@ -374,7 +374,7 @@ export class SettingsSyncService extends Disposable implements ISettingsSyncServ try { await this.remoteUserDataService.write(SettingsSyncService.EXTERNAL_USER_DATA_SETTINGS_KEY, userData.version, userData.content); } catch (e) { - if (e instanceof RemoteUserDataError && e.code === RemoteUserDataErrorCode.InvalidVersion) { + if (e instanceof RemoteUserDataError && e.code === RemoteUserDataErrorCode.VersionExists) { // Rejected as there is a new version. Sync again return this.sync(); } diff --git a/src/vs/workbench/services/userData/common/userData.ts b/src/vs/workbench/services/userData/common/userData.ts index 10ac85fdf93..62babe33d35 100644 --- a/src/vs/workbench/services/userData/common/userData.ts +++ b/src/vs/workbench/services/userData/common/userData.ts @@ -12,7 +12,40 @@ export interface IUserData { } export enum RemoteUserDataErrorCode { - InvalidVersion = 'InvalidVersion' + VersionExists = 'VersionExists', + Unknown = 'Unknown' +} + +export function markAsUserDataError(error: Error, code: RemoteUserDataErrorCode): Error { + error.name = code ? `${code} (UserDataError)` : `UserDataError`; + + return error; +} + +export function toUserDataErrorCode(error: Error | undefined | null): RemoteUserDataErrorCode { + + // Guard against abuse + if (!error) { + return RemoteUserDataErrorCode.Unknown; + } + + // FileSystemProviderError comes with the code + if (error instanceof RemoteUserDataError) { + return error.code; + } + + // Any other error, check for name match by assuming that the error + // went through the markAsFileSystemProviderError() method + const match = /^(.+) \(UserDataError\)$/.exec(error.name); + if (!match) { + return RemoteUserDataErrorCode.Unknown; + } + + switch (match[1]) { + case RemoteUserDataErrorCode.VersionExists: return RemoteUserDataErrorCode.VersionExists; + } + + return RemoteUserDataErrorCode.Unknown; } export class RemoteUserDataError extends Error {