Skip to content

Commit

Permalink
matrix now only refreshes modified rows (#36)
Browse files Browse the repository at this point in the history
  • Loading branch information
sha1n authored Jun 5, 2021
1 parent 3bcec24 commit b8166bf
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 29 deletions.
45 changes: 27 additions & 18 deletions matrix.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,21 +56,23 @@ type MatrixRow interface {
}

type matrixImpl struct {
lines []string
rows []*matrixRow
refreshInterval time.Duration
writer io.Writer
mx *sync.RWMutex
}

type matrixRow struct {
id MatrixCellID
matrix *matrixImpl
id MatrixCellID
matrix *matrixImpl
value string
modified bool
}

// NewMatrix creates a new matrix that writes to the specified writer and refreshes every refreshInterval.
func NewMatrix(writer io.Writer, refreshInterval time.Duration) Matrix {
return &matrixImpl{
lines: []string{},
rows: []*matrixRow{},
refreshInterval: refreshInterval,
writer: writer,
mx: &sync.RWMutex{},
Expand Down Expand Up @@ -127,14 +129,11 @@ func (m *matrixImpl) GetRow(index int) (row MatrixRow, err error) {
if index < 0 {
return nil, errors.New("row index cannot be negative")
}
if index >= len(m.lines) {
if index >= len(m.rows) {
return nil, errors.New("row index exceeds the matrix range")
}

row = &matrixRow{
id: MatrixCellID{row: index},
matrix: m,
}
row = m.rows[index]

return row, err
}
Expand All @@ -148,16 +147,21 @@ func (m *matrixImpl) updateTerminal(resetCursorPosition bool) {
m.mx.Lock()
defer m.mx.Unlock()

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

for _, line := range m.lines {
io.WriteString(m.writer, fmt.Sprintf("%s%s\r\n", TermControlEraseLine, line))
for _, row := range m.rows {
if row.modified {
_, err := io.WriteString(m.writer, fmt.Sprintf("%s%s\n", TermControlEraseLine, row.value))
row.modified = err != nil
} else {
io.WriteString(m.writer, "\n")
}
}

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

Expand Down Expand Up @@ -189,12 +193,14 @@ func (m *matrixImpl) NewRow() MatrixRow {
}

func (m *matrixImpl) newRow() MatrixRow {
index := len(m.lines)
m.lines = append(m.lines, "")
return &matrixRow{
index := len(m.rows)
row := &matrixRow{
id: MatrixCellID{row: index},
matrix: m,
}
m.rows = append(m.rows, row)

return row
}

func (r *matrixRow) WriteString(s string) (n int, err error) {
Expand All @@ -205,8 +211,11 @@ func (r *matrixRow) Write(b []byte) (n int, err error) {
r.matrix.mx.Lock()
defer r.matrix.mx.Unlock()

// we trim line feeds and return characters to prevent breaking the matrix layout
r.matrix.lines[r.id.row] = strings.Trim(string(b), "\n\r")
row := r.matrix.rows[r.id.row]
newValue := strings.Trim(string(b), "\n\r")
row.modified = newValue != row.value
row.value = newValue

return len(b), nil
}

Expand Down
45 changes: 34 additions & 11 deletions matrix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func TestMatrixWritesToTerminalOutput(t *testing.T) {
matrix.NewLineWriter().Write([]byte(examples[1]))
matrix.NewLineStringWriter().WriteString(examples[2])

assertEventualSequence(t, matrix, examples)
assertEventualSequence(t, matrix, expectedRewriteSequenceFor(examples))
}

func TestMatrixUpdatesTerminalOutput(t *testing.T) {
Expand All @@ -38,7 +38,7 @@ func TestMatrixUpdatesTerminalOutput(t *testing.T) {
matrix.NewRow().Update(examples[2])
row2.WriteString(examples[1])

assertEventualSequence(t, matrix, examples)
assertEventualSequence(t, matrix, expectedRewriteSequenceFor(examples))
}

func TestMatrixRowUpdateTrimsLineFeeds(t *testing.T) {
Expand All @@ -47,9 +47,18 @@ func TestMatrixRowUpdateTrimsLineFeeds(t *testing.T) {
matrix, cancel := startNewMatrix()
defer cancel()

matrix.NewRow().Update("\r\n" + expected + "\r\n\r\n\r")
matrix.NewRow().Update("\n" + expected + "\n\n\r")

assertEventualSequence(t, matrix, []string{expected})
assertEventualSequence(t, matrix, expectedRewriteSequenceFor([]string{expected}))
}

func TestNoRewritesWhenNothingChanges(t *testing.T) {
matrix, cancel := startNewMatrix()
defer cancel()

matrix.NewRange(4)

assertEventualSequence(t, matrix, expectedSkipRewriteSequenceFor(4))
}

func TestMatrixStructure(t *testing.T) {
Expand All @@ -62,7 +71,7 @@ func TestMatrixStructure(t *testing.T) {
matrix.NewRow().Update(examples[1])
matrix.NewRow().Update(examples[2])

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

func TestMatrixNewRangeOrder(t *testing.T) {
Expand All @@ -76,7 +85,7 @@ func TestMatrixNewRangeOrder(t *testing.T) {
rows[i].Update(examples[i])
}

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

func TestWriterLineInterface(t *testing.T) {
Expand All @@ -91,7 +100,7 @@ func TestWriterLineInterface(t *testing.T) {
matrix1.NewLineStringWriter().WriteString(example)
matrix2.NewLineWriter().Write([]byte(example))

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

func TestMatrixGetRowByID(t *testing.T) {
Expand Down Expand Up @@ -125,11 +134,11 @@ func TestMatrixGetRowByIdWithIllegalValue(t *testing.T) {
assert.Error(t, err)
}

func assertEventualSequence(t *testing.T, matrix Matrix, examples []string) {
func assertEventualSequence(t *testing.T, matrix Matrix, expected string) {
contantsAllExamplesInOrderFn := func() bool {
return strings.Contains(
matrix.(*matrixImpl).writer.(*bytes.Buffer).String(),
expectedOutputSequenceFor(examples),
expected,
)
}

Expand All @@ -138,12 +147,16 @@ func assertEventualSequence(t *testing.T, matrix Matrix, examples []string) {
time.Second*10,
matrix.RefreshInterval(),
)
}

func expectedSkipRewriteSequenceFor(count int) string {
return strings.Repeat("\n", count)
}
func expectedOutputSequenceFor(examples []string) string {

func expectedRewriteSequenceFor(examples []string) string {
buf := new(bytes.Buffer)
for _, e := range examples {
buf.WriteString(TermControlEraseLine + e + "\r\n")
buf.WriteString(TermControlEraseLine + e + "\n")
}

return buf.String()
Expand All @@ -170,3 +183,13 @@ func generateMultiLineExamples(count int) []string {

return examples
}

func linesOf(m Matrix) []string {
rows := m.(*matrixImpl).rows
lines := make([]string, len(rows))
for i, row := range rows {
lines[i] = row.value
}

return lines
}

0 comments on commit b8166bf

Please sign in to comment.