Quick Ed

A fast, lightweight terminal editor with modal editing and built-in git.

This is a research project — built entirely through prompting using Claude AI.

qe src/main.c
 1  #include "editor.h"
 2  #include "input.h"
 3  #include "render.h"
 4
 5  int main(int argc, char *argv[]) {
 6      editor_init();
 7      lua_bridge_init();
 8
 9      if (file_arg) {
10          buf_open(&E.buf, file_arg);
11          editor_detect_syntax();
12      }
13
14      while (1) {
15          editor_refresh_screen();
16          editor_process_keypress();
17      }
18  }
 main.c                                          18 lines  C   main 
Install Learn more
# Install Quick Ed. Works on Linux & macOS.
$ curl -fsSL https://jeff.lookingforteam.com/qe/install.sh | sh

Requires CMake, a C compiler, and Lua 5.4 headers. The script handles the rest.

Features

vi

Modal Editing

Vim-style modes — Normal, Insert, Visual, Command, Search. Operators (d, y, c) combine with motions and text objects. Count prefixes, dot repeat, macros, and named registers.

</>

Syntax Highlighting

Tokenizer-based highlighting for keywords, types, strings, numbers, and comments. Bracket match highlighting. Language definitions are added via Lua — ships with C, Lua, and Markdown.

[][]

Split Panes

Horizontal and vertical splits with :split and :vsplit. Navigate with Ctrl-W keys. Each pane has independent cursor and scroll state. Two panes can show the same buffer.

📁

File Tree

Directory sidebar with <leader>e or :Tree. Git status coloring — green for new, yellow for modified, red for deleted. Toggle hidden files, expand/collapse directories.

🔍

Fuzzy Finder

Fast subsequence file search with <leader>t. Matched characters highlighted. Open in current pane, horizontal split, or vertical split. Hidden directories auto-excluded.

Embedded Terminal

Open a shell or run commands with :terminal. Full 256-color support, cursor positioning, scroll regions. PTY auto-resizes with the pane. Auto-closes on shell exit.

git

Git Integration

Gutter diff signs, hunk navigation (]c/[c), hunk stage/revert. Blame, diff, log, and commit views. Interactive :Gcommit — stage/unstage files with +/-, commit with :wq.

{..}

Code Folding

Indent-based folding with za/zc/zo/zM/zR. Fold headers show hidden line count. Cursor movement skips folded regions. Scrolling accounts for hidden lines.

Grep & Quickfix

Search across files with :grep. Results open in a quickfix pane — navigate with j/k, jump to match with Enter. Navigate between results with :cn/:cp.

lua

Lua API

Scriptable via a full Lua API. Configure options, bind keys, define syntax, react to editor events, query git state, manipulate buffers, and run shell commands — sync or async. Build plugins from formatters to build systems.

CLI Tools

Use qe as a command-line tool: qe cat, qe diff, qe blame, qe log, qe grep — all with syntax-colored output. Add custom subcommands via Lua.

Undo Tree

Full branching undo history — never lose a change. Browse the tree visually with :revisions. Time-travel with g-/g+ to reach states that linear undo/redo can't. History persists to disk and survives restarts.

Session & State

Save and restore sessions with :mksession / :source or qe -s file. Crash recovery auto-saves unsaved buffers — on reopen, choose to recover, ignore, or delete. Pick up where you left off.

Install

Requirements

CMake 3.16+, a C11 compiler, Lua 5.4 development headers.

macOS (Homebrew)

brew install lua cmake

Fedora / RHEL

sudo dnf install lua-devel cmake gcc

Debian / Ubuntu

sudo apt install liblua5.4-dev cmake gcc

Build from source

git clone https://github.com/obscuren/qe.git
cd qe
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
sudo cmake --install build    # installs to /usr/local/bin/qe

Verify

qe --version

Documentation

Modes

EscNormal mode
i / a / o / OInsert mode
:Command mode
/Search mode
v / V / Ctrl-VVisual / Line / Block

Navigation

h j k lLeft / Down / Up / Right
w / b / eWord forward / back / end
0 / $Start / end of line
gg / GFirst / last line
fc / FcFind char forward / back
tc / TcTill char forward / back
; / ,Repeat last find / reverse
{ / }Paragraph up / down
%Matching bracket
gdGo to local definition
]c / [cNext / prev git hunk
ma / 'aSet mark / jump to mark
Ctrl-O / Ctrl-IJump list back / forward

Operators

dmotionDelete
ymotionYank (copy)
cmotionChange (delete + insert)
dd / yy / ccLine-wise
>> / <<Indent / outdent
p / PPaste after / before
.Repeat last change
u / Ctrl-RUndo / redo
g- / g+Earlier / later undo state

Text Objects

iw / awInner / around word
i" / a"Inner / around quotes
i( / a(Inner / around parens
i{ / a{Inner / around braces
i[ / a[Inner / around brackets

Commands

:w / :q / :wqSave / quit / both
:e fileOpen file
:split / :vsplitHorizontal / vertical split
:terminalEmbedded terminal
:TreeFile tree sidebar
:grep patternSearch across files
:Gblame / :GdiffGit blame / diff
:Glog / :GcommitGit log / interactive commit
:Gadd / :GresetStage / unstage file
:Gstash / :GpopStash / pop changes
:NJump to line N
:bn / :bp / :b NNext / prev / Nth buffer
:cn / :cpNext / prev quickfix result
:close / :onlyClose pane / keep only current
:revisionsBrowse undo tree
:mksession / :sourceSave / restore session
:set nu / :set nonuShow / hide line numbers

Lua API

Configure and extend qe through ~/.config/qe/init.lua. The qe global exposes the full API.

qe.set_option(k, v)Set editor option
qe.bind_key(mode, key, fn)Bind a key to a Lua function
qe.get_line([row])Read a buffer line
qe.set_line(row, text)Replace a buffer line
qe.get_cursor()Returns row, col
qe.on(event, fn)Hook into editor events
qe.git_branch()Current branch name
qe.exec(cmd)Run shell command (sync)
qe.exec_async(cmd, fn)Run shell command (async)
qe.add_syntax(def)Register syntax highlighting
qe.add_command(name, fn)Register a CLI subcommand
-- Auto-format Go files on save
qe.on("BufSave", function(f)
    if f:match("%.go$") then
        local _, code = qe.exec("gofmt -w " .. f)
        if code == 0 then qe.command("e " .. f) end
    end
end)

-- Async background build
qe.bind_key("n", "<leader>m", function()
    qe.exec_async("make -j4", function(out, code)
        qe.print(code == 0 and "Build OK" or "Build failed")
    end)
end)

Macros & Registers

qa ... qRecord macro into register a
@a / @@Replay macro / last macro
"ayy / "apYank / paste named register
"+yy / "+pSystem clipboard yank / paste
:regView all registers

Leader Keys

<leader>tFuzzy file finder
<leader>bFuzzy buffer picker
<leader>rLocal revisions browser
<leader>xOpen terminal
<leader>hsStage hunk under cursor
<leader>hrRevert hunk to HEAD
<leader>gbGit blame
<leader>gdGit diff
<leader>glGit log
<leader>gcGit commit

Default leader: Space

Full documentation in the README.