Skip to main content

Command Prompt

One prompt drives the whole editor. If it isn't typing into a buffer, it's a command: open a file, split a window, run a build, jump to a definition. Each one is a single thing you can type, bind to a key, or chain into an alias.

The payoff is speed that compounds. The fuzzy finder reaches any command from the shortest sequence that singles it out, and commands are named in consistent themed groups, so those sequences are predictable and settle into muscle memory. You don't burn a key binding on every action; you learn one prompt and it keeps getting faster.

This page covers how to open the prompt, how Rune parses what you type, how to drive the fuzzy finder, and how to fire shell commands at will.

Open the command prompt

By default, the activation key depends on editor.mode: a modal editor opens the prompt with :, and a modeless editor with <ctrl-space>. Press that key from any editor or buffer and a one-line prompt opens at the bottom of the screen. Type the command name, then any arguments separated by spaces, then <enter>:

:edit /etc/hosts
:split
:tabclose

Press <esc> or <ctrl-c> to dismiss the prompt without running anything. Press <tab> while typing to complete the command name; press it again to cycle through suggestions.

The key is configurable under command.key, overriding the mode-based default:

command:
key: "<meta-p>"

The choice of key matters more than it looks. In modeless mode a bare printable key would be swallowed by the buffer, so the key must carry a ctrl or alt modifier. Rune rejects anything else and falls back to <ctrl-space>. In exo (exoeditor) mode there is no safe default at all: the prompt key has to be one your guest editor won't capture. :, for instance, is a poor choice with vim, which swallows it before Rune sees it; pick a modified combination like <ctrl-space> instead. See the command-prompt configuration for the full rules and recommended combinations.

The rest of this page uses : as shorthand for "open the prompt".

Arguments and quoting

Rune splits the line on whitespace into a command name and its arguments. Two characters change that behaviour:

ConstructMeaning
'foo bar'One literal argument, no escapes.
"foo bar"One argument; \", \\, and \<newline> are escapes.
\<space>A literal space inside an otherwise unquoted argument.

So these all dispatch the same single-argument edit command:

:edit "my file.go"
:edit 'my file.go'
:edit my\ file.go

Everything else ($, *, ?, |, &, (, ), <, >) passes through verbatim. The prompt does not glob and does not interpret shell operators. That's intentional: each command decides what its arguments mean.

If you need a *.go glob or a real pipe, hand the line to a shell with ! or !! (see below).

Edit the prompt with <shift-esc>

The prompt line is fine for short commands, but editing a long or intricate one a character at a time is tedious. Press <shift-esc> to drop the prompt into edit mode: the line you have typed so far becomes a buffer you edit with a full Rune editor, with cursor motions, word jumps, selection, yank and paste, undo, and auto-pairing all available.

The editor follows your editor.mode: modal mode gives you modal editing with normal-mode motions and text objects, and modeless mode gives you the modeless editor. In exo mode the prompt cannot host your external editor, so it uses your fallback editor (editor.exo.fallback), either modal or modeless. Either way you are editing the command itself, not a file, so the buffer is the single line that will be dispatched.

While edit mode is active, completion, history, and the manual are suspended so your keystrokes go straight to the editor. To leave it:

KeyWhat it does
<shift-esc>Exit edit mode and return to the normal prompt.
<tab>Exit edit mode and return to the normal prompt.
<ctrl-c>Exit edit mode and return to the normal prompt.
<enter>Exit edit mode and dispatch the command.

On exit the edited line is replayed through the prompt as if you had pasted it, so completions and argument parsing are recomputed from the final text. This makes <shift-esc> the fast way to fix the middle of a long command: jump to the word, change it with a motion, and press <enter>.

Variables

Rune expands $VAR placeholders against the current editor state when a command runs. So this works:

:edit $FILE_DIR/README.md

$FILE_DIR resolves to the directory of the focused file. The full set of variables ($FILE, $LINE, $WORD, $WORKSPACE_PATH, $1..$9, and POSIX operators like ${VAR:-default} and ${FILE%.go}_test.go) lives in Aliases.

Run shell commands with ! and !!

Two built-in commands hand the rest of the line to a real shell:

  • !body opens a floating terminal that runs body through your $SHELL and shows its live output. Close the floater with <esc> or let the command exit on its own.
  • !!body runs body headlessly through Rune's built-in shell interpreter. Stdout is discarded; stderr surfaces only if the command fails. Use this for side effects whose output you don't need to see.

Both forms accept pipelines, redirections, command substitutions, and variable references; they're real shells:

:! rg --color=always TODO
:!! git fetch origin
:! echo "$(date) on $(hostname)"
:!! mkdir -p .rune/cache

Both forms run on whichever host the focused workspace lives on, so the same :! ls works in a local workspace and in one mounted over SSH; Rune routes the command to the workspace's executor.

Bind a command to a key

Once a command (built-in or alias) does what you want at the prompt, bind it to a key so you don't have to type it:

command:
aliases:
fmt: "!! gofmt -w $FILE"
keymap:
bindings:
"<c-f>": ":fmt<enter>"

The binding is a literal key sequence: open the prompt, type fmt, press <enter>. See Key combination syntax for the full grammar of <c-f>-style tokens.

Replay keys as macros with echo

Some workflows can't be expressed as a single command and its arguments; they need a small sequence of keystrokes, possibly opening the prompt and pre-filling it. The echo command replays a sequence of keys back into the event loop exactly as if you had typed them. This turns an alias or key binding into a reusable macro.

The sequence uses the same key syntax as bindings: plain characters stand for themselves, and non-character keys are spelled in angle brackets such as <space>, <enter>, and <esc>.

command:
aliases:
# Open the prompt and type `edit `, leaving the cursor ready
# for you to fuzzy-search a file to open in a new tab.
tabnew: "echo {prompt}edit<space>"

The {prompt} instruction

{prompt} opens the command prompt without assuming which key is bound to it. Because the activation key is configurable, hard-coding : would break for anyone using a different key; {prompt} always opens the prompt regardless of command.key. Everything after it in the sequence is typed into the freshly opened prompt:

command:
aliases:
# Pre-fill the prompt with an LSP hover command, ready to run.
hover: "echo {prompt}lsp<space>hover<space>"

Leaving the trailing <space> (and omitting <enter>) stops with the command staged but not yet dispatched, so you can supply an argument or review it before pressing <enter> yourself.

The {wait} instruction

{wait} pauses the replay until the prompt's auto-completer has finished populating its list. Interleave it when a later keystroke depends on the completion results being ready, for example, selecting the first match after typing a query:

command:
aliases:
focusworkspace: "echo {prompt}workspacefocus{wait}<space>"

Registers

{register}X expands the contents of register X (the same registers the editor yanks into) as further key sequence, so you can replay a recorded macro by name. Register expansion is recursive and guards against cycles, and you cannot echo a register while it is actively being recorded.

In the modal editor, normal mode records a macro into a register with q{reg} and replays it with @{reg} (for example qaq to record, @a to play). That recording is stored in the same register, so {register} replays exactly those keys.

For example, record a macro into register a with qa ... q, then wrap it in an alias so the recorded keys replay every time you run it:

command:
aliases:
# Replay the keys recorded into register `a`.
replaya: "echo {register}a"

You can interleave registers with literal keys and the other instructions, for example echo {prompt}edit<space>{register}a<enter> to open the prompt and replay register a as the argument.

Build muscle memory with the fuzzy finder

The prompt's filter is a fuzzy finder, and you can lean on it instead of binding a key for everything. Say you want a new terminal tab but haven't bound terminalnewtab under command.key_bindings. Open the prompt, type enough letters to single out the command, and press <enter>:

:ternt<enter>

The fuzzy search is deterministic: ternt always yields terminalnewtab at the top of the list, so :ternt<tab> resolves to it every time. The first time you reach for a command, spend 30 seconds finding the shortest sequence that ranks it first. From then on you type that sequence on reflex, faster than hunting through a menu, and without spending a key binding.

This is why built-in commands share consistent prefixes, or "themes": terminal*, tab*, window*, and so on. The grouping is deliberate, so once you know the theme you can guess the prefix and let the fuzzy finder do the rest.

One snag: install an extension and a new command may suddenly answer the sequence your fingers have memorized, because it fuzzy-matches with a higher rank. The fix is an alias named exactly after your memorized sequence, pointing at the command you mean:

command:
aliases:
ternt: "terminalnewtab"

Now :ternt<enter> always dispatches terminalnewtab, no matter what else a fresh extension adds to the list.

Re-run a previous command

Press <meta-r> or run the history command to fill the prompt with every line you've previously dispatched, newest first. The prompt now fuzzy-searches that history instead of the command list, and because each entry is the full line (command plus its arguments) you can find and re-run a complex command without retyping it. Type to narrow the list, press <tab> to move through the matches, and <enter> to run the highlighted entry.

History persists across restarts and is capped at the most recent entries. Both the key bound to the history command and the cap are configurable:

command:
history_key: "<meta-r>"
max_history: 20000

See also

  • Aliases: define your own commands, plus $FILE, $1, ${VAR:-default}, and the rest of the expansion grammar.
  • Tasks: long-running commands pinned as tabs or status bars instead of one-shot floaters.
  • Key combination syntax: the spelling of bindings like <ctrl-r> and <m-enter>.
  • Exoeditor: wire a guest terminal editor into the same command machinery.
Ask Rune Agent