首页
/ Starship Bracketed Segments 预设详解:一行命令把 “via/on” 替换为统一括号分段

Starship Bracketed Segments 预设详解:一行命令把 “via/on” 替换为统一括号分段

2026-09-05 12:35:31作者:苗圣禹Peter

本文基于 Starship 仓库中的 Bracketed Segments 预设文档(docs/ar-SA/presets/bracketed-segments.md,阿拉伯语版;对应英文版为 docs/presets/bracketed-segments.md)展开,完整介绍该预设的作用、启用命令与全部配置内容,并结合 starship preset 子命令的源码实现(src/main.rssrc/print.rsbuild.rs)解析预设文件是如何被内嵌、生成和写入配置目录的。读完后,你可以一键应用该预设、读懂预设文件中每一类 format 字符串的语法,并知道如何按需微调。

Starship Bracketed Segments 预设效果截图

一、Bracketed Segments 预设是什么

Starship 的默认提示词在展示各类信息时,会带上自然语言连接词,例如运行时模块默认以 “via ⎈ v1.2.3” 的形式呈现,git 相关模块会以 “on” 等字样衔接分支信息。Bracketed Segments 预设做的事情很简单但很彻底:

它把所有内置模块的 format 改为用方括号包裹当前分段内容,取代 Starship 默认的措辞("via"、"on" 等)。

也就是说,同样的信息源,默认样式是散落的 “via/ on” 文字连接,套用该预设后每个模块都变成 [内容] 形式的独立方块,视觉上更接近 powerline/终端标签风格,各分段之间的边界也更清晰。

两个关键事实值得先明确:

  1. 该预设只覆盖各模块的 format 字符串。预设 TOML 中没有任何 disableddisplay_mode 等其他配置项(可对照 docs/public/presets/toml/bracketed-segments.toml 全文确认),因此模块的启用条件、检测逻辑、样式(style)全部维持 Starship 默认行为不变——在普通目录下看不到的模块,套用预设后依然不会出现。
  2. 它是一次性“整包替换”型预设:starship preset bracketed-segments -o <文件> 生成的 TOML 会作为完整配置文件使用,适合直接作为 starship.toml 的全部内容,而不是和已有配置逐段合并。

二、一行命令启用预设

文档给出的配置命令是:

starship preset bracketed-segments -o ~/.config/starship.toml

这条命令会把 bracketed-segments 预设的完整 TOML 直接写入 ~/.config/starship.toml。命令各部分的含义如下:

  • preset:Starship 的预设子命令,用于打印/落盘内置预设配置;
  • bracketed-segments:预设名称,必须是 Starship 编译时内嵌的预设之一;
  • -o ~/.config/starship.toml:将预设内容输出到指定文件(等价长参数为 --output),而不是打印到 stdout。

src/main.rs 中该子命令的参数定义还可以确认另外三个可用能力:

/// Prints a preset config
Preset {
    /// The name of preset to be printed
    #[clap(required_unless_present("list"), value_enum)]
    name: Option<print::Preset>,
    /// Output the preset to a file instead of stdout
    #[clap(short, long, conflicts_with = "list")]
    output: Option<PathBuf>,
    /// Forcibly overwrite the output file if it already exists
    #[clap(short, long, requires = "output")]
    force: bool,
    /// List out all preset names
    #[clap(short, long)]
    list: bool,
},

对应的实用用法:

# 不带 -o:把预设 TOML 打印到终端,可配合重定向或 cat 查看
starship preset bracketed-segments

# 列出当前二进制内置的全部预设名
starship preset -l

# 目标文件已存在时,需要显式 --force 强制覆盖
starship preset bracketed-segments -o ~/.config/starship.toml --force

force 参数带有 requires = "output" 约束,即只有配合 -o 使用才有意义;list-o 互斥。

三、预设加载机制:从源码看 preset 子命令做了什么

预设并不是运行时去某个目录读取文件,而是在编译期就被内嵌进了二进制。整条链路如下:

  1. 构建阶段:build.rs 扫描 docs/public/presets/toml/ 目录下的所有 .toml 文件(本仓库即 docs/public/presets/toml/ 下的 12 个预设),为每个文件生成 include_str! 条目,拼出 get_preset_list()get_preset_content(name) 两个函数(由 shadow 模块暴露)。文件名去掉 .toml 后缀即为预设名,所以 bracketed-segments.toml 对应预设名 bracketed-segments
  2. 运行阶段:src/print.rsPreset 类型实现 clap 的 ValueEnum,其候选值直接来自 shadow::get_preset_list()preset_command 拿到预设内容后,若指定了 -o 则调用 utils::write_file_atomic 原子写入目标文件,否则写 stdout。
pub fn preset_command(name: Option<Preset>, output: Option<PathBuf>, force: bool, list: bool) {
    if list {
        println!("{}", preset_list());
        return;
    }
    let variant = name.expect("name argument must be specified");
    let content = shadow::get_preset_content(variant.0);
    if let Some(output) = output {
        if let Err(e) = crate::utils::write_file_atomic(&output, content, force) {
            eprintln!("Error writing preset to {output:?}: {e}");
            std::process::exit(1);
        }
    } else if let Err(err) = std::io::stdout().write_all(content.as_bytes()) {
        // ...
    }
}

这意味着两点实操结论:

  • 预设内容的唯一事实来源是仓库中的 docs/public/presets/toml/*.toml,文档页面展示的 TOML 与命令输出完全一致,可直接对照;
  • 仓库测试(src/print.rs)覆盖了 preset -l、各预设可正常打印、写入文件与 --force 覆盖等路径,行为稳定可依赖。

四、预设完整配置(可整段复制)

以下就是 starship preset bracketed-segments -o ... 落盘的全部内容(与 docs/public/presets/toml/bracketed-segments.toml 一致,共 12 个预设文件之一):

"$schema" = 'https://starship.rs/config-schema.json'

[aws]
format = '\[[$symbol($profile)(\($region\))(\[$duration\])]($style)\]'

[azure]
format = '\[$symbol($subscription)\]'

[battery]
format = '\[$symbol$percentage\]'

[buf]
format = '\[$symbol($version)\]'

[bun]
format = '\[$symbol($version)\]'

[c]
format = '\[$symbol($version(-$name))\]'

[cmake]
format = '\[$symbol($version)\]'

[cmd_duration]
format = '\[⏱ $duration\]'

[cobol]
format = '\[$symbol($version)\]'

[conda]
format = '\[$symbol$environment\]'

[container]
format = '\[[$symbol \[$name\]]($style)\]'

[cpp]
format = '\[$symbol($version(-$name))\]'

[crystal]
format = '\[$symbol($version)\]'

[daml]
format = '\[$symbol($version)\]'

[dart]
format = '\[$symbol($version)\]'

[deno]
format = '\[$symbol($version)\]'

[direnv]
format = '\[$symbol$loaded/$allowed\]'

[docker_context]
format = '\[$symbol$context\]'

[dotnet]
format = '\[$symbol($version)(🎯 $tfm)\]'

[elixir]
format = '\[$symbol($version \(OTP $otp_version\))\]'

[elm]
format = '\[$symbol($version)\]'

[erlang]
format = '\[$symbol($version)\]'

[fennel]
format = '\[$symbol($version)\]'

[fortran]
format = '\[$symbol($version)\]'

[fossil_branch]
format = '\[$symbol$branch\]'

[fossil_metrics]
format = '\[+$added\]\[-$deleted\]'

[gcloud]
format = '\[$symbol$account(@$domain)(\($region\))\]'

[git_branch]
format = '\[$symbol$branch\]'

[git_commit]
format = '\[\($hash$tag\)\]'

[git_metrics]
format = '\[+$added\]\[-$deleted\]'

[git_state]
format = '\[$state ($progress_current/$progress_total)\]'

[git_status]
format = '([\[$all_status$ahead_behind\]]($style))'

[gleam]
format = '\[$symbol($version)\]'

[golang]
format = '\[$symbol($version)\]'

[gradle]
format = '\[$symbol($version)\]'

[guix_shell]
format = '\[$symbol\]'

[haskell]
format = '\[$symbol($version)\]'

[haxe]
format = '\[$symbol($version)\]'

[helm]
format = '\[$symbol($version)\]'

[hg_branch]
format = '\[$symbol$branch\]'

[hostname]
format = '\[$ssh_symbol($hostname)\] '

[java]
format = '\[$symbol($version)\]'

[jj_bookmark]
format = '\[$symbol$bookmark(@$remote)$diverged( \(+$overflow_count others\))\]'

[jobs]
format = '\[$symbol$number\]'

[julia]
format = '\[$symbol($version)\]'

[kotlin]
format = '\[$symbol($version)\]'

[kubernetes]
format = '\[$symbol$context( \($namespace\))\]'

[localip]
format = '\[$localipv4\]'

[lua]
format = '\[$symbol($version)\]'

[maven]
format = '\[$symbol($version)\]'

[memory_usage]
format = '\$symbol[$ram( | $swap)\]'

[meson]
format = '\[$symbol$project\]'

[mise]
format = '\[$symbol$health\]'

[mojo]
format = '\[$symbol($version)\]'

[nats]
format = '\[$symbol$name\]'

[netns]
format = '\[[$symbol \[$name\]]($style)\]'

[nim]
format = '\[$symbol($version)\]'

[nix_shell]
format = '\[$symbol$state( \($name\))\]'

[nodejs]
format = '\[$symbol($version)\]'

[ocaml]
format = '\[$symbol($version)(\($switch_indicator$switch_name\))\]'

[odin]
format = '\[$symbol($version )\]'

[opa]
format = '\[$symbol($version)\]'

[openstack]
format = '\[$symbol$cloud(\($project\))\]'

[os]
format = '\[$symbol\]'

[package]
format = '\[$symbol$version\]'

[perl]
format = '\[$symbol($version)\]'

[php]
format = '\[$symbol($version)\]'

[pijul_channel]
format = '\[$symbol$channel\]'

[pixi]
format = '\[$symbol$version( $environment)\]'

[pulumi]
format = '\[$symbol$stack\]'

[purescript]
format = '\[$symbol($version)\]'

[python]
format = '\[${symbol}${pyenv_prefix}(${version})(\($virtualenv\))\]'

[quarto]
format = '\[$symbol($version)\]'

[raku]
format = '\[$symbol($version-$vm_version)\]'

[red]
format = '\[$symbol($version)\]'

[rlang]
format = '\[$symbol($version)\]'

[ruby]
format = '\[$symbol($version)\]'

[rust]
format = '\[$symbol($version)\]'

[scala]
format = '\[$symbol($version)\]'

[shell]
format = '\[$indicator\]'

[singularity]
format = '\[[$symbol\[$env\]]($style)\]'

[solidity]
format = '\[$symbol($version)\]'

[spack]
format = '\[$symbol$environment\]'

[status]
format = '\[$symbol$status\]'

[sudo]
format = '\[as $symbol\]'

[swift]
format = '\[$symbol($version)\]'

[terraform]
format = '\[$symbol$workspace\]'

[time]
format = '\[$time\]'

[typst]
format = '\[$symbol($version)\]'

[username]
format = '\[$user\]'

[vagrant]
format = '\[$symbol($version)\]'

[vcsh]
format = '\vcsh [$symbol$repo\]'

[vlang]
format = '\[$symbol($version)\]'

[xmake]
format = '\[$symbol($version)\]'

[zig]
format = '\[$symbol($version)\]'

第一行 "$schema" 指向 Starship 的配置 JSON Schema(对应仓库内的 docs/public/config-schema.json),用于编辑器在 starship.toml 中获得自动补全与校验,对运行时行为没有影响。

五、format 字符串语法拆解:读懂预设里的每个符号

预设本质上是一套 Starship format 语法模板的批量应用。结合 src/formatter/string_formatter.rs 的解析实现,预设文件中反复出现五类语法:

语法 含义 预设中的典型例子
\[ / \] 转义字符,原样输出方括号;若不转义,[ ] 会被解析器当作分组语法 几乎所有行的首尾 \[\[ ... \]\]
$var / ${var} 变量占位符,渲染时替换为对应模块计算出的值 $symbol$version${pyenv_prefix}
(group) 可选分组:组内变量全部为空时整组不渲染,非空则整组显示 ($version)(\($region\))
($style) 特殊分组:把该模块配置中定义的 style 应用到括号包裹的文本上 每行末尾的 ]($style)
普通文本 原样输出,可用 \- 等方式转义 [c] 中的 -$name 连接符

以几个有代表性的模块格式为例逐句解读:

1. 运行时类模块(如 [nodejs]

format = '\[$symbol($version)\]'

渲染结果是 [⬢ v20.11.0] 这类结构:$symbol 是语言图标,($version) 是可选分组(版本查不到时整个 (...) 消失,方括号里只剩图标),最外层 \[ \] 是转义后的字面方括号,($style) 把整个内容染成该模块的默认颜色。对比默认格式,这里没有任何 "via" 字样——这正是预设标题 "Bracketed Segments" 的含义。

2. 带多级可选信息的模块(如 [aws]

format = '\[[$symbol($profile)(\($region\))(\[$duration\])]($style)\]'

嵌套了三个可选分组:profile 存在才显示 profile,region 存在才显示 (region),duration 存在才显示 [duration]。注意 region 外层的 \( \) 是转义圆括号(字面字符),而包裹它的 (...) 才是可选分组语法——两类圆括号通过反斜杠区分,这是阅读该预设时最容易混淆的地方。

3. 双括号结构(如 [git_status]

format = '([\[$all_status$ahead_behind\]]($style))'

最外层 (...) 是可选分组(没有 git 状态变化时整段不显示),内部 \[\[ ... \]\] 是转义后的字面方括号。渲染效果类似 ([+2 ~1 ⭢ | ↑2↓1]),即“可选分组 + 字面括号”双层结构。

4. 带特殊变量的模块(如 [python]

format = '\[${symbol}${pyenv_prefix}(${version})(\($virtualenv\))\]'

pyenv_prefixvirtualenvsrc/configs/python.rs 提供的模块逻辑填充:pyenv 环境会额外显示 pyenv 版本信息,虚拟环境存在时追加 (venv名)${symbol} 写法与 $symbol 等价,这里用花括号形式避免与相邻变量粘连产生歧义。

5. 无图标模块(如 [hostname][sudo]

[hostname]
format = '\[$ssh_symbol($hostname)\] '

[sudo]
format = '\[as $symbol\]'

hostname 在 SSH 会话下 $ssh_symbol 非空时整段才出现;sudo 则固定渲染成 [as 前缀](保留 "as" 一词作为语义提示),行尾空格用于与下一个模块分隔。

6. 固定文案模块(如 [vcsh][cmd_duration]

[vcsh]
format = '\vcsh [$symbol$repo\]'

[cmd_duration]
format = '\[⏱ $duration\]'

[vcsh] 中的 vcsh 是纯文本前缀,不属于任何可选分组,模块显示时必然出现;[cmd_duration] 则完全依赖 cmd_duration 模块的 min_time 判断(低于阈值的命令不显示),预设本身不改写这个判断逻辑。

7. 双段式模块(如 [git_metrics][fossil_metrics]

format = '\[+$added\]\[-$deleted\]'

一个 format 里放了两个独立括号段:新增行计数用 added_style 着色、删除行计数用 deleted_style 着色,各自可选渲染。

六、使用建议与恢复默认

结合本文源码与配置事实,给出几条实操建议:

  1. 先备份再覆盖-o 会直接写目标文件,若 ~/.config/starship.toml 已有自定义内容,建议先手动备份,或先用 --force 语义确认可以覆盖后再执行;也可以先不带 -o 执行 starship preset bracketed-segments 把内容打印出来审阅。
  2. 恢复默认:删除(或还原备份的)starship.toml 中的相关 format 覆盖即可,模块随即回到默认措辞;预设本身不改动 Starship 二进制。
  3. 微调方式:该预设是“全量 format 覆盖”思路,若只想改个别模块,不必整包套用,可以单独借鉴对应模块的 format 行(如把 [python][git_status] 的写法挪进自己的 starship.toml)。
  4. 验证生效:修改配置后新开终端会话即可看到效果;排查单个模块输出时可配合 starship module python 之类的子命令(见 src/main.rsModule 子命令)单独查看该模块的渲染结果。
  5. 与其他预设的关系:Starship 内置 12 个预设(见 docs/public/presets/toml/,含 nerd-font、no-nerd-font、jetpack、tokyo-night、catppuccin-powerline、gruvbox-rainbow、pastel-powerline、pure-preset、no-runtimes、plain-text、bracketed-segments 等),每个预设都是独立完整配置。从源码结构看,preset 子命令一次只输出一个预设的全部内容,多个预设之间没有自动合并机制,混合风格需要自行挑选各自的 format 行拼接。

七、小结

Bracketed Segments 是 Starship 官方预设中偏“结构重塑”的一类:它不增减信息,只把所有内置模块的输出统一收进 [...] 字面括号,并借助 $symbol$version($style) 保留原有的图标、可选分组和配色。启用只需一条 starship preset bracketed-segments -o ~/.config/starship.toml;其背后是构建期由 build.rs 内嵌 TOML、运行期由 src/print.rs 原子写入的实现链路;而预设全文的每一行都可以按本文第五节的 format 语法表逐符号解读,方便你把它改造成自己的分段风格。

登录后查看全文
热门项目推荐
相关项目推荐