Oh My Zsh Cabal 插件:cabal/cab 补全实现与 Haskell 沙箱状态检测
本文围绕 Oh My Zsh 的 cabal 插件 展开:它专为 Haskell 构建工具 Cabal 提供 zsh 命令补全,并附带一个 cabal_sandbox_info 函数用于判断当前目录是否处于沙箱环境。读完本文,你能掌握插件的启用方式、两套补全函数(cabal 与 cab)覆盖的全部子命令、cabal_sandbox_info 的逐行检测逻辑,以及其在 Oh My Zsh 插件加载机制中的位置。
插件提供的能力概览
cabal 插件 的定位在官方说明中只有两句话,但功能清晰:
- 为 Cabal(Haskell 的构建工具)提供命令补全;
- 提供一个名为
cabal_sandbox_info的函数,打印当前工作目录是否处于沙箱(sandbox)中。
启用方式与其他插件一致,在 zshrc 的 plugins 数组中加入 cabal:
plugins=(... cabal)
插件本体只有一个文件 plugins/cabal/cabal.plugin.zsh,共 93 行,包含两个补全函数和一个沙箱检测函数。
插件的加载流程:从 zshrc 到 compdef
Oh My Zsh 加载插件的过程可以在 oh-my-zsh.sh 中找到完整链路,理解它有助于判断补全何时生效:
- 识别插件:
is_plugin函数(oh-my-zsh.sh#L81-L86)判断plugins/cabal/cabal.plugin.zsh是否存在,存在则把plugins/cabal目录加入fpath(oh-my-zsh.sh#L90-L98); - 执行插件脚本:启动阶段的循环通过
_omz_source逐个 source 各插件文件(oh-my-zsh.sh#L204-L207),cabal.plugin.zsh中的函数定义与compdef注册就在此时执行; - 补全系统初始化:
compinit在插件目录进入fpath之后运行(oh-my-zsh.sh#L124-L135),配合 lib/completion.zsh 中的补全行为配置(菜单选择、忽略大小写等),compdef绑定的补全函数才能被 tab 触发。
cabal 命令补全:_cabal_commands 与 27 个子命令
cabal.plugin.zsh#L12-L53 定义了 _cabal_commands 补全函数,并在末尾无条件执行注册:
compdef _cabal_commands cabal
函数使用 _arguments ':subcommand:->subcommand' 建立第一个参数槽为“子命令”状态,再通过 _describe -t subcommands 'cabal subcommands' subcommands 把子命令连同说明文字展示在补全菜单中。这正是补全菜单里“命令: 描述”格式的由来。
补全覆盖的子命令共 27 个(与 plugins/cabal/cabal.plugin.zsh#L18-L46 一致):
| 子命令 | 补全菜单中的说明 |
|---|---|
bench |
运行基准测试(若有,需通过 UserHooks 配置) |
build |
编译全部目标或指定目标 |
check |
检查包中的常见错误 |
clean |
清理构建产物 |
copy |
将文件复制到安装位置 |
configure |
准备构建包 |
exec |
在 cabal 环境中运行一条命令 |
fetch |
下载包以便稍后安装 |
freeze |
冻结依赖版本 |
get |
获取某个包的源码 |
haddock |
生成 Haddock HTML 文档 |
help |
查看命令帮助 |
hscolour |
以 HTML 格式生成 HsColour 着色代码 |
info |
显示某个包的详细信息 |
init |
交互式创建 .cabal 文件 |
install |
安装一组包 |
list |
列出匹配搜索串的包 |
register |
将本包注册到编译器 |
repl |
为指定目标打开解释器会话 |
report |
上传构建报告到远程服务器 |
run |
运行编译好的可执行文件 |
sandbox |
创建/修改/删除沙箱 |
sdist |
生成源码分发文件(.tar.gz) |
test |
运行测试套件(若有,需通过 UserHooks 配置) |
unpack |
解包供用户查看 |
update |
更新已知包列表 |
upload |
上传源码包到 Hackage |
从源码结构看,这组子命令反映的是带沙箱工作流的旧版 Cabal 工具链(sandbox、freeze、hscolour 等均为当时时代的特征),补全列表是静态硬编码的,不会随 cabal 新版本自动增减命令,这是使用该插件时需要知道的边界。
cab 命令补全:条件注册与 24 个子命令
cabal.plugin.zsh#L55-L91 定义了第二个补全函数 _cab_commands,对应 cab 命令(即 cabal-install 提供的入口)。与 cabal 不同,它的注册是有条件的(plugins/cabal/cabal.plugin.zsh#L93):
command -v cab >/dev/null 2>&1 && { compdef _cab_commands cab }
即只有当 PATH 中存在 cab 命令时才注册补全,避免在无 cabal-install 的环境中产生死绑定。
_cab_commands 采用与 _cabal_commands 完全相同的 _arguments + _describe 结构,覆盖 24 个子命令(见 plugins/cabal/cabal.plugin.zsh#L61-L86):
| 子命令 | 补全菜单中的说明 |
|---|---|
sync |
获取最新的包索引 |
install |
安装包 |
uninstall |
卸载包 |
installed |
列出已安装的包 |
configure |
配置一个 cabal 包 |
build |
构建一个 cabal 包 |
clean |
清理构建目录 |
outdated |
显示过期的包 |
info |
显示某个包的信息 |
sdist |
生成源码分发 tar.gz |
upload |
将 tar.gz 上传到 HackageDB |
get |
在当前目录解包 |
deps |
显示本包的依赖 |
revdeps |
显示反向依赖 |
check |
检查包的一致性 |
genpaths |
生成 Paths_<pkg>.hs |
search |
按包名搜索可用包 |
add |
添加一个源码目录 |
test |
运行测试 |
bench |
运行基准测试 |
doc |
生成手册 |
ghci |
在沙箱中运行 GHCi |
init |
初始化沙箱 |
help |
显示命令帮助信息 |
两套补全的差异也值得注意:cab 列表中有 deps/revdeps/outdated/add 等依赖管理命令,而 cabal 列表中有 freeze/hscolour/register 等编译器侧命令,两者互为补充。
cabal_sandbox_info:沙箱状态检测的实现
plugins/cabal/cabal.plugin.zsh#L1-L10 定义了 cabal_sandbox_info 函数,完整逻辑如下:
function cabal_sandbox_info() {
cabal_files=(*.cabal(N))
if [ $#cabal_files -gt 0 ]; then
if [ -f cabal.sandbox.config ]; then
echo "%{$fg[green]%}sandboxed%{$reset_color%}"
else
echo "%{$fg[red]%}not sandboxed%{$reset_color%}"
fi
fi
}
逐行拆解其判定规则:
cabal_files=(*.cabal(N)):用 zsh glob 收集当前目录下所有.cabal文件。后缀的(N)是 glob 修饰符,含义是“没有匹配时不报错、返回空数组”,避免在普通目录里触发no matches found错误;[ $#cabal_files -gt 0 ]:只有当前目录存在.cabal文件(即处于一个 Haskell 包根目录)时才继续判定,否则函数静默返回、不输出任何内容;[ -f cabal.sandbox.config ]:以cabal.sandbox.config文件是否存在作为“沙箱已激活”的依据——这正是旧版 cabal 沙箱工作流(cabal sandbox init)留下的标志文件;- 输出带色标记:是沙箱输出绿色的
sandboxed,否则输出红色的not sandboxed。%{...%}包裹的转义序列用于让 zsh 把颜色代码识别为不可见字符,保证提示符宽度计算正确。
需要说明的是:cabal.sandbox.config 属于 cabal 1.x/2.x 时代的沙箱机制,现代版本的 cabal 已转向 cabal.project 模型,不再使用沙箱。因此这个函数主要对维护旧式沙箱项目的场景有意义。
在提示符中使用 cabal_sandbox_info
cabal_sandbox_info 是一个普通的 zsh 函数,可以在自定义提示符或主题中直接调用其输出,例如在自己的主题里以 $(cabal_sandbox_info) 的方式取到 sandboxed / not sandboxed 文本。
从源码结构看,它没有被纳入 Oh My Zsh 的主题钩子机制:lib/prompt_info_functions.zsh 维护了一份 *_prompt_info 空实现清单(chruby_prompt_info、rbenv_prompt_info、virtualenv_prompt_info 等),让主题可以无条件引用这些函数;而 cabal_sandbox_info 不在此清单中,也不以 _prompt_info 命名,因此内置主题不会自动展示沙箱状态,需要使用者在自己的提示符配置中显式调用。
使用限制与注意事项
- 适用前提:补全功能要求终端使用 zsh 且 Oh My Zsh 的补全系统正常初始化;
cab的补全还额外要求 PATH 中存在cab可执行文件,否则不注册; - 静态命令列表:两个补全函数的子命令集合都硬编码在插件文件中,新增的 cabal 命令不会自动出现在补全菜单中;
- 无别名副作用:与许多插件不同,该插件不定义任何 alias,仅注册补全与一个函数,不会改变
cabal命令本身的行为; - 沙箱判定的历史语境:
cabal_sandbox_info基于cabal.sandbox.config判定,对应已停用的沙箱工作流,用于新项目时通常只会得到not sandboxed或无输出。
相关文件索引
| 文件 | 说明 |
|---|---|
| plugins/cabal/README.md | 插件官方说明:能力概述与启用方法 |
| plugins/cabal/cabal.plugin.zsh | 插件全部实现:cabal_sandbox_info、_cabal_commands、_cab_commands 及 compdef 注册 |
| oh-my-zsh.sh | 插件识别(is_plugin)、fpath 注入与 compinit 初始化流程 |
| lib/completion.zsh | 全局补全行为配置(菜单选择、大小写匹配等) |
| lib/prompt_info_functions.zsh | 主题提示符函数清单,可据此判断 cabal_sandbox_info 不属于内置钩子 |
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00