MakeupDiff

View Source

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

  • Unified diffs (diff -u, git diff, hg diff, svn diff), including @@ -old,count +new,count @@ section hunk headers and the @@@ ... @@@ headers produced by combined (merge) diffs.
  • Context diffs (diff -c): the *** old / --- new banner pair, the *************** hunk separator, the *** 1,5 **** and --- 1,7 ---- range lines, and the ! change marker.
  • Normal diffs (diff with no flags): 1,5c1,7 / 3d2 / 0a1 command lines and the < / > content markers.
  • Git extended headers: diff --git, index, old mode, new mode, new file mode, deleted file mode, copy from / copy to, rename from / rename to, similarity index, dissimilarity index, Binary files ... differ, GIT binary patch.
  • Subversion / CVS scaffolding: Index: path, the ====... rule, Property changes on:, RCS file:, retrieving revision.
  • git format-patch mail preambles: the mbox From <sha> Mon Sep 17 line plus the usual From: / Date: / Subject: / MIME headers.
  • The \ No newline at end of file marker.

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:

  • a --- immediately followed by a +++ is a banner pair;
  • a --- right after a repository-level heading, or at the very start of the input, is a banner;
  • everything else is ordinary added/removed content.

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.