2025-10-14 20:46:32 +02:00
|
|
|
package textfmt
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
2025-10-23 02:55:49 +02:00
|
|
|
"io"
|
2025-10-31 20:24:37 +01:00
|
|
|
"slices"
|
2025-10-14 20:46:32 +02:00
|
|
|
"strings"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type writer interface {
|
|
|
|
|
write(...any)
|
2025-10-31 20:24:37 +01:00
|
|
|
flush()
|
2025-10-14 20:46:32 +02:00
|
|
|
error() error
|
2025-10-31 20:24:37 +01:00
|
|
|
setErr(err error) // TODO: remove
|
2025-10-14 20:46:32 +02:00
|
|
|
}
|
|
|
|
|
|
2025-10-31 20:24:37 +01:00
|
|
|
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) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --
|
|
|
|
|
|
2025-10-14 20:46:32 +02:00
|
|
|
type ttyWriter struct {
|
2025-10-23 02:55:49 +02:00
|
|
|
w io.Writer
|
2025-10-14 20:46:32 +02:00
|
|
|
internal bool
|
2025-10-23 02:55:49 +02:00
|
|
|
err error
|
2025-10-14 20:46:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type roffWriter struct {
|
2025-10-23 02:55:49 +02:00
|
|
|
w io.Writer
|
2025-10-14 20:46:32 +02:00
|
|
|
internal bool
|
2025-10-23 02:55:49 +02:00
|
|
|
err error
|
2025-10-14 20:46:32 +02:00
|
|
|
}
|
|
|
|
|
|
2025-10-28 00:47:41 +01:00
|
|
|
type mdWriter struct {
|
|
|
|
|
w io.Writer
|
|
|
|
|
internal bool
|
|
|
|
|
err error
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-14 20:46:32 +02:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-31 20:24:37 +01:00
|
|
|
func (w *ttyWriter) flush() {}
|
|
|
|
|
|
2025-10-14 20:46:32 +02:00
|
|
|
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)
|
2025-10-23 02:55:38 +02:00
|
|
|
if w.internal {
|
|
|
|
|
rr = r
|
|
|
|
|
} else {
|
|
|
|
|
for i := range r {
|
|
|
|
|
if r[i] == '\u00a0' {
|
|
|
|
|
rr = append(rr, []rune("\\~")...)
|
|
|
|
|
continue
|
|
|
|
|
}
|
2025-10-14 20:46:32 +02:00
|
|
|
|
2025-10-23 02:55:38 +02:00
|
|
|
rr = append(rr, r[i])
|
|
|
|
|
}
|
2025-10-14 20:46:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if _, err := w.w.Write([]byte(string(rr))); err != nil {
|
|
|
|
|
w.err = err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-31 20:24:37 +01:00
|
|
|
func (w *roffWriter) flush() {}
|
|
|
|
|
|
2025-10-14 20:46:32 +02:00
|
|
|
func (w *roffWriter) error() error {
|
|
|
|
|
return w.err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (w *roffWriter) setErr(err error) {
|
|
|
|
|
w.err = err
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-28 00:47:41 +01:00
|
|
|
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))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-31 20:24:37 +01:00
|
|
|
func (w *mdWriter) flush() {}
|
|
|
|
|
|
2025-10-28 00:47:41 +01:00
|
|
|
func (w *mdWriter) error() error {
|
|
|
|
|
return w.err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (w *mdWriter) setErr(err error) {
|
|
|
|
|
w.err = err
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-14 20:46:32 +02:00
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
}
|