As of mid-2026 the Mojo ecosystem has no line-diffing library. mojo-diff
fills that gap: a pure-Mojo implementation of the Myers O(ND) greedy diff
algorithm (Myers, 1986), exposed through the same opcode model and
unified-diff output that Python's
difflib produces.
unified_diff is byte-for-byte compatible with difflib.unified_diff on
the fixture corpus in test/data/, generated directly by Python's
difflib.
If you know Python's difflib, the core operations map like this:
Python (difflib) |
mojo-diff |
|---|---|
difflib.unified_diff(a, b, fromfile, tofile) |
unified_diff(a, b, fromfile, tofile, n) |
SequenceMatcher(None, a, b).ratio() |
ratio(splitlines_keepends(a), splitlines_keepends(b)) |
SequenceMatcher(None, a, b).get_opcodes() |
get_opcodes(splitlines_keepends(a), splitlines_keepends(b)) |
One shape difference: difflib.unified_diff takes lists of lines, whereas
mojo-diff's unified_diff takes the two whole strings and splits them for you.
ratio and get_opcodes operate on line lists — use splitlines_keepends(s)
to produce them.
get_opcodes(a, b), mirroringSequenceMatcher.get_opcodes(): edit opcodes turningaintob, tagged"equal"/"replace"/"delete"/"insert", same 5-tuple shape as difflib.matching_blocks(a, b), mirroringget_matching_blocks(): sentinel-terminated matching runs.ratio(a, b), mirroringSequenceMatcher.ratio(): similarity as2*M/T.unified_diff(a_text, b_text, from_file, to_file, context=3), mirroringdifflib.unified_diff: standard unified-diff text, matching difflib's hunk grouping and@@range math exactly.splitlines_keepends(text), mirroringstr.readlines(): split on\n, keeping terminators.- Compatibility details: lines keep trailing newlines like
readlines()-fed difflib; a final line with no trailing newline is emitted without one and no GNU-diff\ No newline at end of filemarker (difflib doesn't emit one either); identical inputs produce the empty string; empty ranges use difflib'sstart-1,0form and single-line ranges omit the length.
- Character-level or word-level diffing. Line-level only, in v0.1.
- Match difflib's Ratcliff-Obershelp alignment exactly on repeated
lines. Myers finds a minimal edit script; with heavily repeated
lines that can differ from
SequenceMatcher's alignment, even though both are valid diffs. ndiff,context_diff,HtmlDiff,autojunkheuristics. Not implemented in v0.1.
With pixi:
pixi install
pixi run testOr with uv:
uv venv
uv pip install mojo --index https://whl.modular.com/nightly/simple/ --prerelease allow
.venv/bin/mojo run -I src test/test_diff.mojoRequires a Mojo nightly (>=1.0.0b3).
from diff import get_opcodes, unified_diff, ratio, matching_blocks, OpCode
def main() raises:
var diff = unified_diff(
open("a.txt", "r").read(), open("b.txt", "r").read(), "a.txt", "b.txt"
)
print(diff, end="")--- a.txt
+++ b.txt
@@ -1,7 +1,9 @@
def load(path):
+ if not path:
+ return 0
data = open(path).read()
- rows = data.split(chr(10))
+ lines = data.split(chr(10))
total = 0
- for r in rows:
- total += len(r)
+ for line in lines:
+ total += len(line)
return total
OpCode is {tag, a_start, a_end, b_start, b_end}, the same shape as
difflib's opcodes.
pixi run test32 tests cover opcode generation, matching blocks, ratio calculation, and
splitlines_keepends, plus 11/11 unified-diff fixtures matching
Python's difflib byte-for-byte (identical inputs, single-line files,
multi-hunk diffs at multiple context widths, content-to-empty and
empty-to-content, and missing-trailing-newline cases). A performance test
diffs 5,000 lines in roughly 0.9ms. Fixtures and their expected outputs
are (re)generated with python3 test/data/generate_fixtures.py.
test/fuzz_runner.mojo exercises the diff engine against corrupted or
random input to confirm it never crashes.
Eleven pure-Mojo libraries that mirror familiar Python stdlib and PyPI APIs, filling gaps in the native Mojo ecosystem:
- mojo-xml — general-purpose XML
parsing, an ElementTree-shaped DOM (Python's
xml.etree.ElementTree) - mojo-feed — RSS, Atom, and
JSON Feed parsing (Python's
feedparser) - mojo-captions — SRT and WebVTT subtitle/transcript parsing (no Python stdlib parallel)
- mojo-html — HTML parsing and article extraction (Python's readability)
- mojo-markdown —
CommonMark markdown parsing (Python's
markdown) - mojo-unicodedata —
Unicode normalization and case folding (Python's
unicodedata) - mojo-template — a
Jinja-flavored template engine (Python's
jinja2) - mojo-tar — tar archive
reading and writing (Python's
tarfile) - mojo-redis — a Redis
client (Python's
redis-py) - mojo-url — URL parsing
and encoding (Python's
urllib.parse)
Issues and PRs welcome, especially cases where output diverges from
Python's difflib (attach the two inputs) and repeated-line scenarios
where Myers and Ratcliff-Obershelp disagree. Run pixi run test before
sending a PR, and regenerate fixtures with
python3 test/data/generate_fixtures.py if you add test data.
Built by Conor Bronsdon — host of Chain of Thought, a podcast about AI agents, infrastructure, and engineering. Find me on X or LinkedIn.
This is an independent personal project, not affiliated with, sponsored by, or endorsed by any company. All views expressed are my own.
Licensed under the MIT License.

