userDataSync.ts 8.4 KB
Newer Older
1 2 3 4 5 6 7
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Event } from 'vs/base/common/event';
S
Sandeep Somavarapu 已提交
8
import { IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement';
9
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
10
import { Registry } from 'vs/platform/registry/common/platform';
11
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope, allSettings } from 'vs/platform/configuration/common/configurationRegistry';
12
import { localize } from 'vs/nls';
13 14 15
import { IDisposable } from 'vs/base/common/lifecycle';
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
16
import { ILogService } from 'vs/platform/log/common/log';
17
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
S
Sandeep Somavarapu 已提交
18
import { IStringDictionary } from 'vs/base/common/collections';
19 20
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
import { URI } from 'vs/base/common/uri';
21 22

const CONFIGURATION_SYNC_STORE_KEY = 'configurationSync.store';
23

24
export const DEFAULT_IGNORED_SETTINGS = [
25
	CONFIGURATION_SYNC_STORE_KEY,
26 27 28
	'sync.enable',
	'sync.enableSettings',
	'sync.enableExtensions',
29 30
];

31 32 33 34 35 36 37 38 39 40 41 42 43
export interface ISyncConfiguration {
	sync: {
		enable: boolean,
		enableSettings: boolean,
		enableKeybindings: boolean,
		enableUIState: boolean,
		enableExtensions: boolean,
		keybindingsPerPlatform: boolean,
		ignoredExtensions: string[],
		ignoredSettings: string[]
	}
}

44 45 46 47
export function registerConfiguration(): IDisposable {
	const ignoredSettingsSchemaId = 'vscode://schemas/ignoredSettings';
	const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
	configurationRegistry.registerConfiguration({
48
		id: 'sync',
49
		order: 30,
50
		title: localize('sync', "Sync"),
51 52
		type: 'object',
		properties: {
53
			'sync.enable': {
54
				type: 'boolean',
55
				description: localize('sync.enable', "Enable synchronization."),
56
				default: false,
57 58
				scope: ConfigurationScope.APPLICATION
			},
59
			'sync.enableSettings': {
60
				type: 'boolean',
61
				description: localize('sync.enableSettings', "Enable synchronizing settings."),
62 63 64
				default: true,
				scope: ConfigurationScope.APPLICATION,
			},
S
Sandeep Somavarapu 已提交
65
			'sync.enableKeybindings': {
66
				type: 'boolean',
S
Sandeep Somavarapu 已提交
67
				description: localize('sync.enableKeybindings', "Enable synchronizing keybindings."),
68 69 70
				default: true,
				scope: ConfigurationScope.APPLICATION,
			},
S
Sandeep Somavarapu 已提交
71
			'sync.enableUIState': {
72
				type: 'boolean',
S
Sandeep Somavarapu 已提交
73
				description: localize('sync.enableUIState', "Enable synchronizing UI state (Only Display Language)."),
S
Sandeep Somavarapu 已提交
74 75 76 77 78 79
				default: true,
				scope: ConfigurationScope.APPLICATION,
			},
			'sync.enableExtensions': {
				type: 'boolean',
				description: localize('sync.enableExtensions', "Enable synchronizing extensions."),
80 81 82
				default: true,
				scope: ConfigurationScope.APPLICATION,
			},
83 84 85 86 87 88
			'sync.keybindingsPerPlatform': {
				type: 'boolean',
				description: localize('sync.keybindingsPerPlatform', "Synchronize keybindings per platform."),
				default: true,
				scope: ConfigurationScope.APPLICATION,
			},
89
			'sync.ignoredExtensions': {
90
				'type': 'array',
91
				description: localize('sync.ignoredExtensions', "Configure extensions to be ignored while synchronizing."),
92 93 94 95
				'default': [],
				'scope': ConfigurationScope.APPLICATION,
				uniqueItems: true
			},
96
			'sync.ignoredSettings': {
97
				'type': 'array',
98
				description: localize('sync.ignoredSettings', "Configure settings to be ignored while synchronizing. \nDefault Ignored Settings:\n\n{0}", DEFAULT_IGNORED_SETTINGS.sort().map(setting => `- ${setting}`).join('\n')),
99
				'default': [],
100 101 102 103 104 105 106 107 108 109 110 111
				'scope': ConfigurationScope.APPLICATION,
				$ref: ignoredSettingsSchemaId,
				additionalProperties: true,
				uniqueItems: true
			}
		}
	});
	const registerIgnoredSettingsSchema = () => {
		const jsonRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
		const ignoredSettingsSchema: IJSONSchema = {
			items: {
				type: 'string',
112
				enum: [...Object.keys(allSettings.properties).filter(setting => DEFAULT_IGNORED_SETTINGS.indexOf(setting) === -1), ...DEFAULT_IGNORED_SETTINGS.map(setting => `-${setting}`)]
113
			}
114 115 116 117
		};
		jsonRegistry.registerSchema(ignoredSettingsSchemaId, ignoredSettingsSchema);
	};
	return configurationRegistry.onDidUpdateConfiguration(() => registerIgnoredSettingsSchema());
118
}
119 120 121

export interface IUserData {
	ref: string;
122
	content: string | null;
123 124 125
}

export enum UserDataSyncStoreErrorCode {
S
Sandeep Somavarapu 已提交
126
	Unauthroized = 'Unauthroized',
127 128 129 130 131 132 133 134 135 136 137 138
	Rejected = 'Rejected',
	Unknown = 'Unknown'
}

export class UserDataSyncStoreError extends Error {

	constructor(message: string, public readonly code: UserDataSyncStoreErrorCode) {
		super(message);
	}

}

139 140 141 142 143 144 145 146 147 148 149
export interface IUserDataSyncStore {
	url: string;
	name: string;
	account: string;
}

export function getUserDataSyncStore(configurationService: IConfigurationService): IUserDataSyncStore | undefined {
	const value = configurationService.getValue<IUserDataSyncStore>(CONFIGURATION_SYNC_STORE_KEY);
	return value && value.url && value.name && value.account ? value : undefined;
}

150 151 152
export const IUserDataSyncStoreService = createDecorator<IUserDataSyncStoreService>('IUserDataSyncStoreService');
export interface IUserDataSyncStoreService {
	_serviceBrand: undefined;
153
	readonly userDataSyncStore: IUserDataSyncStore | undefined;
154
	read(key: string, oldValue: IUserData | null): Promise<IUserData>;
155
	write(key: string, content: string, ref: string | null): Promise<string>;
156 157
}

S
Sandeep Somavarapu 已提交
158 159 160 161 162 163
export interface ISyncExtension {
	identifier: IExtensionIdentifier;
	version?: string;
	enabled: boolean;
}

164 165 166 167 168
export interface IGlobalState {
	argv: IStringDictionary<any>;
	storage: IStringDictionary<any>;
}

S
Sandeep Somavarapu 已提交
169
export const enum SyncSource {
S
Sandeep Somavarapu 已提交
170 171 172 173
	Settings = 'Settings',
	Keybindings = 'Keybindings',
	Extensions = 'Extensions',
	UIState = 'UI State'
174
}
175

S
Sandeep Somavarapu 已提交
176
export const enum SyncStatus {
177 178 179 180 181 182 183 184 185 186
	Uninitialized = 'uninitialized',
	Idle = 'idle',
	Syncing = 'syncing',
	HasConflicts = 'hasConflicts',
}

export interface ISynchroniser {
	readonly status: SyncStatus;
	readonly onDidChangeStatus: Event<SyncStatus>;
	readonly onDidChangeLocal: Event<void>;
187 188
	pull(): Promise<void>;
	push(): Promise<void>;
189
	sync(_continue?: boolean): Promise<boolean>;
S
Sandeep Somavarapu 已提交
190
	stop(): void;
191 192
	hasPreviouslySynced(): Promise<boolean>
	hasRemote(): Promise<boolean>;
193 194 195 196 197
}

export const IUserDataSyncService = createDecorator<IUserDataSyncService>('IUserDataSyncService');
export interface IUserDataSyncService extends ISynchroniser {
	_serviceBrand: any;
198
	readonly conflictsSource: SyncSource | null;
199
	removeExtension(identifier: IExtensionIdentifier): Promise<void>;
200
}
201

202 203
export const IUserDataSyncUtilService = createDecorator<IUserDataSyncUtilService>('IUserDataSyncUtilService');
export interface IUserDataSyncUtilService {
204
	_serviceBrand: undefined;
205 206
	resolveUserBindings(userbindings: string[]): Promise<IStringDictionary<string>>;
	resolveFormattingOptions(resource: URI): Promise<FormattingOptions>;
207 208
}

209 210 211 212 213 214 215 216 217 218 219
export const IUserDataAuthTokenService = createDecorator<IUserDataAuthTokenService>('IUserDataAuthTokenService');

export interface IUserDataAuthTokenService {
	_serviceBrand: undefined;

	readonly onDidChangeToken: Event<string | undefined>;

	getToken(): Promise<string | undefined>;
	setToken(accessToken: string | undefined): Promise<void>;
}

220
export const IUserDataSyncLogService = createDecorator<IUserDataSyncLogService>('IUserDataSyncLogService');
S
Sandeep Somavarapu 已提交
221
export interface IUserDataSyncLogService extends ILogService { }
222

S
Sandeep Somavarapu 已提交
223 224 225 226 227
export interface IConflictSetting {
	key: string;
	localValue: any | undefined;
	remoteValue: any | undefined;
}
228

S
Sandeep Somavarapu 已提交
229 230 231
export const ISettingsSyncService = createDecorator<ISettingsSyncService>('ISettingsSyncService');
export interface ISettingsSyncService extends ISynchroniser {
	_serviceBrand: any;
232 233
	readonly onDidChangeConflicts: Event<IConflictSetting[]>;
	readonly conflicts: IConflictSetting[];
S
Sandeep Somavarapu 已提交
234
	resolveConflicts(resolvedConflicts: { key: string, value: any | undefined }[]): Promise<void>;
235 236
}

237
export const CONTEXT_SYNC_STATE = new RawContextKey<string>('syncStatus', SyncStatus.Uninitialized);