peco.peco/reader.go
Daisuke Maki 4c7616b515 Delay the terminal initialization until incoming buffer is ready
This change fixes #144 by delaying all the terminal initialization
until there's something coming in from the standard input.

Without this, for example, a succesive chain of commands that expect to
use stdin will fail, because peco might accidentally grab the stdin
under the hood. With this change, peco is forced to wait to do any
terminal related stuff until some output has been spewed by the
previous command (which is most likely when the command is ready to
give up the control of the terminal), so things work again.
2014-07-07 09:44:35 +09:00

96 lines
2 KiB
Go

package peco
import (
"bufio"
"fmt"
"io"
"os"
"sync"
"time"
)
// BufferReader reads lines from the input, either Stdin or a file.
// If the incoming data is endless, it keeps reading and adding to
// the search buffer, as long as it can.
//
// If you would like to limit the number of lines to keep in the
// buffer, you should set --buffer-size to a number > 0
type BufferReader struct {
*Ctx
input io.ReadCloser
inputReadyCh chan struct{}
}
func (b *BufferReader) InputReadyCh() <-chan struct{} {
return b.inputReadyCh
}
// Loop keeps reading from the input
func (b *BufferReader) Loop() {
defer b.ReleaseWaitGroup()
defer func() { recover() }() // ignore errors
defer func() { close(b.inputReadyCh) }() // Make sure to close notifier
ch := make(chan string, 10)
// scanner.Scan() blocks until the next read or error. But we want to
// exit immediately, so we move it out to its own goroutine
go func() {
defer func() { recover() }()
defer func() { close(ch) }()
scanner := bufio.NewScanner(b.input)
for scanner.Scan() {
ch <- scanner.Text()
}
}()
m := &sync.Mutex{}
once := &sync.Once{}
var refresh *time.Timer
loop := true
for loop {
select {
case <-b.LoopCh():
loop = false
case line, ok := <-ch:
if !ok {
loop = false
continue
}
if line != "" {
once.Do(func() { b.inputReadyCh <- struct{}{} })
m.Lock()
b.lines = append(b.lines, NewNoMatch(line, b.enableSep))
if b.IsBufferOverflowing() {
b.lines = b.lines[1:]
}
m.Unlock()
}
m.Lock()
if refresh == nil {
refresh = time.AfterFunc(100*time.Millisecond, func() {
if !b.ExecQuery() {
b.DrawMatches(b.lines)
}
m.Lock()
refresh = nil
m.Unlock()
})
}
m.Unlock()
}
}
b.input.Close()
// 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 {
b.ExitWith(1)
fmt.Fprintf(os.Stderr, "No buffer to work with was available")
}
}