summaryrefslogtreecommitdiff
path: root/src/util
diff options
context:
space:
mode:
authorJunegunn Choi <junegunn.c@gmail.com>2015-01-12 12:56:17 +0900
committerJunegunn Choi <junegunn.c@gmail.com>2015-01-12 12:56:17 +0900
commitcd847affb79ea6438c9721635724efc6f58e2215 (patch)
treed1e631e3dca8832ee4c495924789f6697c3629cf /src/util
parent7a2bc2cada971c7a390d09b0afda34780ff56fb6 (diff)
downloadfzf-cd847affb79ea6438c9721635724efc6f58e2215.tar.gz
Reorganize source code
Diffstat (limited to 'src/util')
-rw-r--r--src/util/atomicbool.go32
-rw-r--r--src/util/atomicbool_test.go17
-rw-r--r--src/util/eventbox.go80
-rw-r--r--src/util/eventbox_test.go61
-rw-r--r--src/util/util.go56
-rw-r--r--src/util/util_test.go22
6 files changed, 268 insertions, 0 deletions
diff --git a/src/util/atomicbool.go b/src/util/atomicbool.go
new file mode 100644
index 00000000..9e1bdc8f
--- /dev/null
+++ b/src/util/atomicbool.go
@@ -0,0 +1,32 @@
+package util
+
+import "sync"
+
+// AtomicBool is a boxed-class that provides synchronized access to the
+// underlying boolean value
+type AtomicBool struct {
+ mutex sync.Mutex
+ state bool
+}
+
+// NewAtomicBool returns a new AtomicBool
+func NewAtomicBool(initialState bool) *AtomicBool {
+ return &AtomicBool{
+ mutex: sync.Mutex{},
+ state: initialState}
+}
+
+// Get returns the current boolean value synchronously
+func (a *AtomicBool) Get() bool {
+ a.mutex.Lock()
+ defer a.mutex.Unlock()
+ return a.state
+}
+
+// Set updates the boolean value synchronously
+func (a *AtomicBool) Set(newState bool) bool {
+ a.mutex.Lock()
+ defer a.mutex.Unlock()
+ a.state = newState
+ return a.state
+}
diff --git a/src/util/atomicbool_test.go b/src/util/atomicbool_test.go
new file mode 100644
index 00000000..1feff79c
--- /dev/null
+++ b/src/util/atomicbool_test.go
@@ -0,0 +1,17 @@
+package util
+
+import "testing"
+
+func TestAtomicBool(t *testing.T) {
+ if !NewAtomicBool(true).Get() || NewAtomicBool(false).Get() {
+ t.Error("Invalid initial value")
+ }
+
+ ab := NewAtomicBool(true)
+ if ab.Set(false) {
+ t.Error("Invalid return value")
+ }
+ if ab.Get() {
+ t.Error("Invalid state")
+ }
+}
diff --git a/src/util/eventbox.go b/src/util/eventbox.go
new file mode 100644
index 00000000..568ad9f7
--- /dev/null
+++ b/src/util/eventbox.go
@@ -0,0 +1,80 @@
+package util
+
+import "sync"
+
+// EventType is the type for fzf events
+type EventType int
+
+// Events is a type that associates EventType to any data
+type Events map[EventType]interface{}
+
+// EventBox is used for coordinating events
+type EventBox struct {
+ events Events
+ cond *sync.Cond
+ ignore map[EventType]bool
+}
+
+// NewEventBox returns a new EventBox
+func NewEventBox() *EventBox {
+ return &EventBox{
+ events: make(Events),
+ cond: sync.NewCond(&sync.Mutex{}),
+ ignore: make(map[EventType]bool)}
+}
+
+// Wait blocks the goroutine until signaled
+func (b *EventBox) Wait(callback func(*Events)) {
+ b.cond.L.Lock()
+ defer b.cond.L.Unlock()
+
+ if len(b.events) == 0 {
+ b.cond.Wait()
+ }
+
+ callback(&b.events)
+}
+
+// Set turns on the event type on the box
+func (b *EventBox) Set(event EventType, value interface{}) {
+ b.cond.L.Lock()
+ defer b.cond.L.Unlock()
+ b.events[event] = value
+ if _, found := b.ignore[event]; !found {
+ b.cond.Broadcast()
+ }
+}
+
+// Clear clears the events
+// Unsynchronized; should be called within Wait routine
+func (events *Events) Clear() {
+ for event := range *events {
+ delete(*events, event)
+ }
+}
+
+// Peak peaks at the event box if the given event is set
+func (b *EventBox) Peak(event EventType) bool {
+ b.cond.L.Lock()
+ defer b.cond.L.Unlock()
+ _, ok := b.events[event]
+ return ok
+}
+
+// Watch deletes the events from the ignore list
+func (b *EventBox) Watch(events ...EventType) {
+ b.cond.L.Lock()
+ defer b.cond.L.Unlock()
+ for _, event := range events {
+ delete(b.ignore, event)
+ }
+}
+
+// Unwatch adds the events to the ignore list
+func (b *EventBox) Unwatch(events ...EventType) {
+ b.cond.L.Lock()
+ defer b.cond.L.Unlock()
+ for _, event := range events {
+ b.ignore[event] = true
+ }
+}
diff --git a/src/util/eventbox_test.go b/src/util/eventbox_test.go
new file mode 100644
index 00000000..5a9dc302
--- /dev/null
+++ b/src/util/eventbox_test.go
@@ -0,0 +1,61 @@
+package util
+
+import "testing"
+
+// fzf events
+const (
+ EvtReadNew EventType = iota
+ EvtReadFin
+ EvtSearchNew
+ EvtSearchProgress
+ EvtSearchFin
+ EvtClose
+)
+
+func TestEventBox(t *testing.T) {
+ eb := NewEventBox()
+
+ // Wait should return immediately
+ ch := make(chan bool)
+
+ go func() {
+ eb.Set(EvtReadNew, 10)
+ ch <- true
+ <-ch
+ eb.Set(EvtSearchNew, 10)
+ eb.Set(EvtSearchNew, 15)
+ eb.Set(EvtSearchNew, 20)
+ eb.Set(EvtSearchProgress, 30)
+ ch <- true
+ <-ch
+ eb.Set(EvtSearchFin, 40)
+ ch <- true
+ <-ch
+ }()
+
+ count := 0
+ sum := 0
+ looping := true
+ for looping {
+ <-ch
+ eb.Wait(func(events *Events) {
+ for _, value := range *events {
+ switch val := value.(type) {
+ case int:
+ sum += val
+ looping = sum < 100
+ }
+ }
+ events.Clear()
+ })
+ ch <- true
+ count++
+ }
+
+ if count != 3 {
+ t.Error("Invalid number of events", count)
+ }
+ if sum != 100 {
+ t.Error("Invalid sum", sum)
+ }
+}
diff --git a/src/util/util.go b/src/util/util.go
new file mode 100644
index 00000000..14833c04
--- /dev/null
+++ b/src/util/util.go
@@ -0,0 +1,56 @@
+package util
+
+// #include <unistd.h>
+import "C"
+
+import (
+ "os"
+ "time"
+)
+
+// Max returns the largest integer
+func Max(first int, items ...int) int {
+ max := first
+ for _, item := range items {
+ if item > max {
+ max = item
+ }
+ }
+ return max
+}
+
+// Max32 returns the largest 32-bit integer
+func Max32(first int32, second int32) int32 {
+ if first > second {
+ return first
+ }
+ return second
+}
+
+// Constrain limits the given integer with the upper and lower bounds
+func Constrain(val int, min int, max int) int {
+ if val < min {
+ return min
+ }
+ if val > max {
+ return max
+ }
+ return val
+}
+
+// DurWithin limits the given time.Duration with the upper and lower bounds
+func DurWithin(
+ val time.Duration, min time.Duration, max time.Duration) time.Duration {
+ if val < min {
+ return min
+ }
+ if val > max {
+ return max
+ }
+ return val
+}
+
+// IsTty returns true is stdin is a terminal
+func IsTty() bool {
+ return int(C.isatty(C.int(os.Stdin.Fd()))) != 0
+}
diff --git a/src/util/util_test.go b/src/util/util_test.go
new file mode 100644
index 00000000..06cfd4f2
--- /dev/null
+++ b/src/util/util_test.go
@@ -0,0 +1,22 @@
+package util
+
+import "testing"
+
+func TestMax(t *testing.T) {
+ if Max(-2, 5, 1, 4, 3) != 5 {
+ t.Error("Invalid result")
+ }
+}
+
+func TestContrain(t *testing.T) {
+ if Constrain(-3, -1, 3) != -1 {
+ t.Error("Expected", -1)
+ }
+ if Constrain(2, -1, 3) != 2 {
+ t.Error("Expected", 2)
+ }
+
+ if Constrain(5, -1, 3) != 3 {
+ t.Error("Expected", 3)
+ }
+}