- C 94.9%
- Shell 1.4%
- Python 1.1%
- C++ 0.9%
- Makefile 0.6%
- Other 1%
|
|
||
|---|---|---|
| .github/workflows | ||
| cp | ||
| fonts | ||
| kmap | ||
| lang | ||
| libqhtml | ||
| modes | ||
| plugins | ||
| tests | ||
| tools | ||
| .clang-format | ||
| .gitignore | ||
| arabic.c | ||
| armv7l-build.sh | ||
| buffer.c | ||
| cfb.c | ||
| cfb.h | ||
| charset.c | ||
| charset.h | ||
| charsetjis.c | ||
| charsetjis.def | ||
| charsetmore.c | ||
| color.c | ||
| color.h | ||
| config.eg | ||
| configure | ||
| cutils.c | ||
| cutils.h | ||
| display.c | ||
| display.h | ||
| extras.c | ||
| fbfrender.c | ||
| fbfrender.h | ||
| haiku.cpp | ||
| html2png.c | ||
| indic.c | ||
| input.c | ||
| kmap.c | ||
| kmaps | ||
| libfbf.c | ||
| libfbf.h | ||
| libregexp-opcode.h | ||
| libregexp.c | ||
| libregexp.h | ||
| libunicode-table.h | ||
| libunicode.c | ||
| libunicode.h | ||
| LICENSE | ||
| ligatures | ||
| Makefile | ||
| qe-doc.html | ||
| qe-doc.texi | ||
| qe-manual.c | ||
| qe-manual.md | ||
| qe.1 | ||
| qe.c | ||
| qe.h | ||
| qe.tcc | ||
| qemacs.rdef | ||
| qemacs.spec | ||
| qescript.c | ||
| qestyles.h | ||
| README.md | ||
| search.c | ||
| TODO.md | ||
| tqe.c | ||
| tty.c | ||
| unicode_join.c | ||
| unicode_join.h | ||
| unifont.lig | ||
| unix.c | ||
| util.c | ||
| util.h | ||
| variables.c | ||
| variables.h | ||
| VERSION | ||
| wcwidth.c | ||
| wcwidth.h | ||
| win32.c | ||
| x11.c | ||
Evil QEmacs (eqe)
A delightfully unhinged fork of Fabrice Bellard's QEmacs that hacks a native Vim modal state machine directly into a sub-megabyte C codebase.
I don't expect anybody to actually use this. I made it because QEmacs is impossibly tiny, fast, and has incredible zero-copy buffer handling, but my muscle memory is 100% hardwired to modern Vim. Instead of doing the sane thing and using standard tools, I spent an afternoon along with various LLM models jamming a modal text editing engine into QEmacs' internal key dispatch loops.
The implementation is messy, full of hacky C pointer arithmetic, and probably offends both Emacs purists and Vim devotees alike, but whatever, I like it (:
This is an alpha, in progress. It compiles in seconds, boots in under 3ms, uses a couple of MB of RAM, and it just works, it lets you run a awesome hybrid vim/emacs setup on a potato — but it's also a moving target, and the roadmap below is honest about what's missing.
What Actually Works
- Modal state machine: Normal, Insert, Visual, and Visual Line modes wired directly into QEmacs window contexts, with live selection highlighting.
- Motions:
h/j/k/l,w/b,0/^/$,G,gg— plusESC-cancellation of pending commands, because typingd:q!should do the right thing. - Evil priority — emacs chords are ESC: in vi text windows every
Ctrl-*/Meta-*chord behaves asESC(enter/cancel to normal mode) and never reaches the emacs binding table. OnlyC-w(vim window prefix) andC-[(=ESC) are exempt; bareTAB/RET/BS/DELstill edit in insert mode and the minibuffer. Concretely, these are now unreachable from text and all act asESCinstead:- file/buffer:
C-x C-f(find-file),C-x C-s(save-buffer),C-x C-w(write-file),C-x C-v(find-alternate-file),C-x i(insert-file),C-x b/k(switch/kill buffer) — use:w [file]/:q/:wq/:xinstead; - directory:
C-x C-d(dired) can't be opened from a vi text window; - prefix arg:
C-u(universal-argument); - minibuffer (
:///?prompts):C-p/C-nhistory (useup/down, which still work),C-yelectric yank — any chord aborts the prompt back to normal mode, same asC-g.
- file/buffer:
- Operators:
x/xx,dd, and the full Visual trio:d,y,xover any selection.uundoes. - Registers with vim semantics:
pknows charwise from linewise.ddfollowed bypputs the line back below the cursor,dd j pappends it after the next line, and a charwise yank pastes after the cursor character, landing on the last pasted char. Visualpreplaces the selection without clobbering the register being pasted. - Search:
/forward,?backward,n/Nto repeat, with vim-stylewrapscanso searches wrap around the buffer instead of silently giving up. - Substitute:
:%s/old/new/[g]and:s/old/new/, including patterns and replacements that contain spaces. - Indenting:
>>and<<with:set sw=N. - Window chords:
C-w w/C-w C-wcycle focus,C-w h/j/k/ljump directionally — vim-style, withC-w ESCcancelling the chord. They work from text buffers and from the Dired pane (which isn't vi-modal; the binding table covers it), soC-w wping-pongs focus between directory and buffer. (Note:C-x C-dno longer opens dired from a vi text window —C-xisESCthere now.) - Ex command line:
:w [file],:q,:q!,:wq,:x,:<N>(goto line),:set sw=N,:%s/.../.../[g],:wisdom [N](:quotesalias; random coder/philosophy quote popup,Nis 0-based index,qto close — including from normal mode if focus moved elsewhere). - Wisdom over help:
F1,F2 wisdom, or:wisdomshows a random quote (M-xas a chord is pureESCnow, so driveexecute-commandviaF2). The old QEmacsC-hhelp tree is gone (it never fired on standard terminals anyway —^Harrives asKEY_DEL; alldescribe-*/manual/faq commands remain onM-xwith no key bindings);help-for-helpwas deleted. - Terminal theme auto-detect: queries your terminal's foreground/background via OSC 10/11 at startup and adopts the palette, falling back to peach-on-black if the terminal doesn't answer.
Not Yet (the roadmap)
- Numeric counts (
5dd,3w), theemotion, thecoperator, operator+motion combos (dw,d$), and text objects. :e, splits (:sp/:vsp), buffer cycling.- Vi navigation in hex mode (wandering firmware images with
hjklandr-replacing bytes is the dream). - Vim-aligned
w/bword semantics (currentlywstops at the end of the word, not the start of the next one). - Visual block (
Ctrl+v— currentlyC-vaborts to normal mode per the evil-priority rule, so visual block will need a non-Ctrlbinding or an explicit carve-out).
The Numbers
Measured on x86-64 (Debian, gcc-14/clang-19, this repo's default clang config). Your mileage may vary, but probably not by much:
teqe (tiny) |
eqe (full) |
|
|---|---|---|
| binary size, x86-64 stripped | 319 KB | 1.38 MB |
| binary size, ARM static (Miyoo) | 838 KB | 1.55 MB |
| boot to first frame (pty, median of 15) | 1.6 ms | 2.0 ms |
| resident RAM while idle (VmRSS) | 2.2 MB | 3.3 MB |
| peak memory (VmPeak) | 3.8 MB | 4.9 MB |
| clean compile | 2.7 s | 12.5 s |
Native Build (x86_64 / amd64)
Requires nothing but a standard C toolchain and libc:
./configure
make
sudo make install
This builds two terminal binaries:
eqe— the full build (all modes, scripting, languages, docs).teqe— the tiny build (CONFIG_TINY,-Os): the one that fits on a handheld.
Old muscle memory still works: make qe and make tqe are aliases for eqe and teqe. make install installs the binary as qemacs with eqe and qe symlinks.
Miyoo Mini Plus (armv7l)
The whole point. The Miyoo ships an old glibc, so the binaries are cross-compiled statically linked — one file, no libc drama on device:
./armv7l-build.sh teqe # tiny build (838 KB static ARM)
./armv7l-build.sh eqe # full build (1.55 MB static ARM)
Requires the gcc-arm-linux-gnueabihf cross toolchain (apt install gcc-arm-linux-gnueabihf).
You will see linker warnings like:
warning: Using 'getpwent' in statically linked applications requires
at runtime the shared libraries from the glibc version used for linking
These come from glibc's NSS-based user lookups (getpwent/getpwnam in the home-directory code) and are benign for this use — the binary runs fine on the device. Ignore them.
Then copy the binary over and run it (scp teqe mini:/media/sdcard/, or however you shuffle files onto yours). Vi mode is on by default: normal mode at startup, i to insert, ESC (or C-c / any Ctrl/Meta chord) to get back.
Testing
There's a real test suite, because "trust me bro it works on my machines" is not a regression strategy:
make test
85 pty-driven tests drive the actual binary through a virtual terminal — motions, pending commands, search wrap, substitute, visual yank/delete/put, register types, ex commands, emacs-chords-as-ESC, even status-line contents. The harness answers the terminal's OSC color queries and reconstructs the screen, so tests are deterministic. See tests/test_vi.py for the catalog (--list, name/category filters, --binary).
Heritage
Everything upstream QEmacs does still works underneath: huge-file editing via mmap, full Unicode with bidi and Indic/Arabic scripts, in-place hex editing, shell/compile mode with a real VT100 terminal, X11 build (xqe), and more. Read the upstream manual at qe-doc.html for the Emacs half of the family tree.
QEmacs is MIT licensed — see the LICENSE file — and was started in 2000 by Fabrice Bellard and Charlie Gordon. All the modal crimes here are mine. Upstream lives at github.com/qemacs/qemacs.