Skip to content

Commit

Permalink
more abstract line writer interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
Shai Nagar committed May 27, 2021
1 parent 224f97f commit b0a181a
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 28 deletions.
76 changes: 57 additions & 19 deletions matrix.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package termite
import (
"context"
"fmt"
"io"
"sync"
"time"
)
Expand All @@ -11,7 +12,8 @@ import (
type Matrix interface {
Terminal() Terminal
RefreshInterval() time.Duration
NewLine() MatrixLine
NewLineStringWriter() io.StringWriter
NewLineWriter() io.Writer
Start() context.CancelFunc
}

Expand All @@ -27,7 +29,7 @@ type terminalMatrix struct {
mx *sync.RWMutex
}

type terminalLine struct {
type matrixLineWriter struct {
index int
matrix *terminalMatrix
}
Expand All @@ -53,50 +55,86 @@ func (m *terminalMatrix) RefreshInterval() time.Duration {
// Start starts the matrix update process.
// Returns a cancel handle to stop the matrix updates.
func (m *terminalMatrix) Start() context.CancelFunc {
c := NewCursor(m.terminal)
context, cancel := context.WithCancel(context.Background())

waitStart := &sync.WaitGroup{}
waitStart.Add(1)
var drainWaitGroup *sync.WaitGroup

go func() {
timer := time.NewTicker(m.refreshInterval)
drainWaitGroup = &sync.WaitGroup{}
drainWaitGroup.Add(1)
// now that we loaded the drain wait group, we can release the caller
waitStart.Done()

for {
select {
case <-context.Done():
timer.Stop()
m.updateTerminal(false)
drainWaitGroup.Done()
return

case <-timer.C:
if len(m.lines) == 0 {
continue
}

m.mx.Lock()
for _, line := range m.lines {
m.terminal.OverwriteLine(fmt.Sprintf("%s\r\n", line))
}
c.Up(len(m.lines))
m.mx.Unlock()
m.updateTerminal(true)
}
}
}()

return cancel
waitStart.Wait()

return func() {
cancel()
// Wait for the final update to complete
drainWaitGroup.Wait()
}
}

func (m *terminalMatrix) updateTerminal(resetCursorPosition bool) {
c := NewCursor(m.terminal)
m.mx.Lock()
defer m.mx.Unlock()

if len(m.lines) == 0 {
return
}

for _, line := range m.lines {
m.terminal.OverwriteLine(fmt.Sprintf("%s\r\n", line))
}

if resetCursorPosition {
c.Up(len(m.lines))
}
}

// NewRow creates a new matrix row
func (m *terminalMatrix) NewLine() MatrixLine {
// NewLineStringWriter returns a new string writter to interact with a single matrix line
func (m *terminalMatrix) NewLineStringWriter() io.StringWriter {
m.mx.Lock()
defer m.mx.Unlock()

index := len(m.lines)
m.lines = append(m.lines, "")
return &terminalLine{
return &matrixLineWriter{
index: index,
matrix: m,
}
}

func (l *terminalLine) WriteString(s string) {
// NewLineWriter returns a new writer interface to interact with a single matrix line.
func (m *terminalMatrix) NewLineWriter() io.Writer {
return m.NewLineStringWriter().(*matrixLineWriter)
}

func (l *matrixLineWriter) WriteString(s string) (n int, err error) {
return l.Write([]byte(s))
}

func (l *matrixLineWriter) Write(b []byte) (n int, err error) {
l.matrix.mx.Lock()
defer l.matrix.mx.Unlock()

l.matrix.lines[l.index] = s
l.matrix.lines[l.index] = string(b)
return len(b), nil
}
33 changes: 24 additions & 9 deletions matrix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ func TestMatrixWritesToTerminalOutput(t *testing.T) {
matrix, cancel := startMatrix()
defer cancel()

matrix.NewLine().WriteString(examples[0])
matrix.NewLine().WriteString(examples[1])
matrix.NewLine().WriteString(examples[2])
matrix.NewLineStringWriter().WriteString(examples[0])
matrix.NewLineStringWriter().WriteString(examples[1])
matrix.NewLineStringWriter().WriteString(examples[2])

assertEventualSequence(t, matrix, examples)
}
Expand All @@ -31,11 +31,11 @@ func TestMatrixUpdatesTerminalOutput(t *testing.T) {
matrix, cancel := startMatrix()
defer cancel()

matrix.NewLine().WriteString(examples[0])
line2 := matrix.NewLine()
matrix.NewLineStringWriter().WriteString(examples[0])
line2 := matrix.NewLineStringWriter()
line2.WriteString(examples[1])
examples[1] = generateRandomString()
matrix.NewLine().WriteString(examples[2])
matrix.NewLineStringWriter().WriteString(examples[2])
line2.WriteString(examples[1])

assertEventualSequence(t, matrix, examples)
Expand All @@ -47,13 +47,28 @@ func TestMatrixStructure(t *testing.T) {
matrix, cancel := startMatrix()
defer cancel()

matrix.NewLine().WriteString(examples[0])
matrix.NewLine().WriteString(examples[1])
matrix.NewLine().WriteString(examples[2])
matrix.NewLineStringWriter().WriteString(examples[0])
matrix.NewLineStringWriter().WriteString(examples[1])
matrix.NewLineStringWriter().WriteString(examples[2])

assert.Equal(t, examples, matrix.(*terminalMatrix).lines)
}

func TestWriterLineInterface(t *testing.T) {
example := generateRandomString()

matrix1, cancel1 := startMatrix()
defer cancel1()

matrix2, cancel2 := startMatrix()
defer cancel2()

matrix1.NewLineStringWriter().WriteString(example)
matrix2.NewLineWriter().Write([]byte(example))

assert.Equal(t, matrix1.(*terminalMatrix).lines, matrix2.(*terminalMatrix).lines)
}

func assertEventualSequence(t *testing.T, matrix Matrix, examples []string) {
contantsAllExamplesInOrderFn := func() bool {
return strings.Contains(
Expand Down

0 comments on commit b0a181a

Please sign in to comment.