よし、自分でテキストエディタを作ろう
Fine, I'll build my own text editor (dbushell.com)
要約
開発者は、現代のソフトウェア、特にテキストエディタの品質低下に不満を感じ、独自のテキストエディタ開発に着手しました。当初はCanvas要素でレンダリングを試みましたが、アクセシビリティやインタラクティブ性の問題に直面しました。次にcontenteditable属性を持つdiv要素を試したところ、ネイティブな機能(テキスト選択、アンドゥ/リドゥ)を活用できましたが、パフォーマンスの問題が見つかりました。最終的に、より長文のテキストに優れたパフォーマンスを示すtextarea要素が有望であると結論づけ、構文ハイライトなどの機能追加の可能性を探っています。
全文翻訳
Fine, I’ll build my own text editor!
[画像: No AI - Made by Human] 2026年9月1日 火曜日
「もうSublime Textのようなものは作られていない」という意見は多くの人に共感を呼びました。最近のソフトウェアはゴミ同然です。それが私に考えさせました。私はゴミを作るのが得意だ!
なぜ自分でテキストエディタを作れないのか?
VS CodeはMonaco Editorを基盤としていますが、これは<div>のスープ地獄です。長年私のIntel Inside™搭載Macは遅すぎて、VS Codeの波に乗り遅れていました。Apple Siliconを購入して、その問題は解決しました。もしそれが基準なら、私は多くの間違いを犯す余地があります。
Canvas
私の最初の実験では、すべてのレンダリングを<canvas>要素で行いました。
見た目では分かりませんが、CPUは毎秒60〜120フレームでその画像をレンダリングするために多くの作業を行っています。インタラクティブ性の欠如は、テキストエディタにとって明らかな問題です。
「最小限実行可能」な機能のリストを作成し、それらを実装しました。
- ポインターダウンでテキストカーソルを配置
- 矢印キーでテキストカーソルを移動
- 現在行をハイライト
- 入力してテキストを追加
- おしゃれなカーソルアニメーション
次のデモはインタラクティブです。クリックして入力してください。
Vimバインディングについて私に文句を言う前に:黙ってください、もっと差し迫った問題があります。Canvasは何も無料で提供してくれません。多くの望ましい機能の中で、私は以下を欠いています:
- テキスト選択
- 元に戻す/やり直し履歴
- 複数行ペースト
- オーバーフロー スクロール
最後の項目は重要です。カスタムの弾性スクロールバーを実装するほど人生は長くありません。私はずるをして、非表示の要素でネイティブブラウザのオーバーフローを使用することにしました。<canvas>のテキストと一致するようにサイズ設定された<div>があり、スクロール位置を使用して<canvas>のレンダリングオフセットを計算します。
それがどのように進んでいるかには満足していますが、同時に落胆もしています。なぜなら<canvas>は完全にアクセシブルではないからです。テキスト選択やその他の機能を追加し続けることはできますが、根本的なアクセシビリティの問題を解決しているわけではありません。
もっと良いアイデアがありました。
Content editable
<canvas>でテキストをレンダリングする代わりに、オーバーフローする<div>でネイティブにレンダリングし、contenteditable属性で編集可能にすることができます。その属性には、コードに最適なplaintext-onlyという値があります。すべてのコンテンツは単一のテキストノード内に保持されます。
<div
contenteditable="plaintext-only"
autocapitalize="off"
autocorrect="off"
spellcheck="false"
translate="no">
<!-- text goes here -->
</div>
spellcheckのような属性は、入力遅延のスパイクを避けるために無効にする必要があります。この修正を発見するのに何日かかったか推測できますか? 数日!
contenteditableを使用すると、ネイティブのテキスト選択や元に戻す履歴などが得られます。ブラウザによって、多くのアクセシビリティ機能が無料で組み込まれています。
Selection APIは、カスタムテキストカーソルのレンダリングを続けるために使用するメトリクスを提供します。::selectionが利用可能なので、それをスタイル設定することもできます。ネイティブのcaret-colorを不可視に設定しましたが、これはおそらく良くないでしょう。
contenteditableの手法は有望ですが、特定の文字数を超えると奇妙なパフォーマンスの問題に気づきました。ChromiumブラウザはWebKitや現在のFirefoxよりもパフォーマンスが良いですが、予測不可能です。
Textarea
プレーンテキストのcontenteditableの代わりに、単純な<textarea>は実行可能でしょうか? 短く言うと:はい。<textarea>は、長いテキストに対してはるかにパフォーマンスが高いことが判明しました。
この最後のデモでは、構文ハイライトも追加しました。
私の元の計画は、contenteditable要素にカスタム::highlightを使用することでした。<textarea>はCSSハイライトを使用できないため、サードレイヤーが必要になりました。デモ目的で、表示されている行にMicroLighterを適用するために、いくつかの<div>スープを追加しました。
編集: 新しいOpaqueRange APIが<textarea>のカスタムハイライトを解除するという話を聞きました — 素晴らしい!
編集2: そしてEditContext APIは<canvas>の入力を改善します。
CSSハイライトが多すぎることも別のパフォーマンスのボトルネックです。より堅牢な解決策は、Tree-sitterを使用して構文ツリーを生成し、それをウォークして表示されている行のみのハイライトを生成することでしょう。仮想スクロールを完全に回避できることを望んでいましたが、逆スティッキーテクニックを使用して改善できるかもしれません。または、編集するファイルのサイズがパフォーマンスの壁に当たらないため、contenteditableに戻ることもできます。
とにかく、順調に見えますよね?
機能の1%でテキストエディタの90%のようです。ここから先は、フクロウの残りを描くのはかなり簡単です。描き続けたい誘惑に駆られますが、タブインデントのようなすべての小さなことを考えてしまいます。今はタブキーを乗っ取ってスペースを2つ挿入しています…
上記のデモは最適化されておらず、完全にアクセシブルではありませんが、少なくとも負け組から始めているわけではありません。<canvas>でのレンダリングは悪夢だったでしょう。
このプロジェクトは、雨の日のためにしまっておきます。
JavaScriptの文字列とテキスト範囲はUTF-16コードユニットで機能します。バグを単純に導入するのは簡単です。私のデモはそれらでいっぱいだと確信しています。コード例を残して、オタクを釣ります。
"🍋🟩".length; // 5
[..."🍋🟩"].length; // 3
const segmenter = new Intl.Segmenter("en", {granularity: "grapheme"});
[...segmenter.segment("🍋🟩")].length; // 1