column.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. // Package colwriter provides a write filter that formats
  2. // input lines in multiple columns.
  3. //
  4. // The package is a straightforward translation from
  5. // /src/cmd/draw/mc.c in Plan 9 from User Space.
  6. package colwriter
  7. import (
  8. "bytes"
  9. "io"
  10. "unicode/utf8"
  11. )
  12. const (
  13. tab = 4
  14. )
  15. const (
  16. // Print each input line ending in a colon ':' separately.
  17. BreakOnColon uint = 1 << iota
  18. )
  19. // A Writer is a filter that arranges input lines in as many columns as will
  20. // fit in its width. Tab '\t' chars in the input are translated to sequences
  21. // of spaces ending at multiples of 4 positions.
  22. //
  23. // If BreakOnColon is set, each input line ending in a colon ':' is written
  24. // separately.
  25. //
  26. // The Writer assumes that all Unicode code points have the same width; this
  27. // may not be true in some fonts.
  28. type Writer struct {
  29. w io.Writer
  30. buf []byte
  31. width int
  32. flag uint
  33. }
  34. // NewWriter allocates and initializes a new Writer writing to w.
  35. // Parameter width controls the total number of characters on each line
  36. // across all columns.
  37. func NewWriter(w io.Writer, width int, flag uint) *Writer {
  38. return &Writer{
  39. w: w,
  40. width: width,
  41. flag: flag,
  42. }
  43. }
  44. // Write writes p to the writer w. The only errors returned are ones
  45. // encountered while writing to the underlying output stream.
  46. func (w *Writer) Write(p []byte) (n int, err error) {
  47. var linelen int
  48. var lastWasColon bool
  49. for i, c := range p {
  50. w.buf = append(w.buf, c)
  51. linelen++
  52. if c == '\t' {
  53. w.buf[len(w.buf)-1] = ' '
  54. for linelen%tab != 0 {
  55. w.buf = append(w.buf, ' ')
  56. linelen++
  57. }
  58. }
  59. if w.flag&BreakOnColon != 0 && c == ':' {
  60. lastWasColon = true
  61. } else if lastWasColon {
  62. if c == '\n' {
  63. pos := bytes.LastIndex(w.buf[:len(w.buf)-1], []byte{'\n'})
  64. if pos < 0 {
  65. pos = 0
  66. }
  67. line := w.buf[pos:]
  68. w.buf = w.buf[:pos]
  69. if err = w.columnate(); err != nil {
  70. if len(line) < i {
  71. return i - len(line), err
  72. }
  73. return 0, err
  74. }
  75. if n, err := w.w.Write(line); err != nil {
  76. if r := len(line) - n; r < i {
  77. return i - r, err
  78. }
  79. return 0, err
  80. }
  81. }
  82. lastWasColon = false
  83. }
  84. if c == '\n' {
  85. linelen = 0
  86. }
  87. }
  88. return len(p), nil
  89. }
  90. // Flush should be called after the last call to Write to ensure that any data
  91. // buffered in the Writer is written to output.
  92. func (w *Writer) Flush() error {
  93. return w.columnate()
  94. }
  95. func (w *Writer) columnate() error {
  96. words := bytes.Split(w.buf, []byte{'\n'})
  97. w.buf = nil
  98. if len(words[len(words)-1]) == 0 {
  99. words = words[:len(words)-1]
  100. }
  101. maxwidth := 0
  102. for _, wd := range words {
  103. if n := utf8.RuneCount(wd); n > maxwidth {
  104. maxwidth = n
  105. }
  106. }
  107. maxwidth++ // space char
  108. wordsPerLine := w.width / maxwidth
  109. if wordsPerLine <= 0 {
  110. wordsPerLine = 1
  111. }
  112. nlines := (len(words) + wordsPerLine - 1) / wordsPerLine
  113. for i := 0; i < nlines; i++ {
  114. col := 0
  115. endcol := 0
  116. for j := i; j < len(words); j += nlines {
  117. endcol += maxwidth
  118. _, err := w.w.Write(words[j])
  119. if err != nil {
  120. return err
  121. }
  122. col += utf8.RuneCount(words[j])
  123. if j+nlines < len(words) {
  124. for col < endcol {
  125. _, err := w.w.Write([]byte{' '})
  126. if err != nil {
  127. return err
  128. }
  129. col++
  130. }
  131. }
  132. }
  133. _, err := w.w.Write([]byte{'\n'})
  134. if err != nil {
  135. return err
  136. }
  137. }
  138. return nil
  139. }