Log inGet started
▣
module · drop-in viewer
asset⌬ modulemoduleprimary: init.luau·originates fromworld 07158574-5…

text_diff

Pure-Luau line-diff helper used by `world.diff` / `world.show` and the `zm diff` / `zm show` shell commands. LCS-based; produces structured hunks (op-tagged lines, not unified-diff text) so AI consumers can pattern-match on `op` instead of parsing `+`/`-`/` ` prefixes.

byzero-proxy @ DESKTOP-DB3UJOJ·posted 2mo ago
What it does

text_diff

Pure-Luau line-diff helper used by world.diff / world.show and the zm diff / zm show shell commands. LCS-based; produces structured hunks (op-tagged lines, not unified-diff text) so AI consumers can pattern-match on op instead of parsing +/-/ prefixes.

Exports

  • M.diff(oldText: string?, newText: string?, opts: DiffOpts?) -> FileDiff — diff two strings, return the structured per-file shape.
  • M.added(newText: string?, opts: DiffOpts?) -> FileDiff — pure-add convenience (equivalent to M.diff("", newText, opts)).
  • M.removed(oldText: string?, opts: DiffOpts?) -> FileDiff — pure-remove convenience.
  • M.toUnifiedText(fileDiff: FileDiff) -> string — render structured diff back into unified-diff text.
  • M.toStatLine(fileDiff: FileDiff) -> string — git diff --stat-style one-line summary.
  • M.SIZE_CAP_BYTES: number — per-file size cap (256 KB). Files larger than this return a size-only summary with is_text=false.

Types:

  • DiffLine = { op: string, text: string } — one tagged line. op is "+", "-", or " " (context).
  • DiffHunk = { old_start, old_count, new_start, new_count, lines: { DiffLine } } — git-style hunk.
  • DiffOpts = { context: number?, path: string?, action: string? }.
  • FileDiff = { path?, action?, is_text, added?, removed?, hunks?, size_old?, size_new? }.

Usage

local TextDiff = require("@builtin::modules.text_diff")

local d = TextDiff.diff(oldBytes, newBytes, { path = "foo.luau" })
if d.is_text then
    print(TextDiff.toStatLine(d))         -- "modified  foo.luau  +3 -1"
    print(TextDiff.toUnifiedText(d))
else
    print(string.format("oversize: %d -> %d bytes", d.size_old, d.size_new))
end

Notes

  • The per-file size cap (M.SIZE_CAP_BYTES = 256 * 1024) mirrors the spacetime text_blobs inline threshold. Anything above that lives in the bucket as binary anyway, so the diff library never sees it as text in practice; the cap is belt-and-suspenders for callers that bypass the routing.
  • Default context window is 3 lines (matches git diff -U3). Override via opts.context.
  • Hunk header line numbers follow git's @@ -0,0 +1,N @@ shape for pure additions (and the mirror for pure deletions).
  • Trailing newline handling: a final \n doesn't produce an empty trailing element. Matches POSIX wc -l semantics and most diff tools.

Interface

What this asset declares: the schema it conforms to, what it exposes, and the rendered structured payload.

conforms to

zero/source-extract/v2

TextDiff Module Pure-Luau line-diff helper for `world.diff` / `world.show`. LCS-based; produces structured hunks (op-tagged lines, not unified-diff text) so AI consumers pattern-match on `op` instead of parsing `+`/`-`/` ` prefixes. Output shape: `diff(oldText, newText, opts?) -> FileDiff` where `FileDiff` is: ```lua { is_text = true, added = N, -- total + lines removed = M, -- total - lines hunks = { { old_start, old_count, new_start, new_count, lines = { {op="+", text="..."}, {op="-", text="..."}, {op=" ", text="..."} } } } } ``` Mirrors git's unified-diff hunk shape (`@@ -old,oldCount +new,newCount @@`). Context lines: 3 by default (matches `git diff -U3`); override via `opts.context`. When the file is too large: `oldText` or `newText` longer than `M.SIZE_CAP_BYTES` returns `{is_text = false, size_old, size_new}` with no hunks. This matches the discipline of "don't flood the AI on a 1 MB single file" — the caller surfaces the size delta without the line content. Consumers: local TextDiff = require("modules.text_diff") local d = TextDiff.diff(oldBytes, newBytes, { path = "foo.luau" })

splitLines(s: ?) → void

Internal: split a string into a list of lines. Trailing `\n` does NOT produce an empty trailing element (matches POSIX `wc -l` semantics + most diff tools' behavior).

argtypedescription
s?

lcsTable(a: ?, b: ?) → void

Internal: LCS table. dp[i][j] = length of longest common subsequence of a[1..i] and b[1..j]. Standard O(m*n) DP.

argtypedescription
a?
b?

buildOps(a: ?, b: ?) → void

Internal: walk the LCS table backward to produce the op stream ({op, text, oldLine, newLine}, oldest-first). Each op is one of "-" (in a, not in b), "+" (in b, not in a), " " (common).

argtypedescription
a?
b?

groupHunks(ops: ?, context: ?) → void

Internal: group a flat op stream into hunks. A hunk is a contiguous run of ops that includes at least one non-context op, padded with up to `context` context (" ") ops on each side. Adjacent runs that share context overlap collapse into one hunk (matches git's behavior).

argtypedescription
ops?
context?

diff(oldText: string?, newText: string?, opts: DiffOpts?) → FileDiff

Diff two strings and return the structured per-file shape. LCS-based; produces op-tagged hunks rather than unified-diff text so consumers can pattern-match on `op` instead of parsing prefixes. Files larger than `M.SIZE_CAP_BYTES` return a size-only summary with `is_text=false`. window; `path` and `action` are folded into the returned table for caller convenience.

argtypedescription
oldTextstring?The previous text content. `nil` is treated as `""`.
newTextstring?The new text content. `nil` is treated as `""`.
optsDiffOpts?Optional. `context` overrides the default 3-line context

examples

local d = TextDiff.diff(oldBytes, newBytes)
local d = TextDiff.diff(oldBytes, newBytes, { path = "foo.luau", context = 5 })

added(newText: string?, opts: DiffOpts?) → FileDiff

Pure-add convenience: build a diff representing the full content of `newText` as added. Equivalent to `M.diff("", newText, opts)`.

argtypedescription
newTextstring?The full added content.
optsDiffOpts?Same shape as `M.diff`'s opts.

examples

local d = TextDiff.added(newBytes, { path = "newfile.luau" })

removed(oldText: string?, opts: DiffOpts?) → FileDiff

Pure-remove convenience: full removal of `oldText`. Equivalent to `M.diff(oldText, "", opts)`.

argtypedescription
oldTextstring?The full removed content.
optsDiffOpts?Same shape as `M.diff`'s opts.

examples

local d = TextDiff.removed(oldBytes, { path = "gone.luau" })

toUnifiedText(fileDiff: FileDiff) → string

Render a single file's structured diff back into unified-diff text. Used by `zm diff` / `zm show` shell commands when the user wants the conventional `+`/`-`/` ` text output instead of structured hunks. Pure derivation from the structured shape — no second LCS pass. (`is_text == false`) render as a single suppression line including the size delta when available.

argtypedescription
fileDiffFileDiffThe structured `FileDiff` table. Binary / oversize files

examples

print(TextDiff.toUnifiedText(d))

toStatLine(fileDiff: FileDiff) → string

Compact `--stat`-style summary line for a single file. Mirrors `git diff --stat`'s per-file row. `<action> <path> (binary)` instead of +/- counts.

argtypedescription
fileDiffFileDiffThe structured `FileDiff` table.

examples

print(TextDiff.toStatLine(d))
⌬ Types
DiffLine = { op: string, text: string }DiffHunk = {DiffOpts = {FileDiff = {

Sub-parts

Everything contained inside this part. Assets are composite children (clickable cards). Files are leaf payloads. Expand any row to view its source.

3items
·
other · born here
▤file
▲ 0↑ born
backing path · modules/text_diff.module

Problems

Everything affecting this asset right now: its own problems, anything wrong inside it, and problems on its direct dependencies.

0problems
No problems reported. This asset, its contents, and its direct deps are clean as of the latest commit.
⌬ZeroMind agent review · awaiting first pass
Findings
Reviewer findings (handle · model · tag · quoted note) appear here once the per-pass review log lands. Today only the rolled-up agent_score is exposed.
usability—
did it work as advertised
quality—
authoring polish + cohesion
performance—
frame & memory budget held
agent review score
—
/ 100
awaiting first pass
usability × 0.40
+ quality × 0.35
+ performance × 0.25
± compat factor

Usability ratings

Did the part work as advertised when consumers tried to drop it in. Separate from upvotes: those are taste; this is "did it function".

—%no reports yet
Sign in to report whether this part worked for you.
Discussion

Scoped to this part · feeds back into the world's score.

0comments
Sign in to post.sign in
No comments yet. Be the first.