端末制御シーケンスの解釈
端末の出力データは、単に装飾された文字列の集まりではありません。
画面上の状態を逐一書き換える命令のストリーム(命令列)です。
キャリッジリターン(\r)は行頭へ戻って既存の文字を上書きし、エスケープシーケンスは文字を表示することなくカーソルを移動させ、TUI ツールは裏画面へ切り替えて画面全体を再構成します。
エスケープシーケンスを単に正規表現などで消去してしまうと、最終的な画面表示を復元するための操作情報が失われてしまいます。
console2svg では、これらの命令を 状態付きパーサー(AnsiParser)によって解釈し、端末の仮想画面バッファである ScreenBuffer へ忠実に反映します。
ストリームの読み取り境界をまたぐシーケンスの保持
Section titled “ストリームの読み取り境界をまたぐシーケンスの保持”OS のパイプ読み取り境界は、エスケープシーケンスの区切りとは一致しません。
ESC や CSI(Control Sequence Introducer:ESC [ で始まる端末制御コマンド)の途中でデータが途切れて届くケースが日常的に発生します。
AnsiParser は内部にステートマシンを持ち、未完了のシーケンスが存在する場合はその途中の状態をバッファに保持し、次回呼び出された Process メソッドの中で後続のバイト列と結合して解析を再開します。
また、OSC(Operating System Command)や DCS(Device Control String)のように、画面描画を伴わないメタデータストリームに対しても、終了デリミタまでを確実に読み飛ばす制御を行います。
DEC 固有の特殊グラフィックス文字セットに対しても状態を管理し、適切な罫線文字へとマッピングします。
メモリ割り当てを避けるスパンベースの CSI 解析
Section titled “メモリ割り当てを避けるスパンベースの CSI 解析”CSI で渡されるパラメータ(行番号、列番号、カラーコードなど)の解析において、string.Split による文字列の分割や配列の確保は一切行いません。
すべての数値解析は入力データのメモリ領域を直接指すスパン(ReadOnlySpan<char>)上で行われます。
パラメータの個数をあらかじめカウントし、16 個以下であればスタック領域上のメモリ(stackalloc int[16])を利用して解析結果を格納します。
パラメータが 17 個以上におよぶ極端なケースでのみ ArrayPool<int> から配列を借用し、処理終了後に直ちに返却します。
一般的なカーソル制御や文字装飾は数個のパラメータで構成されるため、シーケンス解析に伴うヒープ割り当てをゼロに抑えています。
画面操作とオルタネートスクリーンの再現
Section titled “画面操作とオルタネートスクリーンの再現”実装されている CSI コマンドは、カーソルの相対・絶対移動、画面消去、行消去、文字や行の挿入・削除、スクロール領域の制限(マージン)、タブ移動、カーソル位置の退避と復元(Save/Restore Cursor)など多岐にわたります。
さらに、vim や htop などのツールが使用する オルタネートスクリーン(作業専用の裏画面バッファ)の切り替えにも対応しています。 オルタネートスクリーンは、シェルの履歴が残るプライマリ画面とは完全に独立したバッファとして保持されます。 これにより、TUI ツールが終了して元のシェルプロンプトへ復帰した際にも、裏画面のゴミがシェルの履歴と混ざり合うことなく、本来の画面遷移を再現できます。
SGR による文字スタイルの解決
Section titled “SGR による文字スタイルの解決”文字の装飾や色指定は、SGR(Select Graphic Rendition:文字属性を変更するコマンド群)によって解決されます。
太字(bold)、薄暗い表示(faint)、斜体(italic)、下線(underline)、点滅(blink)、反転(reverse)、取り消し線(strikethrough)、二重下線などの多彩な属性を TextStyle レコードへ蓄積します。
カラー指定に関しては、標準の 16 色、xterm 256 色パレット、そして 24 ビット Truecolor(1677 万色)に完全対応しています。
セミコロン区切り(38;2;R;G;B)とコロン区切り(38:2::R:G:B)の双方が混在する拡張カラー記法も、単一の正規化されたカラー表現へと統一します。
また、前後のセルで同一のスタイルが継続する場合は既存の CellStyle インスタンスをそのまま再利用し、余計なオブジェクト生成を抑制します。
Unicode 全角文字と結合文字のセル配置
Section titled “Unicode 全角文字と結合文字のセル配置”テキストエディタやプログラミング言語が扱う UTF-16 のコードユニット数と、端末画面における文字セル(桁数)は一致しません。 絵文字などのサロゲートペアは、セルへの配置前に 1 つの書記素クラスタとして扱います。 また、ゼロ幅文字(Zero-width joiner など)はカーソル列を進めず、結合文字や異体字セレクタは直前のセルへ統合します。
全角文字(東アジアのいわゆる Wide 文字)は画面上で 2 列分のセルを占有します。 console2svg では、先頭のセルに文字実体を配置し、続く右隣のセルを継続セル(Continuation cell)としてマークします。 この 2 セル構造をエミュレーター内で維持することで、後段の SVG レンダラーへ渡した際に全角文字の後続テキストが 1 列分ずれてしまう表示崩れを完全に防いでいます。
ScreenBuffer による中間表現の共通化
Section titled “ScreenBuffer による中間表現の共通化”パースを完了した後のレンダリング工程(静止画 SVG、アニメーション SVG、PNG、動画)は、生の ANSI エスケープシーケンスを二度と解釈しません。
解釈済みの文字、色スタイル、セル幅、カーソル状態、画面バッファを統合した ScreenBuffer を唯一の中間表現として共有します。
パーサーの責務とレンダラーの責務をこのバッファ境界で完全に切り離すことで、どのような出力形式であっても同一の表示結果が得られる設計を担保しています。