🧠

JavaScriptでターミナルエディタを自作する:kilo.cポーティングの6年後レポート

に公開

JavaScriptでターミナルエディタを自作する:kilo.cポーティングの6年後レポート

「小さく作って深く理解する」エディタ実装記。

インストールの仕方

npm i -g kilo-editor

イントロダクション:なぜJavaScriptでエディタを作るのか?

「エディタの内部構造を理解したい」──これが動機でした。

kilo.cはC言語で書かれた極小エディタで、画面描画・入力処理・バッファ管理などが手作りで詰まっている最高の教材です。この“ミニマルで全部見える”感覚が、徐々に理解ができます。

では、なぜJavaScript?

  • 普段使いの言語で理解が「道具」を理解したい
  • Node.jsの標準API(stdin/stdout/TTY)でどこまでやれるか試してみたかった

6年前は「動いた!すごい!」で終わっていたのですが、あらためて眺めてみました。


技術的な工夫・ポーティングの肝

1. Cのポインタ/メモリ管理を、JSの抽象化で置き換える

kilo.cではバッファ操作や文字列処理にポインタ操作が登場します。JavaScriptでは直接メモリを触れないので、配列や文字列操作で代替します。

  • 行バッファ:erow(編集用の文字列配列)
  • 描画用バッファ:render(タブ展開などの見た目用配列)
  • 画面への描画バッファ:abuf(端末制御シーケンスをまとめて吐く)

2. Node.jsの標準機能で“素のターミナル”を操る

エディタの基本はRAWモードに入ってキー入力をダイレクトに扱うこと。Node.jsなら process.stdin.setRawMode(true) で実現できます。

  • readline.emitKeypressEvents() でキーイベント化
  • process.stdin.on("keypress", ...) で入力ハンドリング
  • process.stdout に ANSI エスケープシーケンスを吐き出して描画

「GUIを一切使わず、端末だけで操作する」感覚が気持ちよかったのを覚えています。

3. RAWモードと描画は“バッファリング”が命

C版と同じく、画面描画は逐次書き込みではなく、
abuf に集めてから一気に process.stdout.write で出力します。

この「バッファリング」戦略がないと、
画面がチカチカしたり描画が遅延したりします。


ソースコード解説(ハイライト)

※ 以下は“ここだけ読めば全体が見える”場所を抜粋して解説します。

1. メインループ:イベントで回るエディタ

kilo.jsではループを手で回しません。
keypress イベントが入力のたびに処理を呼ぶイベント駆動型ループになっています。

Kilo.enableRawMode();
if (typeof this.E.filename !== "undefined") {
  this.editorOpen();
}
this.editorRefreshScreen();
process.stdin.on("keypress", this.editorReadKey.bind(this));
process.stdout.on("resize", this.editorResize.bind(this));
  • RAWモードに入る
  • ファイルを読み込み
  • 初期描画
  • keypressで入力を処理
  • resizeで再描画

Cの無限ループと比べて、JSはイベント駆動のほうが自然です。

2. スクロールとレンダリング:rx/rowoff/coloffが肝

スクロールの基本は「カーソルが画面外に出たらオフセットを動かす」というもの。kilo.jsでは editorScroll() がそれを担っています。

  • rx(レンダリング後のX位置)
  • rowoff(縦スクロール)
  • coloff(横スクロール)

これらを毎回計算して論理カーソル位置と画面上の描画位置を同期します。

3. 画面描画:ANSIシーケンスの積み上げ

描画のコアは editorRefreshScreen()

  • カーソルを消す
  • 画面原点に移動
  • 行を描画
  • ステータスバー/メッセージバー
  • カーソル位置を復元して表示

この手順が、“端末UI”を成立させる最小セットです。

4. 検索機能:配列にヒット位置を積む

検索は「ヒットした位置を配列に積んで、左右キーで巡回する」方式。

  • sx/sy にヒット位置を収集
  • si に現在のインデックス
  • ←/→ で前後に移動

シンプルですが、エディタの“インクリメンタル検索”感が出てくるポイントです。

5. シンタックスハイライト:正規表現の力技

色付けは editorUpdateSyntax() で実装。
正規表現で特定パターンを色付けしています。

  • 数字 / 文字列 / コメント / キーワード
  • ANSIカラーコードで装飾

構文解析器なしでも、ある程度“それっぽく見える”のが面白いところ。


状態遷移図


今振り返って(6年後の視点)

当時の自分に教えたいこと

  • 構造化された状態管理を導入しておけば良かった(今見ると this.E が巨大)
  • “描画”と“ロジック”を分けると将来が楽(UI差し替えが容易)

当時は「動いたら勝ち」だったけど、今は「読みやすさと保守性」も勝ち条件になっている気がします。


まとめ:車輪の再発明でしか得られないもの

エディタを作るのは、“車輪の再発明” です。
でもその過程で手に入るのは、

  • 文字列の扱い方
  • 端末の描画モデル
  • 状態管理の難しさ
  • “UIを手で描く”という体験

が手に入ります。。

もし「エディタの仕組みに興味がある」なら、
自作してみる は最高の学習になります。

Discussion