ElyDoc, ElyTable, ElyShow (LibreOfficeKit), ElyPDF (MuPDF), ElyTree (Git), ElyMark (Milkdown and CodeMirror in CEF). ElyMail and ElyAgent follow.
54 lines
1.4 KiB
JavaScript
54 lines
1.4 KiB
JavaScript
// Myers' diff over two lists.
|
|
|
|
/** Equal pairs between `a` and `b`, in order. */
|
|
export function common(a, b, same) {
|
|
const n = a.length
|
|
const m = b.length
|
|
const max = n + m
|
|
const reach = new Map([[1, 0]])
|
|
const trace = []
|
|
for (let d = 0; d <= max; d++) {
|
|
trace.push(new Map(reach))
|
|
for (let k = -d; k <= d; k += 2) {
|
|
const down = k === -d || (k !== d && reach.get(k - 1) < reach.get(k + 1))
|
|
let x = down ? reach.get(k + 1) : reach.get(k - 1) + 1
|
|
let y = x - k
|
|
while (x < n && y < m && same(a[x], b[y])) {
|
|
x += 1
|
|
y += 1
|
|
}
|
|
reach.set(k, x)
|
|
if (x >= n && y >= m) return walk(trace, n, m)
|
|
}
|
|
}
|
|
throw new Error('diff did not end')
|
|
}
|
|
|
|
/** Back through the trace: each equal pair, in order. */
|
|
function walk(trace, n, m) {
|
|
const pairs = []
|
|
let x = n
|
|
let y = m
|
|
for (let d = trace.length - 1; d > 0; d--) {
|
|
const reach = trace[d]
|
|
const k = x - y
|
|
const down = k === -d || (k !== d && reach.get(k - 1) < reach.get(k + 1))
|
|
const from = down ? k + 1 : k - 1
|
|
const fromX = reach.get(from)
|
|
const fromY = fromX - from
|
|
while (x > fromX + (down ? 0 : 1) && y > fromY + (down ? 1 : 0)) {
|
|
x -= 1
|
|
y -= 1
|
|
pairs.push([x, y])
|
|
}
|
|
x = fromX
|
|
y = fromY
|
|
}
|
|
while (x > 0 && y > 0) {
|
|
x -= 1
|
|
y -= 1
|
|
pairs.push([x, y])
|
|
}
|
|
return pairs.reverse()
|
|
}
|