From 9110ae474a8ac9fd70b6e15aa3cfda368c5d1cb4 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Sat, 19 May 2018 12:25:39 -0400 Subject: statusline fanciness --- go/src/lib/statusline/async.go | 57 ----------------------------- go/src/lib/statusline/ratelimit.go | 58 +++++++++++++++++++++++++++++ go/src/lib/statusline/statuslinue.go | 21 ++++++++--- go/src/lib/statusline/stopwatch.go | 71 ++++++++++++++++++++++++++++++++++++ 4 files changed, 144 insertions(+), 63 deletions(-) delete mode 100644 go/src/lib/statusline/async.go create mode 100644 go/src/lib/statusline/ratelimit.go create mode 100644 go/src/lib/statusline/stopwatch.go (limited to 'go/src/lib/statusline') diff --git a/go/src/lib/statusline/async.go b/go/src/lib/statusline/async.go deleted file mode 100644 index 5b509ac..0000000 --- a/go/src/lib/statusline/async.go +++ /dev/null @@ -1,57 +0,0 @@ -package statusline - -import ( - "io" - "time" -) - -type AsyncStatusLine struct { - lines chan string - end1 chan struct{} - end2 chan struct{} -} - -func NewAsyncStatusLine(out io.Writer, d time.Duration) *AsyncStatusLine { - ret := &AsyncStatusLine{ - lines: make(chan string), - end1: make(chan struct{}), - end2: make(chan struct{}), - } - go func() { - sl := NewStatusLine(out) - ticker := time.NewTicker(d) - var oldLine string - var newLine string - dirty := false - for { - select { - case <-ticker.C: - if dirty && newLine != oldLine { - sl.Put(newLine) - dirty = false - } - case l := <-ret.lines: - newLine = l - dirty = true - case <-ret.end1: - sl.End() - ticker.Stop() - end2 <- struct{}{} - close(end2) - return - } - } - }() - return ret -} - -func (sl *AsyncStatusLine) Put(line string) { - sl.lines <- line -} - -func (sl *AsyncStatusLine) End() { - sl.end1 <- struct{}{} - close(sl.lines) - close(sl.end) - <-sl.end2 -} diff --git a/go/src/lib/statusline/ratelimit.go b/go/src/lib/statusline/ratelimit.go new file mode 100644 index 0000000..970f8e5 --- /dev/null +++ b/go/src/lib/statusline/ratelimit.go @@ -0,0 +1,58 @@ +package statusline + +import ( + "time" +) + +type rateLimiter struct { + lines chan string + end1 chan bool + end2 chan struct{} +} + +func RateLimit(sl StatusLine, d time.Duration) StatusLine { + ret := &rateLimiter{ + lines: make(chan string), + end1: make(chan bool), + end2: make(chan struct{}), + } + go func() { + ticker := time.NewTicker(d) + var oldLine string + var newLine string + dirty := false + for { + select { + case <-ticker.C: + if dirty && newLine != oldLine { + sl.Put(newLine) + } + dirty = false + case line := <-ret.lines: + newLine = line + dirty = true + case keep := <-ret.end1: + if keep && dirty && newLine != oldLine { + sl.Put(newLine) + } + sl.End(keep) + ticker.Stop() + ret.end2 <- struct{}{} + close(ret.end2) + return + } + } + }() + return ret +} + +func (sl *rateLimiter) Put(line string) { + sl.lines <- line +} + +func (sl *rateLimiter) End(keep bool) { + sl.end1 <- keep + close(sl.lines) + close(sl.end1) + <-sl.end2 +} diff --git a/go/src/lib/statusline/statuslinue.go b/go/src/lib/statusline/statuslinue.go index 90ec1af..323c178 100644 --- a/go/src/lib/statusline/statuslinue.go +++ b/go/src/lib/statusline/statuslinue.go @@ -5,20 +5,29 @@ import ( "io" ) -type StatusLine struct { +type StatusLine interface { + Put(line string) + End(keep bool) +} + +type statusLine struct { out io.Writer prevLen int } -func NewStatusLine(out io.Writer) *StatusLine { - return &StatusLine{out: out} +func New(out io.Writer) StatusLine { + return &statusLine{out: out} } -func (sl *StatusLine) Put(line string) { +func (sl *statusLine) Put(line string) { fmt.Fprintf(sl.out, "\r%-[1]*[2]s", sl.prevLen, line) sl.prevLen = len(line) } -func (sl *StatusLine) End() { - fmt.Fprintf(sl.out, "\r%-[1]*[2]s\r", sl.prevLen, "") +func (sl *statusLine) End(keep bool) { + if (keep) { + io.WriteString(sl.out, "\n") + } else { + fmt.Fprintf(sl.out, "\r%-[1]*[2]s\r", sl.prevLen, "") + } } diff --git a/go/src/lib/statusline/stopwatch.go b/go/src/lib/statusline/stopwatch.go new file mode 100644 index 0000000..4e3bb5b --- /dev/null +++ b/go/src/lib/statusline/stopwatch.go @@ -0,0 +1,71 @@ +package statusline + +import ( + "fmt" + "sync" + "time" +) + +type stopWatch struct { + inner StatusLine + precision time.Duration + + start time.Time + line string + + once sync.Once + + lines chan string + end1 chan bool + end2 chan struct{} +} + +func StopWatch(sl StatusLine, precision time.Duration) StatusLine { + return &stopWatch{ + inner: sl, + precision: precision, + lines: make(chan string), + end1: make(chan bool), + end2: make(chan struct{}), + } +} + +func (sw *stopWatch) startWorker() { + go func() { + sw.start = time.Now() + ticker := time.NewTicker(sw.precision) + for { + select { + case <-ticker.C: + sw.tick() + case sw.line = <-sw.lines: + sw.tick() + case keep := <-sw.end1: + sw.tick() + sw.inner.End(keep) + ticker.Stop() + sw.end2 <- struct{}{} + close(sw.end2) + return + } + } + }() +} + +func (sw *stopWatch) tick() { + d := time.Now().Sub(sw.start).Round(sw.precision) + sw.inner.Put(fmt.Sprintf("[ %v ] %s", d, sw.line)) +} + +func (sl *stopWatch) Put(line string) { + sl.once.Do(sl.startWorker) + sl.lines <- line +} + +func (sl *stopWatch) End(keep bool) { + sl.once.Do(sl.startWorker) + sl.end1 <- keep + close(sl.lines) + close(sl.end1) + <-sl.end2 +} -- cgit v1.2.3