Build Your Own Text Editor
"By the end of this, you'll have a working text editor, in C, with syntax highlighting and search, in less than 1000 lines." — Snaptoken, "Build Your Own Text Editor"
A text editor is the most rewarding small-project in the systems phase. By the end you have a usable terminal editor that you wrote from scratch, and you understand: raw terminal mode, escape sequences, terminal redraw, line buffers, incremental search, and syntax highlighting. All in under 1,000 lines of C.
1. Overview & motivation
A terminal text editor needs to:
- Put the terminal in raw mode (no line buffering, no echo).
- Read keystrokes one at a time, including special keys (arrows, Page Up, etc.).
- Maintain a text buffer (array of rows).
- Redraw the screen using VT100 escape sequences.
- Handle editing: insert, delete, newline, file open/save.
- Add search (with incremental cursor jump).
- Add syntax highlighting (rule-based).
What you can only learn by building one:
- Why raw mode matters and how
termiosworks. - Why VT100 escape sequences are how all modern terminals draw text.
- Why a gap buffer or piece table is a real engineering decision (this tutorial uses a simple array of strings, which is the right tradeoff for
< 10kline files). - Why incremental search has a clean implementation if you re-think editing as "snapshots + diffs."
2. Where this fits in the degree
- Phase: Systems
- Semester: 4 (Systems Programming)
- Modules deepened: Module 1 (C fundamentals) — string handling, struct layout, file I/O. Module 4 (systems-level programming) —
termios,read/writedirectly to the terminal.
Cross-phase relevance:
- Sets up the Shell tutorial (also a terminal program, more focused on processes).
- Background for any TUI work (htop, vim, less are all built on the same primitives).
3. Prerequisites
- C: strings, structs, dynamic arrays, file I/O.
- Basic Unix terminal usage.
You do not need to know termios or VT100 escape sequences ahead of time. The tutorial teaches both.
4. Theory & research
Required reading
- Paige Ruten (snaptoken), "Build Your Own Text Editor" — viewsourcecode.org/snaptoken/kilo/. The single canonical tutorial. Walks through the entire
kiloeditor in 184 incremental steps, each with a complete diff. ⭐ the recommended primary. - Salvatore Sanfilippo (antirez), original
kilosource — github.com/antirez/kilo. The 1,000-line editor that the snaptoken tutorial reconstructs.
Recommended
- VT100 escape codes reference — vt100.net (canonical) or Wikipedia's ANSI escape code article.
- POSIX
termios—man 3 termios. Surprisingly readable.
For more advanced editors
- Charles Crowley, "Data Structures for Text Sequences" (1998) — survey of gap buffers, piece tables, ropes. The standard reference.
- Joe Allen, "JOE Editor" source — a real Unix editor in C, more sophisticated than kilo.
- Vim source code — too big to read, but
src/edit.candsrc/screen.care illuminating in small doses.
What to skip
- LSP integration. Modern code editors integrate with language servers. This is a separate large project.
- GUI editing. We're building a terminal editor.
5. Curated tutorial list (from BYO-X)
- C: Build Your Own Text Editor — snaptoken ⭐ recommended primary
- C++: Designing a Simple Text Editor
- Python: Python Tutorial: Make Your Own Text Editor [video]
- Python: Create a Simple Python Text Editor!
- Ruby: Build a Collaborative Text Editor Using Rails — different domain entirely (web-based, real-time collaboration)
- Rust: Hecto: Build your own text editor in Rust — philippflenker.com/hecto/. A Rust port of the snaptoken tutorial. Excellent.
6. Recommended primary path
snaptoken's "Build Your Own Text Editor".
This is the tutorial. 184 numbered steps, each with a unified diff. You can follow it strictly or use it as a reference while building your own. Either way, you'll finish with a usable editor.
For Rust learners: Philipp Flenker's Hecto is a near-direct port with the same structure. Use whichever language you prefer for systems work.
After completing kilo/hecto, the natural next step is to add: a real text storage structure (gap buffer or rope), undo/redo, regex search.
7. Implementation milestones (following snaptoken / kilo)
Milestone 1: Raw mode
Disable line buffering, echo, signal handling. read() returns single bytes.
#include <termios.h>
#include <unistd.h>
struct termios orig_termios;
void disable_raw_mode() {
tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios);
}
void enable_raw_mode() {
tcgetattr(STDIN_FILENO, &orig_termios);
atexit(disable_raw_mode);
struct termios raw = orig_termios;
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
raw.c_oflag &= ~(OPOST);
raw.c_cflag |= (CS8);
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
raw.c_cc[VMIN] = 0;
raw.c_cc[VTIME] = 1;
tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
}
Evidence: Press Ctrl-C — your program receives the byte 0x03 instead of being terminated. Press the arrow keys — your program reads the escape sequence.
Milestone 2: Screen drawing with escape sequences
Clear screen: \x1b[2J. Move cursor to (1,1): \x1b[H. Hide cursor: \x1b[?25l. Draw N rows of tildes (mimicking vim).
write(STDOUT_FILENO, "\x1b[2J", 4);
write(STDOUT_FILENO, "\x1b[H", 3);
for (int y = 0; y < rows; y++) write(STDOUT_FILENO, "~\r\n", 3);
Evidence: Run the editor. See a screen full of ~. Press q to exit cleanly.
Milestone 3: Read keypresses; arrow keys
Map escape sequences (\x1b[A, \x1b[B, ...) to internal key constants. Maintain a cursor position.
enum editorKey {
ARROW_LEFT = 1000, ARROW_RIGHT, ARROW_UP, ARROW_DOWN,
PAGE_UP, PAGE_DOWN, HOME_KEY, END_KEY, DEL_KEY
};
Evidence: Cursor moves around in response to arrows. Page Up / Page Down jump.
Milestone 4: Read a file
Open a file, read lines into an array of erow (editor row) structs.
typedef struct erow {
int size;
char *chars;
} erow;
Evidence: ./editor file.txt displays the file. Arrow keys scroll through it. Status bar shows filename and line count.
Milestone 5: Editing — insert, delete, newline
Implement insert character at cursor, delete character at cursor, newline.
Each edit creates new strings; the row array grows or shrinks. For small files this is fine. (Production editors use gap buffers, piece tables, or ropes. Snaptoken's approach is intentionally simple.)
Evidence: Type into an open file. Backspace deletes. Enter inserts a newline.
Milestone 6: Save
Write the buffer back to disk. Atomic save: write to a temp file, then rename.
Evidence: Open file, edit, save, close, reopen — changes persisted.
Milestone 7: Search
Press Ctrl-F. Show a prompt. As the user types, jump the cursor to the next match. Pressing arrow keys cycles through matches. Escape cancels.
This is the cleanest piece of UX in the tutorial. Incremental feedback as the user types.
Evidence: Search for any string in the file; cursor jumps to it. Repeated arrow press jumps to the next occurrence.
Milestone 8: Syntax highlighting
A highlight byte array parallel to each row's chars. After any change, re-highlight that row.
Simple rule-based: numbers, strings, comments, keywords. Render with VT100 color codes (\x1b[31m for red, etc.).
enum editorHighlight {
HL_NORMAL = 0, HL_NUMBER, HL_STRING, HL_COMMENT, HL_KEYWORD1, HL_MATCH
};
void editorUpdateSyntax(erow *row) {
row->hl = realloc(row->hl, row->rsize);
memset(row->hl, HL_NORMAL, row->rsize);
// ...rule-based scan, set per-byte highlight category
}
Evidence: Open a .c file. Strings, numbers, keywords (int, if, return, ...) appear in different colors.
8. Tests & evidence
Text editors are notoriously hard to unit-test. The realistic evidence is:
| Test | How |
|---|---|
| Opens and displays | ./kilo readme.md displays correctly |
| Edits persist | Open, edit, save, reopen — changes present |
| Search finds | Search for known string, cursor jumps |
| Highlighting | C file shows keywords in color |
| Terminal restoration | After exit, terminal is in normal mode (no hidden cursor, normal echo) |
| Window resize | Resize the terminal — editor redraws without garbling |
| Large file | Open a 10,000-line file — operations remain responsive |
| Edge cases | Empty file, file ending without newline, file with very long lines |
A reviewer should be able to use the editor for one writing session and report no broken behaviors.
9. Common pitfalls
- Forgetting to restore terminal state on crash. Use
atexit. If your editor crashes without restoring, the user's terminal is unusable until theyreset. - Mixing
read()andgetchar(). Use one. Bufferred stdio interacts poorly with raw mode. - VT100 sequences vary across terminals. xterm, tmux, modern terminals support different subsets. Test in your target terminal.
- Off-by-one in cursor positioning. Escape sequences are 1-indexed. C arrays are 0-indexed. Add a
+1consistently. - Re-rendering the whole screen each keypress. Works fine for small files; slow for large ones. Optimization: render only the changed rows. snaptoken builds toward this.
- Buffered output causing flicker. Use a write buffer and flush atomically (one
write()per frame). - No handling of UTF-8. kilo treats text as bytes. Real editors deal with multi-byte characters. Stating this limitation honestly is fine.
10. Extensions
- Gap buffer. Replaces the array-of-strings with a single buffer with a movable gap at the cursor. Editing operations become O(1) amortized.
- Piece table. What VS Code uses internally. Original file + append-only modifications log. Excellent for undo.
- Rope. Tree of strings. Better for very large files.
- Undo/redo. Implement a command stack.
- Configurable keybindings. Parse a config file.
- UTF-8 support. Most users will eventually want this.
- Multiple buffers / split windows. kilo handles one file at a time. Make it handle several.
- Regex search. Plug in a small regex engine. See the Regex Engine tutorial.
The path from kilo to a usable daily-driver editor is long but each step is small and rewarding.
11. Module integration
| Module | What the text editor deepens |
|---|---|
| Sem 4 Module 1 — C fundamentals | Large, well-structured C project. Pointer-heavy. |
| Sem 4 Module 4 — Systems-level programming | termios, raw read/write, signal handling for resize. |
| Regex Engine tutorial | Add regex search as an extension. |
| Shell tutorial | Both are terminal programs using termios. |
12. Portfolio framing
What to publish:
- The source (
kilo.cis fine as a single file; or split intoterminal.c,editor.c,syntax.cif you prefer). - A
Makefile. - A README with screenshots (animated GIF of editing + search is ideal).
- A note on differences from your reference (snaptoken's kilo or hecto): "I implemented X, skipped Y, added Z."
Reviewer entry points:
- README with an animated demo.
editorProcessKeypress()— the input dispatch.editorRefreshScreen()— the draw loop.editorFind()— the incremental search (the project's most distinctive code).
The text editor is a portfolio favorite because it demonstrates working systems code that produces visible, interactive output. Strong choice for an early portfolio piece.
Source
This tutorial draws from the BYO-X catalog "Text Editor" entry. snaptoken's tutorial (and antirez's original kilo) are the canonical references.