pen.go

 1package cellbuf
 2
 3import (
 4	"io"
 5
 6	"github.com/charmbracelet/x/ansi"
 7)
 8
 9// PenWriter is a writer that writes to a buffer and keeps track of the current
10// pen style and link state for the purpose of wrapping with newlines.
11type PenWriter struct {
12	w     io.Writer
13	p     *ansi.Parser
14	style Style
15	link  Link
16}
17
18// NewPenWriter returns a new PenWriter.
19func NewPenWriter(w io.Writer) *PenWriter {
20	pw := &PenWriter{w: w}
21	pw.p = ansi.NewParser()
22	pw.p.SetParamsSize(32)            // 32 parameters
23	pw.p.SetDataSize(4 * 1024 * 1024) // 4MB of data buffer
24	handleCsi := func(cmd ansi.Cmd, params ansi.Params) {
25		if cmd == 'm' {
26			ReadStyle(params, &pw.style)
27		}
28	}
29	handleOsc := func(cmd int, data []byte) {
30		if cmd == 8 {
31			ReadLink(data, &pw.link)
32		}
33	}
34	pw.p.SetHandler(ansi.Handler{
35		HandleCsi: handleCsi,
36		HandleOsc: handleOsc,
37	})
38	return pw
39}
40
41// Style returns the current pen style.
42func (w *PenWriter) Style() Style {
43	return w.style
44}
45
46// Link returns the current pen link.
47func (w *PenWriter) Link() Link {
48	return w.link
49}
50
51// Write writes to the buffer.
52func (w *PenWriter) Write(p []byte) (int, error) {
53	for i := 0; i < len(p); i++ {
54		b := p[i]
55		w.p.Advance(b)
56		if b == '\n' {
57			if !w.style.Empty() {
58				_, _ = w.w.Write([]byte(ansi.ResetStyle))
59			}
60			if !w.link.Empty() {
61				_, _ = w.w.Write([]byte(ansi.ResetHyperlink()))
62			}
63		}
64
65		_, _ = w.w.Write([]byte{b})
66		if b == '\n' {
67			if !w.link.Empty() {
68				_, _ = w.w.Write([]byte(ansi.SetHyperlink(w.link.URL, w.link.Params)))
69			}
70			if !w.style.Empty() {
71				_, _ = w.w.Write([]byte(w.style.Sequence()))
72			}
73		}
74	}
75
76	return len(p), nil
77}
78
79// Close closes the writer and resets the style and link if necessary.
80func (w *PenWriter) Close() error {
81	if !w.style.Empty() {
82		_, _ = w.w.Write([]byte(ansi.ResetStyle))
83	}
84	if !w.link.Empty() {
85		_, _ = w.w.Write([]byte(ansi.ResetHyperlink()))
86	}
87	return nil
88}