Prompt¶
Xonsh ships with two REPL engines. Historically, they have been referred to in xonsh as “shells” or “prompts”, but they will likely be renamed in the future, as they are in fact REPL (read–eval–print loop) engines that power all user interaction with the terminal. These are:
prompt-toolkit or ptk (
$SHELL_TYPE='prompt_toolkit') — the recommended, full-featured engine built on prompt_toolkit. It provides syntax highlighting, multi-line editing, completion menus, custom key bindings, and more. It is included when installing the full package (pip install 'xonsh[full]').readline (
$SHELL_TYPE='readline') — a minimal fallback used when ptk cannot be started for some reason (for example, whenprompt_toolkitis not installed).
This page describes features and customization options of the ptk engine. For the underlying library reference, see the prompt_toolkit official docs.
Customizing the Prompt¶
Customizing the prompt by modifying $PROMPT, $RIGHT_PROMPT or
$BOTTOM_TOOLBAR is probably the most common reason for altering an
environment variable.
The $PROMPT variable can be a string, or it can be a function (of no
arguments) that returns a string. The result can contain keyword arguments,
which will be replaced automatically:
@ $PROMPT = '{user}@{hostname}:{cwd} @ '
snail@home:~ @ # it works!
@ $PROMPT = lambda: '{user}@{hostname}:{cwd} @> '
snail@home:~ @> # so does that!
By default, the following variables are available for use:
user: The username of the current userhostname: The name of the host computercwd: The current working directory. Use$DYNAMIC_CWD_WIDTHto set a maximum width and$DYNAMIC_CWD_ELISION_CHARfor the elision character.short_cwd: A shortened form of the current working directory; e.g.,/path/to/xonshbecomes/p/t/xonshcwd_dir: The dirname of the current working directory, e.g./path/to/cwd_base: The basename of the current working directory, e.g.xonshenv_name: The name of active virtual environment, if any.env_prefix: Prefix characters for active virtual environment (default"(").env_postfix: Postfix characters for active virtual environment (default") ").curr_branch: The name of the current git branch, if any.branch_color:{BOLD_GREEN}if the current git branch is clean, otherwise{BOLD_RED}. Yellow if undetermined.branch_bg_color: Like{branch_color}, but sets a background color.prompt_end:@#if the user has root/admin permissions,@otherwise.current_job: The name of the command currently running in the foreground.gitstatus: Informative git status, like[main|MERGING|+1…2]. Seexonsh.prompt.gitstatusfor customization options.localtime: The current local time, formatted withtime_format.time_format: A time format string, defaulting to"%H:%M:%S".last_return_code: The return code of the last issued command.last_return_code_if_nonzero: The return code if non-zero, otherwiseNone.
Colors¶
Xonsh supports colored output in prompts, print_color, and printx.
Color keywords such as {GREEN} or {BOLD_BLUE} can be used in prompt
strings and with color printing functions.
Color Names¶
RESET: Resets any previously used styling.COLORNAME: Inserts a color code for the following basic colors, which come in regular (dark) and intense (light) forms:BLACKorINTENSE_BLACKREDorINTENSE_REDGREENorINTENSE_GREENYELLOWorINTENSE_YELLOWBLUEorINTENSE_BLUEPURPLEorINTENSE_PURPLECYANorINTENSE_CYANWHITEorINTENSE_WHITE
DEFAULT: The color code for the terminal’s default foreground color.#HEX: A#before a len-3 or len-6 hex code will use that hex color, or the nearest approximation that that is supported by the shell and terminal. For example,#fffand#fafad2are both valid.BACKGROUND_may be added to the beginning of a color name or hex color to set a background color. For example,BACKGROUND_INTENSE_REDandBACKGROUND_#123456can both be used.bg#HEXorBG#HEXare shortcuts for setting a background hex color. Thus you can setbg#0012abor the uppercase version.
Color Modifiers¶
BOLD_— increases font intensity. E.g.BOLD_RED,BOLD_#112233.FAINT_— decreases font intensity. E.g.FAINT_YELLOW.ITALIC_— switches to italic. E.g.ITALIC_BLUE.UNDERLINE_— adds underline. E.g.UNDERLINE_GREEN.SLOWBLINK_— slow blinking text. E.g.SLOWBLINK_PURPLE.FASTBLINK_— fast blinking text. E.g.FASTBLINK_CYAN.INVERT_— swaps foreground and background. E.g.INVERT_WHITE.CONCEAL_— hides text (may not be widely supported). E.g.CONCEAL_BLACK.STRIKETHROUGH_— draws a line through text. E.g.STRIKETHROUGH_RED.
Each modifier has an OFF variant to disable it: BOLDOFF_, FAINTOFF_,
ITALICOFF_, UNDERLINEOFF_, BLINKOFF_, INVERTOFF_, CONCEALOFF_,
STRIKETHROUGHOFF_.
Modifiers can be combined: BOLD_UNDERLINE_INTENSE_BLACK.
Additional Prompt Variables¶
You can make use of additional variables beyond the defaults by adding them to
the PROMPT_FIELDS environment variable. The values in this dictionary should
be strings (which will be inserted into the prompt verbatim), or functions
(which will be called each time the prompt is generated, and the results
of those calls will be inserted into the prompt). For example:
snail@home ~ @ $PROMPT_FIELDS['test'] = "hey"
snail@home ~ @ $PROMPT = "{test} {cwd} @ "
hey ~ @
hey ~ @ import random
hey ~ @ $PROMPT_FIELDS['test'] = lambda: random.randint(1,9)
3 ~ @
5 ~ @
2 ~ @
8 ~ @
Here is an example — a random emoji before the prompt character:
from xonsh.completers.emoji import get_emoji_cache
$PROMPT_FIELDS['random_emoji'] = lambda: @.imp.random.choice(get_emoji_cache())[0]
$PROMPT = $PROMPT.replace("{prompt_end}", "{random_emoji}{prompt_end}")
snail@home ~ 🥗 @ # It helps to visually
snail@home ~ 🍎 @ # identify lines
snail@home ~ 🧀 @ # in your scrollback history
Environment variables and functions are also available with the $
prefix. For example:
snail@home ~ @ $PROMPT = "{$LANG} >"
en_US.utf8 >
Note that some entries of the $PROMPT_FIELDS are not always applicable, for
example, curr_branch returns None if the current directory is not in a
repository. The None will be interpreted as an empty string.
But let’s consider a problem:
snail@home ~/xonsh @ $PROMPT = "{cwd_base} [{curr_branch}] @ "
xonsh [main] @ cd ..
~ [] @
We want the branch to be displayed in square brackets, but we also don’t want
the brackets (and the extra space) to be displayed when there is no branch. The
solution is to add a nested format string (separated with a colon) that will be
invoked only if the value is not None:
snail@home ~/xonsh @ $PROMPT = "{cwd_base}{curr_branch: [{}]} @ "
xonsh [main] @ cd ..
~ @
The curly brackets act as a placeholder, because the additional part is an ordinary format string. What we’re doing here is equivalent to this expression:
" [{}]".format(curr_branch()) if curr_branch() is not None else ""
Multiline Prompt¶
When you enter a multi-line statement (for loop, if block, etc.),
xonsh displays a continuation prompt on each subsequent line. The pattern
is controlled by $MULTILINE_PROMPT (default " ").
The value is repeated to fill the width of the main prompt. It can be a plain string, a string with color markup, or a callable:
prompt @ $MULTILINE_PROMPT = '~*'
prompt @ for i in range(3):
~*~*~*~* print(i)
~*~*~*~*
Both xonsh color keywords ({RED}) and ANSI escape codes (\033[31m)
are supported.
Callable with line_number and width¶
When $MULTILINE_PROMPT is a callable, it receives two keyword arguments:
line_number— the line number of the continuation line. The main prompt is line 1, so the first continuation isline_number=2.width— the visible width (in columns) of the main prompt.
This lets you render unique content per line, for example line numbers:
_ml_colors = ['{CYAN}', '{GREEN}', '{YELLOW}', '{BLUE}', '{PURPLE}', '{RED}']
def _multiline(line_number, width):
c = _ml_colors[line_number % len(_ml_colors)]
return f'{c}{line_number:>{width - 2}}|{{RESET}} '
$MULTILINE_PROMPT = _multiline
Result (each line is a different color):
prompt @
2| for i in range(5):
3| if i > 2:
4| print(i)
5|
Existing callables that accept no arguments continue to work.
Custom Keybindings¶
For the list of default key bindings shipped with xonsh, see Keyboard Shortcuts.
The prompt_toolkit shell has a registry for handling custom keybindings. You
may not like the default keybindings in xonsh, or you may want to add a new key
binding.
This section walks you through how to do this using prompt_toolkit tools to
define keybindings and warns you about potential pitfalls.
All of the code below can be entered into your Run Control File.
This will let you hook directly into the prompt_toolkit keybinding manager. It will not stop you from rendering your
prompt completely unusable, so tread lightly.
Control characters¶
Some ASCII control characters are widely used, and it is generally not recommended to override them. Additionally, certain keybindings are used by xonsh and may affect functionality if changed.
Keystroke |
ASCII control representation |
Default command |
|---|---|---|
|
|
Run command |
|
|
Indent, autocomplete |
|
Backwards history search |
|
|
SIGSTOP current job |
|
|
SIGINT current job |
Useful imports¶
There are a few useful prompt_toolkit tools that will help us create better
bindings:
from prompt_toolkit.keys import Keys
from prompt_toolkit.filters import Condition, EmacsInsertMode, ViInsertMode
Custom keyload function¶
We need our additional keybindings to load after the shell is initialized, so we
define a function that contains all of the custom keybindings and decorate it
with the appropriate event, in this case on_ptk_create.
We’ll start with a toy example that just inserts the text “hi” into the current line of the prompt:
@events.on_ptk_create
def custom_keybindings(bindings, **kw):
@bindings.add(Keys.ControlW) # or just "c-w" string
def say_hi(event):
event.current_buffer.insert_text('hi')
Put that in your Run Control File, restart xonsh and then see if
pressing Ctrl-w does anything (it should!)
What commands can keybindings run?¶
Pretty much anything! Since we’re defining these commands after xonsh has
started up, we can create keybinding events that run subprocess commands with
hardly any effort at all. If we wanted to, say, have a command that runs ls
-l in the current directory:
from prompt_toolkit.application import run_in_terminal
@events.on_ptk_create
def custom_keybindings(bindings, **kw):
@bindings.add(Keys.ControlP)
def run_ls(event):
def _task():
ls -l
run_in_terminal(_task)
run_in_terminal(func) (imported from prompt_toolkit.application)
is the canonical prompt_toolkit idiom for running code that writes
to STDOUT from a keybinding. It temporarily hides the prompt, runs
your function (which can freely print or launch subprocesses), then
redraws the prompt — including $RIGHT_PROMPT and $BOTTOM_TOOLBAR
— above the captured output. Do not use event.cli.renderer.erase()
for this purpose: it resets the renderer’s height bookkeeping and
$BOTTOM_TOOLBAR (and sometimes $RIGHT_PROMPT) will disappear
until the next keypress.
Restrict actions with filters¶
Often we want a key command to only work if certain conditions are met. For
instance, the <TAB> key in xonsh brings up the completions menu, but then it
also cycles through the available completions. We use filters to create this
behavior.
A few helpful filters are included with prompt_toolkit, like
ViInsertMode and EmacsInsertMode, which return True when the
respective insert mode is active.
But it’s also easy to create our own filters that take advantage of xonsh’s beautiful strangeness. Suppose we want a filter to restrict a given command to run only when there are fewer than ten files in a given directory. We just need a function that returns a Bool that matches that requirement and then we decorate it! And remember, those functions can be in xonsh-language, not just pure Python:
from prompt_toolkit.filters import Condition
@Condition
def lt_ten_files():
return len(g`*`) < 10
Note
See the tutorial section on globbing for more globbing options.
Now that the condition is defined, we can pass it as a filter keyword to a keybinding definition:
@events.on_ptk_create
def custom_keybindings(bindings, **kw):
@bindings.add(Keys.ControlL, filter=lt_ten_files)
def ls_if_lt_ten(event):
def _task():
ls -l
run_in_terminal(_task)
With both of those in your .xonshrc, pressing Control L will list the
contents of your current directory if there are fewer than 10 items in it.
Useful? Debatable. Powerful? Yes.
Edit-mode filter¶
A common use of filters is restricting a binding to a specific editing
mode — Emacs insert, Vi insert, or Vi navigation. prompt_toolkit
ships ready-made filters for each; pass them as filter to
bindings.add:
from prompt_toolkit.filters import (
EmacsInsertMode, ViInsertMode, ViNavigationMode,
)
@events.on_ptk_create
def custom_keybindings(bindings, **kw):
@bindings.add('c-l', filter=EmacsInsertMode())
def ls_in_emacs_insert(event):
def _task():
ls -l
run_in_terminal(_task)
@bindings.add('c-l', filter=ViInsertMode() | ViNavigationMode())
def ls_in_any_vi_mode(event):
def _task():
ls -l
run_in_terminal(_task)
The same key (Ctrl-L) can be bound to different actions per mode —
prompt_toolkit picks the binding whose filter is currently
true. Combine with custom Condition filters using &, |, ~
(as in ViInsertMode() & lt_ten_files) for finer-grained rules.
Pre-filling the next command¶
xonsh can pre-fill the prompt input for the next command using two environment variables. This is useful for building interactive workflows, wizards, or keybindings that prepare a command for the user to review and edit before running.
$XONSH_PROMPT_NEXT_CMD¶
Sets the text that will appear in the next prompt as editable input:
$XONSH_PROMPT_NEXT_CMD = 'git commit -m ""'
The next time the prompt appears, git commit -m "" will be pre-filled
and the user can edit it before pressing Enter.
Cursor positioning: Use the <cursor> marker to place the cursor at a
specific position:
$XONSH_PROMPT_NEXT_CMD = 'git commit -m "<cursor>"'
The marker is removed from the text and the cursor is placed at its position — in this case, between the quotes.
Example — a keybinding that prepares a git commit:
@events.on_ptk_create
def custom_keybindings(bindings, **kw):
@bindings.add('c-g')
def prepare_commit(event):
$XONSH_PROMPT_NEXT_CMD = 'git commit -m "<cursor>"'
Now pressing Ctrl-G will pre-fill the next prompt with git commit -m ""
and place the cursor between the quotes.
$XONSH_PROMPT_NEXT_CMD_SUGGESTION¶
Sets a greyed-out suggestion (like auto-suggest from history) for the next prompt. The user can accept it by pressing the right arrow key:
@ $XONSH_PROMPT_NEXT_CMD_SUGGESTION = 'git push origin main'
Unlike $XONSH_PROMPT_NEXT_CMD, this does not pre-fill the input — it only
shows a suggestion that the user can accept or ignore.
Both variables are cleared automatically after being consumed by the prompt.
Virtual Environment in Prompt¶
xonsh obeys the $VIRTUAL_ENV_DISABLE_PROMPT environment variable
as defined by virtualenv.
If this variable is truthy, xonsh will always substitute an empty string
for {env_name}. Note that unlike other shells,
$VIRTUAL_ENV_DISABLE_PROMPT takes effect immediately after being set
— it is not necessary to re-activate the environment.
xonsh also allows for an explicit override of the rendering of {env_name},
via the $VIRTUAL_ENV_PROMPT environment variable. If this variable is
set to a non-empty value, {env_name} will always render as its value,
regardless of whether a virtual environment is active. The value is used
as-is, without the usual {env_prefix} / {env_postfix} wrapping.
$VIRTUAL_ENV_PROMPT is overridden by $VIRTUAL_ENV_DISABLE_PROMPT.
When neither variable is set, {env_name} falls back to the active
environment’s name — determined, in order, from the prompt = ... field
in <venv>/pyvenv.cfg, from the venv directory name, or from
$CONDA_DEFAULT_ENV. The detected name is wrapped in {env_prefix} and
{env_postfix} (( and ``) `` by default).
For example:
@ $PROMPT = '{env_name}@ '
@ source env/bin/activate.xsh
(env) @ $VIRTUAL_ENV_PROMPT = '~~ACTIVE~~ '
~~ACTIVE~~ @ $VIRTUAL_ENV_DISABLE_PROMPT = 1
@ del $VIRTUAL_ENV_PROMPT
@ del $VIRTUAL_ENV_DISABLE_PROMPT
(env) @
Color theme¶
You can view the available styles by typing
@ xonfig styles # list styles
@ xonfig colors paraiso-dark # review how it looks
@ $XONSH_COLOR_STYLE='paraiso-dark' # set a new theme
Registering custom styles¶
If you aren’t happy with the styles provided by us (and pygments), you can create and register custom styles.
To do so, add something similar to your .xonshrc:
from xonsh.tools import register_custom_style
mystyle = {
"Literal.String.Single": "#ff88aa",
"Literal.String.Double": "#ff4488",
"RED": "#008800",
}
register_custom_style("mystyle", mystyle, base="monokai")
$XONSH_COLOR_STYLE = "mystyle"
You can check xonfig colors for the token names. The base style will be used as a fallback for styles you don’t set - pick one from xonfig styles (default is used if omitted).
OSC 7 — Working directory reporting¶
Xonsh automatically emits OSC 7 escape sequences on every directory change and at shell startup. This is an invisible signal that tells the terminal emulator what the current working directory is.
Terminals use it for:
Opening new tabs/splits in the same directory
macOS Terminal.app session restoration after reboot
Showing the path in the terminal title bar or tab
This works out of the box on most modern terminals including macOS Terminal.app, iTerm2, GNOME Terminal, Windows Terminal, WezTerm, and Kitty. No configuration is needed.
See also¶
Environment Variables – prompt-related environment variables
Built-in Events – prompt lifecycle events (
on_pre_prompt,on_post_prompt)