Merge pull request #613 from peco/typed-on-cancel

Use types for OnCancel values
This commit is contained in:
lestrrat 2026-02-16 22:22:09 +09:00 committed by GitHub
commit fe611dde03
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 88 additions and 9 deletions

View file

@ -392,7 +392,7 @@ func doCancel(ctx context.Context, state *Peco, e Event) {
// peco.Cancel -> end program, exit with failure
err := makeIgnorable(errors.New("user canceled"))
if state.onCancel == errorKey {
if state.onCancel == OnCancelError {
err = setExitStatus(err, 1)
}
state.Exit(err)

View file

@ -220,6 +220,64 @@ func TestLocateRcfileYAML(t *testing.T) {
require.Equal(t, filepath.Join(pecoDir, "config.yaml"), file)
}
func TestOnCancelBehavior(t *testing.T) {
t.Run("valid values via JSON", func(t *testing.T) {
for _, tc := range []struct {
input string
expected OnCancelBehavior
}{
{`{"OnCancel":"success"}`, OnCancelSuccess},
{`{"OnCancel":"error"}`, OnCancelError},
{`{}`, ""}, // absent key stays at zero value; default applied later in ApplyConfig
} {
var cfg Config
require.NoError(t, cfg.Init())
require.NoError(t, json.Unmarshal([]byte(tc.input), &cfg))
require.Equal(t, tc.expected, cfg.OnCancel)
}
})
t.Run("valid values via YAML", func(t *testing.T) {
for _, tc := range []struct {
input string
expected OnCancelBehavior
}{
{"OnCancel: success", OnCancelSuccess},
{"OnCancel: error", OnCancelError},
} {
var cfg Config
require.NoError(t, cfg.Init())
require.NoError(t, yaml.Unmarshal([]byte(tc.input), &cfg))
require.Equal(t, tc.expected, cfg.OnCancel)
}
})
t.Run("invalid value via JSON", func(t *testing.T) {
var cfg Config
require.NoError(t, cfg.Init())
err := json.Unmarshal([]byte(`{"OnCancel":"bogus"}`), &cfg)
require.Error(t, err)
require.Contains(t, err.Error(), "bogus")
})
t.Run("invalid value via YAML", func(t *testing.T) {
var cfg Config
require.NoError(t, cfg.Init())
err := yaml.Unmarshal([]byte("OnCancel: bogus"), &cfg)
require.Error(t, err)
require.Contains(t, err.Error(), "bogus")
})
t.Run("invalid CLI option rejected", func(t *testing.T) {
p := newPeco()
var opts CLIOptions
opts.OptOnCancel = "bogus"
err := p.ApplyConfig(opts)
require.Error(t, err)
require.Contains(t, err.Error(), "bogus")
})
}
func TestReadFilenameYAML(t *testing.T) {
dir := t.TempDir()
yamlFile := filepath.Join(dir, "config.yaml")

View file

@ -1,6 +1,7 @@
package peco
import (
"fmt"
"io"
"sync"
"time"
@ -15,11 +16,26 @@ import (
"github.com/peco/peco/pipeline"
)
// OnCancelBehavior specifies what happens when the user cancels peco.
type OnCancelBehavior string
const (
successKey = "success"
errorKey = "error"
OnCancelSuccess OnCancelBehavior = "success"
OnCancelError OnCancelBehavior = "error"
)
func (o *OnCancelBehavior) UnmarshalText(b []byte) error {
switch s := string(b); s {
case "", "success":
*o = OnCancelSuccess
case "error":
*o = OnCancelError
default:
return fmt.Errorf("invalid OnCancel value %q: must be %q or %q", s, OnCancelSuccess, OnCancelError)
}
return nil
}
const (
ToLineAbove PagingRequestType = iota // ToLineAbove moves the selection to the line above
ToScrollPageDown // ToScrollPageDown moves the selection to the next page
@ -84,7 +100,7 @@ type Peco struct {
location Location
maxScanBufferSize int
mutex sync.Mutex
onCancel string
onCancel OnCancelBehavior
printQuery bool
prompt string
query Query
@ -324,7 +340,7 @@ type Config struct {
Prompt string `json:"Prompt" yaml:"Prompt"`
Layout string `json:"Layout" yaml:"Layout"`
Use256Color bool `json:"Use256Color" yaml:"Use256Color"`
OnCancel string `json:"OnCancel" yaml:"OnCancel"`
OnCancel OnCancelBehavior `json:"OnCancel" yaml:"OnCancel"`
CustomMatcher map[string][]string `json:"CustomMatcher" yaml:"CustomMatcher"`
CustomFilter map[string]CustomFilterConfig `json:"CustomFilter" yaml:"CustomFilter"`
QueryExecutionDelay int `json:"QueryExecutionDelay" yaml:"QueryExecutionDelay"`

11
peco.go
View file

@ -644,9 +644,14 @@ func (p *Peco) ApplyConfig(opts CLIOptions) error {
p.use256Color = p.config.Use256Color
p.onCancel = successKey
if opts.OptOnCancel == errorKey || p.config.OnCancel == errorKey {
p.onCancel = errorKey
p.onCancel = p.config.OnCancel
if p.onCancel == "" {
p.onCancel = OnCancelSuccess
}
if opts.OptOnCancel != "" {
if err := p.onCancel.UnmarshalText([]byte(opts.OptOnCancel)); err != nil {
return fmt.Errorf("invalid --on-cancel value: %w", err)
}
}
p.bufferSize = opts.OptBufferSize
if v := opts.OptSelectionPrefix; len(v) > 0 {

View file

@ -398,7 +398,7 @@ func TestApplyConfig(t *testing.T) {
return
}
if !assert.Equal(t, opts.OptOnCancel, p.onCancel, "p.onCancel should be equal to opts.OptOnCancel") {
if !assert.Equal(t, OnCancelBehavior(opts.OptOnCancel), p.onCancel, "p.onCancel should be equal to opts.OptOnCancel") {
return
}