mirror of
https://github.com/peco/peco.git
synced 2026-09-10 07:16:29 -04:00
Fix #543
This commit is contained in:
parent
7715ff482f
commit
ca4a52ba53
|
|
@ -98,6 +98,7 @@ type Peco struct {
|
|||
selectionRangeStart RangeStart
|
||||
exitZeroAndExit bool // True if --exit-0 is enabled
|
||||
selectOneAndExit bool // True if --select-1 is enabled
|
||||
selectAllAndExit bool // True if --select-all is enabled
|
||||
singleKeyJumpMode bool
|
||||
singleKeyJumpPrefixes []rune
|
||||
singleKeyJumpPrefixMap map[rune]uint
|
||||
|
|
@ -455,6 +456,7 @@ type CLIOptions struct {
|
|||
OptLayout string `long:"layout" description:"layout to be used. 'top-down' or 'bottom-up'. default is 'top-down'"`
|
||||
OptSelect1 bool `long:"select-1" description:"select first item and immediately exit if the input contains only 1 item"`
|
||||
OptExitZero bool `long:"exit-0" description:"exit immediately with status 1 if the input is empty"`
|
||||
OptSelectAll bool `long:"select-all" description:"select all items and immediately exit"`
|
||||
OptOnCancel string `long:"on-cancel" description:"specify action on user cancel. 'success' or 'error'.\ndefault is 'success'. This may change in future versions"`
|
||||
OptSelectionPrefix string `long:"selection-prefix" description:"use a prefix instead of changing line color to indicate currently selected lines.\ndefault is to use colors. This option is experimental"`
|
||||
OptExec string `long:"exec" description:"execute command instead of finishing/terminating peco.\nPlease note that this command will receive selected line(s) from stdin,\nand will be executed via '/bin/sh -c' or 'cmd /c'"`
|
||||
|
|
|
|||
23
peco.go
23
peco.go
|
|
@ -319,6 +319,17 @@ func (p *Peco) exitZeroIfPossible() {
|
|||
}
|
||||
}
|
||||
|
||||
func (p *Peco) selectAllAndExitIfPossible() {
|
||||
b := p.CurrentLineBuffer()
|
||||
selection := p.Selection()
|
||||
for i := 0; i < b.Size(); i++ {
|
||||
if l, err := b.LineAt(i); err == nil {
|
||||
selection.Add(l)
|
||||
}
|
||||
}
|
||||
p.Exit(errCollectResults{})
|
||||
}
|
||||
|
||||
func (p *Peco) Run(ctx context.Context) (err error) {
|
||||
if pdebug.Enabled {
|
||||
g := pdebug.Marker("Peco.Run").BindError(&err)
|
||||
|
|
@ -409,6 +420,15 @@ func (p *Peco) Run(ctx context.Context) (err error) {
|
|||
}()
|
||||
}
|
||||
|
||||
// If --select-all is enabled and there is no query, select all lines
|
||||
// from the source and exit immediately
|
||||
if p.selectAllAndExit && p.initialQuery == "" {
|
||||
go func() {
|
||||
<-p.source.SetupDone()
|
||||
p.selectAllAndExitIfPossible()
|
||||
}()
|
||||
}
|
||||
|
||||
readyOnce.Do(func() { close(p.readyCh) })
|
||||
|
||||
// This has tobe AFTER close(p.readyCh), otherwise the query is
|
||||
|
|
@ -426,6 +446,8 @@ func (p *Peco) Run(ctx context.Context) (err error) {
|
|||
// if we only have one item
|
||||
if p.selectOneAndExit {
|
||||
p.ExecQuery(p.selectOneAndExitIfPossible)
|
||||
} else if p.selectAllAndExit {
|
||||
p.ExecQuery(p.selectAllAndExitIfPossible)
|
||||
} else {
|
||||
p.ExecQuery(nil)
|
||||
}
|
||||
|
|
@ -576,6 +598,7 @@ func (p *Peco) ApplyConfig(opts CLIOptions) error {
|
|||
}
|
||||
p.selectOneAndExit = opts.OptSelect1
|
||||
p.exitZeroAndExit = opts.OptExitZero
|
||||
p.selectAllAndExit = opts.OptSelectAll
|
||||
p.printQuery = opts.OptPrintQuery
|
||||
p.initialQuery = opts.OptQuery
|
||||
p.initialFilter = opts.OptInitialFilter
|
||||
|
|
|
|||
173
peco_test.go
173
peco_test.go
|
|
@ -19,6 +19,7 @@ import (
|
|||
"github.com/peco/peco/internal/util"
|
||||
"github.com/peco/peco/line"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type nullHub struct{}
|
||||
|
|
@ -334,6 +335,7 @@ func TestApplyConfig(t *testing.T) {
|
|||
opts.OptLayout = "bottom-up"
|
||||
opts.OptSelect1 = true
|
||||
opts.OptExitZero = true
|
||||
opts.OptSelectAll = true
|
||||
opts.OptOnCancel = "error"
|
||||
opts.OptSelectionPrefix = ">"
|
||||
opts.OptPrintQuery = true
|
||||
|
|
@ -379,6 +381,10 @@ func TestApplyConfig(t *testing.T) {
|
|||
return
|
||||
}
|
||||
|
||||
if !assert.Equal(t, opts.OptSelectAll, p.selectAllAndExit, "p.selectAllAndExit should be equal to opts.OptSelectAll") {
|
||||
return
|
||||
}
|
||||
|
||||
if !assert.Equal(t, opts.OptOnCancel, p.onCancel, "p.onCancel should be equal to opts.OptOnCancel") {
|
||||
return
|
||||
}
|
||||
|
|
@ -594,6 +600,173 @@ func TestExitZero(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestSelectAll(t *testing.T) {
|
||||
t.Run("Multiple lines outputs all lines", func(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
p := newPeco()
|
||||
p.Argv = []string{"--select-all"}
|
||||
p.Stdin = bytes.NewBufferString("foo\nbar\nbaz\n")
|
||||
var out bytes.Buffer
|
||||
p.Stdout = &out
|
||||
|
||||
resultCh := make(chan error)
|
||||
go func() {
|
||||
defer close(resultCh)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case resultCh <- p.Run(ctx):
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
t.Errorf("timeout reached")
|
||||
return
|
||||
case err := <-resultCh:
|
||||
require.True(t, util.IsCollectResultsError(err), "isCollectResultsError")
|
||||
p.PrintResults()
|
||||
}
|
||||
|
||||
require.Equal(t, "foo\nbar\nbaz\n", out.String(), "output should match")
|
||||
})
|
||||
|
||||
t.Run("Single line outputs that line", func(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
p := newPeco()
|
||||
p.Argv = []string{"--select-all"}
|
||||
p.Stdin = bytes.NewBufferString("only\n")
|
||||
var out bytes.Buffer
|
||||
p.Stdout = &out
|
||||
|
||||
resultCh := make(chan error)
|
||||
go func() {
|
||||
defer close(resultCh)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case resultCh <- p.Run(ctx):
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
t.Errorf("timeout reached")
|
||||
return
|
||||
case err := <-resultCh:
|
||||
require.True(t, util.IsCollectResultsError(err), "isCollectResultsError")
|
||||
p.PrintResults()
|
||||
}
|
||||
|
||||
require.Equal(t, "only\n", out.String(), "output should match")
|
||||
})
|
||||
|
||||
t.Run("Empty input outputs nothing", func(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
p := newPeco()
|
||||
p.Argv = []string{"--select-all"}
|
||||
p.Stdin = bytes.NewBufferString("")
|
||||
var out bytes.Buffer
|
||||
p.Stdout = &out
|
||||
|
||||
resultCh := make(chan error)
|
||||
go func() {
|
||||
defer close(resultCh)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case resultCh <- p.Run(ctx):
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
t.Errorf("timeout reached")
|
||||
return
|
||||
case err := <-resultCh:
|
||||
require.True(t, util.IsCollectResultsError(err), "isCollectResultsError")
|
||||
p.PrintResults()
|
||||
}
|
||||
|
||||
require.Empty(t, out.String(), "output should be empty")
|
||||
})
|
||||
|
||||
t.Run("With --print-query outputs query then all lines", func(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
p := newPeco()
|
||||
p.Argv = []string{"--select-all", "--print-query", "--query", "test"}
|
||||
p.Stdin = bytes.NewBufferString("foo\nbar\n")
|
||||
var out bytes.Buffer
|
||||
p.Stdout = &out
|
||||
|
||||
resultCh := make(chan error)
|
||||
go func() {
|
||||
defer close(resultCh)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case resultCh <- p.Run(ctx):
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
t.Errorf("timeout reached")
|
||||
return
|
||||
case err := <-resultCh:
|
||||
require.True(t, util.IsCollectResultsError(err), "isCollectResultsError")
|
||||
p.PrintResults()
|
||||
}
|
||||
|
||||
require.Equal(t, "test\n", out.String(), "output should have query and no matching lines")
|
||||
})
|
||||
|
||||
t.Run("With query filters then selects all matches", func(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
p := newPeco()
|
||||
p.Argv = []string{"--select-all", "--query", "foo"}
|
||||
p.Stdin = bytes.NewBufferString("foo\nbar\nfoobar\n")
|
||||
var out bytes.Buffer
|
||||
p.Stdout = &out
|
||||
|
||||
resultCh := make(chan error)
|
||||
go func() {
|
||||
defer close(resultCh)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case resultCh <- p.Run(ctx):
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
t.Errorf("timeout reached")
|
||||
return
|
||||
case err := <-resultCh:
|
||||
require.True(t, util.IsCollectResultsError(err), "isCollectResultsError")
|
||||
p.PrintResults()
|
||||
}
|
||||
|
||||
require.Equal(t, "foo\nfoobar\n", out.String(), "output should contain only matching lines")
|
||||
})
|
||||
}
|
||||
|
||||
func TestPrintQuery(t *testing.T) {
|
||||
t.Run("Match and print query", func(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
|
|
|
|||
Loading…
Reference in a new issue