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

more smart merging

上级 76801c7c
......@@ -19,6 +19,8 @@ interface ICompareResult {
}
interface IMergeResult {
hasLocalForwarded: boolean;
hasRemoteForwarded: boolean;
added: Set<string>;
removed: Set<string>;
updated: Set<string>;
......@@ -37,78 +39,36 @@ export class KeybindingsMergeService implements IKeybindingsMergeService {
const local = <IUserFriendlyKeybinding[]>parse(localContent);
const remote = <IUserFriendlyKeybinding[]>parse(remoteContent);
const base = baseContent ? <IUserFriendlyKeybinding[]>parse(baseContent) : null;
const normalizedKeys = this.getNormalizedKeys(local, remote, base);
const normalize = (keybinding: IUserFriendlyKeybinding): IUserFriendlyKeybinding => ({
...keybinding,
...{
key: this.keybindingsService.resolveUserBinding(keybinding.key).map(part => part.getUserSettingsLabel()).join(' ')
}
});
const normalizedLocal = local.map(keybinding => normalize(keybinding));
const normalizedRemote = remote.map(keybinding => normalize(keybinding));
const normalizedBase = base ? base.map(keybinding => normalize(keybinding)) : null;
const byKeybinding = (keybindings: IUserFriendlyKeybinding[], normalized: IUserFriendlyKeybinding[]) => {
const map: Map<string, { keybinding: IUserFriendlyKeybinding, normalized: IUserFriendlyKeybinding }[]> = new Map<string, { keybinding: IUserFriendlyKeybinding, normalized: IUserFriendlyKeybinding }[]>();
for (let index = 0; index < normalized.length; index++) {
let value = map.get(normalized[index].key);
if (!value) {
value = [];
map.set(normalized[index].key, value);
}
value.push({ keybinding: keybindings[index], normalized: normalized[index] });
}
return map;
};
let keybindingsMergeResult = this.computeMergeResultByKeybinding(local, remote, base, normalizedKeys);
const localByKeybinding = byKeybinding(local, normalizedLocal);
const remoteByKeybinding = byKeybinding(remote, normalizedRemote);
const baseByKeybinding = base ? byKeybinding(base, normalizedBase!) : null;
const localToRemoteByKeybinding = this.compareByKeybinding(localByKeybinding, remoteByKeybinding);
if (localToRemoteByKeybinding.added.size === 0 && localToRemoteByKeybinding.removed.size === 0 && localToRemoteByKeybinding.updated.size === 0) {
if (!keybindingsMergeResult.hasLocalForwarded && !keybindingsMergeResult.hasRemoteForwarded) {
// No changes found between local and remote.
return { mergeContent: localContent, hasChanges: false, hasConflicts: false };
}
const baseToLocalByKeybinding = baseByKeybinding ? this.compareByKeybinding(baseByKeybinding, localByKeybinding) : { added: keys(localByKeybinding).reduce((r, k) => { r.add(k); return r; }, new Set<string>()), removed: new Set<string>(), updated: new Set<string>() };
if (baseToLocalByKeybinding.added.size === 0 && baseToLocalByKeybinding.removed.size === 0 && baseToLocalByKeybinding.updated.size === 0) {
// Remote has moved forward and local has not. Return remote
if (!keybindingsMergeResult.hasLocalForwarded && keybindingsMergeResult.hasRemoteForwarded) {
return { mergeContent: remoteContent, hasChanges: true, hasConflicts: false };
}
const baseToRemoteByKeybinding = baseByKeybinding ? this.compareByKeybinding(baseByKeybinding, remoteByKeybinding) : { added: keys(remoteByKeybinding).reduce((r, k) => { r.add(k); return r; }, new Set<string>()), removed: new Set<string>(), updated: new Set<string>() };
if (baseToRemoteByKeybinding.added.size === 0 && baseToRemoteByKeybinding.removed.size === 0 && baseToRemoteByKeybinding.updated.size === 0) {
if (keybindingsMergeResult.hasLocalForwarded && !keybindingsMergeResult.hasRemoteForwarded) {
// Local has moved forward and remote has not. Return local.
return { mergeContent: localContent, hasChanges: true, hasConflicts: false };
}
// Both local and remote has moved forward.
const byCommand = (keybindings: IUserFriendlyKeybinding[], normalized: IUserFriendlyKeybinding[]) => {
const map: Map<string, { keybinding: IUserFriendlyKeybinding, normalized: IUserFriendlyKeybinding }[]> = new Map<string, { keybinding: IUserFriendlyKeybinding, normalized: IUserFriendlyKeybinding }[]>();
for (let index = 0; index < normalized.length; index++) {
const command = normalized[index].command[0] === '-' ? normalized[index].command.substring(1) : normalized[index].command;
let value = map.get(command);
if (!value) {
value = [];
map.set(command, value);
}
value.push({ keybinding: keybindings[index], normalized: normalized[index] });
}
return map;
};
const localByCommand = byCommand(local, normalizedLocal);
const remoteByCommand = byCommand(remote, normalizedRemote);
const baseByCommand = base ? byCommand(base, normalizedBase!) : null;
const localToRemoteByCommand = this.compareByCommand(localByCommand, remoteByCommand);
const baseToLocalByCommand = baseByCommand ? this.compareByCommand(baseByCommand, localByCommand) : { added: keys(localByCommand).reduce((r, k) => { r.add(k); return r; }, new Set<string>()), removed: new Set<string>(), updated: new Set<string>() };
const baseToRemoteByCommand = baseByCommand ? this.compareByCommand(baseByCommand, remoteByCommand) : { added: keys(remoteByCommand).reduce((r, k) => { r.add(k); return r; }, new Set<string>()), removed: new Set<string>(), updated: new Set<string>() };
const localByCommand = this.byCommand(local);
const remoteByCommand = this.byCommand(remote);
const baseByCommand = base ? this.byCommand(base) : null;
const localToRemoteByCommand = this.compareByCommand(localByCommand, remoteByCommand, normalizedKeys);
const baseToLocalByCommand = baseByCommand ? this.compareByCommand(baseByCommand, localByCommand, normalizedKeys) : { added: keys(localByCommand).reduce((r, k) => { r.add(k); return r; }, new Set<string>()), removed: new Set<string>(), updated: new Set<string>() };
const baseToRemoteByCommand = baseByCommand ? this.compareByCommand(baseByCommand, remoteByCommand, normalizedKeys) : { added: keys(remoteByCommand).reduce((r, k) => { r.add(k); return r; }, new Set<string>()), removed: new Set<string>(), updated: new Set<string>() };
const commandsMergeResult = this.computeMergeResult(localToRemoteByCommand, baseToLocalByCommand, baseToRemoteByCommand);
const keybindingsMergeResult = this.computeMergeResult(localToRemoteByKeybinding, baseToLocalByKeybinding, baseToRemoteByKeybinding);
const eol = contentUtil.getEol(localContent);
let mergeContent = localContent;
let mergeContentChanged = false;
// Removed commands in Remote
for (const command of values(commandsMergeResult.removed)) {
......@@ -116,8 +76,13 @@ export class KeybindingsMergeService implements IKeybindingsMergeService {
continue;
}
mergeContent = this.removeKeybindings(mergeContent, eol, command);
mergeContentChanged = true;
}
if (mergeContentChanged) {
keybindingsMergeResult = this.computeMergeResultByKeybinding(local, remote, parse(mergeContent), normalizedKeys);
}
mergeContentChanged = false;
// Added commands in remote
for (const command of values(commandsMergeResult.added)) {
if (commandsMergeResult.conflicts.has(command)) {
......@@ -125,13 +90,17 @@ export class KeybindingsMergeService implements IKeybindingsMergeService {
}
const keybindings = remoteByCommand.get(command)!;
// Ignore negated commands
if (keybindings.some(({ normalized }) => normalized.command !== `-${command}` && keybindingsMergeResult.conflicts.has(normalized.key))) {
if (keybindings.some(keybinding => keybinding.command !== `-${command}` && keybindingsMergeResult.conflicts.has(normalizedKeys.get(keybinding.key)!))) {
commandsMergeResult.conflicts.add(command);
continue;
}
mergeContent = this.addKeybindings(mergeContent, eol, keybindings.map(({ keybinding }) => keybinding));
mergeContent = this.addKeybindings(mergeContent, eol, keybindings);
mergeContentChanged = true;
}
if (mergeContentChanged) {
keybindingsMergeResult = this.computeMergeResultByKeybinding(local, remote, parse(mergeContent), normalizedKeys);
}
// Updated commands in Remote
for (const command of values(commandsMergeResult.updated)) {
if (commandsMergeResult.conflicts.has(command)) {
......@@ -139,11 +108,11 @@ export class KeybindingsMergeService implements IKeybindingsMergeService {
}
const keybindings = remoteByCommand.get(command)!;
// Ignore negated commands
if (keybindings.some(({ normalized }) => normalized.command !== `-${command}` && keybindingsMergeResult.conflicts.has(normalized.key))) {
if (keybindings.some(keybinding => keybinding.command !== `-${command}` && keybindingsMergeResult.conflicts.has(normalizedKeys.get(keybinding.key)!))) {
commandsMergeResult.conflicts.add(command);
continue;
}
mergeContent = this.updateKeybindings(mergeContent, eol, command, keybindings.map(({ keybinding }) => keybinding));
mergeContent = this.updateKeybindings(mergeContent, eol, command, keybindings);
}
const hasConflicts = commandsMergeResult.conflicts.size > 0;
......@@ -158,7 +127,7 @@ export class KeybindingsMergeService implements IKeybindingsMergeService {
return { mergeContent, hasChanges: true, hasConflicts };
}
private computeMergeResult(localToRemote: ICompareResult, baseToLocal: ICompareResult, baseToRemote: ICompareResult): IMergeResult {
private computeMergeResult(localToRemote: ICompareResult, baseToLocal: ICompareResult, baseToRemote: ICompareResult): { added: Set<string>, removed: Set<string>, updated: Set<string>, conflicts: Set<string> } {
const added: Set<string> = new Set<string>();
const removed: Set<string> = new Set<string>();
const updated: Set<string> = new Set<string>();
......@@ -249,7 +218,71 @@ export class KeybindingsMergeService implements IKeybindingsMergeService {
return { added, removed, updated, conflicts };
}
private compareByKeybinding(from: Map<string, { keybinding: IUserFriendlyKeybinding, normalized: IUserFriendlyKeybinding }[]>, to: Map<string, { keybinding: IUserFriendlyKeybinding, normalized: IUserFriendlyKeybinding }[]>): ICompareResult {
private getNormalizedKeys(local: IUserFriendlyKeybinding[], remote: IUserFriendlyKeybinding[], base: IUserFriendlyKeybinding[] | null): Map<string, string> {
const keys = new Map<string, string>();
for (const keybinding of [...local, ...remote, ...(base || [])]) {
keys.set(keybinding.key, this.keybindingsService.resolveUserBinding(keybinding.key).map(part => part.getUserSettingsLabel()).join(' '));
}
return keys;
}
private computeMergeResultByKeybinding(local: IUserFriendlyKeybinding[], remote: IUserFriendlyKeybinding[], base: IUserFriendlyKeybinding[] | null, normalizedKeys: Map<string, string>): IMergeResult {
const empty = new Set<string>();
const localByKeybinding = this.byKeybinding(local, normalizedKeys);
const remoteByKeybinding = this.byKeybinding(remote, normalizedKeys);
const baseByKeybinding = base ? this.byKeybinding(base, normalizedKeys) : null;
const localToRemoteByKeybinding = this.compareByKeybinding(localByKeybinding, remoteByKeybinding);
if (localToRemoteByKeybinding.added.size === 0 && localToRemoteByKeybinding.removed.size === 0 && localToRemoteByKeybinding.updated.size === 0) {
return { hasLocalForwarded: false, hasRemoteForwarded: false, added: empty, removed: empty, updated: empty, conflicts: empty };
}
const baseToLocalByKeybinding = baseByKeybinding ? this.compareByKeybinding(baseByKeybinding, localByKeybinding) : { added: keys(localByKeybinding).reduce((r, k) => { r.add(k); return r; }, new Set<string>()), removed: new Set<string>(), updated: new Set<string>() };
if (baseToLocalByKeybinding.added.size === 0 && baseToLocalByKeybinding.removed.size === 0 && baseToLocalByKeybinding.updated.size === 0) {
// Remote has moved forward and local has not.
return { hasLocalForwarded: false, hasRemoteForwarded: true, added: empty, removed: empty, updated: empty, conflicts: empty };
}
const baseToRemoteByKeybinding = baseByKeybinding ? this.compareByKeybinding(baseByKeybinding, remoteByKeybinding) : { added: keys(remoteByKeybinding).reduce((r, k) => { r.add(k); return r; }, new Set<string>()), removed: new Set<string>(), updated: new Set<string>() };
if (baseToRemoteByKeybinding.added.size === 0 && baseToRemoteByKeybinding.removed.size === 0 && baseToRemoteByKeybinding.updated.size === 0) {
return { hasLocalForwarded: true, hasRemoteForwarded: false, added: empty, removed: empty, updated: empty, conflicts: empty };
}
const { added, removed, updated, conflicts } = this.computeMergeResult(localToRemoteByKeybinding, baseToLocalByKeybinding, baseToRemoteByKeybinding);
return { hasLocalForwarded: true, hasRemoteForwarded: true, added, removed, updated, conflicts };
}
private byKeybinding(keybindings: IUserFriendlyKeybinding[], keys: Map<string, string>) {
const map: Map<string, IUserFriendlyKeybinding[]> = new Map<string, IUserFriendlyKeybinding[]>();
for (const keybinding of keybindings) {
const key = keys.get(keybinding.key)!;
let value = map.get(key);
if (!value) {
value = [];
map.set(key, value);
}
value.push(keybinding);
}
return map;
}
private byCommand(keybindings: IUserFriendlyKeybinding[]): Map<string, IUserFriendlyKeybinding[]> {
const map: Map<string, IUserFriendlyKeybinding[]> = new Map<string, IUserFriendlyKeybinding[]>();
for (const keybinding of keybindings) {
const command = keybinding.command[0] === '-' ? keybinding.command.substring(1) : keybinding.command;
let value = map.get(command);
if (!value) {
value = [];
map.set(command, value);
}
value.push(keybinding);
}
return map;
}
private compareByKeybinding(from: Map<string, IUserFriendlyKeybinding[]>, to: Map<string, IUserFriendlyKeybinding[]>): ICompareResult {
const fromKeys = keys(from);
const toKeys = keys(to);
const added = toKeys.filter(key => fromKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set<string>());
......@@ -260,8 +293,8 @@ export class KeybindingsMergeService implements IKeybindingsMergeService {
if (removed.has(key)) {
continue;
}
const value1: IUserFriendlyKeybinding[] = from.get(key)!.map(({ normalized }) => normalized);
const value2: IUserFriendlyKeybinding[] = to.get(key)!.map(({ normalized }) => normalized);
const value1: IUserFriendlyKeybinding[] = from.get(key)!.map(keybinding => ({ ...keybinding, ...{ key } }));
const value2: IUserFriendlyKeybinding[] = to.get(key)!.map(keybinding => ({ ...keybinding, ...{ key } }));
if (!equals(value1, value2, (a, b) => this.isSameKeybinding(a, b))) {
updated.add(key);
}
......@@ -270,7 +303,7 @@ export class KeybindingsMergeService implements IKeybindingsMergeService {
return { added, removed, updated };
}
private compareByCommand(from: Map<string, { keybinding: IUserFriendlyKeybinding, normalized: IUserFriendlyKeybinding }[]>, to: Map<string, { keybinding: IUserFriendlyKeybinding, normalized: IUserFriendlyKeybinding }[]>): ICompareResult {
private compareByCommand(from: Map<string, IUserFriendlyKeybinding[]>, to: Map<string, IUserFriendlyKeybinding[]>, normalizedKeys: Map<string, string>): ICompareResult {
const fromKeys = keys(from);
const toKeys = keys(to);
const added = toKeys.filter(key => fromKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set<string>());
......@@ -281,8 +314,8 @@ export class KeybindingsMergeService implements IKeybindingsMergeService {
if (removed.has(key)) {
continue;
}
const value1: IUserFriendlyKeybinding[] = from.get(key)!.map(({ normalized }) => normalized);
const value2: IUserFriendlyKeybinding[] = to.get(key)!.map(({ normalized }) => normalized);
const value1: IUserFriendlyKeybinding[] = from.get(key)!.map(keybinding => ({ ...keybinding, ...{ key: normalizedKeys.get(keybinding.key)! } }));
const value2: IUserFriendlyKeybinding[] = to.get(key)!.map(keybinding => ({ ...keybinding, ...{ key: normalizedKeys.get(keybinding.key)! } }));
if (!this.areSameKeybindingsWithSameCommand(value1, value2)) {
updated.add(key);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册