Skip to content

Commit

Permalink
feat: add configuration for diff truncation (#5073) (#5333)
Browse files Browse the repository at this point in the history
  • Loading branch information
willieho authored Apr 5, 2024
1 parent db98145 commit 6797b04
Show file tree
Hide file tree
Showing 8 changed files with 285 additions and 29 deletions.
22 changes: 22 additions & 0 deletions docs/config/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2077,6 +2077,28 @@ export default defineConfig({
```
:::
#### diff.truncateThreshold
- **Type**: `number`
- **Default**: `0`
The maximum length of diff result to be displayed. Diffs above this threshold will be truncated.
Truncation won't take effect with default value 0.
#### diff.truncateAnnotation
- **Type**: `string`
- **Default**: `'... Diff result is truncated'`
Annotation that is output at the end of diff result if it's truncated.
#### diff.truncateAnnotationColor
- **Type**: `DiffOptionsColor = (arg: string) => string`
- **Default**: `noColor = (string: string): string => string`
Color of truncate annotation, default is output with no color.
### fakeTimers
- **Type:** `FakeTimerInstallOpts`
Expand Down
36 changes: 21 additions & 15 deletions packages/utils/src/diff/diffLines.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,21 +81,24 @@ function printAnnotation({
return `${aColor(a)}\n${bColor(b)}\n\n`
}

export function printDiffLines(diffs: Array<Diff>, options: DiffOptionsNormalized): string {
export function printDiffLines(diffs: Array<Diff>, truncated: boolean, options: DiffOptionsNormalized): string {
return printAnnotation(options, countChanges(diffs))
+ (options.expand
? joinAlignedDiffsExpand(diffs, options)
: joinAlignedDiffsNoExpand(diffs, options))
+ (options.expand ? joinAlignedDiffsExpand(diffs, options) : joinAlignedDiffsNoExpand(diffs, options))
+ (truncated ? options.truncateAnnotationColor(`\n${options.truncateAnnotation}`) : '')
}

// Compare two arrays of strings line-by-line. Format as comparison lines.
export function diffLinesUnified(aLines: Array<string>, bLines: Array<string>, options?: DiffOptions): string {
const normalizedOptions = normalizeDiffOptions(options)
const [diffs, truncated] = diffLinesRaw(
isEmptyString(aLines) ? [] : aLines,
isEmptyString(bLines) ? [] : bLines,
normalizedOptions,
)
return printDiffLines(
diffLinesRaw(
isEmptyString(aLines) ? [] : aLines,
isEmptyString(bLines) ? [] : bLines,
),
normalizeDiffOptions(options),
diffs,
truncated,
normalizedOptions,
)
}

Expand All @@ -120,7 +123,7 @@ export function diffLinesUnified2(aLinesDisplay: Array<string>, bLinesDisplay: A
return diffLinesUnified(aLinesDisplay, bLinesDisplay, options)
}

const diffs = diffLinesRaw(aLinesCompare, bLinesCompare)
const [diffs, truncated] = diffLinesRaw(aLinesCompare, bLinesCompare, options)

// Replace comparison lines with displayable lines.
let aIndex = 0
Expand All @@ -144,13 +147,16 @@ export function diffLinesUnified2(aLinesDisplay: Array<string>, bLinesDisplay: A
}
})

return printDiffLines(diffs, normalizeDiffOptions(options))
return printDiffLines(diffs, truncated, normalizeDiffOptions(options))
}

// Compare two arrays of strings line-by-line.
export function diffLinesRaw(aLines: Array<string>, bLines: Array<string>): Array<Diff> {
const aLength = aLines.length
const bLength = bLines.length
export function diffLinesRaw(aLines: Array<string>, bLines: Array<string>, options?: DiffOptions): [Array<Diff>, boolean] {
const truncate = options?.truncateThreshold ?? false
const truncateThreshold = Math.max(Math.floor(options?.truncateThreshold ?? 0), 0)
const aLength = truncate ? Math.min(aLines.length, truncateThreshold) : aLines.length
const bLength = truncate ? Math.min(bLines.length, truncateThreshold) : bLines.length
const truncated = aLength !== aLines.length || bLength !== bLines.length

const isCommon = (aIndex: number, bIndex: number) => aLines[aIndex] === bLines[bIndex]

Expand Down Expand Up @@ -185,5 +191,5 @@ export function diffLinesRaw(aLines: Array<string>, bLines: Array<string>): Arra
for (; bIndex !== bLength; bIndex += 1)
diffs.push(new Diff(DIFF_INSERT, bLines[bIndex]))

return diffs
return [diffs, truncated]
}
33 changes: 28 additions & 5 deletions packages/utils/src/diff/diffStrings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,31 @@

import * as diff from 'diff-sequences'
import { DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, Diff } from './cleanupSemantic'
import type { DiffOptions } from './types'

// platforms compatible
function getNewLineSymbol(string: string) {
return string.includes('\r\n') ? '\r\n' : '\n'
}

function diffStrings(a: string, b: string, options?: DiffOptions): [Array<Diff>, boolean] {
const truncate = options?.truncateThreshold ?? false
const truncateThreshold = Math.max(Math.floor(options?.truncateThreshold ?? 0), 0)
let aLength = a.length
let bLength = b.length
if (truncate) {
const aMultipleLines = a.includes('\n')
const bMultipleLines = b.includes('\n')
const aNewLineSymbol = getNewLineSymbol(a)
const bNewLineSymbol = getNewLineSymbol(b)
// multiple-lines string expects a newline to be appended at the end
const _a = aMultipleLines ? `${a.split(aNewLineSymbol, truncateThreshold).join(aNewLineSymbol)}\n` : a
const _b = bMultipleLines ? `${b.split(bNewLineSymbol, truncateThreshold).join(bNewLineSymbol)}\n` : b
aLength = _a.length
bLength = _b.length
}
const truncated = aLength !== a.length || bLength !== b.length

function diffStrings(a: string, b: string): Array<Diff> {
const isCommon = (aIndex: number, bIndex: number) => a[aIndex] === b[bIndex]

let aIndex = 0
Expand All @@ -34,16 +57,16 @@ function diffStrings(a: string, b: string): Array<Diff> {
// @ts-expect-error wrong bundling
const diffSequences = diff.default.default || diff.default

diffSequences(a.length, b.length, isCommon, foundSubsequence)
diffSequences(aLength, bLength, isCommon, foundSubsequence)

// After the last common subsequence, push remaining change items.
if (aIndex !== a.length)
if (aIndex !== aLength)
diffs.push(new Diff(DIFF_DELETE, a.slice(aIndex)))

if (bIndex !== b.length)
if (bIndex !== bLength)
diffs.push(new Diff(DIFF_INSERT, b.slice(bIndex)))

return diffs
return [diffs, truncated]
}

export default diffStrings
4 changes: 4 additions & 0 deletions packages/utils/src/diff/normalizeDiffOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type { DiffOptions, DiffOptionsNormalized } from './types'
export const noColor = (string: string): string => string

const DIFF_CONTEXT_DEFAULT = 5
const DIFF_TRUNCATE_THRESHOLD_DEFAULT = 0 // not truncate

function getDefaultOptions(): DiffOptionsNormalized {
const c = getColors()
Expand All @@ -35,6 +36,9 @@ function getDefaultOptions(): DiffOptionsNormalized {
includeChangeCounts: false,
omitAnnotationLines: false,
patchColor: c.yellow,
truncateThreshold: DIFF_TRUNCATE_THRESHOLD_DEFAULT,
truncateAnnotation: '... Diff result is truncated',
truncateAnnotationColor: noColor,
}
}

Expand Down
11 changes: 6 additions & 5 deletions packages/utils/src/diff/printDiffs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,17 @@ export function diffStringsUnified(a: string, b: string, options?: DiffOptions):
const isMultiline = a.includes('\n') || b.includes('\n')

// getAlignedDiffs assumes that a newline was appended to the strings.
const diffs = diffStringsRaw(
const [diffs, truncated] = diffStringsRaw(
isMultiline ? `${a}\n` : a,
isMultiline ? `${b}\n` : b,
true, // cleanupSemantic
options,
)

if (hasCommonDiff(diffs, isMultiline)) {
const optionsNormalized = normalizeDiffOptions(options)
const lines = getAlignedDiffs(diffs, optionsNormalized.changeColor)
return printDiffLines(lines, optionsNormalized)
return printDiffLines(lines, truncated, optionsNormalized)
}
}

Expand All @@ -51,11 +52,11 @@ export function diffStringsUnified(a: string, b: string, options?: DiffOptions):

// Compare two strings character-by-character.
// Optionally clean up small common substrings, also known as chaff.
export function diffStringsRaw(a: string, b: string, cleanup: boolean): Array<Diff> {
const diffs = diffStrings(a, b)
export function diffStringsRaw(a: string, b: string, cleanup: boolean, options?: DiffOptions): [Array<Diff>, boolean] {
const [diffs, truncated] = diffStrings(a, b, options)

if (cleanup)
cleanupSemantic(diffs) // impure function

return diffs
return [diffs, truncated]
}
6 changes: 6 additions & 0 deletions packages/utils/src/diff/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ export interface DiffOptions {
omitAnnotationLines?: boolean
patchColor?: DiffOptionsColor
compareKeys?: CompareKeys
truncateThreshold?: number
truncateAnnotation?: string
truncateAnnotationColor?: DiffOptionsColor
}

export interface DiffOptionsNormalized {
Expand All @@ -48,4 +51,7 @@ export interface DiffOptionsNormalized {
includeChangeCounts: boolean
omitAnnotationLines: boolean
patchColor: DiffOptionsColor
truncateThreshold: number
truncateAnnotation: string
truncateAnnotationColor: DiffOptionsColor
}
3 changes: 3 additions & 0 deletions packages/vitest/src/types/matcher-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,7 @@ export interface DiffOptions {
patchColor?: Formatter
// pretty-format type
compareKeys?: any
truncateThreshold?: number
truncateAnnotation?: string
truncateAnnotationColor?: Formatter
}
Loading

0 comments on commit 6797b04

Please sign in to comment.