本記事の構成および論理分析にはAI(人工知能)を使用しています。情報の正確性は、システム管理者(UNIXユーザー)による手動検証済みです。
Build Your Own Text Editorを写経してkiloを学んだ記録|C言語とターミナル制御 | UNIX Cafe

1. はじめに
C言語とターミナル制御の学習として、何か最適な教材はないかと探していたところ、snaptoken氏の『Build Your Own Text Editor』という素晴らしいチュートリアルを見つけ、挑戦してみることにしました。
題材は、わずか1000行で書かれたC言語製のミニマルなテキストエディタ『kilo』です。2日ほどかけてじっくり手を動かして写経したコードと、自分なりの詳細な学習メモは、現在GitHubにて公開しています。
GitHubではkilo-shakyo-notesという名前で公開しています。
Repository:https://github.com/k1117n-cmyk/kilo-shakyo-notes
本記事では、今回の自作エディタ開発を通じて学んだ低レイヤの仕組みや設計思想、泥泥臭くデバッグしたつまずきポイント、そしてリポジトリを一般公開するにあたって整理したドキュメント構成などを振り返りとしてまとめています。
2. 取り組んだ教材
今回挑戦した教材は、snaptoken氏が提供している 『Build Your Own Text Editor』 です。
チュートリアルURL
https://viewsourcecode.org/snaptoken/kilo/index.html
本教材は、Redisの作者として著名なantirez氏が開発した、わずか1000行のテキストエディタ『kilo』がベースになっています。
オリジナルコード
https://github.com/antirez/kilo
外部ライブラリを一切使わず、たった1つのC言語ファイルだけでターミナル上で動く本格的なエディタを作り上げていく内容となっており、以下のような低レイヤからアプリケーション層に至るまでの幅広い知識を包括的に学ぶことができます。
▼ 本教材で学べる主な内容:
termiosを使った Raw mode への切り替え(OSによる入力代行のバイパス)- ターミナルからのキー入力の直接読み取りと解析(特殊キーのハンドリング)
- ANSI エスケープシーケンスによる画面制御(カーソル移動、画面クリア)
- Append Buffer による画面の再描画(チラつきのないスムーズな描画)
- 動的な行バッファ(メモリ管理)(行データや文字列の動的確保・拡張)
- 標準I/Oによるファイルの読み込みと保存
- エディタ内のテキスト検索機能
- シンタックスハイライト(言語に応じた文字色・背景色の変更)
3. Raw modeと端末制御
普段私たちが何気なく行っているターミナルへのキー入力は、実はOSの端末制御(TTY)レイヤーが裏で多くの処理を代行してくれているのだと知りました。
チュートリアルを通じて、ターミナルの入力モードを「Raw mode(生モード)」に切り替えることで、キー入力をアプリケーションへよりダイレクトに届けられるようになります。しかしその反面、制御文字(Ctrl+Cなど)や矢印キーといった特殊キーの挙動も、すべて自分のコードで1から解釈(ハンドリング)しなければなりません。
Unix/Linux環境において、termios 構造体を使って入力フラグを細かく制御し、低レイヤな入出力や画面制御を自前でコントロールする仕組みを学べたのは、非常に大きな収穫でした。
画面描画のアーキテクチャ
kiloの描画思想は非常にシンプルで、「キー入力があるたびに内部状態を更新し、画面全体を再描画する」というアプローチを取っています。
画面のクリア、カーソル移動、ステータスバーの表示といった画面制御には、すべてANSIエスケープシーケンスが使われています。専用のUIライブラリを使わず、標準出力へ特定の文字列を送るだけで端末を操作できる仕組みは、実装していて非常に新鮮でした。
開発前は「毎回全体を書き換えるとチラつくのでは?」と思っていましたが、文字列をバッファに溜めて一括出力する工夫(Append Buffer)を取り入れることで、kiloのようなミニマルなエディタであれば、複雑な差分更新を行わなくても十分に実用的な速度で動作することが分かり、設計における割り切りの重要性を学びました。
C言語における文字列とメモリ管理の現実
エディタ内の行データや、画面描画用のバッファ文字列は、必要に応じて動的にメモリを確保する設計になっています。
開発中は malloc、realloc、free をフル活用することになり、高水準言語がいかにメモリ管理を隠蔽してくれているかを改めて感じました。C言語では、文字列の長さが変わるたびに手動でバッファを拡張し、不要になった領域を確実に解放するという「当たり前の管理」をすべて自前で行わなければなりません。
特に realloc によるバッファ拡張時は、メモリの再配置によって既存のポインタが無効化されるリスク(アドレスの移動)が常に伴います。
ポインタの扱いを誤ると簡単にクラッシュ(セグメンテーションフォールト)やメモリリークを引き起こすため、メモリの寿命とバッファサイズを常に意識しながらコードを書く良い訓練になりました。
エディタとしての状態管理
完成版の kilo.c では、エディタ全体の状態(ステート)が1つの構造体に美しく集約されています。
一見するとミニマルなエディタですが、実際に管理している状態は驚くほど多岐にわたります。
▼ 構造体で管理されている主な状態:
- カーソル位置:画面上およびファイル内での現在の座標
- スクロール位置:画面外に隠れている行や列のオフセット
- 行バッファ:読み込んだテキストの全行データ(動的配列)
- ファイル名:現在開いている、または保存予定のファイル名
- 未保存の変更(ダーティフラグ):変更後に保存されたかどうかの真偽値
- ステータスメッセージ:画面最下部に表示する通知テキストと表示時間
最小限の機能しか持たないエディタであっても、裏で管理すべき状態がこれほど多いことに驚きました。
特にタブ文字の存在によって「データ上の位置」と「画面上の見た目の位置」を別々にトラッキングする必要があるなど、エディタならではの複雑さを1か所に集約してカプセル化する設計の大切さがよく分かりました。
4. つまずいたところ:差分の「削除行」という罠
差分の削除行を見落とした
このチュートリアルは、完成したコードをただ眺めるのではなく、全184ステップにわたって少しずつ差分を積み上げていく形式で進みます。
ここで盲点となったのが、「追加された行(プラスの差分)ばかりに気を取られ、削除された行(マイナスの差分)を見落としやすい」 という点でした。
実際に私も、リファクタリングの過程で不要になった古いカーソル移動用のエスケープシーケンス \x1b[H を、editorRefreshScreen() 内から消し忘れてしまうミスを犯しました。その結果、プログラムは正常にビルドできるものの、カーソルが常に画面の左上に固定されて一切動かなくなるという謎のバグに悩まされることになります。
「コンパイルが問題なく通ることと、実行時の挙動が正しいことは別物である」という、プログラミングにおける基本でありながら最も大切な教訓を、身をもって確認することができました。
つまずいたところ:英大文字「O」と数字「0」の識別トラップ
もう一つの大きなつまずきポイントは、エスケープシーケンスの解析処理において、英大文字の「O(オー)」と数字の「0(ゼロ)」を間違えてタイピングしてしまったことです。
このような文字のタイプミスは、構文としてはどちらも有効な文字として扱われるため、コンパイルエラーにはなりません。使用しているフォントによっては画面上の見た目も酷似しているため、コードをただ眺めるだけでは見落としやすく、 結果として「なぜか矢印キーを押したときだけ意図しない挙動になる」という、非常に原因特定が難しいバグとして表面化しました。
C言語や低レイヤな制御においては、文字コードのわずか1ビット、1文字の些細な違いが、プログラム全体の実行時の挙動に直結します。デバッグを通じて、コードの文字一つひとつをいかに厳密に検証すべきかを痛感しました。
C言語における「引数なし関数」の罠とコンパイル警告
このプログラムの実装を通じて、C言語において引数のない関数を定義する際、void func() と void func(void) では意味が全く異なることを学びました。
void func():「引数の数は不特定(任意の数の引数を取れる)」という意味になる。void func(void):「引数はなし(1つも引数を取らない)」ことを明示する。
今回のビルドでは、元のチュートリアルに倣い、バグの芽を事前に排除するために、以下の厳格なオプションを指定しています。
$(CC) kilo.c -o kilo -Wall -Wextra -pedantic -std=c99写経を進める際は、単に「エラーが出ずにコンパイルできるか」だけでなく、「コンパイラから警告(Warning)が出ていないか」を注意して見ると、C言語の歴史的な仕様や隠れたバグの芽に気づく機会になります。
5. GitHubで公開した理由
今回、手元で写経したソースコードと自分なりの補足メモをGitHubで公開することにしました。
GitHubに公開した理由は2つあります。1つは、自分の学習記録をストックするためです。
そしてもう1つは、同じ教材に挑戦している人が、私同じ場所でつまずいた際のトラブルシュートや、完成後の全体構造を確認するための参考になればと考えたからです。
ただし、これは公式翻訳でも元チュートリアルの再配布でもありません。あくまで私個人が実際に手を動かして、気づいた内容を「メモ」として落とし込んだものです。
完成したコードだけでなく、「どこで間違えて、どう解決したか」というプロセスを残すことが、後に続く人たちの創造的な価値につながる信じています。
6. リポジトリのクリーンアップと構成整理
コードをGitHubで一般公開するにあたり、他の人がアクセスした際に迷わないように、リポジトリ内のファイル構成を適切な状態に整理しました。
▼ 実施した主なクリーンアップ
- READMEの日本語化:リポジトリの目的や内容を一目で理解できるようドキュメントを整備
- リポジトリ名の最適化:教材名と目的が明確に伝わるよう
kilo-shakyo-notesに決定 - 不要ファイルの削除:内容が重複していた
articles.mdを削除。 - 公開対象の厳選:実験用に作った
sizeof.cや、ローカルの実行バイナリを排除 .gitignoreの設定:環境依存のビルド成果物(kilo実行ファイルなど)が混入しないよう管理
📁 公開対象の最終ファイル構成:
README.md
kilo.c
kilo_learning_notes.md
kilo_explained.md
makefile
.gitignore個人用の学習リポジトリであっても、公開する以上は、読む人が迷わない構成にしておくのが大事だと感じています。
7. まとめ
わずか1000行程度のミニマルなテキストエディタですが、その中には端末制御や入力処理、画面描画、ファイルI/O、検索、シンタックスハイライトまで、近代的なエディタを形作るエッセンスがすべて詰まっていました。
snaptoken氏が公開されている素晴らしいチュートリアルに出会い、初日のSetupの段階で、こんな基本的なところから解説してもらえるのかという驚きと、好奇心に誘われるまま、夢中になって手を動かし続けた2日間。
今回の経験を通じて、写経とは単にコードを書き写す作業ではなく、「差分を厳密に確認し、コンパイルし、実際に動かし、挙動がおかしい原因を自力で突き止める」というデバッグのプロセスも含めて、初めて本当の学習になるのだと実感することができました。
開発中につまずいた数々の失敗をこうしてメモ(資産)として残したことで、自分自身の理解を深めるだけでなく、次に挑戦する誰かの道標としての形にもできました。
C言語を学び直したい人、あるいは「自分の手で実用的な成果物を作り上げる楽しさ」を味わいたい人にとって、kiloの自作は間違いなく最高の題材です。










