MakeupDiff

A Makeup lexer for diffs and patches.

Diffs are a line-oriented format, so — unlike a lexer for a programming language — this one classifies whole lines rather than individual syntactic atoms.

Supported dialects

Token mapping

ConstructToken type
added lines (+, >):generic_inserted
removed lines (-, <):generic_deleted
changed lines in context diffs (!):generic_strong
hunk headers, range lines, file banners:generic_subheading
repository-level metadata (diff --git, index, ...):generic_heading
section hint trailing a unified hunk header:name_function
mail headers of a git format-patch preamble:comment_preproc
\ No newline at end of file:comment_special
context lines and anything unrecognised:text

Ambiguity of --- and +++

A line starting with --- is either the old file banner of a unified diff or the removal of a line whose content begins with --; the same holds for +++. The tokenizer emits a provisional token for both and resolves it in the postprocessing pass from the surrounding lines:

So a literal --- inside a hunk — right after an @@ header, say — is correctly highlighted as a deleted line rather than as a file banner.

Installation

Add makeup_patch to your list of dependencies in mix.exs:

def deps do
[
{:makeup_patch, "~> 0.1"}
]
end

The lexer will be automatically registered in Makeup for the language names "diff", "udiff" and "patch", and for the file extensions .diff and .patch.

Usage

Once installed, ExDoc and any other tool using Makeup will automatically syntax-highlight diff code blocks (tagged with diff as the language).

You can also use it directly:

alias Makeup.Lexers.DiffLexer
DiffLexer.lex("""
--- a/lib/app.ex
+++ b/lib/app.ex
@@ -1,3 +1,3 @@ defmodule App do
def hello do
- :world
+ :diff
end
""")

The lexer round-trips: for any input, input |> DiffLexer.lex() |> Makeup.Lexer.unlex() returns the original string.

License

MIT—see LICENSE for details.