Files
abra/vendor/github.com/charmbracelet/x/cellbuf/pen.go
2025-11-11 14:18:57 +01:00

93 lines
1.9 KiB
Go

package cellbuf
import (
"io"
"github.com/charmbracelet/x/ansi"
)
// PenWriter is a writer that writes to a buffer and keeps track of the current
// pen style and link state for the purpose of wrapping with newlines.
type PenWriter struct {
w io.Writer
p *ansi.Parser
style Style
link Link
}
// NewPenWriter returns a new PenWriter.
func NewPenWriter(w io.Writer) *PenWriter {
pw := &PenWriter{w: w}
pw.p = ansi.GetParser()
handleCsi := func(cmd ansi.Cmd, params ansi.Params) {
if cmd == 'm' {
ReadStyle(params, &pw.style)
}
}
handleOsc := func(cmd int, data []byte) {
if cmd == 8 {
ReadLink(data, &pw.link)
}
}
pw.p.SetHandler(ansi.Handler{
HandleCsi: handleCsi,
HandleOsc: handleOsc,
})
return pw
}
// Style returns the current pen style.
func (w *PenWriter) Style() Style {
return w.style
}
// Link returns the current pen link.
func (w *PenWriter) Link() Link {
return w.link
}
// Write writes to the buffer.
func (w *PenWriter) Write(p []byte) (int, error) {
for i := range p {
b := p[i]
w.p.Advance(b)
if b == '\n' {
if !w.style.Empty() {
_, _ = w.w.Write([]byte(ansi.ResetStyle))
}
if !w.link.Empty() {
_, _ = w.w.Write([]byte(ansi.ResetHyperlink()))
}
}
_, _ = w.w.Write([]byte{b})
if b == '\n' {
if !w.link.Empty() {
_, _ = w.w.Write([]byte(ansi.SetHyperlink(w.link.URL, w.link.Params)))
}
if !w.style.Empty() {
_, _ = w.w.Write([]byte(w.style.Sequence()))
}
}
}
return len(p), nil
}
// Close closes the writer, resets the style and link if necessary, and releases
// its parser. Calling it is performance critical, but forgetting it does not
// cause safety issues or leaks.
func (w *PenWriter) Close() error {
if !w.style.Empty() {
_, _ = w.w.Write([]byte(ansi.ResetStyle))
}
if !w.link.Empty() {
_, _ = w.w.Write([]byte(ansi.ResetHyperlink()))
}
if w.p != nil {
ansi.PutParser(w.p)
w.p = nil
}
return nil
}