🔖

Zennとdiff記法について

に公開
3

zenn.devのdiff記法についての簡単なまとめ記事です.

(2026年 追記: Zenn の更新により、Prism.js から Shiki にシンタックスハイライターが変わっています. 詳細は 公式の解説記事を参照してください.

ありがたいことに、現在でも diff の上からハイライトが効いています.現状のunified等の各記法ごとの見栄えは、変わることがあると考えられるので、ご自身で確認してください.
この記事の下記文章からもハイライトの当たり方が確認できると思います.

以下執筆当時の文章です.)

はじめに

kkiyama117と申します.

diff + 他言語のシンタックスを zenn に導入するお手伝いを少しだけさせていただきました.
ついでに、という事で diffの出力についても簡単にまとめておきたいと思います.
正直新規性もなく記事にする程でもないのですが、zenn には様々な分野の方がいますし、初めて記事を書きたいという方もいると思います,そんな方のお役に立てば幸いです.
不備や間違い等ありましたらコメント等でご指摘いただければと思います.

そもそも diffって

diffは簡単に言うと、unix系列のOSにおけるコマンドで、ファイル同士の差分をとるプログラムです.
転じて他の差分表示プログラムやフォーマットをdiffと呼ぶこともあるようです.
簡単な歴史等はWikipedia 参照、
記法については後程書きますが、実際の詳しい使い方についてはmacやLinux等のUnix系の環境があればman diffで確認する事をおすすめします.

そうではない人の為に、一応幾つかのオンラインのドキュメントを挙げておきます.

zennのdiff記法

zenn.dev 及び zenn-editor(zenn-cli)では内部でmarkdown->htmlの変換にmarkdown-itを、コードブロックの解析にはPrism.jsを組み合わせて利用しています.

diffにはいくつかの出力形式があるのですが、そのうちPrism.jsでは通常のものに加え、unified,contextといわれる記法に対応しています.
(issue段階ではunifiedのみに言及されていましたが、PRで他の記法に対応しています.)

unified diff の書き方

さて、肝心の diff の書き方について簡単にまとめておきます(そもそも本来は生成されるものですが).
合わせて、Prism.jsからexampleを引用しておきます.

通常のもの

  • headerhunk(ブロック)でファイルの一部分を表す.
    • a: 追加(added)
    • d は削除(deleted)
    • c は変更(changed)
    • a/d/cの各文字の前には元ファイルでの行番号が書かれ、後ろに変更後ファイルでの行番号が出力される.
  • hunkの行頭の山括弧はその行が追加されたのか、または削除されたのかを示している.
    • <が比較された先(1つ目の引数)に存在するもの、>が比較した先(2つ目のファイル)にのみ存在するもの
    • 標準では新旧両ファイルに共通する箇所は出力されない.
example.yaml
7c7
< qt: core
---
> qt: core gui

context

  • headerhunk(ブロック)でファイルの一部分を表す.
  • headerはhunkの位置情報を表す.
    • ***で始まる行が1つ目、---ではじまる行が2つ目のファイルに対応し、hankの起点,終点の各ファイル内での行数を表す.
  • 異なっている行の周辺の各行には先頭に2つのスペースをつける.
  • 2つのファイル間で異なっている行には先頭に表示文字とスペースをつける。
    • !: 2つのファイル間で変更された部分の各行. もう一方のファイルの対応する部分も ! でマークして表示する.
    • +: 2つめのファイルにのみ挿入された行. 1つめのファイルに対応部分はない.
    • -: 1つめのファイルから削除された行.2つめのファイルに対応部分はない.
example.yaml
*** qcli.yml	2014-12-16 11:43:41.000000000 +0800
--- /Users/uranusjr/Desktop/qcli.yml	2014-12-31 11:28:08.000000000 +0800
***************
*** 4,8 ****
  project:
      sources: "src/*.cpp"
      headers: "src/*.h"
!     qt: core
  public_headers: "src/*.h"
--- 4,8 ----
  project:
      sources: "src/*.cpp"
      headers: "src/*.h"
!     qt: core gui
  public_headers: "src/*.h"

unified

  • headerhunk(ブロック)でファイルの一部分を表す.
  • headerはhunkの位置情報を表す.
    • @@ ~ @@で囲まれた位置情報を書く.
    • ***で始まる行が1つ目、---ではじまる行が2つ目のファイルに対応し、hankの起点,終点の各ファイル内での行数を表す.
      • context 記法では起点終点の行数を表示していたが、こちらはhank自体の行の長さを表す.
  • 2つのファイル間で異なっている行には先頭に表示文字とスペースをつける.
    そうでない行にはスペース2つでマークする.
    • +: 1つ目のファイルのみに存在
    • -: 2つ目のファイルのみに存在
example.yaml
--- qcli.yml	2014-12-16 11:43:41.000000000 +0800
+++ /Users/uranusjr/Desktop/qcli.yml	2014-12-31 11:28:08.000000000 +0800
@@ -4,5 +4,5 @@
project:
    sources: "src/*.cpp"
    headers: "src/*.h"
-    qt: core
+    qt: core gui
public_headers: "src/*.h"

zennでは(執筆当時)

https://zenn.dev/zenn/articles/markdown-guide#コードブロック

zenn(および利用しているPrism.js)では上にあげた記号がdiffとしてDOMに変換されます.
他の言語同様にdiffとしてcode fence に指定してあげるとこのdiff形式が使えます.
また、最近のアップデートでdiff jsなどと指定してあげることで、diffと他言語のシンタックスの両方を適応できます.
(使えるはずです、不備があったらすみません......)

今までは差分をとると言語自体のシンタックスハイライトが効きませんでしたが、これからは気兼ねなく利用できますね!

おまけ

今回zennに入った diff language 記法とcontributeについてです.

https://twitter.com/zenn_dev/status/1353519833648820224

もともと zenn-editorのissueにあったもので、前々から「これならcontributeできそうかも」と思っていました.
簡単にやっていることを説明すると、
"一度生成されたdiffのhtmlを正規表現で元の文章に戻してから同時に指定された他の言語でシンタックスを当てて、再度diffとしてhtmlのタグを付ける"
という感じになっています.
結構力技ですね......
(prismjsにdiffにこのpluginがあるので、zennにはそれを修正して正しく読み込ませるだけでしたが......)

2026年現在、運営がクラスメソッドさんに変わるなど様々な変更はありましたが、issueなどで要望を出したり、Pull Request を送ったりすることは出来ると思います. 貢献したい方は、その時々の規約を確認しつつコントリビュートできると良いのではないでしょうか.

Discussion

kkiyama117kkiyama117

補足ですが、見た限りZennで使う場合は、CSSの当たり方的にunifiedが一番よさそうですね

YutaUraYutaUra

参考になりました!そもそもdiffをかけることを知らなかったので、有効に使わせていただきます!

kkiyama117kkiyama117

追記: [Zenn のシンタックスハイライトが PrismJS から Shiki へと変更されました] (https://info.zenn.dev/2026-01-14-update-syntax-highlighter) が、引き続き diff + 他言語 のシンタックスハイライトが当たるようになっていると思われます。

この記事のシンタックスハイライトの当たり方を見ながら、適切な diff を選択するのがよいと思われます