Skip to main content

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:

  1. Put the terminal in raw mode (no line buffering, no echo).
  2. Read keystrokes one at a time, including special keys (arrows, Page Up, etc.).
  3. Maintain a text buffer (array of rows).
  4. Redraw the screen using VT100 escape sequences.
  5. Handle editing: insert, delete, newline, file open/save.
  6. Add search (with incremental cursor jump).
  7. Add syntax highlighting (rule-based).

What you can only learn by building one:

  • Why raw mode matters and how termios works.
  • 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 < 10k line 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/write directly 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 kilo editor in 184 incremental steps, each with a complete diff. ⭐ the recommended primary.
  • Salvatore Sanfilippo (antirez), original kilo sourcegithub.com/antirez/kilo. The 1,000-line editor that the snaptoken tutorial reconstructs.

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.c and src/screen.c are 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 Editorsnaptokenrecommended 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 Rustphilippflenker.com/hecto/. A Rust port of the snaptoken tutorial. Excellent.

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.

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:

TestHow
Opens and displays./kilo readme.md displays correctly
Edits persistOpen, edit, save, reopen — changes present
Search findsSearch for known string, cursor jumps
HighlightingC file shows keywords in color
Terminal restorationAfter exit, terminal is in normal mode (no hidden cursor, normal echo)
Window resizeResize the terminal — editor redraws without garbling
Large fileOpen a 10,000-line file — operations remain responsive
Edge casesEmpty 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 they reset.
  • Mixing read() and getchar(). 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 +1 consistently.
  • 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

ModuleWhat the text editor deepens
Sem 4 Module 1 — C fundamentalsLarge, well-structured C project. Pointer-heavy.
Sem 4 Module 4 — Systems-level programmingtermios, raw read/write, signal handling for resize.
Regex Engine tutorialAdd regex search as an extension.
Shell tutorialBoth are terminal programs using termios.

12. Portfolio framing

What to publish:

  • The source (kilo.c is fine as a single file; or split into terminal.c, editor.c, syntax.c if 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.