世界を動かす技術を、日本語で。

差分のレンダリングについて

概要

Pierre Computer Companyは、大規模なコードレビューの課題を解決するために CodeView コンポーネントを開発。 従来の 仮想化技術 の限界を突破し、パフォーマンスとユーザビリティを両立。 Inverse Sticky Technique によりネイティブなスクロール体験と空白領域の解消を実現。 DiffsHub で実際に大規模なdiffを高速に閲覧可能。 開発者向けにはnpmパッケージ @pierre/diffs としても提供。

Pierre Computer Companyによる大規模コードレビュー体験の革新

  • Pull Request のレビューにおける従来ツールの課題

    • 小規模・中規模の変更には問題なし
    • 大規模な変更では、ファイルごとに読み込みが必要・ナビゲーションの遅延・パフォーマンス低下が発生
    • レビューツールの限界によるワークアラウンドの必要性
  • Pierre Computer Company のアプローチ

    • コード本体やdiff表示の体験を「当たり前に動く」ものへ
    • レビューの周辺機能(ワークフロー、CI、コラボレーション)に集中できる環境の提供
  • Diffs および CodeView コンポーネントの誕生

    • 最初はFile/FileDiffコンポーネントのみ
    • パフォーマンス課題への迅速な対応:
      • シンプルな仮想化機能
      • シンタックスハイライト処理をワーカースレッドへ移動
    • O(n×m)の複雑性やメモリ使用量増大、仮想化による空白問題(blanking)などの根本課題
  • CodeView の設計思想

    • 「どんなdiffでもレンダリングできる」を目標
    • ブラウザやメモリの物理的限界はあるが、実用上ほぼ全てのdiffを即時表示可能
    • DiffsHub.comで実際の挙動を体感可能

ブラウザでのdiffレンダリングの本質的な難しさ

  • 表面的には「テキスト表示」だが、実際は多機能化による複雑性増大

    • シンタックスハイライト、行番号、コメント、テーマ、分割・統合表示、カスタマイズ性など
    • 各機能がDOMの肥大化・処理負荷・レイアウト複雑化を招く
  • 主な技術的課題

    • レンダリング :DOM要素数増加によるブラウザ負荷
    • 処理 :ファイル・diff単位の処理が膨大に
    • メモリ :大規模データ構造の保持によるメモリ圧迫・GC頻発

仮想化(Virtualization)技術の進化

  • 仮想化の基本:表示範囲周辺のみを描画し、DOMを最小化

    • メリット:メモリ・レイアウト・ペイントコストの削減
    • デメリット:スクロール高速化や大きなジャンプ時に空白領域(blanking)が発生しやすい
  • 代表的な仮想化手法

    • ネイティブスクロール+推定高さ
      • ブラウザのスクロールバーやアクセシビリティを活かせる
      • 空白問題が発生しやすい
    • 固定コンテナ+requestAnimationFrame
      • 空白は出ないが、JS負荷増大・スクロールのカクつきリスク
      • Safariなど一部ブラウザの制限にも注意
    • 完全カスタムスクロール
      • スクロール体験の完全制御
      • 実装・保守コストが非常に高い

Inverse Sticky Techniqueによる革新的仮想化

  • Inverse Sticky Technique の概要

    • スクロール時、レンダリング領域の「下端」がビューポート下端に、「上端」が上端に吸着
    • レンダリング範囲内ではネイティブなスクロール体験を維持
    • JSが遅延しても空白領域が発生しない
  • 技術的ポイント

    • CSSのsticky機能を逆転活用
    • stickyのtop/bottomオフセットを動的に計算し、(contentHeight - viewportHeight) * -1 で制御
    • レンダリング更新がフレーム単位で追いつかなくても、空白を出さずに滑らかな表示を維持
  • メリット

    • ネイティブスクロールの快適さと仮想化の効率性の両立
    • TauriなどWebKit環境でも高いユーザビリティ
    • 大規模diffでも即時・快適に閲覧可能

DiffsHubとnpmパッケージの提供

  • DiffsHub.com で大規模diffのリアルタイムプレビュー体験

    • GitHubの任意のパブリックdiffを「github」を「diffshub」に置き換えるだけで閲覧可能
  • npmパッケージ @pierre/diffs

    • 開発者向けにCodeViewコンポーネント等を提供
    • ドキュメントも充実

まとめ

  • Pierre Computer Company は、従来のコードレビュー体験の限界を技術で突破
  • CodeViewInverse Sticky Technique で大規模diffの閲覧パフォーマンスとユーザビリティを両立
  • DiffsHubnpmパッケージ で実際の活用・導入が容易
  • 今後も開発者体験の向上を追求する姿勢

Hackerたちの意見

CodeViewの作り方について、ちょっと技術的な深掘りをしてみたよ。ブラウザで巨大なサイズのdiffを描画できるレビュー面なんだ。

大ファンです :)

機能リクエストです。git diff --color-movedは、移動したコードのチャンクを色で表示します。https://diffs.com/docsを見たけど、これをサポートしてるかどうかはっきりしないので、ぜひ追加してほしいです :)

他に苦しんでる人がいたら、devtoolsのコンソールにこれを貼り付けてみて:document.getElementsByTagName('main')[0].style.margin = '0 auto';

笑った - ありがとう!

ありがとう、彼らはどうやってこんな生活をしてるんだろう。

作者がレイアウトを可愛くしようとする時、常にリーダーモードがあるよね。

左揃えのコンテンツカラムに対する反応には驚いた。結構一般的だと思ってたけど、昔ほど一般的ではないかもね。プレゼンテーションの問題を一つ挙げるとしたら、フォントサイズかな(12pxは明らかに小さすぎる)。それか、等幅フォントの使用(本文には全然向いてない)。ちなみに、HNもこの3つの問題のうち2つを抱えてるよ。

もっとdiff生成のロジックについて話してほしかったな。テキストの描画の最適化よりもね。実際の描画を扱わない私の意見だけど、何が変わったかをどう見せるかについて、もう少し深く掘り下げてほしい。ここには改善の余地がたくさんあると思う。例えば、「ここでホワイトスペースが変わった」みたいに、実際のコード変更がない場合とか、「この大きなインポートリストが変わったけど、コードフォーマットでリストが別の行に折り返された」みたいなこと。GitLabなんかはこれにうまく対処できてないし。追加されたインポートを強調するクリーンなdiffが見たいな。大きなインポートされたシンボルや関数に1行追加しただけで、10行も変更が出るのはちょっとね。

次の大きなプロジェクトの一つは、実はセマンティックdiffをサポートすることなんだ。これが、君がここで求めていることにもっと適していると思うよ。今は普通のgitパッチファイルを使うか、ファイルの2つのバージョンから生成するだけなんだ。

テキストのレンダリングに関する最適化よりも。彼らのこのトピックに対する見解は、この記事のテキストレンダリングの選択やウェブサイトの一般的な美学を考えると、かなり意見が強いように感じるだろうね。

ほとんどのプロジェクトはtree-sitterから始まって、言語ネイティブのパーサーに切り替えるんだよね。どちらにしても、自分で解決するものじゃないし、言語特有の実装を見つけてフロントエンドで何メガバイトものWASMを読み込むか、バックエンドで生成するかって感じだよ。difftasticやsemanticdiffみたいなプロジェクトがたくさんあるし、もちろん「関数名が変更されました」みたいな情報を提供できるよね。30行の+newName -oldNameを見せる代わりに。

ちょっと関連するけど、DiffsHubをブラウザ拡張にすることを考えたことある?プライベートなdiffも提供できるように。私もコードをブラウザ拡張に移植したことがあるから、基本的なコンセプトはうまくいくと思うよ。

スクロールのフレームレートがスムーズでなくても、スクロールがスムーズに感じる必要はないという理論には反対だな。モバイルでは、やっぱりスムーズでないとダメだよ。モバイルでのスクロールは、なんかイマイチな感じがする。指に合わせて60Hz、あるいは120Hzでスムーズにスクロールできる環境に慣れちゃったから、今のは全然ダメなんだよね。これを聞くのはフラストレーションが溜まるのは分かるけど、モバイルデバイスの性能が低いことも影響してるし、まあ、そういうことだね。

デスクトップでもすごく重要だし、ノートパソコンでも同じことが言えるよね。macOSみたいにデフォルトでスクロールをスムーズにしてくれるプラットフォームでは特にそうだけど、WindowsやいろんなLinuxディストリビューションでも、ネイティブスクロールがカクカクしてるとすごく目立つし、カジュアルなユーザーでもイライラするよ。

スクロールのフレームレートがスムーズでなくても、スクロールがスムーズに感じる必要はないという理論には反対だよ。もしかしたら、私が言おうとしていたことを誤解しているかもしれないね。120Hzのデバイスで120Hzのスクロールを目指していたから、私にとってはその仮想化技術の一つが受け入れられなかったんだ。それが新しい解決策(インバーススティッキーテクニック)を考えるきっかけになったんだ。CodeViewは、Javascriptが120Hzに追いつく必要なく、ネイティブフレームレート(120Hz)でスクロールを更新できるシステムを使っているよ。もしhttps://diffshub.comでスクロール中にカクついているのを見ているなら、もっと詳しい情報(デバイスやdiffのリンクなど)を教えてほしいな。それは私たちの体験とは全然違うから。

速くて使いやすいツールを作っている人たちを見るのは本当に嬉しい!diffshubを使うのは素晴らしい体験だよ。作ってくれてありがとう、そして素晴らしい記事もありがとう!(「そこまで」行ったよ)

感謝します、ありがとう!

面白い記事だね。開いた時は最後まで読むとは思わなかったけど、文章がすごく分かりやすくて読みやすかった。最後には、コードのdiffレンダリングを解決しようとするその技術と忍耐に感心したし、GitHubの人たちも同じ努力をしてくれたらいいのにと思ったよ。余談だけど、こういうエージェンティックな使い方がもっと増えていくと思うし、モデルがいろんな可能性を試す能力は本当に大きなギフトだね。

ありがとう、優しい言葉に感謝!あんまり書かないから、これを書くのは結構大変だったよ!

さあ、GitHubはこのブログにエージェントを向けて「これを実装して」って言えるね :)

ディフのパフォーマンスを最適化するためのエンジニアリングを見るのは面白いね。今、CADモデルツリーでディフを生成するFreeCADのワークベンチを作ってるんだけど、ボトルネックはちょっと違うけど、必要なら君の最適化のいくつかを後で実装できるかも(例えば、遅延構文ハイライトとか)。私の主なボトルネックは、リポジトリ内のすべてのオープン+変更されたドキュメントに対して最初に完全なディフを行うことなんだ。ドキュメントのプロパティの保存方法のせいで、ファイルに意味のある変更があるかどうかはフルディフを計算するまでわからないからね(FreeCADはドキュメントを保存するかもしれないけど、意味のある変更はないかもしれないし)。[0] https://github.com/eblanshey/HistoryWorkbench

逆スティッキー技術の意味がよくわからない。スクロールが速すぎると体験が壊れちゃうし(コンテンツがスクロールしない)、少なくとも私には、一瞬のブランクよりももっと邪魔に感じるんだ。もしかしたら、ブランクに慣れすぎてるのかも。それに…ブラウザはトリックなしでディフをレンダリングできるべきじゃない?長いページに対してブラウザの仕事はそんなに大変なの?それとも、これに対して最適化してないだけ?それとも、仮想化には他に理由があるのかな(例えば、メモリ使用量とか)?

うまく説明できてなかったかもしれないけど、レンダリング時間は常にフレームバッファ内に収める必要があるんだ(60Hzで16.6ms、120Hzで8.3ms)。普通の状況下では、たまにフレームバッファを超えても、オーバースクロールがあればスティッキーの境界には達しないよ。達するのは、スクロールがかなり大きなジャンプをする速度のときだけだね — 大きくて速すぎて、何を見ているかの基準がわからなくなることが多い。結局、スクロールはJSとは別のスレッドで管理されてるから、スクロールバーでopt+clickをすると、JavaScriptが追いつけないジャンプをすることになるんだ。フレーム時間内にいてもね。サファリに関しては、requestAnimationFrameが60Hzに制限されているのに、スクロールがGPUで120Hz合成されている場合、60HzのDOM更新で120Hzのスクロールを維持する唯一の方法なんだ。

うーん、ブラウザがこれをネイティブでできるべきだっていうのは確かに良いポイントだね。DOMはすでにコンテンツを保持してるし、ブラウザはビューポート内で何がレンダリングされるかを管理してるから。

それに、Ctrl+Fでの検索をちゃんとサポートできるか、めっちゃ疑わしいんだよね…。それと、ブラウザはスタイルがちょっとシンプルで、JSがなければ、差分をちゃんと表示できるはずだよ。あるテレビシリーズの完全なトランスクリプトが入った6.3MiBのHTMLファイルがあるんだけど、フォーマットはこんな感じだよ。キャラクター#1:「まあ、面白かったね。」キャラクター#2:「はぁ…」キャラクター#1:「手伝おうか。」キャラクター#3:「イエーイ!」[ドン!](これは小さな段落の区切りを作って、シーンの切り替えを示すんだ)。これでスクロールも検索も問題なくできるよ。

大きな差分の問題は、たいてい人間にあるんだよね。大きな差分はレビューするのがすごく難しいから、大きな差分を理解するのに役立つツールがもっとあればいいなって思う。