fix(interactive): 命令面板选中带可选参数的命令也弹出表单
CI / test (push) Failing after 2m4s Details

根因:updatePalette 只在有缺失的 Required flag 时才弹参数表单。但很多
命令的参数是'至少一个'关系(如 branch +batch-protect 的 --names/--from
二选一),无法单独标记 Required,导致 missingRequiredFlags 返回空,
表单被跳过,命令直接执行后报 'no branch names provided'。

修复:把表单触发条件从'有缺失必填参数'改为'命令有任何 flag'。这样
选中任何带参数的命令都弹表单,用户能填可选参数(留空即跳过)。
无 flag 的命令(如 wiki +list、repo +recommend)仍直接执行。

回归测试 TestPaletteSelectionOpensFormForOptionalFlags 验证模拟的
batch-protect(全可选参数)选中后进入 stateForm 而非直接执行。
This commit is contained in:
刘焱 2026-06-08 21:01:04 +08:00
parent c7a2ca3685
commit d12ce0fb57
2 changed files with 65 additions and 6 deletions

View File

@ -0,0 +1,57 @@
package interactive
import (
"testing"
tea "github.com/charmbracelet/bubbletea"
"github.com/gitlink-org/gitlink-cli/shortcuts/common"
)
// TestPaletteSelectionOpensFormForOptionalFlags 验证:选中一个"只有可选参数、
// 无必填参数"的命令(如 branch +batch-protect 的 names/from 二选一)时,
// 仍然弹出参数表单,而非直接执行导致 "no ... provided" 错误。
//
// 回归:之前 updatePalette 只在有缺失的 Required flag 时才弹表单,
// 导致 names/from 这类"至少一个"型参数(无法单独标 Required的命令
// 被直接执行,用户没机会输入参数。
func TestPaletteSelectionOpensFormForOptionalFlags(t *testing.T) {
// 构造一个模拟 branch +batch-protect 的 shortcut所有 flag 都非必填。
shortcuts := map[string][]*common.Shortcut{
"branch": {
{
Name: "batch-protect",
Description: "批量保护分支",
Flags: []common.Flag{
{Name: "names", Usage: "分支名(逗号分隔)"}, // 无 Required
{Name: "from", Usage: "文件路径"}, // 无 Required
},
Run: func(ctx *common.RuntimeContext) error { return nil },
},
},
}
m := newReplModel("owner", "repo")
// 注入测试 registry
m.shortcuts = shortcuts
// 1) 输入 / 触发命令面板
m2, _ := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'/'}})
repl := m2.(replModel)
if repl.state != statePalette {
t.Fatalf("输入 / 后应在 statePalette实际 %v", repl.state)
}
// 2) 模拟用户选中 branch 组
repl.palette.result = &PaletteResult{
Group: "branch",
Shortcut: shortcuts["branch"][0],
}
// 3) 派发一条任意消息触发 updatePalette 的结果检查
m3, _ := repl.Update(tea.KeyMsg{Type: tea.KeyEnter})
final := m3.(replModel)
if final.state != stateForm {
t.Fatalf("选中带可选参数的命令应进入 stateForm实际 %v命令被直接执行了用户没机会填参数", final.state)
}
}

View File

@ -250,17 +250,19 @@ func (m replModel) updatePalette(msg tea.Msg) (tea.Model, tea.Cmd) {
result := m.palette.result
s := result.Shortcut
// Only open the form when required flags are missing.
// Commands with only optional flags execute directly — users can pass
// flags via the direct-command syntax if needed.
missing := missingRequiredFlags(s, map[string]string{})
if len(missing) > 0 {
// Open the form whenever the command has ANY flags — not just required
// ones. Many commands have "at-least-one-of" parameters (e.g. branch
// +batch-protect needs --names OR --from) that can't be marked Required
// individually, so skipping the form for "optional-only" commands left
// users unable to supply them and the command failed at runtime.
// Commands with no flags at all execute directly.
if len(s.Flags) > 0 {
m.form = newFormModel(result.Group, s, m.width, m.height, nil)
m.state = stateForm
return m, nil
}
// No required flags missing → execute directly
// No flags → execute directly
m.state = stateExecuting
return m, tea.Batch(m.executeCommand(result.Group, s, map[string]string{}), m.spinner.Tick)
}