userDataSync.ts 6.7 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 18 19
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';

const CONFIGURATION_SYNC_STORE_KEY = 'configurationSync.store';
20

21
export const DEFAULT_IGNORED_SETTINGS = [
22
	CONFIGURATION_SYNC_STORE_KEY,
23 24 25
	'sync.enable',
	'sync.enableSettings',
	'sync.enableExtensions',
26 27
];

28 29 30 31
export function registerConfiguration(): IDisposable {
	const ignoredSettingsSchemaId = 'vscode://schemas/ignoredSettings';
	const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
	configurationRegistry.registerConfiguration({
32
		id: 'sync',
33
		order: 30,
34
		title: localize('sync', "Sync"),
35 36
		type: 'object',
		properties: {
37
			'sync.enable': {
38
				type: 'boolean',
39
				description: localize('sync.enable', "Enable synchronization."),
40
				default: false,
41 42
				scope: ConfigurationScope.APPLICATION
			},
43
			'sync.enableSettings': {
44
				type: 'boolean',
45
				description: localize('sync.enableSettings', "Enable synchronizing settings."),
46 47 48
				default: true,
				scope: ConfigurationScope.APPLICATION,
			},
49
			'sync.enableExtensions': {
50
				type: 'boolean',
51
				description: localize('sync.enableExtensions', "Enable synchronizing extensions."),
52 53 54
				default: true,
				scope: ConfigurationScope.APPLICATION,
			},
55
			'sync.ignoredExtensions': {
56
				'type': 'array',
57
				description: localize('sync.ignoredExtensions', "Configure extensions to be ignored while synchronizing."),
58 59 60 61
				'default': [],
				'scope': ConfigurationScope.APPLICATION,
				uniqueItems: true
			},
62
			'sync.ignoredSettings': {
63
				'type': 'array',
64
				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')),
65
				'default': [],
66 67 68 69 70 71 72 73 74 75 76 77
				'scope': ConfigurationScope.APPLICATION,
				$ref: ignoredSettingsSchemaId,
				additionalProperties: true,
				uniqueItems: true
			}
		}
	});
	const registerIgnoredSettingsSchema = () => {
		const jsonRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
		const ignoredSettingsSchema: IJSONSchema = {
			items: {
				type: 'string',
78
				enum: [...Object.keys(allSettings.properties).filter(setting => DEFAULT_IGNORED_SETTINGS.indexOf(setting) === -1), ...DEFAULT_IGNORED_SETTINGS.map(setting => `-${setting}`)]
79
			}
80 81 82 83
		};
		jsonRegistry.registerSchema(ignoredSettingsSchemaId, ignoredSettingsSchema);
	};
	return configurationRegistry.onDidUpdateConfiguration(() => registerIgnoredSettingsSchema());
84
}
85 86 87

export interface IUserData {
	ref: string;
88
	content: string | null;
89 90 91
}

export enum UserDataSyncStoreErrorCode {
S
Sandeep Somavarapu 已提交
92
	Unauthroized = 'Unauthroized',
93 94 95 96 97 98 99 100 101 102 103 104
	Rejected = 'Rejected',
	Unknown = 'Unknown'
}

export class UserDataSyncStoreError extends Error {

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

}

105 106 107 108 109 110 111 112 113 114 115
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;
}

116 117 118 119
export const IUserDataSyncStoreService = createDecorator<IUserDataSyncStoreService>('IUserDataSyncStoreService');

export interface IUserDataSyncStoreService {
	_serviceBrand: undefined;
120

121
	readonly userDataSyncStore: IUserDataSyncStore | undefined;
122

123
	read(key: string, oldValue: IUserData | null): Promise<IUserData>;
124
	write(key: string, content: string, ref: string | null): Promise<string>;
125 126
}

S
Sandeep Somavarapu 已提交
127 128 129 130 131 132
export interface ISyncExtension {
	identifier: IExtensionIdentifier;
	version?: string;
	enabled: boolean;
}

S
Sandeep Somavarapu 已提交
133
export const enum SyncSource {
134 135 136
	Settings = 1,
	Extensions
}
137

S
Sandeep Somavarapu 已提交
138
export const enum SyncStatus {
139 140 141 142 143 144 145 146 147 148 149 150
	Uninitialized = 'uninitialized',
	Idle = 'idle',
	Syncing = 'syncing',
	HasConflicts = 'hasConflicts',
}

export interface ISynchroniser {

	readonly status: SyncStatus;
	readonly onDidChangeStatus: Event<SyncStatus>;
	readonly onDidChangeLocal: Event<void>;

151
	sync(_continue?: boolean): Promise<boolean>;
S
Sandeep Somavarapu 已提交
152
	stop(): void;
153 154 155 156 157 158
}

export const IUserDataSyncService = createDecorator<IUserDataSyncService>('IUserDataSyncService');

export interface IUserDataSyncService extends ISynchroniser {
	_serviceBrand: any;
159
	readonly conflictsSource: SyncSource | null;
160 161

	removeExtension(identifier: IExtensionIdentifier): Promise<void>;
162
}
163 164 165 166 167 168 169

export const ISettingsMergeService = createDecorator<ISettingsMergeService>('ISettingsMergeService');

export interface ISettingsMergeService {

	_serviceBrand: undefined;

170
	merge(localContent: string, remoteContent: string, baseContent: string | null, ignoredSettings: string[]): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }>;
171

172
	computeRemoteContent(localContent: string, remoteContent: string, ignoredSettings: string[]): Promise<string>;
173 174

}
175

176 177 178 179 180 181 182 183 184 185
export const IKeybindingsMergeService = createDecorator<IKeybindingsMergeService>('IKeybindingsMergeService');

export interface IKeybindingsMergeService {

	_serviceBrand: undefined;

	merge(localContent: string, remoteContent: string, baseContent: string | null): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }>;

}

186 187 188 189 190 191
export const IUserDataSyncLogService = createDecorator<IUserDataSyncLogService>('IUserDataSyncLogService');

export interface IUserDataSyncLogService extends ILogService {

}

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