import * as Result from "./Result.ts";
declare const TextDiffErrorTypeId = "~effect/TextDiff/TextDiffError";
/**
* One run of an edit script.
*
* **Details**
*
* `Equal` runs carry items kept from `before`, `Delete` runs carry items
* removed from `before`, and `Insert` runs carry items added from `after`.
* Scripts produced by this module never contain empty runs or two adjacent
* runs with the same tag, and within each change the `Delete` run comes before
* the `Insert` run.
*
* @see {@link diff} to compute an edit script
* @stability experimental
* @category models
* @since 4.0.6
*/
export type Edit = EditEqual | EditInsert | EditDelete;
/**
* Run of items present in both sequences.
*
* @stability experimental
* @category models
* @since 4.0.6
*/
export interface EditEqual {
readonly _tag: "Equal";
readonly items: ReadonlyArray;
}
/**
* Run of items present only in the `after` sequence.
*
* @stability experimental
* @category models
* @since 4.0.6
*/
export interface EditInsert {
readonly _tag: "Insert";
readonly items: ReadonlyArray;
}
/**
* Run of items present only in the `before` sequence.
*
* @stability experimental
* @category models
* @since 4.0.6
*/
export interface EditDelete {
readonly _tag: "Delete";
readonly items: ReadonlyArray;
}
declare const TextDiffError_base: new = {}>(args: import("./Types.ts").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("./Cause.ts").YieldableError & {
readonly _tag: "TextDiffError";
} & Readonly;
/**
* Error returned when an edit script does not fit the input it is applied to.
*
* **Example** (Rejecting a script made for other input)
*
* ```ts import.meta.vitest
* import { Result, TextDiff } from "effect"
*
* const edits = TextDiff.diff([1, 2], [1])
*
* Result.mapError(TextDiff.apply([1], edits), TextDiff.isTextDiffError) // => Result.fail(true)
* ```
*
* @see {@link apply} and {@link applyText} for the operations that return it
* @see {@link isTextDiffError} for narrowing unknown values
* @stability experimental
* @category errors
* @since 4.0.6
*/
export declare class TextDiffError extends TextDiffError_base<{
readonly message: string;
}> {
readonly [TextDiffErrorTypeId]: typeof TextDiffErrorTypeId;
}
/**
* Checks whether a value is a `TextDiffError`.
*
* @stability experimental
* @category guards
* @since 4.0.6
*/
export declare const isTextDiffError: (u: unknown) => u is TextDiffError;
/**
* Creates a function that computes a minimal edit script between two arrays
* using a custom equivalence.
*
* **When to use**
*
* Use when items should match by key or by a structural comparison other than
* `Equal.equals`.
*
* **Details**
*
* Unless the `maxEdits` cap is exceeded, the script has the
* fewest possible inserted plus deleted items. `Equal` runs carry the items
* from `before`, not necessarily the exact values from `after`.
*
* **Gotchas**
*
* Without `maxEdits`, work grows with the product of the input size and the
* number of differences, which is quadratic for unrelated inputs. Set
* `maxEdits` for large or untrusted inputs.
*
* **Example** (Matching items by id)
*
* ```ts import.meta.vitest
* import { TextDiff } from "effect"
*
* const diffById = TextDiff.diffWith<{ readonly id: number }>((a, b) => a.id === b.id)
*
* diffById([{ id: 1 }, { id: 2 }], [{ id: 2 }]) // => [{ _tag: "Delete", items: [{ id: 1 }] }, { _tag: "Equal", items: [{ id: 2 }] }]
* ```
*
* @see {@link diff} for the `Equal.equals` variant
* @stability experimental
* @category constructors
* @since 4.0.6
*/
export declare const diffWith: (isEquivalent: (self: A, that: A) => boolean) => (before: ReadonlyArray, after: ReadonlyArray, options?: {
/**
* Largest number of inserted plus deleted items searched for in each
* changed region. Common prefixes, suffixes and matching runs are free.
* When a region needs more edits, the search splits it at the furthest
* point it reached and continues on the rest, and a part that still cannot
* be matched becomes a deletion followed by an insertion. The script stays
* valid, and is minimal whenever the whole diff needs at most `maxEdits`
* edits. For a finite cap, work is O((N + M) * (maxEdits + 1)).
*
* @default Infinity
*/
readonly maxEdits?: number | undefined;
}) => Array>;
/**
* Computes a minimal edit script between two arrays, comparing items with
* `Equal.equals`.
*
* **Details**
*
* Unless the `maxEdits` cap is exceeded, the script has the
* fewest possible inserted plus deleted items. The choice is deterministic,
* and each change lists its deletions before its insertions.
*
* **Gotchas**
*
* Without `maxEdits`, work is quadratic for unrelated inputs. Set `maxEdits`
* when diffing large or untrusted arrays.
*
* **Example** (Diffing two arrays)
*
* ```ts import.meta.vitest
* import { TextDiff } from "effect"
*
* TextDiff.diff([1, 2, 3, 4], [1, 3, 4, 5]) // => [{ _tag: "Equal", items: [1] }, { _tag: "Delete", items: [2] }, { _tag: "Equal", items: [3, 4] }, { _tag: "Insert", items: [5] }]
* ```
*
* @see {@link diffWith} to compare items with a custom equivalence
* @see {@link apply} to replay a script
* @stability experimental
* @category constructors
* @since 4.0.6
*/
export declare const diff: (before: ReadonlyArray, after: ReadonlyArray, options?: {
/**
* Largest number of inserted plus deleted items searched for in each
* changed region. Common prefixes, suffixes and matching runs are free.
* When a region needs more edits, the search splits it at the furthest
* point it reached and continues on the rest, and a part that still cannot
* be matched becomes a deletion followed by an insertion. The script stays
* valid, and is minimal whenever the whole diff needs at most `maxEdits`
* edits. For a finite cap, work is O((N + M) * (maxEdits + 1)).
*
* @default Infinity
*/
readonly maxEdits?: number | undefined;
}) => Array>;
/**
* Computes a minimal line-level edit script between two strings.
*
* **Details**
*
* Each line keeps its `\n` terminator, so `\n` versus `\r\n` endings and a
* missing final newline are reported as changes. Joining the items of a run
* gives the exact text it covers.
*
* **Gotchas**
*
* Without `maxEdits`, work is quadratic for unrelated inputs. Set `maxEdits`
* when diffing large or untrusted text.
*
* **Example** (Diffing lines)
*
* ```ts import.meta.vitest
* import { TextDiff } from "effect"
*
* TextDiff.lines("a\nb\n", "a\nc\n") // => [{ _tag: "Equal", items: ["a\n"] }, { _tag: "Delete", items: ["b\n"] }, { _tag: "Insert", items: ["c\n"] }]
* ```
*
* @see {@link codePoints} for a code point diff
* @see {@link unified} to format a line diff
* @stability experimental
* @category constructors
* @since 4.0.6
*/
export declare const lines: (before: string, after: string, options?: {
/**
* Largest number of inserted plus deleted items searched for in each
* changed region. Common prefixes, suffixes and matching runs are free.
* When a region needs more edits, the search splits it at the furthest
* point it reached and continues on the rest, and a part that still cannot
* be matched becomes a deletion followed by an insertion. The script stays
* valid, and is minimal whenever the whole diff needs at most `maxEdits`
* edits. For a finite cap, work is O((N + M) * (maxEdits + 1)).
*
* @default Infinity
*/
readonly maxEdits?: number | undefined;
}) => Array>;
/**
* Computes a minimal code point edit script between two strings.
*
* **Details**
*
* Items are Unicode code points, so a surrogate pair is never split between
* runs. Grapheme clusters, such as an emoji with modifiers or a letter with
* combining marks, can still be split. The UTF-16 length of a run is the sum
* of its items' lengths.
*
* **Gotchas**
*
* Without `maxEdits`, work is quadratic for unrelated inputs. Set `maxEdits`
* when diffing large or untrusted text.
*
* **Example** (Diffing code points)
*
* ```ts import.meta.vitest
* import { TextDiff } from "effect"
*
* TextDiff.codePoints("cat", "cut") // => [{ _tag: "Equal", items: ["c"] }, { _tag: "Delete", items: ["a"] }, { _tag: "Insert", items: ["u"] }, { _tag: "Equal", items: ["t"] }]
* ```
*
* @see {@link lines} for a line diff
* @see {@link applyText} to replay a script on a string
* @stability experimental
* @category constructors
* @since 4.0.6
*/
export declare const codePoints: (before: string, after: string, options?: {
/**
* Largest number of inserted plus deleted items searched for in each
* changed region. Common prefixes, suffixes and matching runs are free.
* When a region needs more edits, the search splits it at the furthest
* point it reached and continues on the rest, and a part that still cannot
* be matched becomes a deletion followed by an insertion. The script stays
* valid, and is minimal whenever the whole diff needs at most `maxEdits`
* edits. For a finite cap, work is O((N + M) * (maxEdits + 1)).
*
* @default Infinity
*/
readonly maxEdits?: number | undefined;
}) => Array>;
/**
* Applies an edit script to an array.
*
* **Details**
*
* `Equal` and `Delete` runs consume items from `before` by count, `Equal` runs
* copy them to the result, and `Insert` runs add their items. Run contents are
* not compared with `before`, since under a custom equivalence `Equal` items
* can differ from the input.
*
* **Gotchas**
*
* Fails with a {@link TextDiffError} when the runs do not consume exactly
* `before.length` items.
*
* **Example** (Replaying a script)
*
* ```ts import.meta.vitest
* import { Result, TextDiff } from "effect"
*
* const edits = TextDiff.diff([1, 2, 3], [1, 3, 4])
*
* TextDiff.apply([1, 2, 3], edits) // => Result.succeed([1, 3, 4])
* ```
*
* @see {@link applyText} for the string variant
* @see {@link invert} to apply a script in reverse
* @stability experimental
* @category combinators
* @since 4.0.6
*/
export declare const apply: (before: ReadonlyArray, edits: ReadonlyArray>) => Result.Result, TextDiffError>;
/**
* Applies a text edit script to a string.
*
* **Details**
*
* Works with scripts from both {@link lines} and {@link codePoints}. The
* joined items of each `Equal` and `Delete` run must match `before` at the
* current position, so a script made for different text is rejected rather
* than applied.
*
* **Gotchas**
*
* Fails with a {@link TextDiffError} when a run does not match `before`, or
* when the runs do not cover all of it.
*
* **Example** (Replaying a code point diff)
*
* ```ts import.meta.vitest
* import { Result, TextDiff } from "effect"
*
* const edits = TextDiff.codePoints("cat", "cut")
*
* TextDiff.applyText("cat", edits) // => Result.succeed("cut")
* ```
*
* @see {@link apply} for the array variant
* @stability experimental
* @category combinators
* @since 4.0.6
*/
export declare const applyText: (before: string, edits: ReadonlyArray>) => Result.Result;
/**
* Inverts an edit script, so it turns `after` back into `before`.
*
* **Details**
*
* Swaps the items of each change's `Delete` and `Insert` runs, keeping
* deletions before insertions. `Equal` runs are unchanged.
*
* **Example** (Undoing a change)
*
* ```ts import.meta.vitest
* import { Result, TextDiff } from "effect"
*
* const edits = TextDiff.diff([1, 2], [1, 3])
*
* TextDiff.apply([1, 3], TextDiff.invert(edits)) // => Result.succeed([1, 2])
* ```
*
* @stability experimental
* @category combinators
* @since 4.0.6
*/
export declare const invert: (edits: ReadonlyArray>) => Array>;
/**
* Formats a line edit script as a unified diff.
*
* **Details**
*
* The script must come from {@link lines}, or otherwise hold one line per
* item. The output follows `diff -u`: hunks whose context would touch or
* overlap are merged, and a final line without a newline is followed by
* `\ No newline at end of file`. When `fromFile` or `toFile` is set, both
* header lines are emitted and an unset name falls back to the other. A script
* without changes produces an empty string.
*
* **Gotchas**
*
* Among equally small diffs, hunks can differ from those of GNU diff or Git,
* which apply extra heuristics.
*
* **Example** (Formatting a unified diff)
*
* ```ts import.meta.vitest
* import { TextDiff } from "effect"
*
* TextDiff.unified(TextDiff.lines("a\nb\n", "a\nc\n")) // => "@@ -1,2 +1,2 @@\n a\n-b\n+c\n"
* ```
*
* @see {@link lines} to compute a line diff
* @stability experimental
* @category formatting
* @since 4.0.6
*/
export declare const unified: (edits: ReadonlyArray>, options?: {
/**
* Number of unchanged lines shown around each change.
*
* @default 3
*/
readonly context?: number | undefined;
/**
* Name shown in the `---` header line.
*/
readonly fromFile?: string | undefined;
/**
* Name shown in the `+++` header line.
*/
readonly toFile?: string | undefined;
}) => string;
export {};
//# sourceMappingURL=TextDiff.d.ts.map