- 既存のHowlエディタの限界(開発停止、検索の遅さ、SSH非対応、ターミナル未対応)を理由に、自ら新しいTUIテキストエディタを開発した
- Helix、VS Code、Vim、Neovim、Emacsなど13種類のエディタを試したが、求めていた**操作感(Fingerspitzengefühl)**を満たすエディタはなかった
- 当初は個人向けに最小限の機能だけを実装し、性能・Unicode・多言語対応などは後回しにして段階的に拡張した
- 開発の過程で独自の正規表現エンジン、ファイルブラウザ、TUIベースのレンダリング、ターミナルバッファ統合などを自作した
- プロジェクト全体検索、シンタックスハイライト、キャッシュ処理、マルチスレッドの作業分配などで性能最適化手法を多数適用した
- 最終的に自分のワークフローに完璧に合うツールを完成させ、生産性とプログラミングの楽しさを取り戻したと語っている
既存エディタの限界と代替の模索
- 約10年間使ってきたHowlエディタの問題点が、自作に踏み切るきっかけになった
- 数年間開発が停止しており、自分でフォークを維持していたが、MoonScriptで書かれていたため踏み込んだ修正が難しかった
- プロジェクト全体のファイル検索性能が不足しており、作業の流れが途切れてしまっていた
- GUIエディタであるため、SSH接続によるリモート利用ができなかった
- 統合ターミナルがないため、外部コマンド実行時にライブな相互操作ができず、ANSIエスケープコードの大半も未対応だった
- Helix、VS Code、Sublime Text、Vim、Zed、Neovim、Emacs、Geany、Micro、Lite XL、Lapce、GNOME Builder、Kakouneなど13種類のエディタを試した
- それぞれ長所はあったものの、求める**操作感(Fingerspitzengefühl)**には届かなかった
- Helixを最も長く使ったが、1か月後には興味を失った
初期開発戦略
- 開始段階ではスコープを最小限に制限した
- 自分以外の利用者向け機能は除外し、設定はすべてハードコードした
- 性能最適化は後回しにし、
Stringベースのバッファから始めた
- Unicodeグラフェムの完全対応は見送り、
£記号が単一カラムを占めれば十分と判断した
- シンタックスハイライトは主に使う少数の言語だけをサポートし、それ以外は汎用的な区切り文字ベースのハイライトで代用した
- 2回目の試みでは簡単なTUIフレームワークを先に構築したが、時間が経つにつれてその大半を取り払い、より直接的で細かなアプローチへ移行した
Dogfoodingの実践
- エディタが単一ファイルを開いて編集・保存できる最小機能の閾値に達した後、次の3つを始めた
nanoの代わりに自作エディタを使い、システムファイル編集やメモ作成でも強制的に使用した
- 欠けている機能、バグ、異常動作、限界を見つけるたびに、プロジェクトの**
README.mdに記録**した
- いらだつほどの問題は即座に修正した
- この3つの実践により、作業量は月1時間から週に数時間へと増えた
- 全体で約10,000行あるコードのほとんどは、直近6か月以内に書かれた
カーソル操作
- カーソル操作は実装難易度の高い領域だった
ctrl + shift + leftのようなキー操作は利用者には当然でも、ロジック実装は複雑だった
- 高水準入力を原始的な演算の組み合わせとして実装することが重要な助言だという
- 例: 単語単位のバックスペース → 単語単位のカーソル移動 + 範囲選択 + 削除へ分解
- undo/redoを実装する際は、この3つの動作を1つのグループとして束ねる必要があり、そうすることで直感的な結果が保証される
- モーダルエディタがこうした原始演算を利用者に直接露出している理由も理解できた
ファイルブラウザ
- Howlのファイルブラウザは、他のエディタへ移行できなかった決定的な理由だった
- 即時更新されるファジーフィルタが非常に優秀で、1〜2回キー入力するだけで目的のファイルが見つかることがほとんどだった
- ファイルが存在しなければインラインで作成できた
~/を入力するとホームディレクトリへ自動切り替えされた
- メイン編集ウィンドウで開こうとしているファイルのプレビュー表示があった
- 他のエディタがファイルを開く問題をマウス依存、GTK標準ダイアログ、ファイル名推測などで解決していることへの不満があった
- 自作時にはLevenshtein distanceのような複雑な方式ではなく、単純な3つの基準で十分だった
- フィルタ文字列で始まるかどうか
- フィルタ文字列を含むかどうか
- 最も最近の更新/アクセス時刻
- 大文字小文字を無視したマッチングを許可しつつ、大文字小文字が一致した場合は順位を少し引き上げる
- 数万ファイルあるプロジェクトでも、2キー入力後には約95%の確率で上位2件以内に目的のファイルが入る
正規表現エンジン
- 正規表現はプロジェクト全体検索、シンタックスハイライト、バッファ内検索の3か所で使われている
- 既存の
regex-automataクレートではなく自作した理由
- Rustのraw string構文のような文脈依存のエッジケースに対応する必要があった
- プロジェクト自体が自前のスタックを構築して理解するための練習でもあった
- 初期実装では、パース用クレート
chumskyで正規表現構文を解析し、ASTを文字ごとに巡回する遅い方式だった
- その後、段階的に最適化を進めた
- 単一パスオプティマイザ: 繰り返し現れる文字マッチのグループを単一の
Stringノードに変換し、正確な文字列検索を行う
- 共通接頭辞の抽出: たとえば
hel[(lo)p]から共通接頭辞helを見つけ、その位置でのみマッチを行う → プロジェクト全体検索で大きな性能向上
- ASTウォーカをRustの動的呼び出しベースのthreaded code VMとして再実装
- threaded code VMを**CPS(Continuation-Passing Style)**形式へ変換し、各VM命令が後続命令をtail-callすることでコンパイラ最適化を活用
- Rustの遅い動的関数呼び出しをvtableルックアップなしでラップし、多くの正規表現命令のコード生成を数個の機械語命令まで縮小
- 可能な限り多くの正規表現命令をUnicodeコードポイントではなくバイト単位で実装し、UTF-8の設計のおかげでASCII向け最適化手法がマルチバイトコードポイントにも有効だった
- jump LUTチェーンへのコンパイルも試したが、ベンチマークではthreaded code比で20〜30%速い程度にとどまり、柔軟性が大きく損なわれたため採用しなかった
- 最終結果として、Rust向けで最も複雑なシンタックスハイライトでも、50,000行の自動生成バインディングファイルをクリーンな状態から10ミリ秒未満で全体ハイライトできる
シンタックスハイライトキャッシュ
- 当初は変更のたびにファイル全体を再ハイライトしていたが、大きなファイルでは性能低下が起きた
- オンデマンドのトークンハイライトキャッシュを実装した
- ほぼ同じ大きさのチャンク単位でトークンをハイライトする
- バッファに変更(damage)が発生したら、その位置と重なる、またはそれ以降のチャンクだけを無効化する
- 最も悲観的なケース(大きなファイルの中ほどを編集)でも、damage以前のハイライト状態は維持され、画面下方以降はハイライト情報が要求されないため処理不要となる
- 需要駆動(demand-driven)のアプローチなので、同じバッファの異なる部分を表示する複数パネルでも正常に機能する
プロジェクト全体検索
- 検索プロセスは4段階
- 現在のディレクトリから逆方向に
.git/ディレクトリを探してプロジェクトルートを決定する
- プロジェクトルート配下のすべてのディレクトリを再帰的に走査し、検索パターンをファイル内容にマッチさせる
- 各ヒットからファイルスニペットを抽出し、結果プレビューのためにシンタックスハイライトを適用する
- 現在のパスからの探索距離に応じて結果を順位付けする(近いファイルほど高順位)
- ビルドディレクトリなどを避ける既定のフィルタリング規則を適用する
- マルチスレッドで処理し、基本的なwork-stealing方式でスレッド間に作業を割り当てる
- すべてのスレッドが消費者であり生産者でもある特殊な構造で、終了検知の問題を解決した
- 待機中のスレッドがアトミックカウンタを増やし、そのカウンタがワーカー数に達し、かつ作業キューが空なら全体終了とする
- 正規表現最適化と現代的なSSDの速度により、Velorenのような大規模コードベースでも単純パターン検索はほぼ即座に終わる
- フレームグラフ上では大半がIOバウンドだった
- エディタ内で大規模コードベースを思考の速度で検索できることが、生産性に大きく寄与した
ターミナルエミュレータバッファ
- パネルベースのエディタでは、1つのパネルをターミナルウィンドウとして使う機能の利便性が高い
- ANSIパーサを自作しようとしたが、OSC52やKitty keyboard protocolなど最新のターミナルレンダリング機能の対応範囲は広大だった
- **
alacritty_terminal**クレートを利用し、Alacrittyターミナルエミュレータのエスケープシーケンスパーサとターミナル状態管理ロジックを再利用した
- その結果、
screen/tmuxの中核機能を置き換えられ、より豊富なエスケープシーケンス対応も得られた
レンダリング最適化
- TUIベースとはいえ、リモートのモバイル接続時の帯域は依然として重要だった
- ダブルバッファリング: ターミナル画面の内部コピーを二重に保持する
- 再描画時に前フレームと比較し、変更されたセルだけANSIエスケープシーケンスを出力する
- カーソル移動、スタイルモード変更などのシーケンスも本当に必要なときだけ出力する
- ほとんどのターミナルエミュレータ(Ghosttyを除く)では、エディタのターミナルパネルで大きなファイルを
catしたあとエディタを閉じるほうが、ホストターミナルで直接catするより速い
alacritty_terminalがホストターミナルへのstdoutバイト処理コストを遮断してくれるため
結論: 自分だけのツールを作ろう
- 自作エディタは自分のワークフローに完璧に合うツールとして定着した
- 自作エディタや自作ツールづくりが無意味な苦行だという通説に反対している
- 4つの利点
- 完全なカスタマイズ: 欲しい動作だけを行い、それ以上でも以下でもない
- 多様な技術の学習: 正規表現、ANSI、擬似端末(pty)、TUI設計、UTF-8の細部など汎用的に役立つ技術を深く理解できる
- 長期的な生産性向上: 自分のツールを完全に理解し、個人のワークフローに合った機能を組み込むことで、ツールとの摩擦を減らせる
- 純粋な楽しさ: 自己完結した問題を解き、その結果を指先で感じられる体験がプログラミングへの愛を呼び戻し、何年ぶりかでコードを書きながら満面の笑みを浮かべ、ひとりで笑ってしまう経験につながった
- テキストエディタでなくてもよいので自分だけのツールを作ることを勧め、難しい部分を統計的な箱(AIなど)に任せず、挑戦そのものを楽しむことを強調している
5件のコメント
実はこの文章でいちばん驚いたのは、この一語ですね。
Fingerspitzengefühl
Finger(指) + Spitzen(先端) + Gefühl(感覚)
指先で操作するときの感覚を表す言葉があるドイツ語は、本当に……はぁ……
「finger」もドイツ語だったんですね。英語だと思っていました……
同じ語群なので、基礎語彙はかなり共有しています。
ドイツ語は単語の組み合わせが無限にできるんですよね(笑)
Hacker Newsの意見
読んでいる間ずっと楽しかった。友人たちにも自分でテキストエディタを作ってみるよう勧めている
私は「Left」という自作エディタをもう10年近く使っている。最初は完璧ではなかったが、LeftでLeftを修正しながら育ててきた。毎朝、自分の手で作った道具を開くときの喜びは、その時間の20倍の価値を返してくれる
「人生で一度は家を建て、木を植え、エディタを作るべきだ」という言葉がある。私は最後のものから始めた
PicoLispベースのViスタイルエディタ Vip にある一文だ
私も最初から自分でテキストエディタを作ってみた。機能が多いので、LSP、tree-sitter、fzfのような外部ツールを積極的に活用した。
sucklessスタイルで、コードを少し直すだけでカスタマイズできるように設計した。
最初の数週間はバグだらけだったが、直すたびに少しずつ安定していった。私のプロジェクト hat を参照してほしい
何かおすすめのテキスト編集ライブラリはあるだろうか?
GUIが必須なので、フォントレンダラやグラフィックスコンテキストまで自分で扱う必要がある。
単純なコンソール用では私には使えず、GUIだけ作っても編集機能がないので、その両方が必要だ。
驚くことに、こうした要件を満たす純粋なAPI形式のライブラリはなかなか見つからない。
たいていは完成済みのエディタか、巨大なフレームワーク級のものだ。
単に大容量テキストファイルを高速に扱える基本的な編集エンジンがあればいいのだが
trolley のようなツールで、ghosttyベースのネイティブUIのように包むこともできる
代わりにSDLとSDL_ttfの組み合わせはかなり良い選択だ。SDL3_ttfでは文字列処理も改善されている
antirezの「kilo」エディタを再実装してみた。
元のコード と チュートリアル がよくできていて、ターミナルモードとC言語の基礎を学ぶのに素晴らしいプロジェクトだった
90年代にCOBOLとASMファイルのために自分でエディタを作った思い出がある。
シンタックスハイライト、高速バッファリング、スクリーンセーバーまで備えていた。
Pentium 120で動いていて、今のVSCodeより1000倍は速かった記憶がある
あの頃はHTMLタグを全部大文字で書いていた
この記事の主人公が作ったエディタは zte だ
「難しい部分を統計ボックスに押し込もうとする誘惑に抗え」という一文が本当に印象的だった
私も自分の自作エディタを使っている。他の人はあまり関心を持たないが、自分で作った道具から得られる価値は大きい
F5でリンクを開ける簡単な「ブラウザ」機能もある
Josh BarrettoはSuper Mario 64 GBA portを作った天才だ。彼のエディタならぜひ使ってみたい