mdmark

差分を表示する仕組み

取り出し元から 2 つのリビジョンの本文を受け、行と文字を比べて描く流れと、埋め込んだ HTML の切り替え

使う側が渡した取り出し元から、読む人が文書の変更を色分けで読めるようになるまでの考え方。 手順は 差分を表示する。

取り出し元は使う側が持つ

mdmark は Git や YAML の読み方を持たない。 使う側が、リビジョンの一覧を返す revisions と、リビジョンごとの本文を返す content を持つ取り出し元(DiffSource)を書き、mount の diffSource に渡す。 リビジョンは文書の変更の 1 回分で、id・at と、任意の label・author を持つ。

mdmark は取り出し元を、読む人が差分を開いたときだけ呼ぶ。 mount を呼んだだけでは呼ばない。

開いてから描くまでの流れ

  1. 読む人が、題の後ろに付いた「変更を見る」のボタンを押す。題を残して本文を隠す
  2. revisions を呼び、リビジョンの一覧を受ける。形が合うかを確かめる
  3. 一番新しいリビジョンを後、その 1 つ前を前に選び、content をそれぞれ呼ぶ
  4. 2 つの本文を比べ、差分の行を描く
  5. 読む人が前と後を選び直すたびに、選んだ 2 つの本文を呼び直して描き直す

リビジョンが 1 つだけのときは、比べる相手が無いため content を呼ばず、その旨を知らせる。 取り出し元の待ちが 1 秒を過ぎると読み込み中の枠を出し、2 秒を過ぎると回る表示を足す。 待つ間に選び直す・閉じる・update・destroy があると、前の待ちの結果は描かない。 待ちを時間で打ち切るかは取り出し元が決め、打ち切るときは reject で返す。

行と文字の比べ方

比べるのは、描いた結果ではなく Markdown のソースの行。

  1. 2 つのソースを行ごとに比べ、前にだけある行を削除した行、後にだけある行を追加した行、両方にある行を同じ行にする
  2. 変わった塊の中で、削除した行と追加した行を前から順に対にする
  3. 対の行を語ごとに比べ、変わった語だけを濃い色にする

語の区切りは Intl.Segmenter(ja・word)で決めるため、日本語の文も語の切れ目で比べる。 Intl.Segmenter を持たないブラウザでは、1 文字ずつ比べる。

削除した行は赤、追加した行は緑で出す。 色が見分けられなくても読めるよう、行頭に -・+ の印を置き、読み上げの名前に「削除した行」「追加した行」を持たせる。 色は --mdmark-color-diff-* の CSS 変数で決まり、使う側が上書きできる(テーマを上書きする)。 2 つの本文が同じときは行を出さず、変更が無いことを知らせる。

埋め込んだ HTML

差分の中の <!-- html --> 〜 <!-- /html --> の範囲は、範囲の右端に「HTML」のラベルを置く。 既定はソースの行と文字の色分けで、本文のほかの行と同じように読める。

前か後で中身が変わった範囲だけ、「ソース」「プレビュー」のボタンを持つ。 「プレビュー」を押すと、範囲の前と後の HTML を、文書に描くときと同じ埋め込んだ HTML の描き方(script 要素とイベントの属性を落とす)で並べる。 プレビューでも、書いたスクリプトは動かない。 「ソース」を押すと、行の色分けに戻る。

前か後にしか無い範囲は、無い側に「{前か後}のリビジョンには、この範囲がありません」を出す。

ミニマップ

長い差分は、画面の右端のミニマップで送れる。 差分の行を出している間、差分の一番近いスクロールする祖先(無ければ画面)の右端に、その高さいっぱいの縦の帯を貼り付ける。

帯は読み上げの対象にしない。 差分を閉じる・選び直す・destroy で、帯と受け取りを外す。 印刷では帯を隠す。

取り出せないとき

revisions か content が reject したか、返した値がリビジョンの形に合わないときは、例外を投げず、差分の場所に知らせの枠を出す。 文書の本文は描いたまま残り、読む人は本文に戻れる。

起きたこと 差分の場所
revisions を取り出せない 差分の場所の全体を、「変更の履歴を表示できません」の知らせの枠にする
content を取り出せない 選ぶ欄を残し、差分の行の場所を「このリビジョンの本文を表示できません」の知らせの枠にする。別のリビジョンを選び直すと差分を描き直す

取り出し元の関数の名前・文書の id・リビジョンの id・元のエラーは console.error に出る。