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.
All views, opinions, and statements expressed on this account/in this repo are solely my own and are made in my personal capacity. They do not reflect, and should not be construed as reflecting, the views, positions, or policies of Modular. This account is not affiliated with, authorized by, or endorsed by my employer in any way.
Licensed under the MIT License.

