fix: avoid inserting completion config inside multi-line shell commands#796
Merged
jackwener merged 3 commits intojackwener:mainfrom Apr 5, 2026
Merged
Conversation
The postinstall zshrc insertion logic splits backslash-continued blocks (e.g. zinit stanzas) when it finds a compinit match inside them, which breaks the user's shell config. Walk backward past continuation lines so the insertion lands before the entire logical command.
Replace the fragile compinit-searching splice logic with a simple append, matching the strategy already used for bash. This avoids breaking multi-line commands (e.g. zinit blocks with zicompinit). Still detects existing compinit to avoid adding a duplicate call.
jackwener
reviewed
Apr 5, 2026
Owner
jackwener
left a comment
There was a problem hiding this comment.
Review: PR #796
Overall: 代码改动清晰,问题定位准确。用 append 替代 splice 确实从根本上解决了多行命令块被劈开的问题,且与 bash 策略统一,简化了代码。
✅ 优点
- Root cause 分析准确——
/compinit/正则匹配到zicompinit导致在续行块中间插入 - Append 方案简单可靠,不需要处理续行、heredoc 等各种 shell 语法边界情况
- 仍保留了 compinit 存在性检测,避免重复添加
⚠️ 需要确认的一个语义变化
旧逻辑将 fpath=(...) 插入到 compinit 之前,这是有意义的——compinit 只在调用时扫描 $fpath,之后追加的路径不会被扫描到。
新逻辑追加到文件末尾,对 oh-my-zsh 用户意味着:
source $ZSH/oh-my-zsh.sh先执行(其中调用了 compinit)- 然后才设置
fpath=(...) - 此时 compinit 已经执行过了,新的 fpath 不会被扫描
实际影响:oh-my-zsh 用户安装后需要手动 exec zsh 或重启终端,completions 才能生效。但因为 hasCompinit=true 时不会追加 autoload -Uz compinit && compinit,所以第二次 shell 启动时 oh-my-zsh 的 compinit 仍然只会在 fpath 设置之前执行。
建议:当 hasCompinit=true 时,在 fpath 之后也追加一行 compinit 调用,确保追加位置的 fpath 能被扫描到:
const addition = hasCompinit
? `\n${marker}\n${fpathLine}\ncompinit\n`
: `\n${marker}\n${fpathLine}\n${autoloadLine}\n`;或者接受当前行为(大多数用户重启终端后就没问题,因为 oh-my-zsh 的 compinit 运行时 fpath 已经在 .zshrc 顶部通过其他方式配置好了)。
这个 trade-off 需要 maintainer 决定。如果接受当前行为,PR 可以直接合入。
小建议
- PR 描述中 oh-my-zsh 场景验证还未打勾,建议补充测试。
Result: Approve with minor suggestion
Replace the fragile .zshrc/.bashrc modification logic with a safer approach: only write completion files and print setup instructions. The previous approach tried to parse and splice into rc files, which broke multi-line shell commands (e.g. zinit blocks with backslash continuations matching /compinit/). Instead of attempting to fix the parser, remove rc modification entirely — this matches the approach used by rustup, homebrew, and other CLI tools. Closes jackwener#788
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
postinstall.js中的ensureZshFpath()在用户.zshrc中插入 completion 配置时,会在多行命令块内部错误插入,导致 shell 配置语法损坏、zsh 启动失败。Root Cause
插入逻辑逐行扫描
.zshrc,查找第一个匹配/compinit/的非注释行,然后lines.splice()在该行之前插入fpath=(...)配置。但它完全没有考虑 shell 反斜杠续行(\)——当匹配行位于一个多行命令块内部时,插入会将该命令劈成两半。Reproduce
用户
.zshrc包含 zinit 多行配置(zicompinit匹配了/compinit/):运行
npm install -g opencli后,.zshrc变为:这导致 zsh 启动时
zinit命令语法错误。Fix
用末尾追加(
appendFileSync)替代插入点查找(splice),与 bash 的处理策略对齐。之前的
ensureZshFpath()扫描.zshrc寻找compinit行再 splice 插入——这对任何多行命令块都不安全(反斜杠续行、heredoc 等)。新方案直接追加到文件末尾,同时检测是否已有compinit调用以避免重复添加。三个 shell 的策略现在统一为:
~/.zsh/completions/_opencli.zshrc~/.bash_completion.d/opencli.bashrc~/.config/fish/completions/opencli.fishTest plan
zicompinit)的.zshrc不会被劈开.zshrc末尾autoload -Uz compinit && compinitzsh -n ~/.zshrc语法检查通过.zshrc追加时包含 compinit 行