Lisp Machine ideas, and Emacs
Several of Jonquil's features come from the Lisp Machines, the MIT CADR and Symbolics Genera. This page says what those machines were, how Emacs relates to them, and where Jonquil sits next to both.
What a Lisp Machine was
A Lisp Machine was a whole computer where everything was Lisp: the operating system, the file system, the network, the window system and your own programs, all live objects in one running image. Any of it could be inspected, changed or debugged while it ran, and the environment explained itself. You could ask a key what it does, click a result to open the object, and when an error happened the debugger opened with the stack still there. The Lisp Machine museum documents both systems in depth.
How Emacs relates
The Lisp Machines had their own Emacs-style editor, Zwei (Zmacs), written in Lisp and running inside that live system. GNU Emacs came out of the same MIT culture and keeps the central idea at a smaller size: its core is a Lisp interpreter, so the editor is a running Lisp program you can change from inside itself. It runs as an application on an ordinary operating system, though, so your files, processes and network aren't Lisp objects. You can think of Emacs as the Lisp Machine's live, self-describing environment without the rest of the computer.
Where Jonquil sits
Jonquil heads the same way, and a lot of what it takes from the Lisp Machines Emacs took first:
| Jonquil | Emacs |
|---|---|
| Describe Key, Key Bindings, Recent Keys | C-h k, C-h b, C-h l |
| The keys for the symbol at the caret, in the status bar | eldoc shows the signature, not the keys; Genera's mouse documentation line, which named each click's action on the thing under the pointer |
| The command palette (Cmd/Ctrl+Shift+P) | M-x |
| The locations list and F8 / Shift+F8 | next-error, xref |
| Find Usages in Project, by how each place uses the name and which var it names | xref-find-references (M-?); Genera's who-calls, resolved from each file's ns form rather than from a callers database |
| Save the layout, Use layout: name | window-configuration-to-register and jump-to-register; Genera's Select key brought up a whole activity |
| Switch to a recent tab, Go back to the tab used before | switch-to-buffer, most recent first |
| Switch to a recent panel, Cmd/Ctrl+Alt+1 to 8 for each panel | none built in: C-x o cycles windows; Genera's Select key, where Select L brings up the Lisp Listener, is where this comes from |
| The docs panel: a symbol's doc or a guide page beside the code, with Back, Forward and bookmarks | describe-function and the *Help* buffer, with l and r to go back and forward; Genera's Document Examiner, whose reading list and cross-references this follows |
| The Messages panel: agent actions, files changed on disk, connections, with an unread count | the *Messages* buffer, which keeps the echo area's lines; Genera's notifications and Converse are where this comes from |
| Time the form at the caret | benchmark-run |
| Count how often each line runs, beside the code | profiler-start's call tree and elp, which sample or time functions; Genera's metering and gcov-style line counts are closer |
| Live console results: click to inspect, right-click verbs | none built in: CIDER inspects the last result; Genera's presentations are where this comes from |
| Trace the function at the caret, the Trace panel | trace-function and the *trace-output* buffer; CIDER's cider-toggle-trace-var |
| The Processes panel: the fibers your program spawned, Interrupt and Kill | list-processes (M-x proced for OS processes); Genera's Peek, which showed every process and let you arrest or kill one |
| Compare with the file on disk, git HEAD, another tab or the last Run, Take the other version here | diff-buffer-with-file, vc-diff and ediff's per-hunk copy; Genera's Compare Files |
| The changed-form marks, Run the forms changed since they last ran | none built in: diff-hl marks lines changed since the last commit, not since the last eval |
| Change sets: the changed forms as a named, reviewable bundle you can apply to the REPL or revert | none built in: git stash and branches work on whole files and never touch the running Lisp |
| Show the last error's frames and locals | the *Backtrace* buffer (debug-on-error) |
| Break on error, Continue | debug-on-error while the error is live: e evaluates in a frame, c continues |
| Toggle Breakpoint (F9): pause at a line, live at once | debug-on-entry and Edebug's breakpoints (edebug-defun, then b), which instrument a function the same way |
| Step Into (F7), Step Over (F10), Step Out (Shift+F7) in forms holding a breakpoint, or every function of a tab instrumented for stepping | Edebug stepping (SPC to the next stop point, o to step out), within instrumented definitions |
| Retry, Edit and retry, Define and retry | none close: the Elisp debugger resumes a live stack, which an unwound one can't |
| Save World, Restore World: your session's data, one world per project | none close: desktop-save keeps buffers, not values; Genera's Save World wrote the whole machine, this writes data vars only |
| Open last session's replay: what the project evaluated last time, as a script in a new tab that runs only when you Run it | none close: desktop-save keeps buffers, not the forms you evaluated |
| Plugins written in Clojure, in the settings folder | Emacs Lisp packages |
Three things set it apart:
- Your code runs in the editor's own runtime. Emacs is extended in Emacs Lisp, a dialect separate from the language you're working in. Jonquil is written in Clojure on Jolt, and your Jolt code runs on that same runtime: inline in Jonquil's process for a loose file, or in a Jolt child process for a project (see Dialects and runtimes). The inspector, its history and Time the form look at your program's real values, not the editor's.
- The debugger reads that runtime. Jolt's
jolt.debugkeeps the frames and locals of a throw, and the Frames panel (Show the last error's frames and locals, see Help and the inspector) lists them for your own code: a frame opens at its line, a local opens in the inspector, and a form typed in the panel evaluates with that frame's locals bound. With Break on error the panel opens before the stack unwinds, and a form runs on the thread that threw, inside its bindings. It needs a Jolt release withjolt.debug. Emacs debugs Emacs Lisp well, but debugging Clojure there goes through CIDER and nREPL, a separate layer. - It stays a Clojure IDE. Emacs turns everything into a buffer: mail, a shell, a file manager. Jonquil takes Lisp Machine ideas only where they help you write and understand Clojure code, and leaves the rest.
This page is docs/guide/lisp-machine.md in the Jonquil repository.