Renderers over a line-token TextDiff. Output is a pure function of the diff and the renderer's settings: the same input gives the same bytes on every platform. Every output line ends in \n, whatever the input's line endings.
Hunks (both formats)
- Lines are the line tokens of the diff. A line's terminator (
\n,\r\nor a lone\r) is not shown. - A change is surrounded by
ContextLinesequal lines (default 3). Two changes whose gap is at most twice that share one hunk, so no line appears twice. With 0 context every change is its own hunk. - A hunk starts with
@@ -a,b +c,d @@: one-based first line and line count on each side. A count of 1 is written without,1. An empty range names the line before it (-0,0at the start of the text). - Lines are prefixed
' '(context),-(only in the old text) and+(only in the new text). Deletions of a changed block come before its insertions. - A shown line that had no terminator is followed by
\ No newline at end of file. - When
OldNameorNewNameis set,--- oldand+++ newprecede the first hunk. - An identical pair renders as the empty string.
Terminal
- Colour (default on): hunk headers cyan (
ESC[36m), file headers bold, deletions red (ESC[31m), insertions green (ESC[32m), the no-newline note dim; each styled line ends withESC[0m. Colour off writes the same text without any escape sequence. - Word level (colour on only): in a changed block that has both deleted and inserted lines, the block's old and new lines are diffed word by word and the changed words are wrapped in reverse video (
ESC[7m…ESC[27m). Skipped when either side of the block exceeds 8192 chars, or when under 30% of the longer side (counting non-white-space chars) is shared — those lines are rewrites and marking everything is noise. - Control characters in the text (below U+0020 except tab, U+007F, U+0080–U+009F) are replaced by a visible picture (U+2400 block, U+2421, U+FFFD) so compared text cannot drive the terminal.
Markdown
- Line diff: the hunks above in a fenced block opened with
diff. The fence is three backticks, or one more than the longest backtick run in the content. - Inline diff (any token kind): the new text, each deletion wrapped in
<del>and each insertion in<ins>. A run that spans line breaks is wrapped per line, with the breaks outside the tags.&and<are escaped as&and<; other Markdown is left as written.<del>/<ins>is used rather than~~/**because those collide with markup already in the text and cannot be escaped reliably.
Reading views
Three views read a TextDiff without token arithmetic.
TextDiff.Pieces: oneDiffRun(Kind, OldLine, NewLine, Text)per edit run, with the run's text as it stands (line breaks included) and the 1-based line each side starts on (nullon the side that lacks the run). Works for every token kind.TextDiff.Lines: the pieces cut at line breaks (\n,\r\n, a lone\r), the breaks dropped. For a line diff that is one entry per line; for a word or character diff a changed line shows as several entries with the same line number. A side's line number advances when a piece starts a new line on that side, so an unterminated last line is one line.Diff.SideBySide(old, new)/SideBySideDiff.From(lineDiff): aligned rows. A run of deleted lines followed by a run of inserted lines is paired line by line intoModifiedrows (the longer run's remainder isDeletedorInsertedrows with the other cell null). A modified row's cells hold word-levelPartsfromDiff.Wordsplus semantic cleanup of the two lines: Equal and Delete on the left, Equal and Insert on the right. Needs a diff made with line tokens.