🔖

edrs開発ログ 2026/04/10

に公開

仮想挿入テキストの基盤を置いた

唐突に始めます。
最近作っているcrateの開発ログです。

edrsとは

Rustで書いているターミナルエディタです。vimライクな操作感を軸に、LSP統合・マルチスクリーン分割・独自バッファ構造などを自前で実装しています。
自分が欲しいだけなのでまだ非公開ですが、そのうち出します。
また、edrsそのものの紹介記事もそのうち出します。

TL;DR

  • additive-uiブランチで作業中
  • バッファに存在しない行を挿入する仕組みを作成

additive-ui ブランチの背景

もともとの描画はシンプルな構造でした。バッファの内容をそのままビューポートに切り出し、毎フレーム全体を再描画する方式です。ステータスラインなど一部は差分描画できていましたが、バッファ表示部分は丸ごと流し込みでした。

この構造には根本的な弱点があります。バッファに存在しないテキストを画面に表示できないことです。inlay hintsやCodeLensのような「表示上だけ存在する情報」を差し込む余地がありません。

そこで feature/additive-ui ブランチで描画レイヤーを再設計しました。

  • 描画内容を ScreenBufferVec<Cell>)に一度書き出す
  • 前フレームのバッファとdiffを取り、変化があったセルだけをcrosstermで流す
  • これにより、バッファ内容の上に任意のオーバーレイを重ねられる構造になった

このブランチの守備範囲は広く、フローティングウィンドウ・通知・ポップアップなど「ベース表示の上に乗るもの全般」を対象にしています。

昨日までの状態

Cellベースバッファへの移行が完了した時点で、以下が動いていました。

  • Screen分割線の描画
  • 各セグメントへのバッファ内容表示
  • 診断情報の注釈表示(下線など)
  • ステータスライン・タブ名行

ただし「バッファ内容」はあくまで実際にバッファに存在する行のみ。仮想的な行を挟み込む仕組みはまだありませんでした。

今日:バッファ行と表示行のマッピング導入

inlay hintsやCodeLensを表示するためには、バッファの行番号と画面上の行番号がズレる状況を扱える必要があります。仮想行を1行挿入すれば、それ以降の表示行はバッファ行より1つ多くなります。

この対応として LineMapping を導入しました。

表示行ごとに、その行が何者かを表すエントリを持ちます。

LineMapping::entries[i] → 表示行 i 行目の情報

エントリは2種類のバリアントです。

#[derive(Debug)]
pub struct LineMapping {
    entries: Vec<LineMappingEntry>,
}

#[derive(Clone, Debug)]
pub enum LineMappingEntry {
    BufferLine(usize),
    VirtualRow { original: usize, text: String },
}

BufferLine は通常のバッファ行で、インデックスはバッファ上の行番号です。VirtualRow は仮想行で、original はこの行が紐付くバッファ行のインデックス(その行の直後に挿入される)を示します。

タブごとに独自のmappingインスタンスを持ち、必要に応じて再構築されます。

この設計により、描画ループは entries を順に走査するだけでよくなります。BufferLine なら従来通りバッファから内容を引き、VirtualRow なら保持しているテキストをそのまま描画する、という分岐です。

また、診断表示など既存の座標変換ロジックも LineMapping ベースに統一しました。

現時点での見た目

変わっていません。

VirtualRow を追加する側の仕組みがまだないので、mappingは常に BufferLine のみで構成されています。土台だけ置いた状態です。

次のステップ

まずinlay hintsのLSP接続です。inlay hintsはバッファ行の途中にインラインで差し込む表示なので、LineMapping ではなく ScreenBuffer へのセル挿入で対応します(後続セルを押し出す関数はすでにあります)。LSP側から受け取った情報を描画パスに流し込む部分が残っています。

VirtualRow が活きるのはその次のCodeLens対応です。行番号を持たない行をバッファとは独立して表示する用途に使います。

この記事はclaude7割、EwdErna3割の労力で書きました。

Discussion