These belong to plugins, not the core: each plugin owns its concept and offers verbs for it in its contract. They are shown because authors meet them constantly, and they show the shape every provider follows. Their exact lists will grow as the default plugins are written. Each verb that adds something makes it for its caller, so it goes when the caller goes.
Commands and keys
(commands:define (name) "doc" body …)plugin
Does. Defines a command: a procedure you can call as (name), bind to a key by its name, and run with M-x.
Underneath. A macro exported by commands. It expands to one definition, (define name (commands:register! 'name "doc" (lambda () body …))); register! adds the command to commands’ table as the caller’s entry and returns the procedure. Keys bind the name, so evaluating the form again with C-M-x changes what the key runs.
Tuna
(commands:define(say-hello)"Insert a greeting at point."(buffers:insert!"Hello, world!"))
Emacs
(defun say-hello ()"Insert a greeting at point."(interactive)(insert "Hello, world!"))
Where Tuna differs. A command goes when the plugin that defined it turns off. An Emacs function stays defined until fmakunbound or restart.
Does. Binds a key in the global map, or in a mode’s map when mode is given. command is a command’s name (a symbol) or a procedure. unbind! removes your binding, or hides another owner’s while you are on.
Where Tuna differs. A mode’s bindings are named by the mode, not by a keymap variable the mode must define. Removing the line, or turning the plugin off, removes the binding and shows the one it covered.
Modes
(modes:define-major name "doc" [parent]), (modes:define-minor name "doc")plugin
Does. Declares a major or minor mode. The declaration is how modes knows which plugin a mode came from: while that plugin is off, a buffer in its major mode is treated as Fundamental mode, and gets its mode back when the plugin returns.
Tuna
(modes:define-major git-status-mode "Shows the repository's status.")(modes:define-minor flymake-mode "Checks the buffer as you type.")
Emacs
(define-derived-mode git-status-mode special-mode "Git-Status""Shows the repository's status.")(define-minor-mode flymake-mode "Checks the buffer as you type.")
Where Tuna differs. An Emacs mode is a function that sets up the buffer when called. A Tuna mode is a declared name; what it means (bindings, faces, scoped options and hooks) is attached to the name by whoever wants to, through the verbs here.
(modes:set-major! id mode), (modes:enable-minor! id mode), (modes:disable-minor! id mode), (modes:major id)plugin
Does. Set and read a buffer’s modes. A minor mode enabled through enable-minor! is the caller’s: it is tied with on-caller-end, so it goes when the caller turns off.
Tuna
(modes:set-major! id 'git-status-mode)(modes:enable-minor! id 'flymake-mode)(modes:major id); => git-status-mode
(modes:set! 'mode option v) is described with options.
Faces
(faces:define name attributes "doc"), (faces:set! face attributes), (faces:attributes face)plugin
Does.define declares a face with its default look; set! gives it your look, as a layer, so a theme that turns off leaves the default; attributes returns the winning look, for the display. Attributes are a property list of plain data: foreground, background, bold, italic, underline. The display turns them into CSS.
Underneath. Faces are their own contract, not the display’s, because highlighters, checkers and themes use them whatever the display is.
Tuna
(faces:define flymake-error '(underline (wave "#dc322f"))"Text a checker found an error in.")(faces:set!'highlight'(foreground "#b58900"))
Emacs
(defface flymake-error '((t:underline(:style wave :color"Red1")))"Face used for marking error regions.")(set-face-attribute 'highlightnil:foreground"#b58900")
Where Tuna differs. A theme in Emacs is a set of face settings applied with enable-theme and removed with disable-theme. In Tuna any faces:set! is a layer, so every plugin’s face settings behave like a theme’s.
Buffers
The current buffer is the one text procedures act on, as in Emacs. The command loop makes the selected window’s buffer current before each command; buffers:with makes another one current for a body.
buffers:current, (buffers:with id body …)plugin
Does.current is the current buffer’s id, a dynamic value. with runs the body with id current.
Does. Insert at point, empty the buffer, delete a range, move point, and read point, size, the whole text and the current line. Positions count characters from 0.
Where Tuna differs. Emacs positions start at 1; Tuna’s at 0, as Scheme strings do.
(buffers:create name) and (buffers:view name)plugin
Does.create returns the user’s buffer of that name, made if needed; no plugin closes it. view returns the caller’s view of that name, made if needed; it goes when the caller turns off and stays across its reloads. A buffer the user made with that name is never taken by a view.
Does. List the buffers; ask whether one is live; its name, its visited file, whether it is internal (its name starts with a space); its text; kill it (the hook kill-query-hook may refuse); and scope, the current buffer’s scope, for a hook entry or layer meant for that buffer only.
(buffers:set-faces! id spans), (buffers:set-margin! id marks)plugin
Does. Give a buffer your faces, each span (from to face), or your margin marks, each (line text face). Each call replaces your whole set for that buffer; '() removes it. On a dead buffer they do nothing, so a late worker result is harmless. When the buffer is killed, every plugin’s faces and marks on it go.
Tuna
(buffers:set-faces! id '((05 flymake-error)(1217 flymake-error)))(buffers:set-margin! id '((3"!" flymake-error)))
Emacs
(let((ov (make-overlay 16 buf)))(overlay-put ov 'face'flymake-error))(overlay-put ov 'before-string(propertize "!"'display'((margin left-margin)"!")))
Where Tuna differs. Emacs makes one overlay object at a time and the package must delete its overlays itself. Tuna replaces a caller’s whole set at once, which suits a worker that computes a buffer’s spans in one go, and removes it with the caller.
Does. Called after each change (with id, position, deleted text, inserted text); after a kill (with the id); before a kill (with the id; an entry returning #f refuses).
Does. Contribute to what is drawn: a mode-line item (procedure gets a buffer id and returns a string or #f); a popup list with one selected item; hide the popup. Each is the caller’s and goes with it. Because these write core state, the next frame is drawn with them.
(layout:show! id [window]), (layout:selected-buffer)plugin
Does. Show a buffer in a window (the selected one by default); the buffer of the selected window.
Tuna
(layout:show! view)
Emacs
(pop-to-buffer view)
Processes
Processes you start (a shell, a build) are your work: they keep running when the terminal plugin turns off, reloads or is swapped, and across a clean exit of Tuna. The process service owns them.
(processes:start! id command), (processes:start-own name command)plugin
Does.start! starts the user’s process for buffer id; killing that buffer ends it. start-own starts a process the caller needs for itself (a language server), which ends when the caller does; it makes something new, hence no !.
Tuna
(processes:start! id "bash -i")(define server (processes:start-own"lsp""clangd"))
Where Tuna differs. Emacs ends its processes at exit, asking first. Tuna keeps the user’s processes and reattaches them at the next start, as tmux does.
(processes:follow! id on-output), (processes:send! id string), (processes:running? id)plugin
Does.follow! calls on-output with each new piece of the process’s output, once, in order, on the editor thread. send! writes to its input. running? asks whether it runs.
Underneath. Each process lives in a small helper program that owns its pipes and keeps its output with offsets. follow! starts the service’s reader worker as the caller, so a reload of the terminal stops the reader and the next run’s follow! resumes from the offset already delivered. Nothing is lost or repeated, across reloads and crashes.
Tuna
(processes:follow! id (lambda(text)(buffers:with id (buffers:insert! text))))(processes:send! id "ls\n")