A modeless editor for the terminal

The terminal editor you already know how to use.

CMeDit brings the ease of a desktop editor to the speed of a terminal: drop-down menus, full mouse support, and the shortcuts your fingers already know. No modes. No new muscle memory. Ctrl+S saves, Ctrl+F finds, Ctrl+Q quits. That's the whole tutorial.

v0.3.1 · free & open source (GPL-3.0) · one binary, zero runtime dependencies · Linux, macOS & Windows

Why CMeDit

A desktop editor's manners.
A terminal's metabolism.

Terminal editors make you choose: learn a decades-old command language, or settle for something bare-bones. CMeDit refuses the choice. It's a full graphical-style editor that happens to be drawn in character cells.

ZERO LEARNING CURVE

Nothing to memorise

Modeless, like every desktop app you've ever used. Shift+arrows select, the mouse selects and drags, double-click picks a word, Ctrl+C/Ctrl+V talk to your system clipboard. Can't remember a shortcut? It's in a menu, with the shortcut printed next to it. You will never look up how to exit.

A REAL INTERFACE

Point at things

A drop-down menu bar. A VS Code-style file explorer you can drag to resize. Dialogs with buttons. A scrollbar with a thumb you can grab. Even the status bar is clickable: tap Ln, Col to jump to a line, tap LF to switch line endings. Everything visible, everything discoverable.

TERMINAL NATIVE

Instant, everywhere

One self-contained binary that starts in milliseconds, opens multi-megabyte files instantly, and repaints only the cells that changed. It runs identically on your laptop, a container, or a server on the far side of an ocean.

Modern editor, no window system

The features you'd miss are all here.

  • Fuzzy file open: Ctrl+P, type a few letters, hit Enter. Recent files first, cursor restored to where you left it.
  • Command palette: Ctrl+Shift+P (or > in the file picker) fuzzy-searches every command in the editor.
  • Search across your project: F4 greps thousands of files in the background with regex, globs and match-case/whole-word toggles; click any result to jump to it.
  • Replace with a safety net: project-wide replace opens every affected file as an unsaved tab for review. Nothing touches disk until you say Save All.
  • Go to definition: F12 or Ctrl+Click, across the workspace, for Python, JS/TS, SQL, shell, Go, Ruby, PHP and Haskell. No language servers to install.
  • Word completion: Ctrl+Space completes from every open buffer, nearest match first. Works offline, works everywhere.
  • Navigation history: Alt+←/Alt+→ walk back and forward through your jumps across files, like a browser.
  • Syntax highlighting for Python, JavaScript/TypeScript, SQL, HTML/XML, CSS/SCSS/LESS, shell, Markdown, JSON, YAML, TOML, INI, FreeMarker, Jinja and more — multi-line strings and comments included.
  • Multiple files in one session: switch with Alt+1…9 or the Window menu; unsaved work is guarded on quit.
  • Line surgery: duplicate, move, join, delete lines; toggle comments with Ctrl+/ in the file's own comment syntax.
  • Find as you type: every match highlighted live with a running count; previous search terms recalled across sessions.
  • Five themes plus auto: dark and light that inherit your terminal's background, and three fully-painted themes — cherry-blossom, flashbang, midnight — that look the same anywhere. The picker previews each one live, and auto follows a system light/dark switch on the fly.
  • It remembers: recent files, cursor positions, search history and your preferences persist in a plain-text config.
  • Unicode done right: CJK, emoji and combining characters line up; tab stops are honoured.

Real-time diagnostics, no language server

Linting that already knew
how to run.

CMeDit auto-detects the linters already on your machine — ruff, flake8, eslint, stylelint, pyright and shellcheck — and re-lints the active file about half a second after you stop typing. No LSP install, no per-project extension marketplace, no per-host server process. Your unsaved buffer streams to the tool over stdin, so the diagnostics reflect what's on screen.

  • Squigglies in the terminal: curly red/amber underlines where the terminal supports them, straight ones where it doesn't. Line numbers tint in the diagnostic's colour too, so you can scan for problems at a glance.
  • An honest counter: 2E 1W on the status bar. Click it to jump to the first problem; F8 (Find ▸ Next Problem) cycles.
  • Rest on a marker to read it — the message and rule id show up in the hint bar, so no floating tooltip clobbers the code you're reading.
  • Project configs apply: linters run with the workspace root as their working directory, so your pyproject.toml, eslint.config.mjs or .shellcheckrc takes effect automatically. eslint and stylelint are picked up from node_modules/.bin too.
  • A Settings row per linter shows install status; if a tool is missing it prints the exact command to install it. pyright is save-time-only and off by default (it's slow); flake8 is skipped when ruff is present.
  • Works over SSH the same way it works locally — because there's nothing to install on either end that isn't already there.

The two diagnostics above are ruff's F401 (unused import json) and E722 (bare except). Nothing was configured — ruff was on $PATH, cmedit noticed, ran it on the buffer, and drew the underlines.

The remote-work superpower

Do work over SSH that terminals
aren't supposed to be able to do.

An SSH session is the fastest way to be on a machine, and usually the most limiting. CMeDit closes the gap: the workflows that normally force you to scp files home, mount remote drives, or install a remote IDE server just… work, inside the terminal you already have open.

Open the spreadsheet. On the server.

CSV and TSV files open as a real spreadsheet grid: navigate cells, edit in place, insert rows and columns, freeze the header, select rectangles to copy or clear, and sort by any column with Alt+S. The sort is numeric-aware, and one more keystroke flips it descending. Saving writes clean, properly-quoted RFC-4180 CSV.

Look at the picture. No download.

Open a PNG, JPEG, GIF, WebP or BMP and CMeDit renders it right in the terminal: at true pixel resolution on terminals with kitty graphics or sixel, and in sharp 24-bit half-block cells everywhere else. Animated GIFs play in place — natively on kitty terminals (a swap of graphics placements, no re-transmit), stepped elsewhere. Drag a rectangle to zoom; a small picture sits centred at native resolution instead of being blown up. That chart a job wrote to /var/log? Just open it.

Search the whole tree, right there.

Project-wide find & replace runs on the remote box where the files live. No syncing a working copy locally first. It prunes .git and node_modules, skips binaries, and streams results in as they're found.

you@prod: cmedit exports/orders.csv
A B C ↓ D 1 order_id region amount status 2 10412 eu-west 8,940.00 shipped 3 10387 us-east 4,102.50 pending 4 10305 ap-south 1,988.75 shipped 5 10510 eu-west 412.00 refunded
orders.csv  table Cell C2sorted ↓ CUTF-8LF
Alt+S SortAlt+T Text view^C Copy^S Save
$ ssh you@prod $ cmedit exports/orders.csv # a spreadsheet, in your terminal $ cmedit build/flamegraph.png # yes, this works too

Engineering, not vibes

Fast because every byte is on purpose.

CMeDit is built from first principles directly on the terminal, with no UI framework in between, so it controls exactly what gets drawn, when, and how it travels.

Diff rendering

Each frame repaints only the cells that changed and flushes in a single write, wrapped in synchronized output so you never see tearing.

Input coalescing

Held keys, fast typing and wheel scrolling are batched per frame, so the display never falls behind your hands.

Instant big files

Multi-megabyte files open immediately; the genuinely huge ones load on a background thread with a spinner, never a freeze.

Regex that can't hang

The search engine is linear-time by construction, so a pathological pattern like (a+)+b simply cannot lock up a project-wide search.

Guarded opens

Binary files are detected and refused with a clear message instead of exploding into a million junk lines.

Structure-sharing undo

Snapshots share the unchanged text, so deep undo history stays cheap even in large files.

Background everything

Workspace searches, file loads and the quick-open file walk all stream results in without blocking a keystroke.

Honest fallbacks

At startup CMeDit probes what your terminal can do and upgrades feature-by-feature. No probe reply means no risky escape codes: a plain terminal gets a plain, correct stream.

ON A MODERN TERMINALQuietly upgraded

  • Pixel-perfect images and animated GIFs via kitty graphics or sixel, aspect-corrected to your real cell size
  • Tear-free frames (synchronized output) and hardware scroll regions
  • Auto light/dark theme that follows the terminal's background, live
  • Curly diagnostic and bracket-match underlines, pointer-shape hints, desktop notifications
  • OSC 8 hyperlinks: URLs in your text and file names in the explorer / search results are Ctrl+Click-openable

ON A BARE-BONES TERMINALStill completely at home

  • The full editor: menus, mouse, explorer, search, CSV mode, all of it
  • Images render in Unicode half-blocks, or an ASCII ramp on request
  • Portable ANSI output only: nothing a dumb terminal would corrupt
  • Every upgrade is opt-in by evidence, so it never guesses wrong

And the rest

Small things, done properly.

File explorer that manages files

Create (Ins), rename (F2) and delete (Del) from the tree; open buffers follow renames, even under a renamed folder.

It notices the outside world

Files changed on disk get a ◆ marker within seconds. A git pull in another window shows up by itself.

Bracket matching

The pair at your cursor is highlighted; Ctrl+] jumps between them. The scan is bounded, so huge files never stall.

Line endings & BOM

Flip LF ⇄ CRLF and toggle the UTF-8 BOM from the View menu, or just click them on the status bar.

Word wrap & line numbers

Alt+Z and Alt+L, plus whitespace markers, insert/overwrite, auto-indent, and tab or space indentation with block indent/outdent.

Save-time cleanups

Opt-in trailing-whitespace trimming and final-newline fixing, applied as a normal undoable edit, never behind your back.

Session memory

Reopen a file, today or next week, and the cursor is exactly where you left it.

Built-in manual

F1 shows a keyboard card; the full manual opens as a searchable document inside the editor itself.

Live-preview settings

File ▸ Settings (Ctrl+,): arrow-key value pickers, changes apply behind the dialog, Save writes them back to your config file surgically — your comments survive.

Clickable links

URLs in your text and file names in the explorer, search results and status bar are OSC 8 hyperlinks — Ctrl+Click opens them, in any terminal that supports links.

Native Windows port

The same codebase with a Windows platform layer (hand-rolled kernel32 FFI, no extra packages). Runs in Windows Terminal; legacy conhost gets the portable fallback path like any minimal terminal.

Explorer that reads at a glance

File names are tinted by type (source green, docs cyan, data yellow, binaries dimmed); displayable images get a magenta ❏ marker so you can spot them without opening.

Get it

Clone, make, edit.

CMeDit builds offline with a plain make; its only dependencies ship with the compiler. The result is a single binary you can drop onto any machine, including the ones you SSH into.

$ git clone https://github.com/appmakernetwork/cmedit $ cd cmedit $ make # needs GHC 9.0+, nothing else $ ./cmedit . # open the current folder as a workspace

Free software under the GPL-3.0. For full system-clipboard integration, install any of xclip, wl-clipboard, xsel or use macOS's built-in pbcopy. Otherwise CMeDit falls back to the OSC 52 escape, which is what makes clipboard-over-SSH work anyway.

Required reading

The official release white paper

CMeDit is written in Haskell, a language with a storied tradition of producing white papers instead of products. Out of respect for that tradition, our first public release ships with both. The paper covers the architecture, the from-scratch image decoders and regex engine, and the unprecedented result that the software itself actually exists.

Read the white paper →