mitaki28.info blog

Markdown の差分更新を実装してみた

はじめに

何番煎じかわかりませんが,Markdown のライブプレビューを実装してみました.

使い方

  • 左のテキストエリアに文字を入力すると自動的に右の画面にプレビューが表示されます.
  • 文法はいわゆる Markdown です.
  • 画面同期: 入力のたびに編集位置と対応する変換後の位置に自動的にスクロールします.
  • 構文ハイライト:
    function sqr(x) {
      return x * x
    }
    
  • 自動保存: 編集中の内容は自動的に保存され,次の起動時に復元されます.
  • 数式: $\LaTeX$ 記法の数式を利用できるように独自拡張しています.$ax+b$のように$で囲まれた文章は $ax+b$ のような,インライン数式になります.
    $$
    \sum_{i=1}^n=\frac{n(n+1)}{2}
    $$
    
    のように,$$で囲まれたブロックは $$ \sum_{i=1}^n=\frac{n(n+1)}{2} $$ のようなディスプレイ数式になります.

作った動機

この手のライブプレビュアーは山ほどあるんですが,ほとんどのものがマシン性能にものを言わせて毎回フルレンダリングしてるせいで,数式みたいにレンダリングに時間が掛かるものが文章中にあるとガクガクになるのをなんとかしたかった.

使ってる技術について

レンダリングを削減するとなると,差分だけ更新しようという話になるんですが、最近巷で話題らしい react は木の差分更新を実装してるらしいのでその記事を参考に実装してみることにしました.(実装自体は読んでないので本当にあっているかどうかは知らない)

大雑把に言うと,commonmark.js という Markdown の AST を吐き出してくれるライブラリがあるので,変更のたびに各ノードのハッシュ値を計算して子の構造まで含めて完全に同じノードを適当に使いまわしています.あとは,なるべくDOMの変更が少なくなるようにヒューリスティックを使ったりしてます.

エディタ部分には Ace Editorを使っています.この辺は

あたりを参考にしていますが,ASTにコードの位置情報が保存されているため,従来のものより正確に同期をとれます.

課題

  • ぶっちゃけまだほとんどテストしてないのでバグる可能性が高い
    • 執筆中の大事なデータが消えたとかの責任は取れません
  • $a$, $b$, $c$, $d$, $e$, $f$ みたいにたくさんの数式が書かれている状態で先頭に新たに数式を追加しようとした場合,ひとつ目の$を入力した瞬間にすべての括弧の対応関係がずれるのでとんでもなくレンダリングに時間がかかる.
    • 対応関係の曖昧な記号を構文エラーにすれば解決できそう
  • ASTのパース自体は差分更新してないので,結局更新ごとに全文章を読んでる
    • パースも差分更新したいけど実装がつらすぎる.
    • 4000行ぐらいのテキストを編集してもリアルタイムにレンダリングできてるので,そこまでやる需要はなさそう.
  • Chrome 以外の環境でテストしてない