package textfmt import ( "fmt" "io" "slices" "strings" ) type writer interface { write(...any) flush() error() error setErr(err error) // TODO: remove } type textWriter struct { err error out io.Writer } type editor struct { out writer pending []rune replace map[string]string } func (w *textWriter) write(a ...any) { if w.err != nil { return } for _, ai := range a { if _, err := w.out.Write([]byte(fmt.Sprint(ai))); err != nil { w.err = err return } } } func (w *textWriter) flush() {} func (w *textWriter) error() error { return w.err } func (e *textWriter) setErr(err error) { } func (e *editor) write(a ...any) { for _, ai := range a { s := fmt.Sprint(ai) r := []rune(s) for key, replacement := range e.replace { rk := []rune(key) if len(e.pending) >= len(rk) { continue } if !slices.Equal(e.pending, rk[:len(e.pending)]) { continue } if len(r) < len(rk)-len(e.pending) { if slices.Equal(r, rk[len(e.pending):len(e.pending)+len(r)]) { e.pending = append(e.pending, r...) r = nil break } continue } if slices.Equal(r[:len(rk)-len(e.pending)], rk[len(e.pending):]) { r = []rune(replacement) e.pending = nil break } } e.out.write(string(r)) } } func (e *editor) flush() { e.out.write(string(e.pending)) e.out.flush() } func (e *editor) error() error { return e.out.error() } func (e *editor) setErr(err error) { } // -- type ttyWriter struct { w io.Writer internal bool err error } type roffWriter struct { w io.Writer internal bool err error } type mdWriter struct { w io.Writer internal bool err error } func (w *ttyWriter) write(a ...any) { for _, ai := range a { if w.err != nil { return } s := fmt.Sprint(ai) r := []rune(s) if !w.internal { for i := range r { if r[i] == '\u00a0' { r[i] = ' ' } } } if _, err := w.w.Write([]byte(string(r))); err != nil { w.err = err } } } func (w *ttyWriter) flush() {} func (w *ttyWriter) error() error { return w.err } func (w *ttyWriter) setErr(err error) { w.err = err } func (w *roffWriter) write(a ...any) { for _, ai := range a { if w.err != nil { return } var rr []rune s := fmt.Sprint(ai) r := []rune(s) if w.internal { rr = r } else { for i := range r { if r[i] == '\u00a0' { rr = append(rr, []rune("\\~")...) continue } rr = append(rr, r[i]) } } if _, err := w.w.Write([]byte(string(rr))); err != nil { w.err = err } } } func (w *roffWriter) flush() {} func (w *roffWriter) error() error { return w.err } func (w *roffWriter) setErr(err error) { w.err = err } func (w *mdWriter) write(a ...any) { for _, ai := range a { if w.err != nil { return } s := fmt.Sprint(ai) r := []rune(s) if !w.internal { for i := range r { if r[i] == '\u00a0' { r[i] = ' ' } } } s = string(r) _, w.err = w.w.Write([]byte(s)) } } func (w *mdWriter) flush() {} func (w *mdWriter) error() error { return w.err } func (w *mdWriter) setErr(err error) { w.err = err } func writeLines(w writer, txt string, indentFirst, indentRest int) { lines := strings.Split(txt, "\n") for i, l := range lines { if i > 0 { w.write("\n") } indent := indentFirst if i > 0 { indent = indentRest } w.write(timesn(" ", indent)) w.write(l) } }