Suggestions and completions
Two different things happen while you type, and they answer different questions.
A suggestion is the rest of a whole command you have run before, in grey after the cursor. It answers "what did I type last time?"
A completion is a list of ways to finish the word under the cursor. It answers "what are my options here?"
| Key | Does |
|---|---|
→ |
Take the grey text, or the chosen item from the list |
return |
Take the chosen item. With nothing chosen, submit the line as always |
↑ ↓ |
Choose an item in the list |
escape |
Close the list. The escape still reaches the shell |
tab |
Untouched. This is the shell's own completion |
Return is the one key here with a job it must never lose, so it is only taken once you have deliberately chosen something with an arrow. To run what you actually typed while a list is open, press escape first — or just carry on typing, and the list follows the word.
Both need shell integration, which is what reports where the prompt ends. Both can be turned off under Shell in settings.
Why tab is left alone
The shell's completion knows things ours cannot: the branches in this repository, the hosts in your ssh config, the paths on the machine at the other end of a connection, the subcommands of every tool that ships a completion script. It has been extended by every package you have installed.
Taking tab would replace all of that with a list of filenames. So tab goes
to the shell, untouched, and everything here is on keys the shell was not
using: the right arrow at the end of a line has nowhere to go, and the up and
down arrows are only taken while a list is on screen for them to move in.
That is also why nothing in the list is selected until you press an arrow. With
a preselected first item, → would insert something other than the grey text
your eye is reading.
Where suggestions come from
Two sources, because neither is enough on its own:
- The shell's history file —
~/.zsh_historyor~/.bash_history, or whereverHISTFILEpoints. It holds everything up to the last time a shell exited. zsh does not write to it during a session unless you have configured it to, so it knows nothing about the last ten minutes. - This session — eterm sees every command as it is submitted, so it knows the last ten minutes exactly. Its own record is searched first.
Directory beats recency. make deploy typed in one project and make dev
typed in another are both matches for make, and the one from the directory
you are standing in is the one you meant. Only commands eterm saw itself carry
a directory; history files do not record one.
Repeats collapse: running the same command twice in a row leaves one entry.
Where completions come from
Which of the three you get depends on where the cursor is, worked out with the same tokeniser that colours the line, so the two can never disagree about where a word begins.
-
Commands — in command position: at the start of a line, or after
|,&&,;. From PATH and the shell builtins, with commands you have actually run offered first, in the order you last used them. -
Options — when the word starts with
-. From the options that command has been given before in your history.A hand-written database of every tool's flags is the only way to know them all, and it is out of date the day it ships. What you have actually typed is a much smaller list, and a true one. The cost is honest: a flag you have never used is not offered.
-
Paths — everywhere else. From the filesystem, relative to the working directory, with directories marked by a trailing slash so the next completion can carry on inside. Hidden files appear only when the word starts with a dot.
Nothing is offered for an empty word: every command on the machine is a list, not a suggestion. Nothing is offered inside a word either — completing there would rewrite text you can see and did not ask to change.
What this cannot do
eterm does not own the input line. The shell draws it, and eterm reads the cells and types into them like a very fast person would. That has limits worth knowing:
- Accepting types the characters. For a case-insensitive match —
GITfindinggit— that means backspaces first, which you may see for a frame. - Nothing is offered when the shell is not at a prompt. Inside
vimorlessthere is no command line to complete. - A completion in the middle of a line is not offered, only at the end.
- Aliases and shell functions are invisible, both here and to the colouring. They live inside the shell, and nothing outside it can see them.
The project's own commands
cmd-shift-p in a project lists what its manifests declare, above the
application's own commands and with the file each came from beside it:
| File | Becomes |
|---|---|
package.json |
npm run <script>, or pnpm/yarn/bun if the lockfile says so |
composer.json |
composer <script> |
Makefile |
make <target> |
justfile |
just <recipe> |
Read from the directory the focused session is standing in, when the palette is opened. Move to another project and it is another project's list.
Nothing here is guessed. A command appears because a file in front of you says
it exists, which is also why a Rust project shows nothing: Cargo.toml declares
dependencies, not commands, and cargo test is already one keystroke away in
the history suggestions.
Hooks are left out — Composer's post-update-cmd, npm's prebuild — because
they run themselves. An npm script only counts as a hook when the script it
wraps exists too, so prepare and postcss are still offered.
Choosing a command runs it. It came out of your own project's file, and picking it from a list is the same deliberate act as typing it.