userDataSync.ts 7.9 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
	sync(_continue?: boolean): Promise<boolean>;
S
Sandeep Somavarapu 已提交
188
	stop(): void;
189 190 191 192 193
}

export const IUserDataSyncService = createDecorator<IUserDataSyncService>('IUserDataSyncService');
export interface IUserDataSyncService extends ISynchroniser {
	_serviceBrand: any;
194
	readonly conflictsSource: SyncSource | null;
195
	removeExtension(identifier: IExtensionIdentifier): Promise<void>;
196
}
197

198 199
export const IUserDataSyncUtilService = createDecorator<IUserDataSyncUtilService>('IUserDataSyncUtilService');
export interface IUserDataSyncUtilService {
200
	_serviceBrand: undefined;
201 202
	resolveUserBindings(userbindings: string[]): Promise<IStringDictionary<string>>;
	resolveFormattingOptions(resource: URI): Promise<FormattingOptions>;
203 204
}

205
export const IUserDataSyncLogService = createDecorator<IUserDataSyncLogService>('IUserDataSyncLogService');
S
Sandeep Somavarapu 已提交
206
export interface IUserDataSyncLogService extends ILogService { }
207

S
Sandeep Somavarapu 已提交
208 209 210 211 212
export interface IConflictSetting {
	key: string;
	localValue: any | undefined;
	remoteValue: any | undefined;
}
213

S
Sandeep Somavarapu 已提交
214 215 216
export const ISettingsSyncService = createDecorator<ISettingsSyncService>('ISettingsSyncService');
export interface ISettingsSyncService extends ISynchroniser {
	_serviceBrand: any;
217 218
	readonly onDidChangeConflicts: Event<IConflictSetting[]>;
	readonly conflicts: IConflictSetting[];
S
Sandeep Somavarapu 已提交
219
	resolveConflicts(resolvedConflicts: { key: string, value: any | undefined }[]): Promise<void>;
220 221
}

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