Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add keep selection feature #230

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 9 additions & 7 deletions action.go
Expand Up @@ -174,11 +174,12 @@ func doRotateMatcher(i *Input, ev termbox.Event) {
}

func doToggleSelection(i *Input, _ termbox.Event) {
if i.selection.Has(i.currentLine) {
i.selection.Remove(i.currentLine)
l := i.GetCurrentAt(i.currentLine - 1)
if i.selection.Has(l.Index()) {
i.selection.Remove(l.Index())
return
}
i.selection.Add(i.currentLine)
i.selection.Add(l.Index())
}

func doToggleRangeMode(i *Input, _ termbox.Event) {
Expand Down Expand Up @@ -220,19 +221,20 @@ func doSelectVisible(i *Input, _ termbox.Event) {
func doFinish(i *Input, _ termbox.Event) {
// Must end with all the selected lines.
if i.SelectionLen() == 0 {
i.SelectionAdd(i.currentLine)
l := i.GetCurrentAt(i.currentLine - 1)
i.SelectionAdd(l.Index())
}

i.resultCh = make(chan Line)
go func() {
max := i.GetCurrentLen()
max := i.GetLinesCount()
for x := 1; x <= max; x++ {
if x > i.GetCurrentLen() {
if x > i.GetLinesCount() {
break
}

if i.selection.Has(x) {
i.resultCh <- i.GetCurrentAt(x - 1)
i.resultCh <- i.GetLineAt(x - 1)
}
}
close(i.resultCh)
Expand Down
10 changes: 10 additions & 0 deletions ctx.go
Expand Up @@ -250,6 +250,16 @@ func (c *Ctx) GetLinesCount() int {
return len(c.lines)
}

func (c *Ctx) GetLineAt(i int) Line {
c.currentMutex.Lock()
defer c.currentMutex.Unlock()

if i < 0 || len(c.lines) <= i {
panic(fmt.Sprintf("GetCurrentAt: index out of range (%d)", i))
}
return c.lines[i]
}

func (c *Ctx) IsBufferOverflowing() bool {
if c.bufferSize <= 0 {
return false
Expand Down
1 change: 0 additions & 1 deletion filter.go
Expand Up @@ -17,7 +17,6 @@ func (f *Filter) Work(cancel chan struct{}, q HubReq) {
}
f.SetCurrent(f.Matcher().Line(cancel, query, f.Buffer()))
f.SendStatusMsg("")
f.SelectionClear()
f.DrawMatches(nil)
}

Expand Down
25 changes: 13 additions & 12 deletions layout.go
Expand Up @@ -307,18 +307,6 @@ func (l *ListArea) Draw(targets []Line, perPage int) {
var y int
var fgAttr, bgAttr termbox.Attribute
for n := 0; n < perPage; n++ {
switch {
case n+currentPage.offset == l.currentLine-1:
fgAttr = l.config.Style.SelectedFG()
bgAttr = l.config.Style.SelectedBG()
case l.SelectionContains(n + currentPage.offset + 1):
fgAttr = l.config.Style.SavedSelectionFG()
bgAttr = l.config.Style.SavedSelectionBG()
default:
fgAttr = l.config.Style.BasicFG()
bgAttr = l.config.Style.BasicBG()
}

targetIdx := currentPage.offset + n
if targetIdx >= len(targets) {
break
Expand All @@ -331,6 +319,19 @@ func (l *ListArea) Draw(targets []Line, perPage int) {
}

target := targets[targetIdx]

switch {
case n+currentPage.offset == l.currentLine-1:
fgAttr = l.config.Style.SelectedFG()
bgAttr = l.config.Style.SelectedBG()
case l.SelectionContains(target.Index()):
fgAttr = l.config.Style.SavedSelectionFG()
bgAttr = l.config.Style.SavedSelectionBG()
default:
fgAttr = l.config.Style.BasicFG()
bgAttr = l.config.Style.BasicBG()
}

line := target.DisplayString()
matches := target.Indices()
if matches == nil {
Expand Down
22 changes: 17 additions & 5 deletions line.go
Expand Up @@ -18,20 +18,24 @@ type Line interface {
DisplayString() string // Line to be displayed
Output() string // Output string to be displayed after peco is done
Indices() [][]int // If the type allows, indices into matched portions of the string
Index() int // Index in lines
SetIndex(int) // Set index in lines
}

// baseLine is the common implementation between RawLine and MatchedLine
type baseLine struct {
buf string
sepLoc int
displayString string
idx int
}

func newBaseLine(v string, enableSep bool) *baseLine {
func newBaseLine(v string, idx int, enableSep bool) *baseLine {
m := &baseLine{
v,
-1,
"",
idx,
}
if !enableSep {
return m
Expand Down Expand Up @@ -72,15 +76,23 @@ func (m baseLine) Output() string {
return m.buf
}

func (m baseLine) Index() int {
return m.idx
}

func (m *baseLine) SetIndex(idx int) {
m.idx = idx
}

// RawLine implements the Line interface. It represents a line with no matches,
// which means that it can only be used in the initial unfiltered view
type RawLine struct {
*baseLine
}

// NewRawLine creates a RawLine struct
func NewRawLine(v string, enableSep bool) *RawLine {
return &RawLine{newBaseLine(v, enableSep)}
func NewRawLine(v string, idx int, enableSep bool) *RawLine {
return &RawLine{newBaseLine(v, idx, enableSep)}
}

// Indices always returns nil
Expand All @@ -96,8 +108,8 @@ type MatchedLine struct {
}

// NewMatchedLine creates a new MatchedLine struct
func NewMatchedLine(v string, enableSep bool, m [][]int) *MatchedLine {
return &MatchedLine{newBaseLine(v, enableSep), m}
func NewMatchedLine(v string, idx int, enableSep bool, m [][]int) *MatchedLine {
return &MatchedLine{newBaseLine(v, idx, enableSep), m}
}

// Indices returns the indices in the buffer that matched
Expand Down
13 changes: 10 additions & 3 deletions matchers.go
Expand Up @@ -325,7 +325,7 @@ func (m *RegexpMatcher) Line(quit chan struct{}, q string, buffer []Line) []Line
continue
}

iter <- NewMatchedLine(match.Buffer(), m.enableSep, ms)
iter <- NewMatchedLine(match.Buffer(), match.Index(), m.enableSep, ms)
}
iter <- nil
}()
Expand Down Expand Up @@ -450,7 +450,7 @@ func (m *CustomMatcher) Line(quit chan struct{}, q string, buffer []Line) []Line
results := []Line{}
if q == "" {
for _, match := range buffer {
results = append(results, NewMatchedLine(match.Buffer(), m.enableSep, nil))
results = append(results, NewMatchedLine(match.Buffer(), match.Index(), m.enableSep, nil))
}
return results
}
Expand All @@ -462,6 +462,7 @@ func (m *CustomMatcher) Line(quit chan struct{}, q string, buffer []Line) []Line
matcherInput += match.DisplayString() + "\n"
lines = append(lines, match)
}
idx := 0
args := []string{}
for _, arg := range m.args {
if arg == "$QUERY" {
Expand Down Expand Up @@ -497,7 +498,13 @@ func (m *CustomMatcher) Line(quit chan struct{}, q string, buffer []Line) []Line
for {
b, _, err := buf.ReadLine()
if len(b) > 0 {
iter <- NewMatchedLine(string(b), m.enableSep, nil)
for i, line := range lines[idx:] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コードを細かく見てないんですが、このループの効率の悪さが若干気になります…
他に方法はないものでしょうか

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

指摘ありがとうございます!
いい方法がぱっと思いつかなくて苦し紛れでした…。
なにか妙案がないか考えてみてますが、いまの Custom Matcher のときの選択のロジックから元の行数を得るのはなかなか難しそうな気がしています。

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

子供と風呂に入りながら「ああ、そういうことだな」と気づきましたw>CustomMatcher
仕様変えないと難しそうですね

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

もともとの仕様はシンプルで CustomMatcher 作りやかったので、余計なことをしてしまったかもしれません 😲
といいつつ絞り込みつつ選択していくのはべんりだと思っているのでいい塩梅になるといいのですが。

if line.DisplayString() == string(b) {
idx = idx + i + 1
break
}
}
iter <- NewMatchedLine(string(b), idx, m.enableSep, nil)
}
if err != nil {
break
Expand Down
6 changes: 3 additions & 3 deletions matchers_test.go
Expand Up @@ -28,7 +28,7 @@ func TestANSIColorStrip(t *testing.T) {
func TestNewMatch(t *testing.T) {
var m Line

m = NewRawLine("Hello, World!", false)
m = NewRawLine("Hello, World!", 1, false)
if m.Indices() != nil {
t.Errorf("NoMatch.Indices() must always return nil")
}
Expand Down Expand Up @@ -56,11 +56,11 @@ func TestNewMatch(t *testing.T) {
}

makeDidMatch := func(buf string) (string, Line) {
return buf, NewMatchedLine(buf, true, [][]int{{0, 5}})
return buf, NewMatchedLine(buf, 1, true, [][]int{{0, 5}})
}

makeNoMatch := func(buf string) (string, Line) {
return buf, NewRawLine(buf, true)
return buf, NewRawLine(buf, 1, true)
}

nullsepCheck(makeNoMatch("Hello, World!"))
Expand Down
10 changes: 9 additions & 1 deletion reader.go
Expand Up @@ -50,6 +50,7 @@ func (b *BufferReader) Loop() {
once := &sync.Once{}
var refresh *time.Timer

overflow := false
loop := true
for loop {
select {
Expand All @@ -67,10 +68,11 @@ func (b *BufferReader) Loop() {

// Make sure we lock access to b.lines
m.Lock()
b.SetLines(append(b.GetLines(), NewRawLine(line, b.enableSep)))
b.SetLines(append(b.GetLines(), NewRawLine(line, b.GetLinesCount()+1, b.enableSep)))
if b.IsBufferOverflowing() {
lines := b.GetLines()
b.SetLines(lines[1:])
overflow = true
}
m.Unlock()
}
Expand Down Expand Up @@ -98,4 +100,10 @@ func (b *BufferReader) Loop() {
b.ExitWith(1)
fmt.Fprintf(os.Stderr, "No buffer to work with was available")
}

if overflow {
for i, line := range b.GetLines() {
line.SetIndex(i + 1)
}
}
}