starship/starship
Prompt rendering
The end-to-end flow that turns a starship prompt invocation into a styled string the shell prints. This is the most important code path — it runs on every redraw — and its performance profile shapes a lot of the codebase.
End-to-end timeline
sequenceDiagram
participant Shell
participant Main as src/main.rs
participant Print as src/print.rs
participant Context
participant Modules as src/modules/*
participant Stdout
Shell->>Main: starship prompt --status=$? --jobs=$# ...
Main->>Main: Cli::try_parse() (clap)
Main->>Main: logger::init(); init_global_threadpool()
Main->>Print: print::prompt(properties, target)
Print->>Context: Context::new
Note over Context: read $STARSHIP_SHELL,<br/>parse starship.toml,<br/>canonicalize cwd
Print->>Print: load_formatter_and_modules
Note over Print: parse format string,<br/>collect $variable set
Print->>Modules: par_iter() over enabled modules
Modules->>Context: try_begin_scan / get_repo / exec_cmd
Modules-->>Print: Vec<Segment> per module
Print->>Print: AnsiStrings (collapse redundant escapes)
Print->>Print: wrap_colorseq_for_shell (PS1 markers)
Print->>Stdout: write!Step by step
- Parse CLI args.
clapparses the subcommand (almost alwaysprompt) and thePropertiesflags from the init script. - Initialize the logger and rayon pool.
logger::init()readsSTARSHIP_LOG.init_global_threadpool()builds a pool withmin(cpus, 8)workers (override viaSTARSHIP_NUM_THREADS). A separaterayon::spawntask cleans up old log files in the background. - Build the
Context.Context::new(args, target)reads$STARSHIP_SHELL, resolves the working directory (--path→current_dir()→$PWD→--logical-path), and parses~/.config/starship.toml. The repo and dir-contents lazy caches are not yet populated. - Pick the format string.
load_formatter_and_moduleschooses among:Target::Main→formatTarget::Right→right_formatTarget::Continuation→continuation_promptTarget::Profile(name)→ user-defined[profiles] <name>first, theninternal_profiles[<name>](e.g. the built-inclaude-codeprofile)
- Parse the format string with
StringFormatter::new, then askVariableHolder::get_variablesfor the set of$variablenames referenced. - Run modules in parallel. For the special
$allvariable,all_modules_uniq(&modules)returns allPROMPT_ORDERentries not already in the format. For each variable,handle_moduleeither:- Looks up the named module in
ALL_MODULESand dispatches viamodules::handle, - Or expands
custom/env_varto the configured user-defined entries. Modules dispatched via$allrun underpar_iter().flat_map. Per-module timing is recorded inModule.duration.
- Looks up the named module in
- Assemble segments.
Module::set_segments(...)populates each module; the synthetic root module aggregates them;ansi_strings_for_width(Some(width))emits aVec<AnsiString>. - Strip and wrap escapes.
nu_ansi_term::AnsiStrings(&segments).to_string()collapses adjacent escape codes;wrap_colorseq_for_shellrewrites them into shell-specific zero-width markers (\[\]for bash,%{%}for zsh, etc.) so the shell's prompt-width calculation is correct. - Apply target-specific tweaks. Right prompts strip newlines. Tcsh has
!and\nquirks that get patched. Fish gets a leading\x1b[Jto clear the screen (workaround for fish bugs #739, #279). - Write to stdout. The writer is
io::stdout().lock()for atomicity.
Performance levers
- Module skipping: a module that is not referenced by
format(orright_format/ the chosen profile /$allminusdisabledmodules) does not run at all. - Lazy caches on
Contextmean only the first git/dir-contents access pays the cost. - Parallelism: rayon's pool runs disjoint modules concurrently.
- Timeouts:
scan_timeout(30ms default) bounds the directory scan;command_timeout(500ms default) bounds everyContext::exec_cmd. - Selective
gixfeatures: theCargo.tomlcomment links to issue #4251 explaining how default features were trimmed for prompt latency.
The right prompt
starship prompt --right selects Target::Right, which uses right_format instead of format. After rendering, the renderer strips newlines (buf = buf.replace('\n', "")) because most shells do not allow multi-line right prompts. The shell-side wiring that draws the right prompt is shell-specific — see Init scripts.
The continuation prompt
starship prompt --continuation uses continuation_prompt from StarshipRootConfig, defaults to [∙](bright-black) . It bypasses the standard format-string path and goes through a one-line StringFormatter invocation.
The profile prompt
starship prompt --profile <name> uses Target::Profile(name). Both user [profiles] and internal_profiles are searched. Used for the Claude Code statusline today; users can register their own (e.g., a transient prompt).
Failure modes
- Bad format string: parse error is logged at
error; the prompt falls back to literal>. - Bad TOML: parse error is logged at
error; the entire config is treated as missing and defaults are used. - Module panic (very rare; modules are written to never panic): Rust's default panic behavior kicks in. The shell sees an empty prompt and starship exits with a backtrace if
RUST_BACKTRACEis set. TERM=dumb: The renderer printsStarship disabled due to TERM=dumb >and skips everything. Seeget_prompt.
Related pages
- Architecture — broader overview of the same flow.
- Format string DSL — how
formatis parsed. - Configuration — how
starship.tomlis loaded. - Init scripts — how the shell calls
starship prompt.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.