首页
/ Oh My Zsh Cabal 插件:cabal/cab 补全实现与 Haskell 沙箱状态检测

Oh My Zsh Cabal 插件:cabal/cab 补全实现与 Haskell 沙箱状态检测

2026-09-04 20:18:47作者:冯梦姬Eddie

本文围绕 Oh My Zsh 的 cabal 插件 展开:它专为 Haskell 构建工具 Cabal 提供 zsh 命令补全,并附带一个 cabal_sandbox_info 函数用于判断当前目录是否处于沙箱环境。读完本文,你能掌握插件的启用方式、两套补全函数(cabalcab)覆盖的全部子命令、cabal_sandbox_info 的逐行检测逻辑,以及其在 Oh My Zsh 插件加载机制中的位置。

插件提供的能力概览

cabal 插件 的定位在官方说明中只有两句话,但功能清晰:

  1. 为 Cabal(Haskell 的构建工具)提供命令补全;
  2. 提供一个名为 cabal_sandbox_info 的函数,打印当前工作目录是否处于沙箱(sandbox)中。

启用方式与其他插件一致,在 zshrc 的 plugins 数组中加入 cabal

plugins=(... cabal)

插件本体只有一个文件 plugins/cabal/cabal.plugin.zsh,共 93 行,包含两个补全函数和一个沙箱检测函数。

插件的加载流程:从 zshrc 到 compdef

Oh My Zsh 加载插件的过程可以在 oh-my-zsh.sh 中找到完整链路,理解它有助于判断补全何时生效:

  1. 识别插件is_plugin 函数(oh-my-zsh.sh#L81-L86)判断 plugins/cabal/cabal.plugin.zsh 是否存在,存在则把 plugins/cabal 目录加入 fpathoh-my-zsh.sh#L90-L98);
  2. 执行插件脚本:启动阶段的循环通过 _omz_source 逐个 source 各插件文件(oh-my-zsh.sh#L204-L207),cabal.plugin.zsh 中的函数定义与 compdef 注册就在此时执行;
  3. 补全系统初始化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 工具链(sandboxfreezehscolour 等均为当时时代的特征),补全列表是静态硬编码的,不会随 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
}

逐行拆解其判定规则:

  1. cabal_files=(*.cabal(N)):用 zsh glob 收集当前目录下所有 .cabal 文件。后缀的 (N) 是 glob 修饰符,含义是“没有匹配时不报错、返回空数组”,避免在普通目录里触发 no matches found 错误;
  2. [ $#cabal_files -gt 0 ]:只有当前目录存在 .cabal 文件(即处于一个 Haskell 包根目录)时才继续判定,否则函数静默返回、不输出任何内容;
  3. [ -f cabal.sandbox.config ]:以 cabal.sandbox.config 文件是否存在作为“沙箱已激活”的依据——这正是旧版 cabal 沙箱工作流(cabal sandbox init)留下的标志文件;
  4. 输出带色标记:是沙箱输出绿色的 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_inforbenv_prompt_infovirtualenv_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_commandscompdef 注册
oh-my-zsh.sh 插件识别(is_plugin)、fpath 注入与 compinit 初始化流程
lib/completion.zsh 全局补全行为配置(菜单选择、大小写匹配等)
lib/prompt_info_functions.zsh 主题提示符函数清单,可据此判断 cabal_sandbox_info 不属于内置钩子
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
docsdocs
暂无描述
Markdown
889
5.78 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384
flutter_flutterflutter_flutter
本仓库是 Flutter SDK 与 Flutter Engine 的 OpenHarmony 适配版本,由 CPF-Flutter 团队维护。开发者可使用熟悉的 Flutter 技术栈开发 OpenHarmony 应用,3.35.7 及以后的适配版本可基于本仓库源码构建支持 OpenHarmony 的 Flutter Engine。
Dart
1.17 K
341