mirror of
https://github.com/peco/peco.git
synced 2026-09-10 07:16:29 -04:00
121 lines
2.6 KiB
Go
121 lines
2.6 KiB
Go
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.
|
|
//
|
|
// Items are collected under the source read lock first, then added to dst
|
|
// after releasing that lock. This avoids deadlocks when dst == s and
|
|
// prevents ABBA deadlocks during concurrent bidirectional copies.
|
|
func (s *Set) Copy(dst *Set) {
|
|
s.mutex.RLock()
|
|
items := make([]line.Line, 0, s.tree.Len())
|
|
s.tree.Ascend(func(it btree.Item) bool {
|
|
if l, ok := it.(line.Line); ok {
|
|
items = append(items, l)
|
|
}
|
|
return true
|
|
})
|
|
s.mutex.RUnlock()
|
|
|
|
for _, l := range items {
|
|
dst.Add(l)
|
|
}
|
|
}
|
|
|
|
// 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
|
|
}
|