wrap.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. package wrap
  2. import (
  3. "bytes"
  4. "strings"
  5. "unicode"
  6. "github.com/mattn/go-runewidth"
  7. "github.com/muesli/reflow/ansi"
  8. )
  9. var (
  10. defaultNewline = []rune{'\n'}
  11. defaultTabWidth = 4
  12. )
  13. type Wrap struct {
  14. Limit int
  15. Newline []rune
  16. KeepNewlines bool
  17. PreserveSpace bool
  18. TabWidth int
  19. buf *bytes.Buffer
  20. lineLen int
  21. ansi bool
  22. forcefulNewline bool
  23. }
  24. // NewWriter returns a new instance of a wrapping writer, initialized with
  25. // default settings.
  26. func NewWriter(limit int) *Wrap {
  27. return &Wrap{
  28. Limit: limit,
  29. Newline: defaultNewline,
  30. KeepNewlines: true,
  31. // Keep whitespaces following a forceful line break. If disabled,
  32. // leading whitespaces in a line are only kept if the line break
  33. // was not forceful, meaning a line break that was already present
  34. // in the input
  35. PreserveSpace: false,
  36. TabWidth: defaultTabWidth,
  37. buf: &bytes.Buffer{},
  38. }
  39. }
  40. // Bytes is shorthand for declaring a new default Wrap instance,
  41. // used to immediately wrap a byte slice.
  42. func Bytes(b []byte, limit int) []byte {
  43. f := NewWriter(limit)
  44. _, _ = f.Write(b)
  45. return f.buf.Bytes()
  46. }
  47. func (w *Wrap) addNewLine() {
  48. _, _ = w.buf.WriteRune('\n')
  49. w.lineLen = 0
  50. }
  51. // String is shorthand for declaring a new default Wrap instance,
  52. // used to immediately wrap a string.
  53. func String(s string, limit int) string {
  54. return string(Bytes([]byte(s), limit))
  55. }
  56. func (w *Wrap) Write(b []byte) (int, error) {
  57. s := strings.Replace(string(b), "\t", strings.Repeat(" ", w.TabWidth), -1)
  58. if !w.KeepNewlines {
  59. s = strings.Replace(s, "\n", "", -1)
  60. }
  61. width := ansi.PrintableRuneWidth(s)
  62. if w.Limit <= 0 || w.lineLen+width <= w.Limit {
  63. w.lineLen += width
  64. return w.buf.Write(b)
  65. }
  66. for _, c := range s {
  67. if c == ansi.Marker {
  68. w.ansi = true
  69. } else if w.ansi {
  70. if ansi.IsTerminator(c) {
  71. w.ansi = false
  72. }
  73. } else if inGroup(w.Newline, c) {
  74. w.addNewLine()
  75. w.forcefulNewline = false
  76. continue
  77. } else {
  78. width := runewidth.RuneWidth(c)
  79. if w.lineLen+width > w.Limit {
  80. w.addNewLine()
  81. w.forcefulNewline = true
  82. }
  83. if w.lineLen == 0 {
  84. if w.forcefulNewline && !w.PreserveSpace && unicode.IsSpace(c) {
  85. continue
  86. }
  87. } else {
  88. w.forcefulNewline = false
  89. }
  90. w.lineLen += width
  91. }
  92. _, _ = w.buf.WriteRune(c)
  93. }
  94. return len(b), nil
  95. }
  96. // Bytes returns the wrapped result as a byte slice.
  97. func (w *Wrap) Bytes() []byte {
  98. return w.buf.Bytes()
  99. }
  100. // String returns the wrapped result as a string.
  101. func (w *Wrap) String() string {
  102. return w.buf.String()
  103. }
  104. func inGroup(a []rune, c rune) bool {
  105. for _, v := range a {
  106. if v == c {
  107. return true
  108. }
  109. }
  110. return false
  111. }