Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(assert): cleanup _diff.ts #4086

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 74 additions & 96 deletions assert/_diff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,12 @@ interface FarthestPoint {
id: number;
}

export const DiffType = {
removed: "removed",
common: "common",
added: "added",
} as const;

export type DiffType = keyof typeof DiffType;
type DiffType = "added" | "removed" | "common";

export interface DiffResult<T> {
type: DiffType;
value: T;
details?: Array<DiffResult<T>>;
details?: DiffResult<T>[];
}

const REMOVED = 1;
Expand Down Expand Up @@ -61,7 +55,7 @@ function ensureDefined<T>(item?: T): T {
* @param A Actual value
* @param B Expected value
*/
export function diff<T>(A: T[], B: T[]): Array<DiffResult<T>> {
export function diff<T>(A: T[], B: T[]): DiffResult<T>[] {
const prefixCommon = createCommon(A, B);
const suffixCommon = createCommon(
A.slice(prefixCommon.length),
Expand All @@ -81,20 +75,18 @@ export function diff<T>(A: T[], B: T[]): Array<DiffResult<T>> {
if (!M && !N && !suffixCommon.length && !prefixCommon.length) return [];
if (!N) {
return [
...prefixCommon.map(
(c): DiffResult<typeof c> => ({ type: DiffType.common, value: c }),
),
...A.map(
(a): DiffResult<typeof a> => ({
type: swapped ? DiffType.added : DiffType.removed,
value: a,
}),
),
...suffixCommon.map(
(c): DiffResult<typeof c> => ({ type: DiffType.common, value: c }),
),
...prefixCommon.map((
value,
) => ({ type: "common", value } as DiffResult<T>)),
...A.map((
value,
) => ({ type: swapped ? "added" : "removed", value } as DiffResult<T>)),
...suffixCommon.map((
value,
) => ({ type: "common", value } as DiffResult<T>)),
];
}

const offset = N;
const delta = M - N;
const size = M + N + 1;
Expand All @@ -121,37 +113,33 @@ export function diff<T>(A: T[], B: T[]): Array<DiffResult<T>> {
B: T[],
current: FarthestPoint,
swapped: boolean,
): Array<{
type: DiffType;
value: T;
}> {
const M = A.length;
const N = B.length;
const result: { type: DiffType; value: T }[] = [];
let a = M - 1;
let b = N - 1;
): DiffResult<T>[] {
const result: DiffResult<T>[] = [];
let a = A.length - 1;
let b = B.length - 1;
let j = routes[current.id];
let type = routes[current.id + diffTypesPtrOffset];
while (true) {
if (!j && !type) break;
const prev = j!;
if (type === REMOVED) {
result.unshift({
type: swapped ? DiffType.removed : DiffType.added,
value: B[b]!,
});
b -= 1;
} else if (type === ADDED) {
result.unshift({
type: swapped ? DiffType.added : DiffType.removed,
value: A[a]!,
});
a -= 1;
} else {
result.unshift({ type: DiffType.common, value: A[a]! });
a -= 1;
b -= 1;

while (j || type) {
const prev = j;
let diffType: DiffType = "common";
let value = A[a];
switch (type) {
case REMOVED:
diffType = swapped ? "removed" : "added";
value = B[b];
b--;
break;
case ADDED:
diffType = swapped ? "added" : "removed";
a--;
break;
default:
a--;
b--;
break;
}
result.unshift({ type: diffType, value });
j = routes[prev];
type = routes[prev + diffTypesPtrOffset];
}
Expand All @@ -164,26 +152,21 @@ export function diff<T>(A: T[], B: T[]): Array<DiffResult<T>> {
k: number,
M: number,
): FarthestPoint {
if (slide && slide.y === -1 && down && down.y === -1) {
if (!slide || slide.y === -1) {
return { y: 0, id: 0 };
}
const isAdding = (down?.y === -1) ||
k === M ||
(slide?.y || 0) > (down?.y || 0) + 1;
if (slide && isAdding) {
if (!down || down.y === -1 || k === M || slide.y > down.y + 1) {
const prev = slide.id;
ptr++;
routes[ptr] = prev;
routes[ptr + diffTypesPtrOffset] = ADDED;
return { y: slide.y, id: ptr };
} else if (down && !isAdding) {
} else {
const prev = down.id;
ptr++;
routes[ptr] = prev;
routes[ptr + diffTypesPtrOffset] = REMOVED;
return { y: down.y + 1, id: ptr };
} else {
throw new Error("Unexpected missing FarthestPoint");
}
}

Expand All @@ -201,11 +184,10 @@ export function diff<T>(A: T[], B: T[]): Array<DiffResult<T>> {
const fp = createFP(slide, down, k, M);
while (fp.y + k < M && fp.y < N && A[fp.y + k] === B[fp.y]) {
const prev = fp.id;
ptr++;
fp.id = ptr;
fp.y += 1;
routes[ptr] = prev;
routes[ptr + diffTypesPtrOffset] = COMMON;
fp.id = ++ptr;
fp.y++;
routes[fp.id] = prev;
routes[fp.id + diffTypesPtrOffset] = COMMON;
}
return fp;
}
Expand Down Expand Up @@ -243,14 +225,11 @@ export function diff<T>(A: T[], B: T[]): Array<DiffResult<T>> {
);
currentFP = ensureDefined(fp[delta + offset]);
}

return [
...prefixCommon.map(
(c): DiffResult<typeof c> => ({ type: DiffType.common, value: c }),
),
...backTrace(A, B, currentFP, swapped),
...suffixCommon.map(
(c): DiffResult<typeof c> => ({ type: DiffType.common, value: c }),
),
...prefixCommon.map((c) => ({ type: "common", value: c } as DiffResult<T>)),
...backTrace(A, B, fp[delta + offset], swapped),
...suffixCommon.map((c) => ({ type: "common", value: c } as DiffResult<T>)),
];
}

Expand Down Expand Up @@ -326,23 +305,21 @@ export function diffstr(A: string, B: string) {
// and merge "space-diff" if surrounded by word-diff for cleaner displays
function createDetails(
line: DiffResult<string>,
tokens: Array<DiffResult<string>>,
tokens: DiffResult<string>[],
) {
return tokens.filter(({ type }) =>
type === line.type || type === DiffType.common
).map((result, i, t) => {
const token = t[i - 1];
if (
(result.type === DiffType.common) && token &&
(token.type === t[i + 1]?.type) && /\s+/.test(result.value)
) {
return {
...result,
type: token.type,
};
}
return result;
});
return tokens.filter(({ type }) => type === line.type || type === "common")
.map((result, i, t) => {
if (
(result.type === "common") && (t[i - 1]) &&
(t[i - 1]?.type === t[i + 1]?.type) && /\s+/.test(result.value)
) {
return {
...result,
type: t[i - 1].type,
};
}
return result;
});
}

// Compute multi-line diff
Expand All @@ -351,12 +328,13 @@ export function diffstr(A: string, B: string) {
tokenize(`${unescape(B)}\n`),
);

const added = [], removed = [];
const added: DiffResult<string>[] = [];
const removed: DiffResult<string>[] = [];
for (const result of diffResult) {
if (result.type === DiffType.added) {
if (result.type === "added") {
added.push(result);
}
if (result.type === DiffType.removed) {
if (result.type === "removed") {
removed.push(result);
}
}
Expand All @@ -366,8 +344,8 @@ export function diffstr(A: string, B: string) {
const aLines = hasMoreRemovedLines ? added : removed;
const bLines = hasMoreRemovedLines ? removed : added;
for (const a of aLines) {
let tokens = [] as Array<DiffResult<string>>,
b: undefined | DiffResult<string>;
let tokens: DiffResult<string>[] = [];
let b: undefined | DiffResult<string>;
// Search another diff line with at least one common token
while (bLines.length) {
b = bLines.shift();
Expand All @@ -379,7 +357,7 @@ export function diffstr(A: string, B: string) {
tokens = diff(tokenized[0], tokenized[1]);
if (
tokens.some(({ type, value }) =>
type === DiffType.common && value.trim().length
type === "common" && value.trim().length
)
) {
break;
Expand Down Expand Up @@ -408,10 +386,10 @@ function createColor(
// https://github.com/denoland/deno_std/issues/2575
background = false;
switch (diffType) {
case DiffType.added:
case "added":
return (s: string): string =>
background ? bgGreen(white(s)) : green(bold(s));
case DiffType.removed:
case "removed":
return (s: string): string => background ? bgRed(white(s)) : red(bold(s));
default:
return white;
Expand All @@ -424,9 +402,9 @@ function createColor(
*/
function createSign(diffType: DiffType): string {
switch (diffType) {
case DiffType.added:
case "added":
return "+ ";
case DiffType.removed:
case "removed":
return "- ";
default:
return " ";
Expand All @@ -450,7 +428,7 @@ export function buildMessage(
diffResult.forEach((result: DiffResult<string>) => {
const c = createColor(result.type);
const line = result.details?.map((detail) =>
detail.type !== DiffType.common
detail.type !== "common"
? createColor(detail.type, { background: true })(detail.value)
: detail.value
).join("") ?? result.value;
Expand Down
Loading