mirror of
https://github.com/peco/peco.git
synced 2026-09-10 07:16:29 -04:00
Fix a bunch of race conditions
This commit is contained in:
parent
4952013023
commit
2f1f4cbb63
42
action.go
42
action.go
|
|
@ -151,10 +151,10 @@ func doAcceptChar(i *Input, ev termbox.Event) {
|
|||
}
|
||||
|
||||
if ev.Ch > 0 {
|
||||
if i.QueryLen() == i.CaretPos().Int() {
|
||||
if i.QueryLen() == i.CaretPos() {
|
||||
i.AppendQuery(ev.Ch)
|
||||
} else {
|
||||
i.InsertQueryAt(ev.Ch, i.CaretPos().Int())
|
||||
i.InsertQueryAt(ev.Ch, i.CaretPos())
|
||||
}
|
||||
i.MoveCaretPos(1)
|
||||
i.DrawPrompt() // Update prompt before running query
|
||||
|
|
@ -180,7 +180,7 @@ func doToggleSelection(i *Input, _ termbox.Event) {
|
|||
|
||||
func doToggleRangeMode(i *Input, _ termbox.Event) {
|
||||
if i.IsRangeMode() {
|
||||
for _, line := range i.SelectedRange() {
|
||||
for _, line := range i.SelectedRange().GetSelection() {
|
||||
i.selection.Add(line)
|
||||
}
|
||||
i.selection.Add(i.currentLine)
|
||||
|
|
@ -225,9 +225,9 @@ func doFinish(i *Input, _ termbox.Event) {
|
|||
}
|
||||
|
||||
i.result = []Match{}
|
||||
for _, lineno := range append(i.selection, i.SelectedRange()...) {
|
||||
if lineno <= len(i.current) {
|
||||
i.result = append(i.result, i.current[lineno-1])
|
||||
for _, lineno := range append(i.selection.GetSelection(), i.SelectedRange().GetSelection()...) {
|
||||
if lineno <= i.GetCurrentLen() {
|
||||
i.result = append(i.result, i.GetCurrentAt(lineno-1))
|
||||
}
|
||||
}
|
||||
i.ExitWith(0)
|
||||
|
|
@ -286,7 +286,7 @@ func doDeleteBackwardWord(i *Input, _ termbox.Event) {
|
|||
}
|
||||
|
||||
q := i.Query()
|
||||
start := i.CaretPos().Int()
|
||||
start := i.CaretPos()
|
||||
if l := len(q); l <= start {
|
||||
start = l
|
||||
}
|
||||
|
|
@ -299,7 +299,7 @@ func doDeleteBackwardWord(i *Input, _ termbox.Event) {
|
|||
found := false
|
||||
for pos := start - 1; pos >= 0; pos-- {
|
||||
if sepFunc(q[pos]) {
|
||||
buf := make([]rune, q.QueryLen()-(start-pos-1))
|
||||
buf := make([]rune, len(q)-(start-pos-1))
|
||||
copy(buf, q[:pos+1])
|
||||
copy(buf[pos+1:], q[start:])
|
||||
i.SetQuery(buf)
|
||||
|
|
@ -322,12 +322,12 @@ func doDeleteBackwardWord(i *Input, _ termbox.Event) {
|
|||
}
|
||||
|
||||
func doForwardWord(i *Input, _ termbox.Event) {
|
||||
if i.CaretPos().Int() >= i.QueryLen() {
|
||||
if i.CaretPos() >= i.QueryLen() {
|
||||
return
|
||||
}
|
||||
|
||||
foundSpace := false
|
||||
for pos := i.CaretPos().Int(); pos < i.QueryLen(); pos++ {
|
||||
for pos := i.CaretPos(); pos < i.QueryLen(); pos++ {
|
||||
r := i.Query()[pos]
|
||||
if foundSpace {
|
||||
if !unicode.IsSpace(r) {
|
||||
|
|
@ -349,11 +349,11 @@ func doForwardWord(i *Input, _ termbox.Event) {
|
|||
}
|
||||
|
||||
func doBackwardWord(i *Input, _ termbox.Event) {
|
||||
if i.CaretPos().Int() == 0 {
|
||||
if i.CaretPos() == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if i.CaretPos().Int() >= i.QueryLen() {
|
||||
if i.CaretPos() >= i.QueryLen() {
|
||||
i.MoveCaretPos(-1)
|
||||
}
|
||||
|
||||
|
|
@ -361,8 +361,8 @@ func doBackwardWord(i *Input, _ termbox.Event) {
|
|||
// rewind to the end of the previous word, and then do the
|
||||
// search all over again
|
||||
SEARCH_PREV_WORD:
|
||||
if unicode.IsSpace(i.Query()[i.CaretPos().Int()]) {
|
||||
for pos := i.CaretPos().Int(); pos > 0; pos-- {
|
||||
if unicode.IsSpace(i.Query()[i.CaretPos()]) {
|
||||
for pos := i.CaretPos(); pos > 0; pos-- {
|
||||
if !unicode.IsSpace(i.Query()[pos]) {
|
||||
i.SetCaretPos(pos)
|
||||
break
|
||||
|
|
@ -392,7 +392,7 @@ SEARCH_PREV_WORD:
|
|||
}
|
||||
|
||||
func doForwardChar(i *Input, _ termbox.Event) {
|
||||
if i.CaretPos().Int() >= i.QueryLen() {
|
||||
if i.CaretPos() >= i.QueryLen() {
|
||||
return
|
||||
}
|
||||
i.MoveCaretPos(1)
|
||||
|
|
@ -408,11 +408,11 @@ func doBackwardChar(i *Input, _ termbox.Event) {
|
|||
}
|
||||
|
||||
func doDeleteForwardWord(i *Input, _ termbox.Event) {
|
||||
if i.QueryLen() <= i.CaretPos().Int() {
|
||||
if i.QueryLen() <= i.CaretPos() {
|
||||
return
|
||||
}
|
||||
|
||||
start := i.CaretPos().Int()
|
||||
start := i.CaretPos()
|
||||
|
||||
// If we are on a word (non-Space, delete till the end of the word.
|
||||
// If we are on a space, delete till the end of space.
|
||||
|
|
@ -477,7 +477,7 @@ func doKillBeginningOfLine(i *Input, _ termbox.Event) {
|
|||
}
|
||||
|
||||
func doKillEndOfLine(i *Input, _ termbox.Event) {
|
||||
if i.QueryLen() <= i.CaretPos().Int() {
|
||||
if i.QueryLen() <= i.CaretPos() {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -496,11 +496,11 @@ func doDeleteAll(i *Input, _ termbox.Event) {
|
|||
}
|
||||
|
||||
func doDeleteForwardChar(i *Input, _ termbox.Event) {
|
||||
if i.QueryLen() <= i.CaretPos().Int() {
|
||||
if i.QueryLen() <= i.CaretPos() {
|
||||
return
|
||||
}
|
||||
|
||||
pos := i.CaretPos().Int()
|
||||
pos := i.CaretPos()
|
||||
buf := make([]rune, i.QueryLen()-1)
|
||||
copy(buf, i.Query()[:i.CaretPos()])
|
||||
copy(buf[i.CaretPos():], i.Query()[i.CaretPos()+1:])
|
||||
|
|
@ -520,7 +520,7 @@ func doDeleteBackwardChar(i *Input, ev termbox.Event) {
|
|||
return
|
||||
}
|
||||
|
||||
pos := i.CaretPos().Int()
|
||||
pos := i.CaretPos()
|
||||
switch pos {
|
||||
case 0:
|
||||
// No op
|
||||
|
|
|
|||
|
|
@ -41,8 +41,8 @@ func TestActionNames(t *testing.T) {
|
|||
}
|
||||
|
||||
func expectCaretPos(t *testing.T, c interface {
|
||||
CaretPos() CaretPosition
|
||||
}, expect CaretPosition) bool {
|
||||
CaretPos() int
|
||||
}, expect int) bool {
|
||||
if c.CaretPos() != expect {
|
||||
t.Errorf("Expected caret position %d, got %d", expect, c.CaretPos())
|
||||
return false
|
||||
|
|
@ -75,7 +75,7 @@ func TestDoDeleteForwardChar(t *testing.T) {
|
|||
doDeleteForwardChar(input, termbox.Event{})
|
||||
|
||||
expectQueryString(t, ctx, "Hello World!")
|
||||
expectCaretPos(t, ctx, CaretPosition(runewidth.StringWidth(ctx.QueryString())))
|
||||
expectCaretPos(t, ctx, runewidth.StringWidth(ctx.QueryString()))
|
||||
|
||||
ctx.SetCaretPos(0)
|
||||
doDeleteForwardChar(input, termbox.Event{})
|
||||
|
|
@ -99,7 +99,7 @@ func TestDoDeleteForwardWord(t *testing.T) {
|
|||
doDeleteForwardWord(input, termbox.Event{})
|
||||
|
||||
expectQueryString(t, ctx, "Hello World!")
|
||||
expectCaretPos(t, ctx, CaretPosition(runewidth.StringWidth(ctx.QueryString())))
|
||||
expectCaretPos(t, ctx, runewidth.StringWidth(ctx.QueryString()))
|
||||
|
||||
ctx.SetCaretPos(0)
|
||||
doDeleteForwardWord(input, termbox.Event{})
|
||||
|
|
@ -128,7 +128,7 @@ func TestDoDeleteBackwardChar(t *testing.T) {
|
|||
doDeleteBackwardChar(input, termbox.Event{})
|
||||
|
||||
expectQueryString(t, ctx, "Hell, World")
|
||||
expectCaretPos(t, ctx, CaretPosition(runewidth.StringWidth(ctx.QueryString())))
|
||||
expectCaretPos(t, ctx, runewidth.StringWidth(ctx.QueryString()))
|
||||
|
||||
ctx.SetCaretPos(0)
|
||||
doDeleteBackwardChar(input, termbox.Event{})
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ func main() {
|
|||
func setupDeps() {
|
||||
deps := map[string]string{
|
||||
"github.com/jessevdk/go-flags": "8ec9564882e7923e632f012761c81c46dcf5bec1",
|
||||
"github.com/mattn/go-runewidth": "36f63b8223e701c16f36010094fb6e84ffbaf8e0",
|
||||
"github.com/mattn/go-runewidth": "63c378b851290989b19ca955468386485f118c65",
|
||||
"github.com/nsf/termbox-go": "bb19a81afd4bc2729799d1fedb19f7bd7ee284cf",
|
||||
}
|
||||
|
||||
|
|
|
|||
138
ctx.go
138
ctx.go
|
|
@ -37,66 +37,90 @@ type PageInfo struct {
|
|||
perPage int
|
||||
}
|
||||
|
||||
type CaretPosition int
|
||||
|
||||
func (p CaretPosition) Int() int {
|
||||
return int(p)
|
||||
type CaretPosition struct {
|
||||
pos int
|
||||
mutex *sync.Mutex
|
||||
}
|
||||
|
||||
func (p CaretPosition) CaretPos() CaretPosition {
|
||||
return p
|
||||
func (p CaretPosition) Int() int {
|
||||
p.mutex.Lock()
|
||||
defer p.mutex.Unlock()
|
||||
return int(p.pos)
|
||||
}
|
||||
|
||||
func (p CaretPosition) CaretPos() int {
|
||||
p.mutex.Lock()
|
||||
defer p.mutex.Unlock()
|
||||
return p.pos
|
||||
}
|
||||
|
||||
func (p *CaretPosition) SetCaretPos(where int) {
|
||||
*p = CaretPosition(where)
|
||||
p.mutex.Lock()
|
||||
defer p.mutex.Unlock()
|
||||
p.pos = where
|
||||
}
|
||||
|
||||
func (p *CaretPosition) MoveCaretPos(offset int) {
|
||||
*p = CaretPosition(p.Int() + offset)
|
||||
p.SetCaretPos(p.Int() + offset)
|
||||
}
|
||||
|
||||
type FilterQuery []rune
|
||||
type FilterQuery struct {
|
||||
query []rune
|
||||
mutex *sync.Mutex
|
||||
}
|
||||
|
||||
func (q FilterQuery) Query() FilterQuery {
|
||||
return q
|
||||
func (q FilterQuery) Query() []rune {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
return q.query[:]
|
||||
}
|
||||
|
||||
func (q FilterQuery) QueryString() string {
|
||||
return string(q)
|
||||
qbytes := q.Query()
|
||||
return string(qbytes)
|
||||
}
|
||||
|
||||
func (q FilterQuery) QueryLen() int {
|
||||
return len(q)
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
return len(q.query)
|
||||
}
|
||||
|
||||
func (q *FilterQuery) AppendQuery(r rune) {
|
||||
*q = FilterQuery(append([]rune(*q), r))
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
q.query = append(q.query, r)
|
||||
}
|
||||
|
||||
func (q *FilterQuery) InsertQueryAt(ch rune, where int) {
|
||||
sq := []rune(*q)
|
||||
buf := make([]rune, q.QueryLen()+1)
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
sq := q.query
|
||||
buf := make([]rune, len(sq)+1)
|
||||
copy(buf, sq[:where])
|
||||
buf[where] = ch
|
||||
copy(buf[where+1:], sq[where:])
|
||||
*q = FilterQuery(buf)
|
||||
q.query = buf
|
||||
}
|
||||
|
||||
// Ctx contains all the important data. while you can easily access
|
||||
// data in this struct from anwyehre, only do so via channels
|
||||
type Ctx struct {
|
||||
*Hub
|
||||
CaretPosition
|
||||
FilterQuery
|
||||
*CaretPosition
|
||||
*FilterQuery
|
||||
enableSep bool
|
||||
result []Match
|
||||
mutex sync.Mutex
|
||||
mutex *sync.Mutex
|
||||
currentLine int
|
||||
currentPage *PageInfo
|
||||
maxPage int
|
||||
selection Selection
|
||||
selection *Selection
|
||||
lines []Match
|
||||
linesMutex *sync.Mutex
|
||||
current []Match
|
||||
currentMutex *sync.Mutex
|
||||
bufferSize int
|
||||
config *Config
|
||||
Matchers []Matcher
|
||||
|
|
@ -111,15 +135,17 @@ type Ctx struct {
|
|||
func NewCtx(o CtxOptions) *Ctx {
|
||||
c := &Ctx{
|
||||
Hub: NewHub(),
|
||||
CaretPosition: 0,
|
||||
FilterQuery: FilterQuery{},
|
||||
CaretPosition: &CaretPosition{0, &sync.Mutex{}},
|
||||
FilterQuery: &FilterQuery{[]rune{}, &sync.Mutex{}},
|
||||
result: []Match{},
|
||||
mutex: sync.Mutex{},
|
||||
mutex: &sync.Mutex{},
|
||||
currentPage: &PageInfo{0, 1, 0},
|
||||
maxPage: 0,
|
||||
selection: Selection([]int{}),
|
||||
selection: NewSelection(),
|
||||
lines: []Match{},
|
||||
linesMutex: &sync.Mutex{},
|
||||
current: nil,
|
||||
currentMutex: &sync.Mutex{},
|
||||
config: NewConfig(),
|
||||
Matchers: nil,
|
||||
currentMatcher: 0,
|
||||
|
|
@ -173,6 +199,24 @@ func (c *Ctx) ReadConfig(file string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *Ctx) SetLines(newLines []Match) {
|
||||
c.linesMutex.Lock()
|
||||
defer c.linesMutex.Unlock()
|
||||
c.lines = newLines
|
||||
}
|
||||
|
||||
func (c *Ctx) GetLines() []Match {
|
||||
c.linesMutex.Lock()
|
||||
defer c.linesMutex.Unlock()
|
||||
return c.lines[:]
|
||||
}
|
||||
|
||||
func (c *Ctx) GetLinesCount() int {
|
||||
c.linesMutex.Lock()
|
||||
defer c.linesMutex.Unlock()
|
||||
return len(c.lines)
|
||||
}
|
||||
|
||||
func (c *Ctx) IsBufferOverflowing() bool {
|
||||
if c.bufferSize <= 0 {
|
||||
return false
|
||||
|
|
@ -185,9 +229,17 @@ func (c *Ctx) IsRangeMode() bool {
|
|||
return c.selectionRangeStart != invalidSelectionRange
|
||||
}
|
||||
|
||||
func (c *Ctx) SelectedRange() Selection {
|
||||
func (c *Ctx) SelectionClear() {
|
||||
c.selection.Clear()
|
||||
}
|
||||
|
||||
func (c *Ctx) SelectionContains(n int) bool {
|
||||
return c.selection.Has(n)
|
||||
}
|
||||
|
||||
func (c *Ctx) SelectedRange() *Selection {
|
||||
if !c.IsRangeMode() {
|
||||
return Selection{}
|
||||
return NewSelection()
|
||||
}
|
||||
|
||||
selectedLines := []int{}
|
||||
|
|
@ -200,7 +252,33 @@ func (c *Ctx) SelectedRange() Selection {
|
|||
selectedLines = append(selectedLines, i)
|
||||
}
|
||||
}
|
||||
return Selection(selectedLines)
|
||||
s := NewSelection()
|
||||
s.selection = selectedLines
|
||||
return s
|
||||
}
|
||||
|
||||
func (c *Ctx) GetCurrent() []Match {
|
||||
c.currentMutex.Lock()
|
||||
defer c.currentMutex.Unlock()
|
||||
return c.current[:]
|
||||
}
|
||||
|
||||
func (c *Ctx) GetCurrentLen() int {
|
||||
c.currentMutex.Lock()
|
||||
defer c.currentMutex.Unlock()
|
||||
return len(c.current)
|
||||
}
|
||||
|
||||
func (c *Ctx) SetCurrent(newMatches []Match) {
|
||||
c.currentMutex.Lock()
|
||||
defer c.currentMutex.Unlock()
|
||||
c.current = newMatches
|
||||
}
|
||||
|
||||
func (c *Ctx) GetCurrentAt(i int) Match {
|
||||
c.currentMutex.Lock()
|
||||
defer c.currentMutex.Unlock()
|
||||
return c.current[i]
|
||||
}
|
||||
|
||||
func (c *Ctx) Result() []Match {
|
||||
|
|
@ -273,7 +351,9 @@ func (c *Ctx) NewInput() *Input {
|
|||
}
|
||||
|
||||
func (c *Ctx) SetQuery(q []rune) {
|
||||
c.FilterQuery = FilterQuery(q)
|
||||
c.FilterQuery.mutex.Lock()
|
||||
c.FilterQuery.query = q
|
||||
c.FilterQuery.mutex.Unlock()
|
||||
c.SetCaretPos(c.QueryLen())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ func (f *Filter) Work(cancel chan struct{}, q HubReq) {
|
|||
f.DrawMatches(nil)
|
||||
return
|
||||
}
|
||||
f.current = f.Matcher().Match(cancel, query, f.Buffer())
|
||||
f.SetCurrent(f.Matcher().Match(cancel, query, f.Buffer()))
|
||||
f.SendStatusMsg("")
|
||||
f.selection.Clear()
|
||||
f.SelectionClear()
|
||||
f.DrawMatches(nil)
|
||||
}
|
||||
|
||||
|
|
|
|||
18
layout.go
18
layout.go
|
|
@ -149,11 +149,11 @@ func (u UserPrompt) Draw() {
|
|||
u.SetCaretPos(0) // sanity
|
||||
}
|
||||
|
||||
if u.CaretPos().Int() > u.QueryLen() { // XXX Do we really need this?
|
||||
if u.CaretPos() > u.QueryLen() { // XXX Do we really need this?
|
||||
u.SetCaretPos(u.QueryLen())
|
||||
}
|
||||
|
||||
if u.CaretPos().Int() == u.QueryLen() {
|
||||
if u.CaretPos() == u.QueryLen() {
|
||||
// the entire string + the caret after the string
|
||||
fg := u.config.Style.QueryFG()
|
||||
bg := u.config.Style.QueryBG()
|
||||
|
|
@ -168,7 +168,7 @@ func (u UserPrompt) Draw() {
|
|||
for i, r := range []rune(u.Query()) {
|
||||
fg := u.config.Style.QueryFG()
|
||||
bg := u.config.Style.QueryBG()
|
||||
if i == u.CaretPos().Int() {
|
||||
if i == u.CaretPos() {
|
||||
fg |= termbox.AttrReverse
|
||||
bg |= termbox.AttrReverse
|
||||
}
|
||||
|
|
@ -210,6 +210,12 @@ func (s *StatusBar) stopTimer() {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *StatusBar) setClearTimer(t *time.Timer) {
|
||||
s.timerMutex.Lock()
|
||||
defer s.timerMutex.Unlock()
|
||||
s.clearTimer = t
|
||||
}
|
||||
|
||||
// PrintStatus prints a new status message. This also resets the
|
||||
// timer created by ClearStatus()
|
||||
func (s *StatusBar) PrintStatus(msg string, clearDelay time.Duration) {
|
||||
|
|
@ -252,9 +258,9 @@ func (s *StatusBar) PrintStatus(msg string, clearDelay time.Duration) {
|
|||
// if everything is successful AND the clearDelay timer is specified,
|
||||
// then set a timer to clear the status
|
||||
if clearDelay != 0 {
|
||||
s.clearTimer = time.AfterFunc(clearDelay, func() {
|
||||
s.setClearTimer(time.AfterFunc(clearDelay, func() {
|
||||
s.PrintStatus("", 0)
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -288,7 +294,7 @@ func (l *ListArea) Draw(targets []Match, perPage int) {
|
|||
case n+currentPage.offset == l.currentLine-1:
|
||||
fgAttr = l.config.Style.SelectedFG()
|
||||
bgAttr = l.config.Style.SelectedBG()
|
||||
case l.selection.Has(n+currentPage.offset+1) || l.SelectedRange().Has(n+currentPage.offset+1):
|
||||
case l.SelectionContains(n+currentPage.offset+1) || l.SelectedRange().Has(n+currentPage.offset+1):
|
||||
fgAttr = l.config.Style.SavedSelectionFG()
|
||||
bgAttr = l.config.Style.SavedSelectionBG()
|
||||
default:
|
||||
|
|
|
|||
10
reader.go
10
reader.go
|
|
@ -67,9 +67,11 @@ func (b *BufferReader) Loop() {
|
|||
|
||||
// Make sure we lock access to b.lines
|
||||
m.Lock()
|
||||
b.lines = append(b.lines, NewNoMatch(line, b.enableSep))
|
||||
b.SetLines(append(b.GetLines(), NewNoMatch(line, b.enableSep)))
|
||||
if b.IsBufferOverflowing() {
|
||||
b.lines = b.lines[1:]
|
||||
lines := b.GetLines()
|
||||
b.SetLines(lines[1:])
|
||||
|
||||
}
|
||||
m.Unlock()
|
||||
}
|
||||
|
|
@ -78,7 +80,7 @@ func (b *BufferReader) Loop() {
|
|||
if refresh == nil {
|
||||
refresh = time.AfterFunc(100*time.Millisecond, func() {
|
||||
if !b.ExecQuery() {
|
||||
b.DrawMatches(b.lines)
|
||||
b.DrawMatches(b.GetLines())
|
||||
}
|
||||
m.Lock()
|
||||
refresh = nil
|
||||
|
|
@ -93,7 +95,7 @@ func (b *BufferReader) Loop() {
|
|||
|
||||
// Out of the reader loop. If at this point we have no buffer,
|
||||
// that means we have no buffer, so we should quit.
|
||||
if len(b.lines) == 0 {
|
||||
if b.GetLinesCount() == 0 {
|
||||
b.ExitWith(1)
|
||||
fmt.Fprintf(os.Stderr, "No buffer to work with was available")
|
||||
}
|
||||
|
|
|
|||
55
selection.go
55
selection.go
|
|
@ -1,15 +1,34 @@
|
|||
package peco
|
||||
|
||||
import "sort"
|
||||
import (
|
||||
"sort"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Selection stores the line numbers that were selected by the user.
|
||||
// The contents of the Selection is always sorted from smallest to
|
||||
// largest line number
|
||||
type Selection []int
|
||||
type Selection struct {
|
||||
selection []int
|
||||
mutex *sync.Mutex
|
||||
}
|
||||
|
||||
func NewSelection() *Selection {
|
||||
return &Selection{nil,&sync.Mutex{}}
|
||||
}
|
||||
|
||||
func (s *Selection) GetSelection() []int {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
return s.selection[:]
|
||||
}
|
||||
|
||||
// Has returns true if line `v` is in the selection
|
||||
func (s Selection) Has(v int) bool {
|
||||
for _, i := range []int(s) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
for _, i := range s.selection {
|
||||
if i == v {
|
||||
return true
|
||||
}
|
||||
|
|
@ -23,18 +42,23 @@ func (s *Selection) Add(v int) {
|
|||
if s.Has(v) {
|
||||
return
|
||||
}
|
||||
*s = Selection(append([]int(*s), v))
|
||||
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
s.selection = append(s.selection, v)
|
||||
sort.Sort(s)
|
||||
}
|
||||
|
||||
// Remove removes the specified line number from the selection
|
||||
func (s *Selection) Remove(v int) {
|
||||
a := []int(*s)
|
||||
for k, i := range a {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
for k, i := range s.selection {
|
||||
if i == v {
|
||||
tmp := a[:k]
|
||||
tmp = append(tmp, a[k+1:]...)
|
||||
*s = Selection(tmp)
|
||||
tmp := s.selection[:k]
|
||||
tmp = append(tmp, s.selection[k+1:]...)
|
||||
s.selection = tmp
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
@ -42,22 +66,25 @@ func (s *Selection) Remove(v int) {
|
|||
|
||||
// Clear empties the selection
|
||||
func (s *Selection) Clear() {
|
||||
*s = Selection([]int{})
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
s.selection = []int{}
|
||||
}
|
||||
|
||||
// Len returns the number of elements in the selection. Satisfies
|
||||
// sort.Interface
|
||||
func (s Selection) Len() int {
|
||||
return len(s)
|
||||
return len(s.selection)
|
||||
}
|
||||
|
||||
// Swap swaps the elements in indices i and j. Satisfies sort.Interface
|
||||
func (s Selection) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
func (s *Selection) Swap(i, j int) {
|
||||
s.selection[i], s.selection[j] = s.selection[j], s.selection[i]
|
||||
}
|
||||
|
||||
// Less returns true if element at index i is less than the element at
|
||||
// index j. Satisfies sort.Interface
|
||||
func (s Selection) Less(i, j int) bool {
|
||||
return s[i] < s[j]
|
||||
return s.selection[i] < s.selection[j]
|
||||
}
|
||||
|
|
|
|||
5
view.go
5
view.go
|
|
@ -68,13 +68,12 @@ func (v *View) drawScreenNoLock(targets []Match) {
|
|||
if current := v.current; current != nil {
|
||||
targets = v.current
|
||||
} else {
|
||||
targets = v.lines
|
||||
targets = v.GetLines()
|
||||
}
|
||||
}
|
||||
|
||||
v.layout.DrawScreen(targets)
|
||||
// FIXME
|
||||
v.current = targets
|
||||
v.SetCurrent(targets)
|
||||
}
|
||||
|
||||
func (v *View) drawScreen(targets []Match) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue