From 944adeb2f9ca31c1a6cb7a8497555d97ea0e51d8 Mon Sep 17 00:00:00 2001 From: Rose Hogenson Date: Thu, 17 Apr 2025 16:32:40 -0700 Subject: No more Elm Architecture --- internal/render/render.go | 74 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 internal/render/render.go (limited to 'internal/render') 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() +} -- cgit v1.3.1