mirror of
https://github.com/peco/peco.git
synced 2026-09-10 07:16:29 -04:00
158 lines
4 KiB
Go
158 lines
4 KiB
Go
package peco
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// Hub acts as the messaging hub between components -- that is,
|
|
// it controls how the communication that goes through channels
|
|
// are handled.
|
|
type Hub struct {
|
|
isSync bool
|
|
mutex *sync.Mutex
|
|
loopCh chan struct{}
|
|
queryCh chan HubReq
|
|
drawCh chan HubReq
|
|
statusMsgCh chan HubReq
|
|
clearStatusCh chan HubReq
|
|
pagingCh chan HubReq
|
|
}
|
|
|
|
// HubReq is a wrapper around the actual requst value that needs
|
|
// to be passed. It contains an optional channel field which can
|
|
// be filled to force synchronous communication between the
|
|
// sender and receiver
|
|
type HubReq struct {
|
|
data interface{}
|
|
replyCh chan struct{}
|
|
}
|
|
|
|
// DataInterface returns the underlying data as interface{}
|
|
func (hr HubReq) DataInterface() interface{} {
|
|
return hr.data
|
|
}
|
|
|
|
// DataString returns the underlying data as a string. Panics
|
|
// if type conversion fails.
|
|
func (hr HubReq) DataString() string {
|
|
return hr.data.(string)
|
|
}
|
|
|
|
// Done marks the request as done. If Hub is operating in
|
|
// asynchronous mode (default), it's a no op. Otherwise it
|
|
// sends a message back the reply channel to finish up the
|
|
// synchronous communication
|
|
func (hr HubReq) Done() {
|
|
if hr.replyCh == nil {
|
|
return
|
|
}
|
|
hr.replyCh <- struct{}{}
|
|
}
|
|
|
|
// NewHub creates a new Hub struct
|
|
func NewHub() *Hub {
|
|
return &Hub{
|
|
false,
|
|
&sync.Mutex{},
|
|
make(chan struct{}), // loopCh. You never send messages to this. no point in buffering
|
|
make(chan HubReq, 5), // queryCh.
|
|
make(chan HubReq, 5), // drawCh.
|
|
make(chan HubReq, 5), // statusMsgCh
|
|
make(chan HubReq, 5), // clearStatusCh
|
|
make(chan HubReq, 5), // pagingCh
|
|
}
|
|
}
|
|
|
|
// Batch allows you to synchronously send messages during the
|
|
// scope of f() being executed.
|
|
func (h *Hub) Batch(f func()) {
|
|
// lock during this operation
|
|
h.mutex.Lock()
|
|
defer h.mutex.Unlock()
|
|
|
|
// temporarily set isSync = true
|
|
o := h.isSync
|
|
h.isSync = true
|
|
defer func() { h.isSync = o }()
|
|
|
|
// ignore panics
|
|
defer func() { recover() }()
|
|
|
|
f()
|
|
}
|
|
|
|
// low-level utility
|
|
func send(ch chan HubReq, r HubReq, needReply bool) {
|
|
if needReply {
|
|
r.replyCh = make(chan struct{})
|
|
defer func() { <-r.replyCh }()
|
|
}
|
|
|
|
ch <- r
|
|
}
|
|
|
|
// QueryCh returns the underlying channel for queries
|
|
func (h *Hub) QueryCh() chan HubReq {
|
|
return h.queryCh
|
|
}
|
|
|
|
// SendQuery sends the query string to be processed by the Filter
|
|
func (h *Hub) SendQuery(q string) {
|
|
send(h.QueryCh(), HubReq{q, nil}, h.isSync)
|
|
}
|
|
|
|
// LoopCh returns the channel to control the main execution loop.
|
|
// Nothing should ever be sent through this channel. The only way
|
|
// the channel communicates anything to its receivers is when
|
|
// it is closed -- which is when peco is done.
|
|
func (h *Hub) LoopCh() chan struct{} {
|
|
return h.loopCh
|
|
}
|
|
|
|
// DrawCh returns the channel to redraw the terminal display
|
|
func (h *Hub) DrawCh() chan HubReq {
|
|
return h.drawCh
|
|
}
|
|
|
|
// SendDraw sends a request to redraw the terminal display
|
|
func (h *Hub) SendDraw(matches []Match) {
|
|
send(h.DrawCh(), HubReq{matches, nil}, h.isSync)
|
|
}
|
|
|
|
// StatusMsgCh returns the channel to update the status message
|
|
func (h *Hub) StatusMsgCh() chan HubReq {
|
|
return h.statusMsgCh
|
|
}
|
|
|
|
// SendStatusMsg sends a string to be displayed in the status message
|
|
func (h *Hub) SendStatusMsg(q string) {
|
|
send(h.StatusMsgCh(), HubReq{q, nil}, h.isSync)
|
|
}
|
|
|
|
func (h *Hub) ClearStatusCh() chan HubReq {
|
|
return h.clearStatusCh
|
|
}
|
|
|
|
// SendClearStatus sends a request to clear the status message in
|
|
// `d` duration. If a new status message is sent before the clear
|
|
// request is executed, the clear instruction will be canceled
|
|
func (h *Hub) SendClearStatus(d time.Duration) {
|
|
send(h.ClearStatusCh(), HubReq{d, nil}, h.isSync)
|
|
}
|
|
|
|
// PagingCh returns the channel to page through the results
|
|
func (h *Hub) PagingCh() chan HubReq {
|
|
return h.pagingCh
|
|
}
|
|
|
|
// SendPaging sends a request to move the cursor around
|
|
func (h *Hub) SendPaging(x PagingRequest) {
|
|
send(h.PagingCh(), HubReq{x, nil}, h.isSync)
|
|
}
|
|
|
|
// Stop closes the LoopCh so that peco shutsdown
|
|
func (h *Hub) Stop() {
|
|
close(h.LoopCh())
|
|
}
|