package textfmt import ( "bytes" "code.squareroundforest.org/arpio/textedit" "fmt" "io" ) type writer interface { write(...any) flush() error() error setErr(err error) // TODO: remove } type roffWriter struct { w io.Writer err error } type mdWriter struct { w io.Writer err error } type wrapper func(io.Writer) (io.Writer, func() error) type errorWriter struct { out io.Writer err error } func (w *errorWriter) Write(p []byte) (int, error) { if w.err != nil { return 0, w.err } var n int n, w.err = w.out.Write(p) return n, w.err } func newRoffWriter(out io.Writer, internal bool) *roffWriter { if internal { return &roffWriter{w: out} } return &roffWriter{ w: textedit.New( out, textedit.Replace("\u00a0", "\\~"), ), } } func (w *roffWriter) write(a ...any) { for _, ai := range a { if w.err != nil { return } if _, err := w.w.Write([]byte(fmt.Sprint(ai))); err != nil { w.err = err return } } } func (w *roffWriter) flush() { if w.err != nil { return } if f, ok := w.w.(interface{ Flush() error }); ok { if err := f.Flush(); err != nil { w.err = err } } } func (w *roffWriter) error() error { return w.err } func (w *roffWriter) setErr(err error) { w.err = err } func newMDWriter(out io.Writer, internal bool) *mdWriter { if internal { return &mdWriter{w: out} } return &mdWriter{ w: textedit.New( out, textedit.Replace("\u00a0", " "), ), } } func (w *mdWriter) write(a ...any) { for _, ai := range a { if w.err != nil { return } if _, err := w.w.Write([]byte(fmt.Sprint(ai))); err != nil { w.err = err return } } } func (w *mdWriter) flush() { if w.err != nil { return } if f, ok := w.w.(interface{ Flush() error }); ok { if err := f.Flush(); err != nil { w.err = err } } } func (w *mdWriter) error() error { return w.err } func (w *mdWriter) setErr(err error) { w.err = err } func writeWith(out io.Writer, w ...wrapper) (io.Writer, func() (io.Writer, error)) { var f []func() error ww := out for i := len(w) - 1; i >= 0; i-- { var fi func() error ww, fi = w[i](ww) f = append(f, fi) } return ww, func() (io.Writer, error) { for _, fi := range f { if err := fi(); err != nil { return out, err } } return out, nil } } func errorHandler(out io.Writer) (io.Writer, func() error) { ew := errorWriter{out: out} return &ew, func() error { return ew.err } } func editor(e textedit.Editor) wrapper { return func(out io.Writer) (io.Writer, func() error) { ew := textedit.New(out, e) return ew, func() error { return ew.Flush() } } } func editString(s string, e ...wrapper) string { var b bytes.Buffer w, finish := writeWith(&b, e...) w.Write([]byte(s)) finish() return b.String() } func ttyNBSP() wrapper { return editor(textedit.Replace("\u00a0", " ")) } func roffNBSP() wrapper { return editor(textedit.Replace("\u00a0", "\\~")) } func mdNBSP() wrapper { return editor(textedit.Replace("\u00a0", " ")) } func singleLine() wrapper { return editor(textedit.SingleLine()) } func indent(first, rest int) wrapper { return editor(textedit.Indent(timesn(" ", first), timesn(" ", rest))) } func wrap(firstWidth, restWidth int) wrapper { return editor(textedit.Wrap(firstWidth, restWidth)) } func wrapIndent(first, rest, firstWidth, restWidth int) wrapper { return editor(textedit.WrapIndent(timesn(" ", first), timesn(" ", rest), firstWidth, restWidth)) } func write(out io.Writer, a ...any) { for _, ai := range a { if _, err := out.Write([]byte(fmt.Sprint(ai))); err != nil { return } } }