1
0
mirror of https://github.com/mum4k/termdash.git synced 2025-04-28 13:48:51 +08:00
termdash/terminal/faketerm/faketerm.go

141 lines
2.9 KiB
Go
Raw Normal View History

// Package faketerm is a fake implementation of the terminal for the use in tests.
package faketerm
import (
"bytes"
"context"
"errors"
"fmt"
"image"
"log"
"github.com/mum4k/termdash/area"
"github.com/mum4k/termdash/cell"
"github.com/mum4k/termdash/terminalapi"
)
// Option is used to provide options.
type Option interface {
// set sets the provided option.
set(*Terminal)
}
// option implements Option.
type option func(*Terminal)
// set implements Option.set.
func (o option) set(t *Terminal) {
o(t)
}
// Terminal is a fake terminal.
// This implementation is thread-safe.
type Terminal struct {
// buffer holds the terminal cells.
buffer cell.Buffer
}
// New returns a new fake Terminal.
func New(size image.Point, opts ...Option) (*Terminal, error) {
b, err := cell.NewBuffer(size)
if err != nil {
return nil, err
}
t := &Terminal{
buffer: b,
}
for _, opt := range opts {
opt.set(t)
}
return t, nil
}
// MustNew is like New, but panics on all errors.
func MustNew(size image.Point, opts ...Option) *Terminal {
ft, err := New(size, opts...)
if err != nil {
log.Fatalf("New => unexpected error: %v", err)
}
return ft
}
// BackBuffer returns the back buffer of the fake terminal.
func (t *Terminal) BackBuffer() cell.Buffer {
return t.buffer
}
// String prints out the buffer into a string.
// This includes the cell runes only, cell options are ignored.
// Implements fmt.Stringer.
func (t *Terminal) String() string {
size := t.Size()
var b bytes.Buffer
for row := 0; row < size.Y; row++ {
for col := 0; col < size.X; col++ {
r := t.buffer[col][row].Rune
if r == 0 {
r = ' '
}
b.WriteRune(r)
}
b.WriteRune('\n')
}
return b.String()
}
// Implements terminalapi.Terminal.Size.
func (t *Terminal) Size() image.Point {
return t.buffer.Size()
}
// Implements terminalapi.Terminal.Clear.
func (t *Terminal) Clear(opts ...cell.Option) error {
b, err := cell.NewBuffer(t.buffer.Size())
if err != nil {
return err
}
t.buffer = b
return nil
}
// Implements terminalapi.Terminal.Flush.
func (t *Terminal) Flush() error {
return errors.New("unimplemented")
}
// Implements terminalapi.Terminal.SetCursor.
func (t *Terminal) SetCursor(p image.Point) {
log.Fatal("unimplemented")
}
// Implements terminalapi.Terminal.HideCursor.
func (t *Terminal) HideCursor() {
log.Fatal("unimplemented")
}
// Implements terminalapi.Terminal.SetCell.
func (t *Terminal) SetCell(p image.Point, r rune, opts ...cell.Option) error {
ar, err := area.FromSize(t.buffer.Size())
if err != nil {
return err
}
if !p.In(ar) {
return fmt.Errorf("cell at point %+v falls out of the terminal area %+v", p, ar)
}
cell := t.buffer[p.X][p.Y]
cell.Rune = r
cell.Apply(opts...)
return nil
}
// Implements terminalapi.Terminal.Event.
func (t *Terminal) Event(ctx context.Context) terminalapi.Event {
log.Fatal("unimplemented")
return nil
}
// Closes the terminal. This is a no-op on the fake terminal.
func (t *Terminal) Close() {}