Skip to content

Commit 1f87aee

Browse files
inhereclaude
andcommitted
docs(plan): D2 选项 API 命名采用 SharedOpts
将 PersistentFlags 统一改名 SharedOpts(更短、贴 gcli Opts 词汇); 字段 sharedFs / sharedMerged, 概念词改'共享选项'(≈cobra persistent); 保留 cobra 引用; 同步更新 TODO 标签。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent edcaf4e commit 1f87aee

2 files changed

Lines changed: 43 additions & 40 deletions

File tree

docs/TODO.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@
5656
- [x] 泛型 API:`gflag.Opt[T]/BindVar[T]`,类型安全、可扩展,老 API 保留(commit 99ffdfa)
5757
- 对标:kong / go-arg / go-flags 原生支持 slice/map/enum/Duration(go-arg 连 Duration 都内置)
5858

59-
- [ ] **D2 持久化选项继承模型**(伤筋动骨、建议独立里程碑)— [plans/feat-D2-persistent-options.md](plans/feat-D2-persistent-options.md)
60-
- [ ] 新增「持久化选项」中间层:`Command.PersistentFlags()``CliOpt.Persistent` 标记
61-
dispatch 到子命令时把祖先链持久选项 merge 进叶子 flag set(与 args 重排契合:可写在叶子段任意位置)
59+
- [ ] **D2 共享选项(SharedOpts)继承模型**(伤筋动骨、建议独立里程碑)— [plans/feat-D2-persistent-options.md](plans/feat-D2-persistent-options.md)
60+
- [ ] 新增「共享选项」中间层:`Command.SharedOpts() *gflag.Flags`
61+
dispatch 到子命令时把祖先链共享选项 merge 进叶子 flag set(与 args 重排契合:可写在叶子段任意位置)
6262
- [ ] 进程级单例 `gOpts` 改为 per-App 实例,解决多 App/并发共享(CHANGELOG v3.4.0 已记此坑)
63-
- [ ] 明确「全局(App) / 持久(命令子树) / 局部(命令)」三层语义 + 文档 + 专项测试(持久选项 × 多级)
63+
- [ ] 明确「全局(App) / 共享(命令子树) / 局部(命令)」三层语义 + 文档 + 专项测试(共享选项 × 多级)
6464
- 对标:cobra 的 `PersistentFlags` / `LocalFlags` / `InheritedFlags` 三层模型

docs/plans/feat-D2-persistent-options.md

Lines changed: 39 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
# 功能实现计划:D2 持久化选项继承模型
1+
# 功能实现计划:D2 共享选项继承模型(SharedOpts,≈ cobra PersistentFlags)
22

33
> 状态:**待实施**(建议独立里程碑,分阶段推进)
4-
> 范围:`cmd.go`持久选项存储/合并/分发)+ 新增 gflag 合并基元 + 可选的 `gcli.go`(gOpts per-App)。
4+
> 范围:`cmd.go`共享选项存储/合并/分发)+ 新增 gflag 合并基元 + 可选的 `gcli.go`(gOpts per-App)。
55
> 依据:[../compare-with-others.zh-CN.md](../compare-with-others.zh-CN.md) 差距 5;[../TODO.md](../TODO.md) D2。
66
> 对标:cobra 的 `PersistentFlags` / `LocalFlags` / `InheritedFlags` 三层模型。
77
@@ -14,7 +14,7 @@
1414
`bindingOpts(fs)``gcli.go:211`)绑定 `--help/-h``--version/-V`。App 复用它
1515
`app.go:128` `app.opts = gOpts``app.go:203` 绑到 `app.fs`)。
1616
2. **命令局部选项**:每个命令在 `Config` 里用 `c.BoolOpt/StrOpt/FromStruct/...` 绑到 `c.Flags`
17-
- **缺中间层**:父命令定义、其所有子命令自动继承的「持久选项」不存在。多级命令里要让
17+
- **缺中间层**:父命令定义、其所有子命令自动继承的「共享选项」不存在。多级命令里要让
1818
`git --git-dir <path> <subcmd> ...` 这种「父级选项对所有子命令生效」,只能在每个子命令重复声明。
1919
- **分发链路**`cmd.go:409` `innerDispatch`):`parseOptions` → 找子命令 →
2020
`sub.innerDispatch(args[1:])` 递归。解析在子命令名处停止(首个位置参数)。
@@ -31,14 +31,17 @@
3131
|| 定义者 | 作用范围 |
3232
|---|---|---|
3333
| 全局(global) | App(现 `gOpts`| 所有命令 |
34-
| 持久(persistent) | 某命令 | 该命令**及其所有子孙命令** |
34+
| 共享(shared) | 某命令 | 该命令**及其所有子孙命令** |
3535
| 局部(local) | 某命令 | 仅该命令 |
3636

37-
#### 动机:现状 vs 持久选项(直观对照)
37+
> API 命名采用 **`SharedOpts()`**(比 `PersistentFlags` 短、贴 gcli 的 `Opts` 词汇);
38+
> 下文「共享选项」≈ cobra 的 persistent flags。
39+
40+
#### 动机:现状 vs 共享选项(直观对照)
3841

3942
命令树 `app remote(父) → add(子)``--verbose` 定义在 `remote` 上。
4043

41-
| 输入 | 现状(remote 局部选项) | 持久选项后 |
44+
| 输入 | 现状(remote 局部选项) | 共享选项后 |
4245
|---|---|---|
4346
| `app remote --verbose add` | ✅ 写在子命令名**之前**,由 remote 解析 ||
4447
| `app remote add --verbose` | ❌ remote 解析到 `add` 即停,`--verbose` 交给 add;**add 不认识** → 报错 | ✅ add **继承**`--verbose`,能解析 |
@@ -50,7 +53,7 @@
5053

5154
> **只解决父 → 子方向**:不会让子命令自己的局部选项能写在子命令名之前。
5255
53-
1. **D2.1**(核心):新增持久选项中间层,父命令的持久选项被子孙命令继承,且与 args 重排契合
56+
1. **D2.1**(核心):新增共享选项中间层,父命令的共享选项被子孙命令继承,且与 args 重排契合
5457
(可写在叶子段任意位置)。
5558
2. **D2.2**(可选、有风险):`gOpts` 由进程单例改为 per-App 实例,修多 App/并发共享。
5659
3. **D2.3**:三层语义文档 + help 渲染(继承选项分组显示)+ 专项测试。
@@ -59,27 +62,27 @@
5962

6063
## 方案
6164

62-
### D2.1 持久选项 tier(核心)
65+
### D2.1 共享选项 tier(核心)
6366

6467
#### API(复用现有全部绑定方法)
6568

66-
`Command.PersistentFlags() *gflag.Flags`惰性创建并返回命令专属的持久选项持有器 `c.pFlags`
69+
`Command.SharedOpts() *gflag.Flags`惰性创建并返回命令专属的共享选项持有器 `c.sharedFs`
6770
用户在它上面像平时一样绑定 —— 自动获得 `BoolOpt/StrOpt/IntOpt/DurationOpt/Opt[T]/FromStruct/...`
6871
全部能力:
6972

7073
```go
7174
var gitDir string
72-
top.PersistentFlags().StrOpt(&gitDir, "git-dir", "", "", "the git work dir")
73-
// 或结构体:top.PersistentFlags().FromStruct(&persistentOpts)
75+
top.SharedOpts().StrOpt(&gitDir, "git-dir", "", "", "the git work dir")
76+
// 或结构体:top.SharedOpts().FromStruct(&sharedOpts)
7477
```
7578

76-
> `PersistentFlags() *Flags`(对标 cobra `cmd.PersistentFlags()`)而非 `CliOpt.Persistent` 标记,
77-
> 因为前者零成本复用所有绑定方法,且 local/persistent 清晰分离。`c.pFlags` 仅作**定义来源**
79+
> `SharedOpts() *Flags`(对标 cobra `cmd.PersistentFlags()`)而非 `CliOpt.Shared` 标记,
80+
> 因为前者零成本复用所有绑定方法,且 local/shared 清晰分离。`c.sharedFs` 仅作**定义来源**
7881
> 自身永不单独 Parse。
7982
8083
#### 存储
8184

82-
`Command` 增加字段 `pFlags *gflag.Flags`(lazy,`PersistentFlags()` 内首次创建)。
85+
`Command` 增加字段 `sharedFs *gflag.Flags`(lazy,`SharedOpts()` 内首次创建)。
8386

8487
#### 合并基元(gflag 新增)
8588

@@ -111,30 +114,30 @@ func (p *Parser) InheritOptsFrom(src *Parser) {
111114
`cmd.go` `parseOptions``:493`)中、`c.Parse(args)` 之前合并:
112115

113116
```go
114-
if !c.persistentMerged {
115-
// 沿祖先链(含自身)收集持久选项, 从根到叶顺序合并进 c.Flags
117+
if !c.sharedMerged {
118+
// 沿祖先链(含自身)收集共享选项, 从根到叶顺序合并进 c.Flags
116119
for _, anc := range c.ancestorsWithSelf() { // [root,...,parent,self]
117-
if anc.pFlags != nil {
118-
c.Flags.InheritOptsFrom(&anc.pFlags.Flags)
120+
if anc.sharedFs != nil {
121+
c.Flags.InheritOptsFrom(&anc.sharedFs.Flags)
119122
}
120123
}
121-
c.persistentMerged = true
124+
c.sharedMerged = true
122125
}
123126
```
124127

125-
- 自身的 `pFlags` 也并入 `c.Flags` → 命令**自己**也能识别它定义的持久选项
128+
- 自身的 `sharedFs` 也并入 `c.Flags` → 命令**自己**也能识别它定义的共享选项
126129
(写在子命令名之前的场景)。
127-
- 幂等 `persistentMerged bool`,避免重复注册。
128-
- **与 reorder 契合**合并后持久选项已进入叶子的 flag set,`optMeta` 能识别其取值性,
130+
- 幂等 `sharedMerged bool`,避免重复注册。
131+
- **与 reorder 契合**合并后共享选项已进入叶子的 flag set,`optMeta` 能识别其取值性,
129132
写在叶子 arguments 之后也会被正确重排+解析。
130133

131134
#### 多级解析走查
132135

133-
`app top sub --git-dir /x arg``--git-dir` 是 top 的持久选项):
136+
`app top sub --git-dir /x arg``--git-dir` 是 top 的共享选项):
134137
- app.fs 解析全局(重排关闭) → 停在 `top` → dispatch top `[sub,--git-dir,/x,arg]`
135-
- top.parseOptions:合并 top.pFlags 进 top.Flags;top.Parse 停在 `sub` → dispatch sub `[--git-dir,/x,arg]`
136-
- sub.parseOptions:合并祖先(top)的 pFlags 进 sub.Flags → sub.Parse 识别 `--git-dir`=/x,写回共享 ptr,
137-
`arg` 余下 → doExecute。✅ 持久选项在叶子段任意位置可用
138+
- top.parseOptions:合并 top.sharedFs 进 top.Flags;top.Parse 停在 `sub` → dispatch sub `[--git-dir,/x,arg]`
139+
- sub.parseOptions:合并祖先(top)的 sharedFs 进 sub.Flags → sub.Parse 识别 `--git-dir`=/x,写回共享 ptr,
140+
`arg` 余下 → doExecute。✅ 共享选项在叶子段任意位置可用
138141

139142
#### help 渲染
140143

@@ -162,29 +165,29 @@ if !c.persistentMerged {
162165

163166
## 风险 / 兼容
164167

165-
- **D2.1 增量、无破坏**:不碰现有 local/global 绑定路径;命令未用 `PersistentFlags()`
166-
`pFlags==nil`,合并是 no-op,行为完全不变。
168+
- **D2.1 增量、无破坏**:不碰现有 local/global 绑定路径;命令未用 `SharedOpts()`
169+
`sharedFs==nil`,合并是 no-op,行为完全不变。
167170
- **同名冲突**:局部同名选项优先(合并时 `HasOption` 跳过),语义明确且安全。
168-
- **Required 持久选项**:在「实际执行命令」处校验(合并后随 `validateAll` 生效),需在测试中固化。
171+
- **Required 共享选项**:在「实际执行命令」处校验(合并后随 `validateAll` 生效),需在测试中固化。
169172
- **D2.2 有破坏风险**:改单例为 per-App 可能影响依赖 `gcli.GOpts()` 全局读取的代码;
170173
单独评估、独立提交、可暂缓。
171174

172175
## 测试(`github.com/gookit/goutil/x/assert`
173176

174-
- **继承**:top 定义持久 `--git-dir``app top sub --git-dir /x` → sub 内读到 `/x`
177+
- **继承**:top 定义共享 `--git-dir``app top sub --git-dir /x` → sub 内读到 `/x`
175178
- **任意位置**`app top sub arg --git-dir /x`(重排)→ 同样生效,arg 仍为参数。
176-
- **多级继承**:三层 `app a b c`,a 的持久选项在 c 可用。
179+
- **多级继承**:三层 `app a b c`,a 的共享选项在 c 可用(沿祖先链合并)
177180
- **局部优先**:子命令定义同名局部选项时,覆盖继承(互不写串)。
178-
- **自身可用**`app top --git-dir /x sub` → top 段即识别持久选项
179-
- **Required 持久**:缺失时在执行命令处报错。
180-
- **未用持久**:现有用例全绿(pFlags==nil,零行为变化)。
181+
- **自身可用**`app top --git-dir /x sub` → top 段即识别共享选项
182+
- **Required 共享**:缺失时在执行命令处报错。
183+
- **未用共享**:现有用例全绿(sharedFs==nil,零行为变化)。
181184
- **(D2.2)** 两个 App 实例的 verbose/strict 互不影响;并发 `-shuffle` 稳定。
182185

183186
## 提交拆分(按 R002)
184187

185188
1. `feat(gflag): 增加 Parser.InheritOptsFrom 选项合并基元`
186-
2. `feat(gcli): Command.PersistentFlags 持久选项定义 + 分发时合并(含幂等)`
187-
3. `test(gcli): 持久选项继承/任意位置/局部优先/Required 用例`
189+
2. `feat(gcli): Command.SharedOpts 共享选项定义 + 分发时合并(含幂等)`
190+
3. `test(gcli): 共享选项继承/任意位置/局部优先/Required 用例`
188191
4. `docs: 三层选项模型说明(README/CHANGELOG) + struct/示例`
189192
5. (可选)`feat(gcli): help 渲染继承选项分组(Inherited Options)`
190193
6. (可选/独立评估)`refactor(gcli): gOpts 改 per-App 实例 + 多 App 测试`

0 commit comments

Comments
 (0)