跳转至

第 6 章:主题与提示符 —— 表现层如何消费公共状态

6.1 主题不是渲染器框架

Oh My Zsh 没有统一的 theme class 或组件接口。主题本质上就是一个会被 source 的 zsh 文件,通常直接设置 PROMPTRPROMPT 和若干颜色/函数。

公共约定来自 lib/

lib/theme-and-appearance.zsh  → 颜色与主题默认常量
lib/git.zsh                   → git_prompt_info / git_prompt_status
lib/prompt_info_functions.zsh → ruby/pyenv/nvm 等 prompt helper
lib/vcs_info.zsh              → vcs_info 的安全修补

所以主题的稳定性来自“共享函数名和变量名”,而不是来自类型系统。

6.2 最小主题:Robby Russell

themes/robbyrussell.zsh-theme 只有几行:

PROMPT="%(?:green-arrow:red-arrow) %c"
PROMPT+=' $(git_prompt_info)'
ZSH_THEME_GIT_PROMPT_PREFIX="git:("
ZSH_THEME_GIT_PROMPT_SUFFIX=") "

它做了三件事:

  1. 使用 zsh prompt condition 显示上一条命令成功/失败;
  2. %c 显示当前目录;
  3. 调用公共 git_prompt_info 拼接仓库状态。

这说明“主题”可以很薄:复杂的 Git 解析不需要每个主题重复实现。

6.3 Git prompt 的公共协议

lib/git.zsh 先通过 __git_prompt_git 包装 Git 命令,把 GIT_OPTIONAL_LOCKS=0 只作用于 prompt 查询,避免 prompt 刷新对其他 Git 进程产生可选锁影响。

_omz_git_prompt_info 大致按下面顺序取 ref:

  1. 当前 branch;
  2. 当前 tag;
  3. short SHA;
  4. 调用 parse_git_dirty 附加 dirty/clean 标记;
  5. 根据 ZSH_THEME_GIT_PROMPT_* 变量拼接结果。

_omz_git_prompt_status 则使用 git status --porcelain -b,把 untracked、added、modified、renamed、deleted、unmerged、ahead、behind 等状态映射成主题可配置的字符串。

6.4 dirty 判断不是布尔值那么简单

parse_git_dirty 会考虑:

  • oh-my-zsh.hide-dirty:是否完全关闭 dirty 检查;
  • DISABLE_UNTRACKED_FILES_DIRTY=true:是否忽略未跟踪文件;
  • GIT_STATUS_IGNORE_SUBMODULES:如何处理 submodule;
  • ZSH_THEME_GIT_PROMPT_DIRTY/CLEAN:最后显示什么。

大仓库中的 untracked 扫描会明显拖慢每个 prompt,因此这个开关是性能控制,而不只是视觉设置。

6.5 异步 Git prompt

当前快照的 lib/git.zsh 支持通过 :omz:alpha:lib:git zstyle 选择异步模式。启用后:

  1. git_prompt_info 不立即执行 Git,而是读取 _OMZ_ASYNC_OUTPUT
  2. _defer_async_git_register 先检查 prompt 变量中是否真正使用了该函数;
  3. 只有使用了函数才注册异步 handler;
  4. 下一次 prompt 刷新时显示后台结果。

“按需注册”很重要:不使用 Git prompt 的主题不会为它付出异步任务成本。异步功能是 alpha 级别,遇到提示消失或延迟异常时可以:

zstyle ':omz:alpha:lib:git' async-prompt no

6.6 Agnoster:一个更复杂的主题

Agnoster 把 prompt 拆成 segment,用背景色和 Powerline 字符连接。它会按需显示:

  • user@host;
  • 当前目录;
  • Git branch、dirty、ahead/behind;
  • virtualenv、AWS profile、状态码和后台 job。

它要求 Powerline patched font,因为 等字符依赖字体 glyph。这个依赖不是 Oh My Zsh 核心可以解决的,主题文件只能在注释中说明环境要求。

6.7 prompt 中的 % 与不可信文本

Git branch、目录名和远端信息可能包含 %。源码多处使用 ${value//\%/%%},把普通百分号转义成 prompt expansion 可接受的形式;lib/vcs_info.zsh 还对 vcs_info hook 字段做了类似修补。

这是一个容易被忽略的安全点:如果外部仓库名称被直接放进 PROMPT,它可能被 zsh 当成 prompt 格式重新解释。主题开发应始终对来自 Git、文件名或外部命令的文本做 prompt-safe 处理。

6.8 编写自定义主题的最小模板

# $ZSH_CUSTOM/themes/minimal-safe.zsh-theme
autoload -Uz add-zsh-hook

function _my_prompt_precmd() {
  PROMPT="%F{cyan}%~%f $(git_prompt_info) %# "
}

add-zsh-hook precmd _my_prompt_precmd

比直接在 source 时一次性计算更稳妥,因为目录和 Git 状态会随命令变化。主题还应避免每次 prompt 都启动多个昂贵的外部进程。