提交 ad601533 编写于 作者: S Sandeep Somavarapu

setting to ignore during sync

上级 a2862cf9
......@@ -51,7 +51,7 @@ import { IFileService } from 'vs/platform/files/common/files';
import { DiskFileSystemProvider } from 'vs/platform/files/electron-browser/diskFileSystemProvider';
import { Schemas } from 'vs/base/common/network';
import { IProductService } from 'vs/platform/product/common/productService';
import { IUserDataSyncService, IUserDataSyncStoreService, ISettingsMergeService } from 'vs/platform/userDataSync/common/userDataSync';
import { IUserDataSyncService, IUserDataSyncStoreService, ISettingsMergeService, registerConfiguration } from 'vs/platform/userDataSync/common/userDataSync';
import { UserDataSyncService, UserDataAutoSync } from 'vs/platform/userDataSync/common/userDataSyncService';
import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService';
import { UserDataSyncChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc';
......@@ -122,11 +122,6 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
const windowsService = new WindowsService(mainProcessService);
services.set(IWindowsService, windowsService);
const activeWindowManager = new ActiveWindowManager(windowsService);
const activeWindowRouter = new StaticRouter(ctx => activeWindowManager.getActiveClientId().then(id => ctx === id));
const settingsMergeChannel = server.getChannel('settingsMerge', activeWindowRouter);
services.set(ISettingsMergeService, new SettingsMergeChannelClient(settingsMergeChannel));
// Files
const fileService = new FileService(logService);
services.set(IFileService, fileService);
......@@ -173,8 +168,15 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService));
services.set(ILocalizationsService, new SyncDescriptor(LocalizationsService));
services.set(IDiagnosticsService, new SyncDescriptor(DiagnosticsService));
// User Data Sync Contributions
const activeWindowManager = new ActiveWindowManager(windowsService);
const activeWindowRouter = new StaticRouter(ctx => activeWindowManager.getActiveClientId().then(id => ctx === id));
const settingsMergeChannel = server.getChannel('settingsMerge', activeWindowRouter);
services.set(ISettingsMergeService, new SettingsMergeChannelClient(settingsMergeChannel));
services.set(IUserDataSyncStoreService, new SyncDescriptor(UserDataSyncStoreService));
services.set(IUserDataSyncService, new SyncDescriptor(UserDataSyncService));
registerConfiguration();
const instantiationService2 = instantiationService.createChild(services);
......
......@@ -70,8 +70,7 @@ export class ExtensionsSynchroniser extends Disposable implements ISynchroniser
}
async sync(): Promise<boolean> {
const syncExtensions = this.configurationService.getValue<boolean>('userConfiguration.syncExtensions');
if (syncExtensions === false) {
if (!this.configurationService.getValue<boolean>('userConfiguration.syncExtensions')) {
return false;
}
......
......@@ -15,6 +15,8 @@ import { CancelablePromise, createCancelablePromise, ThrottledDelayer } from 'vs
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { URI } from 'vs/base/common/uri';
import { joinPath } from 'vs/base/common/resources';
import { IStringDictionary } from 'vs/base/common/collections';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
interface ISyncPreviewResult {
readonly fileContent: IFileContent | null;
......@@ -47,6 +49,7 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
@IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService,
@ISettingsMergeService private readonly settingsMergeService: ISettingsMergeService,
@ILogService private readonly logService: ILogService,
@IConfigurationService private readonly configurationService: IConfigurationService,
) {
super();
this.lastSyncSettingsResource = joinPath(this.environmentService.userRoamingDataHome, '.lastSyncSettings.json');
......@@ -135,7 +138,8 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
let { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged } = await this.syncPreviewResultPromise;
if (hasRemoteChanged) {
const ref = await this.writeToRemote(content, remoteUserData.ref);
const remoteContent = remoteUserData.content ? await this.settingsMergeService.computeRemoteContent(content, remoteUserData.content, this.getIgnoredSettings()) : content;
const ref = await this.writeToRemote(remoteContent, remoteUserData.ref);
remoteUserData = { ref, content };
}
if (hasLocalChanged) {
......@@ -176,45 +180,40 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
let hasRemoteChanged: boolean = false;
let hasConflicts: boolean = false;
// First time sync to remote
if (fileContent && !remoteContent) {
this.logService.trace('Settings Sync: Remote contents does not exist. So sync with settings file.');
hasRemoteChanged = true;
await this.fileService.writeFile(this.environmentService.settingsSyncPreviewResource, VSBuffer.fromString(fileContent.value.toString()));
return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts };
}
// Settings file does not exist, so sync with remote contents.
if (remoteContent && !fileContent) {
this.logService.trace('Settings Sync: Settings file does not exist. So sync with remote contents');
hasLocalChanged = true;
await this.fileService.writeFile(this.environmentService.settingsSyncPreviewResource, VSBuffer.fromString(remoteContent));
return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts };
}
if (fileContent && remoteContent) {
const localContent: string = fileContent.value.toString();
if (remoteContent) {
const localContent: string = fileContent ? fileContent.value.toString() : '{}';
if (!lastSyncData // First time sync
|| lastSyncData.content !== localContent // Local has moved forwarded
|| lastSyncData.content !== remoteContent // Remote has moved forwarded
) {
this.logService.trace('Settings Sync: Merging remote contents with settings file.');
const result = await this.settingsMergeService.merge(localContent, remoteContent, lastSyncData ? lastSyncData.content : null);
const result = await this.settingsMergeService.merge(localContent, remoteContent, lastSyncData ? lastSyncData.content : null, this.getIgnoredSettings());
// Sync only if there are changes
if (result.hasChanges) {
hasLocalChanged = result.mergeContent !== localContent;
hasRemoteChanged = result.mergeContent !== remoteContent;
hasConflicts = result.hasConflicts;
await this.fileService.writeFile(this.environmentService.settingsSyncPreviewResource, VSBuffer.fromString(result.mergeContent));
return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts };
}
}
return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts };
}
// First time sync to remote
if (fileContent) {
this.logService.trace('Settings Sync: Remote contents does not exist. So sync with settings file.');
hasRemoteChanged = true;
await this.fileService.writeFile(this.environmentService.settingsSyncPreviewResource, VSBuffer.fromString(fileContent.value.toString()));
return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts };
}
this.logService.trace('Settings Sync: No changes.');
return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts };
}
private getIgnoredSettings(): IStringDictionary<boolean> {
return this.configurationService.getValue<IStringDictionary<boolean>>('userConfiguration.ignoreSettings');
}
private async getLastSyncUserData(): Promise<IUserData | null> {
try {
const content = await this.fileService.readFile(this.lastSyncSettingsResource);
......
......@@ -6,6 +6,7 @@
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { Event } from 'vs/base/common/event';
import { ISettingsMergeService } from 'vs/platform/userDataSync/common/userDataSync';
import { IStringDictionary } from 'vs/base/common/collections';
export class SettingsMergeChannel implements IServerChannel {
......@@ -17,7 +18,8 @@ export class SettingsMergeChannel implements IServerChannel {
call(context: any, command: string, args?: any): Promise<any> {
switch (command) {
case 'merge': return this.service.merge(args[0], args[1], args[2]);
case 'merge': return this.service.merge(args[0], args[1], args[2], args[3]);
case 'computeRemoteContent': return this.service.computeRemoteContent(args[0], args[1], args[2]);
}
throw new Error('Invalid call');
}
......@@ -30,8 +32,12 @@ export class SettingsMergeChannelClient implements ISettingsMergeService {
constructor(private readonly channel: IChannel) {
}
merge(localContent: string, remoteContent: string, baseContent: string | null): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }> {
return this.channel.call('merge', [localContent, remoteContent, baseContent]);
merge(localContent: string, remoteContent: string, baseContent: string | null, ignoredSettings: IStringDictionary<boolean>): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }> {
return this.channel.call('merge', [localContent, remoteContent, baseContent, ignoredSettings]);
}
computeRemoteContent(localContent: string, remoteContent: string, ignoredSettings: IStringDictionary<boolean>): Promise<string> {
return this.channel.call('computeRemoteContent', [localContent, remoteContent, ignoredSettings]);
}
}
......@@ -7,6 +7,52 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
import { Event } from 'vs/base/common/event';
import { IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement';
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IStringDictionary } from 'vs/base/common/collections';
import { Registry } from 'vs/platform/registry/common/platform';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { localize } from 'vs/nls';
export function registerConfiguration() {
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
.registerConfiguration({
id: 'userConfiguration',
order: 30,
title: localize('userConfiguration', "User Configuration"),
type: 'object',
properties: {
'userConfiguration.enableSync': {
type: 'boolean',
description: localize('userConfiguration.enableSync', "When enabled, synchronises User Configuration: Settings, Keybindings, Extensions & Snippets."),
default: true,
scope: ConfigurationScope.APPLICATION
},
'userConfiguration.syncExtensions': {
type: 'boolean',
description: localize('userConfiguration.syncExtensions', "When enabled extensions are synchronised while synchronising user configuration."),
default: true,
scope: ConfigurationScope.APPLICATION,
},
'userConfiguration.ignoreSettings': {
'type': 'object',
description: localize('userConfiguration.ignoreSettings', "Configure settings to be ignored while syncing"),
'default': {
'userConfiguration.enableSync': true,
'userConfiguration.syncExtensions': true,
'userConfiguration.ignoreSettings': true
},
'scope': ConfigurationScope.APPLICATION,
'additionalProperties': {
'anyOf': [
{
'type': 'boolean',
'description': localize('ignoredSetting', "Id of the stting to be ignored. Set to true or false to enable or disable."),
}
]
}
}
}
});
}
export interface IUserData {
ref: string;
......@@ -85,7 +131,9 @@ export interface ISettingsMergeService {
_serviceBrand: undefined;
merge(localContent: string, remoteContent: string, baseContent: string | null): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }>;
merge(localContent: string, remoteContent: string, baseContent: string | null, ignoredSettings: IStringDictionary<boolean>): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }>;
computeRemoteContent(localContent: string, remoteContent: string, ignoredSettings: IStringDictionary<boolean>): Promise<string>;
}
......
......@@ -134,8 +134,7 @@ export class UserDataAutoSync extends Disposable {
}
private isSyncEnabled(): boolean {
const { user: userLocal } = this.configurationService.inspect<boolean>('userConfiguration.enableSync');
return userLocal === undefined || userLocal;
return this.configurationService.getValue<boolean>('userConfiguration.enableSync');
}
}
......@@ -4,13 +4,12 @@
*--------------------------------------------------------------------------------------------*/
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IUserDataSyncService, SyncStatus, SyncSource, CONTEXT_SYNC_STATE } from 'vs/platform/userDataSync/common/userDataSync';
import { IUserDataSyncService, SyncStatus, SyncSource, CONTEXT_SYNC_STATE, registerConfiguration } from 'vs/platform/userDataSync/common/userDataSync';
import { localize } from 'vs/nls';
import { Disposable, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { Registry } from 'vs/platform/registry/common/platform';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { MenuRegistry, MenuId, IMenuItem } from 'vs/platform/actions/common/actions';
import { IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
......@@ -37,27 +36,7 @@ class UserDataSyncConfigurationContribution implements IWorkbenchContribution {
@IProductService productService: IProductService
) {
if (productService.settingsSyncStoreUrl) {
Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
.registerConfiguration({
id: 'userConfiguration',
order: 30,
title: localize('userConfiguration', "User Configuration"),
type: 'object',
properties: {
'userConfiguration.enableSync': {
type: 'boolean',
description: localize('userConfiguration.enableSync', "When enabled, synchronises User Configuration: Settings, Keybindings, Extensions & Snippets."),
default: true,
scope: ConfigurationScope.APPLICATION
},
'userConfiguration.syncExtensions': {
type: 'boolean',
description: localize('userConfiguration.syncExtensions', "When enabled extensions are synchronised while synchronising user configuration."),
default: true,
scope: ConfigurationScope.APPLICATION
}
}
});
registerConfiguration();
}
}
}
......
......@@ -24,23 +24,23 @@ class SettingsMergeService implements ISettingsMergeService {
constructor(
@IModelService private readonly modelService: IModelService,
@IModeService private readonly modeService: IModeService
@IModeService private readonly modeService: IModeService,
) { }
async merge(localContent: string, remoteContent: string, baseContent: string | null): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }> {
async merge(localContent: string, remoteContent: string, baseContent: string | null, ignoredSettings: IStringDictionary<boolean>): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }> {
const local = parse(localContent);
const remote = parse(remoteContent);
const base = baseContent ? parse(baseContent) : null;
const localToRemote = this.compare(local, remote);
const localToRemote = this.compare(local, remote, ignoredSettings);
if (localToRemote.added.size === 0 && localToRemote.removed.size === 0 && localToRemote.updated.size === 0) {
// No changes found between local and remote.
return { mergeContent: localContent, hasChanges: false, hasConflicts: false };
}
const conflicts: Set<string> = new Set<string>();
const baseToLocal = base ? this.compare(base, local) : { added: Object.keys(local).reduce((r, k) => { r.add(k); return r; }, new Set<string>()), removed: new Set<string>(), updated: new Set<string>() };
const baseToRemote = base ? this.compare(base, remote) : { added: Object.keys(remote).reduce((r, k) => { r.add(k); return r; }, new Set<string>()), removed: new Set<string>(), updated: new Set<string>() };
const baseToLocal = base ? this.compare(base, local, ignoredSettings) : { added: Object.keys(local).reduce((r, k) => { r.add(k); return r; }, new Set<string>()), removed: new Set<string>(), updated: new Set<string>() };
const baseToRemote = base ? this.compare(base, remote, ignoredSettings) : { added: Object.keys(remote).reduce((r, k) => { r.add(k); return r; }, new Set<string>()), removed: new Set<string>(), updated: new Set<string>() };
const settingsPreviewModel = this.modelService.createModel(localContent, this.modeService.create('jsonc'));
// Removed settings in Local
......@@ -151,6 +151,18 @@ class SettingsMergeService implements ISettingsMergeService {
return { mergeContent: settingsPreviewModel.getValue(), hasChanges: true, hasConflicts: conflicts.size > 0 };
}
async computeRemoteContent(localContent: string, remoteContent: string, ignoredSettings: IStringDictionary<boolean>): Promise<string> {
const remote = parse(remoteContent);
const remoteModel = this.modelService.createModel(localContent, this.modeService.create('jsonc'));
for (const key of Object.keys(ignoredSettings)) {
if (ignoredSettings[key]) {
this.editSetting(remoteModel, key, undefined);
this.editSetting(remoteModel, key, remote[key]);
}
}
return remoteModel.getValue();
}
private editSetting(model: ITextModel, key: string, value: any | undefined): void {
const insertSpaces = false;
const tabSize = 4;
......@@ -168,9 +180,9 @@ class SettingsMergeService implements ISettingsMergeService {
}
}
private compare(from: IStringDictionary<any>, to: IStringDictionary<any>): { added: Set<string>, removed: Set<string>, updated: Set<string> } {
const fromKeys = Object.keys(from);
const toKeys = Object.keys(to);
private compare(from: IStringDictionary<any>, to: IStringDictionary<any>, ignored: IStringDictionary<boolean>): { added: Set<string>, removed: Set<string>, updated: Set<string> } {
const fromKeys = Object.keys(from).filter(key => !ignored[key]);
const toKeys = Object.keys(to).filter(key => !ignored[key]);
const added = toKeys.filter(key => fromKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set<string>());
const removed = fromKeys.filter(key => toKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set<string>());
const updated: Set<string> = new Set<string>();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册