in src/vs/platform/userDataSync/common/settingsMerge.ts [80:227]
export function merge(originalLocalContent: string, originalRemoteContent: string, baseContent: string | null, ignoredSettings: string[], resolvedConflicts: { key: string, value: any | undefined }[], formattingOptions: FormattingOptions): IMergeResult {
const localContentWithoutIgnoredSettings = updateIgnoredSettings(originalLocalContent, originalRemoteContent, ignoredSettings, formattingOptions);
const localForwarded = baseContent !== localContentWithoutIgnoredSettings;
const remoteForwarded = baseContent !== originalRemoteContent;
/* no changes */
if (!localForwarded && !remoteForwarded) {
return { conflictsSettings: [], localContent: null, remoteContent: null, hasConflicts: false };
}
/* local has changed and remote has not */
if (localForwarded && !remoteForwarded) {
return { conflictsSettings: [], localContent: null, remoteContent: localContentWithoutIgnoredSettings, hasConflicts: false };
}
/* remote has changed and local has not */
if (remoteForwarded && !localForwarded) {
return { conflictsSettings: [], localContent: updateIgnoredSettings(originalRemoteContent, originalLocalContent, ignoredSettings, formattingOptions), remoteContent: null, hasConflicts: false };
}
/* local is empty and not synced before */
if (baseContent === null && isEmpty(originalLocalContent)) {
const localContent = areSame(originalLocalContent, originalRemoteContent, ignoredSettings) ? null : updateIgnoredSettings(originalRemoteContent, originalLocalContent, ignoredSettings, formattingOptions);
return { conflictsSettings: [], localContent, remoteContent: null, hasConflicts: false };
}
/* remote and local has changed */
let localContent = originalLocalContent;
let remoteContent = originalRemoteContent;
const local = parse(originalLocalContent);
const remote = parse(originalRemoteContent);
const base = baseContent ? parse(baseContent) : null;
const ignored = ignoredSettings.reduce((set, key) => { set.add(key); return set; }, new Set<string>());
const localToRemote = compare(local, remote, ignored);
const baseToLocal = compare(base, local, ignored);
const baseToRemote = compare(base, remote, ignored);
const conflicts: Map<string, IConflictSetting> = new Map<string, IConflictSetting>();
const handledConflicts: Set<string> = new Set<string>();
const handleConflict = (conflictKey: string): void => {
handledConflicts.add(conflictKey);
const resolvedConflict = resolvedConflicts.filter(({ key }) => key === conflictKey)[0];
if (resolvedConflict) {
localContent = contentUtil.edit(localContent, [conflictKey], resolvedConflict.value, formattingOptions);
remoteContent = contentUtil.edit(remoteContent, [conflictKey], resolvedConflict.value, formattingOptions);
} else {
conflicts.set(conflictKey, { key: conflictKey, localValue: local[conflictKey], remoteValue: remote[conflictKey] });
}
};
// Removed settings in Local
for (const key of values(baseToLocal.removed)) {
// Conflict - Got updated in remote.
if (baseToRemote.updated.has(key)) {
handleConflict(key);
}
// Also remove in remote
else {
remoteContent = contentUtil.edit(remoteContent, [key], undefined, formattingOptions);
}
}
// Removed settings in Remote
for (const key of values(baseToRemote.removed)) {
if (handledConflicts.has(key)) {
continue;
}
// Conflict - Got updated in local
if (baseToLocal.updated.has(key)) {
handleConflict(key);
}
// Also remove in locals
else {
localContent = contentUtil.edit(localContent, [key], undefined, formattingOptions);
}
}
// Updated settings in Local
for (const key of values(baseToLocal.updated)) {
if (handledConflicts.has(key)) {
continue;
}
// Got updated in remote
if (baseToRemote.updated.has(key)) {
// Has different value
if (localToRemote.updated.has(key)) {
handleConflict(key);
}
} else {
remoteContent = contentUtil.edit(remoteContent, [key], local[key], formattingOptions);
}
}
// Updated settings in Remote
for (const key of values(baseToRemote.updated)) {
if (handledConflicts.has(key)) {
continue;
}
// Got updated in local
if (baseToLocal.updated.has(key)) {
// Has different value
if (localToRemote.updated.has(key)) {
handleConflict(key);
}
} else {
localContent = contentUtil.edit(localContent, [key], remote[key], formattingOptions);
}
}
// Added settings in Local
for (const key of values(baseToLocal.added)) {
if (handledConflicts.has(key)) {
continue;
}
// Got added in remote
if (baseToRemote.added.has(key)) {
// Has different value
if (localToRemote.updated.has(key)) {
handleConflict(key);
}
} else {
remoteContent = addSetting(key, localContent, remoteContent, formattingOptions);
}
}
// Added settings in remote
for (const key of values(baseToRemote.added)) {
if (handledConflicts.has(key)) {
continue;
}
// Got added in local
if (baseToLocal.added.has(key)) {
// Has different value
if (localToRemote.updated.has(key)) {
handleConflict(key);
}
} else {
localContent = addSetting(key, remoteContent, localContent, formattingOptions);
}
}
const hasConflicts = conflicts.size > 0 || !areSame(localContent, remoteContent, ignoredSettings);
const hasLocalChanged = hasConflicts || !areSame(localContent, originalLocalContent, []);
const hasRemoteChanged = hasConflicts || !areSame(remoteContent, originalRemoteContent, []);
return { localContent: hasLocalChanged ? localContent : null, remoteContent: hasRemoteChanged ? remoteContent : null, conflictsSettings: values(conflicts), hasConflicts };
}