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:
- config.yaml
- config.star
command:
key: "<meta-p>"
"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:
| Construct | Meaning |
|---|---|
'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:
| Key | What 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:
!bodyopens a floating terminal that runsbodythrough your$SHELLand shows its live output. Close the floater with<esc>or let the command exit on its own.!!bodyrunsbodyheadlessly 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:
- config.yaml
- config.star
command:
aliases:
fmt: "!! gofmt -w $FILE"
keymap:
bindings:
"<c-f>": ":fmt<enter>"
"aliases": {
"fmt": "!! gofmt -w $FILE",
},
"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>.
- config.yaml
- config.star
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>"
"aliases": {
"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:
- config.yaml
- config.star
command:
aliases:
# Pre-fill the prompt with an LSP hover command, ready to run.
hover: "echo {prompt}lsp<space>hover<space>"
"aliases": {
"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:
- config.yaml
- config.star
command:
aliases:
focusworkspace: "echo {prompt}workspacefocus{wait}<space>"
"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 qa … q 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:
- config.yaml
- config.star
command:
aliases:
# Replay the keys recorded into register `a`.
replaya: "echo {register}a"
"aliases": {
"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:
- config.yaml
- config.star
command:
aliases:
ternt: "terminalnewtab"
"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:
- config.yaml
- config.star
command:
history_key: "<meta-r>"
max_history: 20000
"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.