A Text Editor as a User Interface (Dave Gauer)
- title
- A Text Editor as a User Interface (Dave Gauer)
- type
- summary
- summary
- Using $EDITOR as a UI bridges the gap between CLI args and a full TUI; precedents are crontab -e, git commit, visudo, and vipw
- parent
- editor-as-ui
- tags
- cli, unix, shell, design-pattern
- sources
- text-editor-as-ui
- created
- 2026-04-30
- updated
- 2026-04-30
Dave Gauer (ratfactor) writes a card-format post on a pattern he's been using more and more: when a CLI tool needs more than a few flags but doesn't deserve a full TUI, hand the user a temp file and shell out to $EDITOR. The framing is the gap itself. Argument parsing is cheap. A real TUI takes "years" of effort. The middle is where most personal tooling actually lives, and Unix has been quietly sitting on a solution since the early days.
The precedents are familiar: crontab -e for the cron table, git commit for the commit message, visudo for /etc/sudoers, vipw for /etc/passwd. All four open a temp file, let the user edit, then validate and apply. vipw is the cleanest demonstration because consistency-checking is its whole purpose โ the man page advertises that it "will not allow a password file with a 'mangled' entry to be installed."
The three-line version
The minimal demo is a shell script that runs rev on whatever you type:
FILE=$(mktemp)
$EDITOR $FILE
rev < $FILE
Three lines, and the user gets the entire expressive surface of their editor โ search, syntax highlighting, multi-cursor, paste from clipboard, anything they've already configured. Gauer's point: the cost of giving the user that power is one line of script. Building a TUI text-input widget that approached the same capability would take a year and need its own documentation.
The yt-dlp example
The richer example is a Ruby wrapper around yt-dlp that downloads a video to a chosen subdirectory under /media/video. The script:
- Globs the destination directory to enumerate available subdirs.
- Reads the previous run's settings from a fixed path (
download.txt) โ it persists across invocations, not amktemp. - Writes a four-line file: a comment listing the available subdirs, then the chosen subdir, base filename, and URL on three following lines.
- Shells out to
vimon the file. - Reads the file back, validates, and runs
yt-dlpwith a timestamp-suffixed output name.
# cs_lectures film howto sketchbooks watercolor
howto
fix_toilet
https://example.com/how-to/fix-a-toilet.html
Two design choices are worth pulling out. The available subdirectories appear as a comment header so you don't have to remember them โ the editor screen becomes the documentation. And the settings file is reused, not random, so the previous run's URL/subdir are sitting there as defaults the next time you launch.
The timestamp suffix on the filename (fix_toilet_1777324491.mp4) sidesteps having to compute "next sequence number" โ sorting by mtime or filename gives you the same ordering for free.
When the pattern fits
Gauer ends with a working rule: if the parameter set grows past three or four lines, switch to a structured format inside the same file โ .ini, .conf, .toml. The point is that the format stays trivial to parse without a library. The whole technique loses its appeal once you need a parser more elaborate than each_with_index.
He calls out find, ffmpeg, and rsync as natural candidates โ tools whose CLIs are dense enough that even regular users keep a notes file of working invocations. An editor-as-UI wrapper turns that notes file into the actual interface.
Why it works
A few things about the pattern are easy to miss:
- Zero documentation. The user already knows their editor. There's nothing to teach about navigation, undo, or autocomplete.
- Composable. The temp file is just text, so it works with version control, diffs, and shell pipes if you want to inspect what was actually run.
- Stateful by default. A persistent settings file (versus
mktemp) means the second invocation is one keystroke away from the first. - Unprivileged. No curses, no terminal modes, no signal handling. The script can be killed, resumed, or piped, and the editor handles all the terminal complexity.
The pattern is most famous from git commit, which also demonstrates the validation step โ an empty message aborts the commit. Gauer's own scripts do the same with explicit existence and directory checks after the editor returns.
Related
- editor-as-ui โ the broader concept page
- clean-code-coding-agents โ the same minimum-context philosophy applied to the LLM era
- ratfactor โ Dave Gauer's "card" blog