A Fork of QEmacs that hacks a native Vim/Neovim inspired modal state machine directly into it
  • C 94.9%
  • Shell 1.4%
  • Python 1.1%
  • C++ 0.9%
  • Makefile 0.6%
  • Other 1%
Find a file
markmental 58f03bfd37
Some checks failed
ci / Linux (Ubuntu) (push) Has been cancelled
ci / Linux 32-bits (push) Has been cancelled
ci / macOS (push) Has been cancelled
ci / macOS-1 (push) Has been cancelled
ci / openbsd (push) Has been cancelled
ci / freebsd (push) Has been cancelled
Add new wisdom for 0.1.1
2026-09-07 18:30:22 -04:00
.github/workflows Clang: improve C colorizer for declarations 2026-07-17 16:59:08 +02:00
cp Update mapping tables 2020-11-07 17:51:16 +01:00
fonts Initial revision 2004-05-29 10:17:37 +00:00
kmap kmap: support the entire Arabic keybord 2026-05-19 23:09:24 +02:00
lang Add colorizer for the Lobster language 2026-08-03 09:02:36 +02:00
libqhtml Improve style handling 2026-07-05 20:42:10 +02:00
modes Add new wisdom for 0.1.1 2026-09-07 18:30:22 -04:00
plugins Remove references to global qe_state (#45) 2024-12-01 23:34:00 +01:00
tests Prioritize vim/evil keybinds over emacs ones, bump version to 0.1.1. code cleanup 2026-09-07 18:12:20 -04:00
tools fix clang 17 compilation warnings 2026-03-14 21:33:53 +01:00
.clang-format Prioritize vim/evil keybinds over emacs ones, bump version to 0.1.1. code cleanup 2026-09-07 18:12:20 -04:00
.gitignore Update README for Evil Quick Emacs as well as prepare build files 2026-09-04 19:28:06 -04:00
arabic.c Change license to MIT 2023-03-05 02:04:02 +01:00
armv7l-build.sh Make visual mode, yanking and the ARMv7l build more robust 2026-09-06 01:52:09 -04:00
buffer.c debugging: add qe_trace_debug to help debugging 2026-07-31 11:11:26 +02:00
cfb.c Startup: fix crash bug on set_style() from the config file 2026-07-19 15:17:31 +02:00
cfb.h Change license to MIT 2023-03-05 02:04:02 +01:00
charset.c bufed/dired: fix layout for non printing characters 2026-05-18 12:31:06 +02:00
charset.h Remove references to global qe_state (#45) 2024-12-01 23:34:00 +01:00
charsetjis.c Remove references to global qe_state (#45) 2024-12-01 23:34:00 +01:00
charsetjis.def Improve completion system 2020-10-27 08:38:12 +01:00
charsetmore.c Remove references to global qe_state (#45) 2024-12-01 23:34:00 +01:00
color.c Improve style handling 2026-07-05 20:42:10 +02:00
color.h Improve style handling 2026-07-05 20:42:10 +02:00
config.eg Initial post on github 2020-10-24 23:59:54 +02:00
configure Buffer/file: add diff-buffer-file 2026-06-04 15:08:24 +02:00
cutils.c Improve directory handling 2026-07-08 09:51:55 +02:00
cutils.h Improve directory handling 2026-07-08 09:51:55 +02:00
display.c Startup: fix crash bug on set_style() from the config file 2026-07-19 15:17:31 +02:00
display.h Fix put_status/put_error behavior (#47) 2024-12-03 22:52:55 +01:00
extras.c Remove help in favor of wisdom 2026-09-05 20:28:20 -04:00
fbfrender.c Improve find_file_next and next-error 2026-07-06 20:41:55 +02:00
fbfrender.h Change license to MIT 2023-03-05 02:04:02 +01:00
haiku.cpp Startup: fix crash bug on set_style() from the config file 2026-07-19 15:17:31 +02:00
html2png.c Improve style handling 2026-07-05 20:42:10 +02:00
indic.c Change license to MIT 2023-03-05 02:04:02 +01:00
input.c input: add select-input-method command 2026-05-20 20:35:56 +02:00
kmap.c Remove references to global qe_state (#45) 2024-12-01 23:34:00 +01:00
kmaps added more Hebrew keymaps 2007-12-09 23:31:38 +00:00
libfbf.c Change license to MIT 2023-03-05 02:04:02 +01:00
libfbf.h Change license to MIT 2023-03-05 02:04:02 +01:00
libregexp-opcode.h Add regex support for search and replace 2023-03-05 13:34:19 +01:00
libregexp.c use FALLTHROUGH instead of fallthrough to avoid clash with x11 2026-03-14 21:55:25 +01:00
libregexp.h Add regex support for search and replace 2023-03-05 13:34:19 +01:00
libunicode-table.h Add regex support for search and replace 2023-03-05 13:34:19 +01:00
libunicode.c Fix linux compilation problems 2023-03-05 14:36:44 +01:00
libunicode.h Add regex support for search and replace 2023-03-05 13:34:19 +01:00
LICENSE Improve C-like language colorizers (#53) 2024-12-14 20:27:25 +01:00
ligatures Add charset probing functions 2014-03-01 22:37:26 +00:00
Makefile Update README for Evil Quick Emacs as well as prepare build files 2026-09-04 19:28:06 -04:00
qe-doc.html Remove help in favor of wisdom 2026-09-05 20:28:20 -04:00
qe-doc.texi Remove help in favor of wisdom 2026-09-05 20:28:20 -04:00
qe-manual.c Improve documentation, configure and CI(#31) 2024-05-20 19:43:01 +02:00
qe-manual.md Simplify path constructions' 2026-07-08 10:44:54 +02:00
qe.1 Initial revision 2004-05-29 10:17:37 +00:00
qe.c Prioritize vim/evil keybinds over emacs ones, bump version to 0.1.1. code cleanup 2026-09-07 18:12:20 -04:00
qe.h Prioritize vim/evil keybinds over emacs ones, bump version to 0.1.1. code cleanup 2026-09-07 18:12:20 -04:00
qe.tcc Initial revision 2004-05-29 10:17:37 +00:00
qemacs.rdef haiku: add qemacs.rdef icon 2017-03-28 20:11:55 +00:00
qemacs.spec fixed typos, comments 2006-12-09 19:02:07 +00:00
qescript.c Simplify mode probing 2026-06-05 18:42:56 -04:00
qestyles.h dired: add dedicated mode styles 2026-07-19 23:18:27 +02:00
README.md Prioritize vim/evil keybinds over emacs ones, bump version to 0.1.1. code cleanup 2026-09-07 18:12:20 -04:00
search.c Remove help in favor of wisdom 2026-09-05 20:28:20 -04:00
TODO.md Improve style handling 2026-07-05 20:42:10 +02:00
tqe.c Start of evil vim modifications, rudimentary and basic vim mode 2026-09-04 14:23:33 -04:00
tty.c Stabilize vim features some more, allow it to handle tty colors and properly refresh after vim commands 2026-09-04 15:47:12 -04:00
unicode_join.c Fix asan crash bug (#49) 2024-12-05 12:04:58 +01:00
unicode_join.h Update copyrights 2024-07-28 00:00:03 +02:00
unifont.lig Initial revision 2004-05-29 10:17:37 +00:00
unix.c Start of evil vim modifications, rudimentary and basic vim mode 2026-09-04 14:23:33 -04:00
util.c Simplify path constructions' 2026-07-08 10:44:54 +02:00
util.h Simplify path constructions' 2026-07-08 10:44:54 +02:00
variables.c Remove help in favor of wisdom 2026-09-05 20:28:20 -04:00
variables.h Fix compilation warning 2026-06-15 14:00:49 +02:00
VERSION Prioritize vim/evil keybinds over emacs ones, bump version to 0.1.1. code cleanup 2026-09-07 18:12:20 -04:00
wcwidth.c Update glyph width methodology 2022-09-27 22:30:05 +02:00
wcwidth.h add include guard in wcwidth.h 2022-09-27 22:38:29 +02:00
win32.c Remove references to global qe_state (#45) 2024-12-01 23:34:00 +01:00
x11.c Remove help in favor of wisdom 2026-09-05 20:28:20 -04:00

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 — plus ESC-cancellation of pending commands, because typing d:q! should do the right thing.
  • Evil priority — emacs chords are ESC: in vi text windows every Ctrl-* / Meta-* chord behaves as ESC (enter/cancel to normal mode) and never reaches the emacs binding table. Only C-w (vim window prefix) and C-[ (= ESC) are exempt; bare TAB/RET/BS/DEL still edit in insert mode and the minibuffer. Concretely, these are now unreachable from text and all act as ESC instead:
    • 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 / :x instead;
    • 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-n history (use up/down, which still work), C-y electric yank — any chord aborts the prompt back to normal mode, same as C-g.
  • Operators: x/xx, dd, and the full Visual trio: d, y, x over any selection. u undoes.
  • Registers with vim semantics: p knows charwise from linewise. dd followed by p puts the line back below the cursor, dd j p appends it after the next line, and a charwise yank pastes after the cursor character, landing on the last pasted char. Visual p replaces the selection without clobbering the register being pasted.
  • Search: / forward, ? backward, n/N to repeat, with vim-style wrapscan so 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-w cycle focus, C-w h/j/k/l jump directionally — vim-style, with C-w ESC cancelling the chord. They work from text buffers and from the Dired pane (which isn't vi-modal; the binding table covers it), so C-w w ping-pongs focus between directory and buffer. (Note: C-x C-d no longer opens dired from a vi text window — C-x is ESC there now.)
  • Ex command line: :w [file], :q, :q!, :wq, :x, :<N> (goto line), :set sw=N, :%s/.../.../[g], :wisdom [N] (:quotes alias; random coder/philosophy quote popup, N is 0-based index, q to close — including from normal mode if focus moved elsewhere).
  • Wisdom over help: F1, F2 wisdom, or :wisdom shows a random quote (M-x as a chord is pure ESC now, so drive execute-command via F2). The old QEmacs C-h help tree is gone (it never fired on standard terminals anyway — ^H arrives as KEY_DEL; all describe-*/manual/faq commands remain on M-x with no key bindings); help-for-help was 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), the e motion, the c operator, operator+motion combos (dw, d$), and text objects.
  • :e, splits (:sp/:vsp), buffer cycling.
  • Vi navigation in hex mode (wandering firmware images with hjkl and r-replacing bytes is the dream).
  • Vim-aligned w/b word semantics (currently w stops at the end of the word, not the start of the next one).
  • Visual block (Ctrl+v — currently C-v aborts to normal mode per the evil-priority rule, so visual block will need a non-Ctrl binding 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.