diff options
| author | Rose Hogenson <rosehogenson@posteo.net> | 2025-04-17 16:32:40 -0700 |
|---|---|---|
| committer | Rose Hogenson <rosehogenson@posteo.net> | 2025-04-17 17:22:32 -0700 |
| commit | 944adeb2f9ca31c1a6cb7a8497555d97ea0e51d8 (patch) | |
| tree | dfc03dae93def8af1036294cfd6310032e4d07dc /internal/render | |
| parent | 842398c670ab22faf598dbc6f7530b6cc53cfa40 (diff) | |
| download | ccp-944adeb2f9ca31c1a6cb7a8497555d97ea0e51d8.tar.zst | |
No more Elm Architecture
Diffstat (limited to 'internal/render')
| -rw-r--r-- | internal/render/render.go | 74 |
1 files changed, 74 insertions, 0 deletions
diff --git a/internal/render/render.go b/internal/render/render.go new file mode 100644 index 0000000..877e2bd --- /dev/null +++ b/internal/render/render.go @@ -0,0 +1,74 @@ +// Package render renders to the terminal using ANSI escape sequences. +package render + +import ( + "bufio" + "bytes" + "fmt" + "os" +) + +// Renderer updates a terminal UI. Typical usage looks like +// +// r := render.New() +// // Game loop +// for { +// // Update state +// +// r.Clear() +// fmt.Fprintf(r, "Render UI by writing to r using io.Writer") +// r.Flush() +// } +type Renderer struct { + w bufio.Writer + prevLines int +} + +// New creates a new Renderer +func New() *Renderer { + r := &Renderer{} + r.w.Reset(os.Stderr) + return r +} + +// Clear clears the screen before rendering a new frame. +func (r *Renderer) Clear() { + if r.prevLines > 0 { + fmt.Fprintf(&r.w, "\033[%dA", r.prevLines) + } + r.prevLines = 0 + r.w.WriteString("\r") +} + +// Write implements io.Writer. +func (r *Renderer) Write(buf []byte) (int, error) { + totalBytes := 0 + for len(buf) > 0 { + i := bytes.IndexByte(buf, '\n') + if i < 0 { + n, err := r.w.Write(buf) + totalBytes += n + return totalBytes, err + } + line := buf[:i] + buf = buf[i+1:] + n, err := r.w.Write(line) + totalBytes += n + if err != nil { + return totalBytes, err + } + if _, err := r.w.WriteString("\033[K\n"); err != nil { + return totalBytes, err + } + totalBytes++ + r.prevLines++ + } + return totalBytes, nil +} + +// Flush flushes the internal buffer to stdout. Flush should be called at the +// end of every frame. +func (r *Renderer) Flush() { + r.w.WriteString("\033[J") + r.w.Flush() +} |
