Zennとdiff記法について

5 min read読了の目安(約4500字 2

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

はじめに

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を、コードブロックの解析にはprismjsを組み合わせて利用しています.

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

unified diff の書き方

さて、肝心の diff の書き方について簡単にまとめておきます(そもそも本来は生成されるものですが).
合わせて、prismjsから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(および利用しているprismjs)では上にあげた記号が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にはそれを修正して正しく読み込ませるだけでしたが......)

僕はたまに暇な時にissueを見て

  • 「確かにこれがあったら便利だな」と思ったもの
  • zenn-editorの範囲で完結しそうなもの

を見かけたらちょっとやってみる位の緩いスタンスでcommitしているのですが、memberの方々がお忙しい中でもreviewや検討をしてくださるので楽しくcontributeできています.
(memberのお二方には本当に感謝しかありません.)

まだzenn-editorにはcontributing guidelineはないのですが、

と通常のOSSへのcontributionのマナーなどに気をつければ特に問題なくcontributeできるかと思います.

今後のzennの運営方針によってはどうなっていくかは分かりませんが、ユーザーから運営への距離が近くgithubから簡単に要望やバグ報告が出来るので皆さんも是非積極的にcontributeしてみては如何でしょうか?

この記事に贈られたバッジ