mirror of
https://github.com/peco/peco.git
synced 2026-09-10 07:16:29 -04:00
extract Selection object to its own package
This commit is contained in:
parent
37a9b23c61
commit
bdf9b33e6d
20
action.go
20
action.go
|
|
@ -11,12 +11,12 @@ import (
|
|||
|
||||
"context"
|
||||
|
||||
"github.com/google/btree"
|
||||
"github.com/lestrrat-go/pdebug"
|
||||
"github.com/peco/peco/hub"
|
||||
"github.com/peco/peco/internal/keyseq"
|
||||
"github.com/peco/peco/internal/util"
|
||||
"github.com/peco/peco/line"
|
||||
"github.com/peco/peco/selection"
|
||||
)
|
||||
|
||||
// Action describes an action that can be executed upon receiving user input.
|
||||
|
|
@ -196,7 +196,7 @@ func init() {
|
|||
}
|
||||
|
||||
// selectLine marks the line as dirty and adds it to the selection.
|
||||
func selectLine(l line.Line, s *Selection) {
|
||||
func selectLine(l line.Line, s *selection.Set) {
|
||||
l.SetDirty(true)
|
||||
s.Add(l)
|
||||
}
|
||||
|
|
@ -368,7 +368,7 @@ func doFinish(ctx context.Context, state *Peco, _ Event) {
|
|||
return
|
||||
}
|
||||
|
||||
sel := NewSelection()
|
||||
sel := selection.New()
|
||||
state.Selection().Copy(sel)
|
||||
if sel.Len() == 0 {
|
||||
if l, err := state.CurrentLineBuffer().LineAt(state.Location().LineNumber()); err == nil {
|
||||
|
|
@ -377,12 +377,8 @@ func doFinish(ctx context.Context, state *Peco, _ Event) {
|
|||
}
|
||||
|
||||
var stdin bytes.Buffer
|
||||
sel.Ascend(func(it btree.Item) bool {
|
||||
line, ok := it.(line.Line)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
stdin.WriteString(line.Buffer())
|
||||
sel.Ascend(func(l line.Line) bool {
|
||||
stdin.WriteString(l.Buffer())
|
||||
stdin.WriteRune('\n')
|
||||
return true
|
||||
})
|
||||
|
|
@ -870,11 +866,7 @@ func doGoToAdjacentSelection(ctx context.Context, state *Peco, forward bool) {
|
|||
}
|
||||
found := false
|
||||
|
||||
selection.Ascend(func(it btree.Item) bool {
|
||||
l, ok := it.(line.Line)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
selection.Ascend(func(l line.Line) bool {
|
||||
id := l.ID()
|
||||
if forward {
|
||||
if id > currentLine && id < target {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/peco/peco/hub"
|
||||
"github.com/peco/peco/internal/keyseq"
|
||||
"github.com/peco/peco/line"
|
||||
"github.com/peco/peco/selection"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
|
@ -201,7 +202,7 @@ func TestPagingActions(t *testing.T) {
|
|||
rHub := &recordingHub{}
|
||||
state := New()
|
||||
state.hub = rHub
|
||||
state.selection = NewSelection()
|
||||
state.selection = selection.New()
|
||||
state.currentLineBuffer = NewMemoryBuffer(0)
|
||||
|
||||
action, ok := nameToActions[tt.action]
|
||||
|
|
@ -549,7 +550,7 @@ func TestGHIssue574_PreviousSelectionLastLineNotUpdated(t *testing.T) {
|
|||
|
||||
state := New()
|
||||
state.hub = rHub
|
||||
state.selection = NewSelection()
|
||||
state.selection = selection.New()
|
||||
|
||||
// Set the current line buffer to our prepared buffer.
|
||||
state.currentLineBuffer = mb
|
||||
|
|
@ -648,7 +649,7 @@ func TestNextSelectionNavigation(t *testing.T) {
|
|||
|
||||
state := New()
|
||||
state.hub = rHub
|
||||
state.selection = NewSelection()
|
||||
state.selection = selection.New()
|
||||
state.currentLineBuffer = mb
|
||||
|
||||
state.Selection().Add(lines[1]) // ID=20
|
||||
|
|
@ -727,7 +728,7 @@ func TestGHIssue428_PgUpPgDnDefaultBindings(t *testing.T) {
|
|||
|
||||
state := New()
|
||||
state.hub = rHub
|
||||
state.selection = NewSelection()
|
||||
state.selection = selection.New()
|
||||
state.currentLineBuffer = NewMemoryBuffer(0)
|
||||
|
||||
// Populate the keymap with defaults (no custom config).
|
||||
|
|
@ -772,7 +773,7 @@ func TestGHIssue455_RefreshScreenSendsForceSync(t *testing.T) {
|
|||
|
||||
state := New()
|
||||
state.hub = rHub
|
||||
state.selection = NewSelection()
|
||||
state.selection = selection.New()
|
||||
state.currentLineBuffer = NewMemoryBuffer(0)
|
||||
|
||||
doRefreshScreen(ctx, state, Event{})
|
||||
|
|
@ -801,7 +802,7 @@ func TestDoFreezeResults(t *testing.T) {
|
|||
rHub := &recordingHub{}
|
||||
state := New()
|
||||
state.hub = rHub
|
||||
state.selection = NewSelection()
|
||||
state.selection = selection.New()
|
||||
|
||||
lines := makeLines("alpha", "beta", "gamma")
|
||||
mb := NewMemoryBuffer(0)
|
||||
|
|
@ -834,7 +835,7 @@ func TestDoFreezeResults(t *testing.T) {
|
|||
rHub := &recordingHub{}
|
||||
state := New()
|
||||
state.hub = rHub
|
||||
state.selection = NewSelection()
|
||||
state.selection = selection.New()
|
||||
state.source = &Source{}
|
||||
|
||||
lines := makeLines("frozen1", "frozen2")
|
||||
|
|
@ -856,7 +857,7 @@ func TestDoFreezeResults(t *testing.T) {
|
|||
rHub := &recordingHub{}
|
||||
state := New()
|
||||
state.hub = rHub
|
||||
state.selection = NewSelection()
|
||||
state.selection = selection.New()
|
||||
|
||||
origLines := makeLines("orig1", "orig2", "orig3")
|
||||
origSource := &Source{}
|
||||
|
|
@ -886,7 +887,7 @@ func TestDoFreezeResults(t *testing.T) {
|
|||
rHub := &recordingHub{}
|
||||
state := New()
|
||||
state.hub = rHub
|
||||
state.selection = NewSelection()
|
||||
state.selection = selection.New()
|
||||
state.currentLineBuffer = NewMemoryBuffer(0)
|
||||
|
||||
doFreezeResults(ctx, state, Event{})
|
||||
|
|
@ -901,7 +902,7 @@ func TestDoFreezeResults(t *testing.T) {
|
|||
rHub := &recordingHub{}
|
||||
state := New()
|
||||
state.hub = rHub
|
||||
state.selection = NewSelection()
|
||||
state.selection = selection.New()
|
||||
|
||||
doUnfreezeResults(ctx, state, Event{})
|
||||
|
||||
|
|
@ -1068,7 +1069,7 @@ func TestDoZoomInOut(t *testing.T) {
|
|||
rHub := &recordingHub{}
|
||||
state := New()
|
||||
state.hub = rHub
|
||||
state.selection = NewSelection()
|
||||
state.selection = selection.New()
|
||||
state.source = &Source{}
|
||||
state.source.lines = source.lines
|
||||
state.currentLineBuffer = filtered
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/peco/peco/hub"
|
||||
"github.com/peco/peco/line"
|
||||
"github.com/peco/peco/pipeline"
|
||||
"github.com/peco/peco/selection"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
|
@ -277,7 +278,7 @@ func TestFrozenCacheInvalidation(t *testing.T) {
|
|||
state := New()
|
||||
rHub := &recordingHub{}
|
||||
state.hub = rHub
|
||||
state.selection = NewSelection()
|
||||
state.selection = selection.New()
|
||||
|
||||
// Populate filters (needed by Filter.Work)
|
||||
state.filters.Add(filter.NewIgnoreCase())
|
||||
|
|
|
|||
35
peco.go
35
peco.go
|
|
@ -16,13 +16,13 @@ import (
|
|||
|
||||
"context"
|
||||
|
||||
"github.com/google/btree"
|
||||
"github.com/lestrrat-go/pdebug"
|
||||
"github.com/peco/peco/filter"
|
||||
"github.com/peco/peco/hub"
|
||||
"github.com/peco/peco/internal/util"
|
||||
"github.com/peco/peco/line"
|
||||
"github.com/peco/peco/pipeline"
|
||||
"github.com/peco/peco/selection"
|
||||
"github.com/peco/peco/sig"
|
||||
)
|
||||
|
||||
|
|
@ -74,9 +74,9 @@ type Peco struct {
|
|||
readyCh chan struct{}
|
||||
resultCh chan line.Line
|
||||
screen Screen
|
||||
selection *Selection
|
||||
selection *selection.Set
|
||||
selectionPrefix string
|
||||
selectionRangeStart RangeStart
|
||||
selectionRangeStart selection.RangeStart
|
||||
exitZeroAndExit bool // True if --exit-0 is enabled
|
||||
selectOneAndExit bool // True if --select-1 is enabled
|
||||
selectOneTriggered atomic.Bool
|
||||
|
|
@ -211,7 +211,7 @@ func New() *Peco {
|
|||
readyCh: make(chan struct{}),
|
||||
configReader: defaultConfigReader,
|
||||
screen: NewTcellScreen(),
|
||||
selection: NewSelection(),
|
||||
selection: selection.New(),
|
||||
maxScanBufferSize: bufio.MaxScanTokenSize,
|
||||
}
|
||||
}
|
||||
|
|
@ -264,28 +264,11 @@ func (p *Peco) SetResultCh(ch chan line.Line) {
|
|||
p.resultCh = ch
|
||||
}
|
||||
|
||||
func (p *Peco) Selection() *Selection {
|
||||
func (p *Peco) Selection() *selection.Set {
|
||||
return p.selection
|
||||
}
|
||||
|
||||
func (s RangeStart) Valid() bool {
|
||||
return s.valid
|
||||
}
|
||||
|
||||
func (s RangeStart) Value() int {
|
||||
return s.val
|
||||
}
|
||||
|
||||
func (s *RangeStart) SetValue(n int) {
|
||||
s.val = n
|
||||
s.valid = true
|
||||
}
|
||||
|
||||
func (s *RangeStart) Reset() {
|
||||
s.valid = false
|
||||
}
|
||||
|
||||
func (p *Peco) SelectionRangeStart() *RangeStart {
|
||||
func (p *Peco) SelectionRangeStart() *selection.RangeStart {
|
||||
return &p.selectionRangeStart
|
||||
}
|
||||
|
||||
|
|
@ -1086,11 +1069,7 @@ func (p *Peco) PrintResults() {
|
|||
p.SetResultCh(make(chan line.Line))
|
||||
go func() {
|
||||
defer close(p.resultCh)
|
||||
p.selection.Ascend(func(it btree.Item) bool {
|
||||
l, ok := it.(line.Line)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
p.selection.Ascend(func(l line.Line) bool {
|
||||
p.ResultCh() <- l
|
||||
return true
|
||||
})
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import (
|
|||
"github.com/peco/peco/internal/keyseq"
|
||||
"github.com/peco/peco/internal/util"
|
||||
"github.com/peco/peco/line"
|
||||
"github.com/peco/peco/selection"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
|
|
@ -302,7 +303,7 @@ func TestIDGen(t *testing.T) {
|
|||
lines = append(lines, line.NewRaw(idgen.Next(), fmt.Sprintf("%d", i), false, false))
|
||||
}
|
||||
|
||||
sel := NewSelection()
|
||||
sel := selection.New()
|
||||
for _, l := range lines {
|
||||
if sel.Has(l) {
|
||||
t.Errorf("Collision detected %d", l.ID())
|
||||
|
|
|
|||
84
selection.go
84
selection.go
|
|
@ -1,84 +0,0 @@
|
|||
package peco
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/google/btree"
|
||||
"github.com/peco/peco/line"
|
||||
)
|
||||
|
||||
// Selection stores the line ids that were selected by the user.
|
||||
// The contents of the Selection is always sorted from smallest to
|
||||
// largest line ID
|
||||
type Selection struct {
|
||||
mutex sync.RWMutex
|
||||
tree *btree.BTree
|
||||
}
|
||||
|
||||
// RangeStart tracks the starting position of a range selection.
|
||||
type RangeStart struct {
|
||||
val int
|
||||
valid bool
|
||||
}
|
||||
|
||||
// NewSelection creates a new empty Selection
|
||||
func NewSelection() *Selection {
|
||||
s := &Selection{}
|
||||
s.Reset()
|
||||
return s
|
||||
}
|
||||
|
||||
// Add adds a new line to the selection. If the line already
|
||||
// exists in the selection, it is silently ignored
|
||||
func (s *Selection) Add(l line.Line) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
s.tree.ReplaceOrInsert(l)
|
||||
}
|
||||
|
||||
// Copy copies all selected lines from s into dst.
|
||||
func (s *Selection) Copy(dst *Selection) {
|
||||
s.Ascend(func(it btree.Item) bool {
|
||||
l, ok := it.(line.Line)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
dst.Add(l)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// Remove removes the specified line from the selection
|
||||
func (s *Selection) Remove(l line.Line) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
s.tree.Delete(l)
|
||||
}
|
||||
|
||||
// Reset clears all selected indices from the selection.
|
||||
func (s *Selection) Reset() {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
s.tree = btree.New(32)
|
||||
}
|
||||
|
||||
// Has reports whether the given line is in the selection.
|
||||
func (s *Selection) Has(x line.Line) bool {
|
||||
s.mutex.RLock()
|
||||
defer s.mutex.RUnlock()
|
||||
return s.tree.Has(x)
|
||||
}
|
||||
|
||||
// Len returns the number of selected lines.
|
||||
func (s *Selection) Len() int {
|
||||
s.mutex.RLock()
|
||||
defer s.mutex.RUnlock()
|
||||
return s.tree.Len()
|
||||
}
|
||||
|
||||
// Ascend iterates over selected lines in ascending order, calling i for each.
|
||||
func (s *Selection) Ascend(i btree.ItemIterator) {
|
||||
s.mutex.RLock()
|
||||
defer s.mutex.RUnlock()
|
||||
s.tree.Ascend(i)
|
||||
}
|
||||
107
selection/selection.go
Normal file
107
selection/selection.go
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
package selection
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/google/btree"
|
||||
"github.com/peco/peco/line"
|
||||
)
|
||||
|
||||
// Set stores the line ids that were selected by the user.
|
||||
// The contents of the Set is always sorted from smallest to
|
||||
// largest line ID.
|
||||
type Set struct {
|
||||
mutex sync.RWMutex
|
||||
tree *btree.BTree
|
||||
}
|
||||
|
||||
// RangeStart tracks the starting position of a range selection.
|
||||
type RangeStart struct {
|
||||
val int
|
||||
valid bool
|
||||
}
|
||||
|
||||
// New creates a new empty Set.
|
||||
func New() *Set {
|
||||
s := &Set{}
|
||||
s.Reset()
|
||||
return s
|
||||
}
|
||||
|
||||
// Add adds a new line to the selection. If the line already
|
||||
// exists in the selection, it is silently ignored.
|
||||
func (s *Set) Add(l line.Line) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
s.tree.ReplaceOrInsert(l)
|
||||
}
|
||||
|
||||
// Copy copies all selected lines from s into dst.
|
||||
func (s *Set) Copy(dst *Set) {
|
||||
s.Ascend(func(l line.Line) bool {
|
||||
dst.Add(l)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// Remove removes the specified line from the selection.
|
||||
func (s *Set) Remove(l line.Line) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
s.tree.Delete(l)
|
||||
}
|
||||
|
||||
// Reset clears all selected indices from the selection.
|
||||
func (s *Set) Reset() {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
s.tree = btree.New(32)
|
||||
}
|
||||
|
||||
// Has reports whether the given line is in the selection.
|
||||
func (s *Set) Has(x line.Line) bool {
|
||||
s.mutex.RLock()
|
||||
defer s.mutex.RUnlock()
|
||||
return s.tree.Has(x)
|
||||
}
|
||||
|
||||
// Len returns the number of selected lines.
|
||||
func (s *Set) Len() int {
|
||||
s.mutex.RLock()
|
||||
defer s.mutex.RUnlock()
|
||||
return s.tree.Len()
|
||||
}
|
||||
|
||||
// Ascend iterates over selected lines in ascending order, calling fn for each.
|
||||
func (s *Set) Ascend(fn func(line.Line) bool) {
|
||||
s.mutex.RLock()
|
||||
defer s.mutex.RUnlock()
|
||||
s.tree.Ascend(func(it btree.Item) bool {
|
||||
l, ok := it.(line.Line)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
return fn(l)
|
||||
})
|
||||
}
|
||||
|
||||
// Valid reports whether the RangeStart has been set.
|
||||
func (s RangeStart) Valid() bool {
|
||||
return s.valid
|
||||
}
|
||||
|
||||
// Value returns the starting line index of the range.
|
||||
func (s RangeStart) Value() int {
|
||||
return s.val
|
||||
}
|
||||
|
||||
// SetValue sets the starting position and marks it as valid.
|
||||
func (s *RangeStart) SetValue(n int) {
|
||||
s.val = n
|
||||
s.valid = true
|
||||
}
|
||||
|
||||
// Reset clears the range start, marking it as invalid.
|
||||
func (s *RangeStart) Reset() {
|
||||
s.valid = false
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package peco
|
||||
package selection
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
|
@ -8,7 +8,7 @@ import (
|
|||
)
|
||||
|
||||
func TestSelection(t *testing.T) {
|
||||
s := NewSelection()
|
||||
s := New()
|
||||
|
||||
var i uint64
|
||||
alice := line.NewRaw(i, "Alice", false, false)
|
||||
Loading…
Reference in a new issue