amseltools/vendor/github.com/muesli/reflow/wrap/wrap.go
2023-03-29 13:26:21 +00:00

135 lines
2.6 KiB
Go

package wrap
import (
"bytes"
"strings"
"unicode"
"github.com/mattn/go-runewidth"
"github.com/muesli/reflow/ansi"
)
var (
defaultNewline = []rune{'\n'}
defaultTabWidth = 4
)
type Wrap struct {
Limit int
Newline []rune
KeepNewlines bool
PreserveSpace bool
TabWidth int
buf *bytes.Buffer
lineLen int
ansi bool
forcefulNewline bool
}
// NewWriter returns a new instance of a wrapping writer, initialized with
// default settings.
func NewWriter(limit int) *Wrap {
return &Wrap{
Limit: limit,
Newline: defaultNewline,
KeepNewlines: true,
// Keep whitespaces following a forceful line break. If disabled,
// leading whitespaces in a line are only kept if the line break
// was not forceful, meaning a line break that was already present
// in the input
PreserveSpace: false,
TabWidth: defaultTabWidth,
buf: &bytes.Buffer{},
}
}
// Bytes is shorthand for declaring a new default Wrap instance,
// used to immediately wrap a byte slice.
func Bytes(b []byte, limit int) []byte {
f := NewWriter(limit)
_, _ = f.Write(b)
return f.buf.Bytes()
}
func (w *Wrap) addNewLine() {
_, _ = w.buf.WriteRune('\n')
w.lineLen = 0
}
// String is shorthand for declaring a new default Wrap instance,
// used to immediately wrap a string.
func String(s string, limit int) string {
return string(Bytes([]byte(s), limit))
}
func (w *Wrap) Write(b []byte) (int, error) {
s := strings.Replace(string(b), "\t", strings.Repeat(" ", w.TabWidth), -1)
if !w.KeepNewlines {
s = strings.Replace(s, "\n", "", -1)
}
width := ansi.PrintableRuneWidth(s)
if w.Limit <= 0 || w.lineLen+width <= w.Limit {
w.lineLen += width
return w.buf.Write(b)
}
for _, c := range s {
if c == ansi.Marker {
w.ansi = true
} else if w.ansi {
if ansi.IsTerminator(c) {
w.ansi = false
}
} else if inGroup(w.Newline, c) {
w.addNewLine()
w.forcefulNewline = false
continue
} else {
width := runewidth.RuneWidth(c)
if w.lineLen+width > w.Limit {
w.addNewLine()
w.forcefulNewline = true
}
if w.lineLen == 0 {
if w.forcefulNewline && !w.PreserveSpace && unicode.IsSpace(c) {
continue
}
} else {
w.forcefulNewline = false
}
w.lineLen += width
}
_, _ = w.buf.WriteRune(c)
}
return len(b), nil
}
// Bytes returns the wrapped result as a byte slice.
func (w *Wrap) Bytes() []byte {
return w.buf.Bytes()
}
// String returns the wrapped result as a string.
func (w *Wrap) String() string {
return w.buf.String()
}
func inGroup(a []rune, c rune) bool {
for _, v := range a {
if v == c {
return true
}
}
return false
}