mirror of
https://github.com/peco/peco.git
synced 2026-09-10 15:26:29 -04:00
167 lines
3.6 KiB
Go
167 lines
3.6 KiB
Go
package buffer
|
|
|
|
import (
|
|
"time"
|
|
|
|
"context"
|
|
|
|
"github.com/lestrrat-go/pdebug/v2"
|
|
runewidth "github.com/mattn/go-runewidth"
|
|
"github.com/peco/peco/internal/location"
|
|
"github.com/peco/peco/line"
|
|
"github.com/peco/peco/pipeline"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
func Crop(src Buffer, loc *location.Location) *Filtered {
|
|
return NewFiltered(src, loc.Page(), loc.PerPage())
|
|
}
|
|
|
|
func NewFiltered(src Buffer, page, perPage int) *Filtered {
|
|
fb := Filtered{
|
|
src: src,
|
|
}
|
|
|
|
start := perPage * (page - 1)
|
|
|
|
// if for whatever reason we wanted a page that goes over the
|
|
// capacity of the original buffer, we don't need to do any more
|
|
// calculations. bail out
|
|
if start > src.Size() {
|
|
return &fb
|
|
}
|
|
|
|
// Copy over the selections that are applicable to this filtered buffer.
|
|
selection := make([]int, 0, src.Size())
|
|
end := start + perPage
|
|
if end >= src.Size() {
|
|
end = src.Size()
|
|
}
|
|
|
|
lines := src.LinesInRange(start, end)
|
|
var maxcols int
|
|
for i := start; i < end; i++ {
|
|
selection = append(selection, i)
|
|
cols := runewidth.StringWidth(lines[i-start].DisplayString())
|
|
if cols > maxcols {
|
|
maxcols = cols
|
|
}
|
|
}
|
|
fb.selection = selection
|
|
fb.maxcols = maxcols
|
|
|
|
return &fb
|
|
}
|
|
|
|
func (flb *Filtered) LinesInRange(_ int, _ int) []line.Line {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// MaxColumn returns the max column size, which controls the amount we
|
|
// can scroll to the right
|
|
func (flb *Filtered) MaxColumn() int {
|
|
return flb.maxcols
|
|
}
|
|
|
|
// LineAt returns the line at index `i`. Note that the i-th element
|
|
// in this filtered buffer may actually correspond to a totally
|
|
// different line number in the source buffer.
|
|
func (flb Filtered) LineAt(i int) (line.Line, error) {
|
|
if i >= len(flb.selection) {
|
|
return nil, errors.Errorf("specified index %d is out of range", len(flb.selection))
|
|
}
|
|
return flb.src.LineAt(flb.selection[i])
|
|
}
|
|
|
|
// Size returns the number of lines in the buffer
|
|
func (flb Filtered) Size() int {
|
|
return len(flb.selection)
|
|
}
|
|
|
|
func NewMemory() *Memory {
|
|
mb := &Memory{}
|
|
mb.Reset()
|
|
return mb
|
|
}
|
|
|
|
func (mb *Memory) Size() int {
|
|
mb.mutex.RLock()
|
|
defer mb.mutex.RUnlock()
|
|
return len(mb.lines)
|
|
}
|
|
|
|
func (mb *Memory) Reset() {
|
|
mb.mutex.Lock()
|
|
defer mb.mutex.Unlock()
|
|
if pdebug.Enabled {
|
|
g := pdebug.Marker(context.TODO(), "Memory.Reset")
|
|
defer g.End()
|
|
}
|
|
mb.done = make(chan struct{})
|
|
mb.lines = []line.Line(nil)
|
|
}
|
|
|
|
func (mb *Memory) Done() <-chan struct{} {
|
|
mb.mutex.RLock()
|
|
defer mb.mutex.RUnlock()
|
|
return mb.done
|
|
}
|
|
|
|
func (mb *Memory) Accept(ctx context.Context, in chan interface{}, _ pipeline.ChanOutput) {
|
|
if pdebug.Enabled {
|
|
g := pdebug.Marker(ctx, "Memory.Accept")
|
|
defer g.End()
|
|
}
|
|
defer func() {
|
|
mb.mutex.Lock()
|
|
close(mb.done)
|
|
mb.mutex.Unlock()
|
|
}()
|
|
|
|
start := time.Now()
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
if pdebug.Enabled {
|
|
pdebug.Printf(ctx, "Memory received context done")
|
|
}
|
|
return
|
|
case v := <-in:
|
|
switch v := v.(type) {
|
|
case error:
|
|
if pipeline.IsEndMark(v) {
|
|
if pdebug.Enabled {
|
|
pdebug.Printf(ctx, "Memory received end mark (read %d lines, %s since starting accept loop)", len(mb.lines), time.Since(start).String())
|
|
}
|
|
return
|
|
}
|
|
case line.Line:
|
|
mb.mutex.Lock()
|
|
mb.lines = append(mb.lines, v)
|
|
mb.mutex.Unlock()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (mb *Memory) LineAt(n int) (line.Line, error) {
|
|
mb.mutex.RLock()
|
|
defer mb.mutex.RUnlock()
|
|
return LineAt(mb.lines, n)
|
|
}
|
|
|
|
func (mb *Memory) LinesInRange(start, end int) []line.Line {
|
|
mb.mutex.RLock()
|
|
defer mb.mutex.RUnlock()
|
|
return mb.lines[start:end]
|
|
}
|
|
|
|
func LineAt(lines []line.Line, n int) (line.Line, error) {
|
|
// TODO: This code smells
|
|
if s := len(lines); s <= 0 || n >= s {
|
|
return nil, errors.New("empty buffer")
|
|
}
|
|
|
|
return lines[n], nil
|
|
}
|