|
| 1 | +package utils |
| 2 | + |
| 3 | +import ( |
| 4 | + "bufio" |
| 5 | + "io" |
| 6 | + "strings" |
| 7 | +) |
| 8 | + |
| 9 | +const defaultTabWidth = 8 |
| 10 | + |
| 11 | +// formattingWriter implements StringWriter interface. |
| 12 | +// It writes strings to the underlying writer indenting and wrapping the text. |
| 13 | +type formattingWriter struct { |
| 14 | + raw io.StringWriter |
| 15 | + width int |
| 16 | + indent indents |
| 17 | + tabWidth int |
| 18 | + |
| 19 | + currentLine int |
| 20 | + written int |
| 21 | + indentReset bool |
| 22 | +} |
| 23 | + |
| 24 | +var _ io.StringWriter = (*formattingWriter)(nil) |
| 25 | + |
| 26 | +func makeFormattingWriter(w io.StringWriter, width int, tabWidth int, indent ...int) *formattingWriter { |
| 27 | + return &formattingWriter{raw: w, width: width, indent: indent, tabWidth: tabWidth} |
| 28 | +} |
| 29 | + |
| 30 | +// write is an internal method that writes the string to the underlying writer. |
| 31 | +func (w *formattingWriter) write(s string) error { |
| 32 | + n, err := w.raw.WriteString(s) |
| 33 | + w.currentLine += n |
| 34 | + w.written += n |
| 35 | + return err //nolint:wrapcheck |
| 36 | +} |
| 37 | + |
| 38 | +// writePadding is an internal method, that takes the next indent value and the |
| 39 | +// writes the according number of spaces. If the indent value is negative, it is |
| 40 | +// calculated as tabulation offset, i.e. the spaces are added to reach the |
| 41 | +// required offset. Returns true if a new line has been written. |
| 42 | +func (w *formattingWriter) writePadding() (bool, error) { |
| 43 | + w.indentReset = false |
| 44 | + padding := w.indent.popNotLast() |
| 45 | + if padding < 0 { |
| 46 | + // Indent to tabulation. |
| 47 | + padding = -padding |
| 48 | + if padding > w.currentLine { |
| 49 | + padding -= w.currentLine |
| 50 | + } else { |
| 51 | + return true, nil |
| 52 | + } |
| 53 | + } else if w.currentLine+padding > w.width { |
| 54 | + return true, nil |
| 55 | + } |
| 56 | + return false, w.write(strings.Repeat(" ", padding)) |
| 57 | +} |
| 58 | + |
| 59 | +// ln is an internal method that writes a new line to the underlying writer. |
| 60 | +func (w *formattingWriter) ln() error { |
| 61 | + _, err := w.raw.WriteString("\n") |
| 62 | + w.currentLine = 0 |
| 63 | + return err //nolint:wrapcheck |
| 64 | +} |
| 65 | + |
| 66 | +// SetIndent updates the indentation for the following writings. |
| 67 | +func (w *formattingWriter) SetIndent(indent ...int) { |
| 68 | + w.indentReset = true |
| 69 | + w.indent = indent |
| 70 | +} |
| 71 | + |
| 72 | +// WriteString writes the string to the underlying writer, with the configured |
| 73 | +// indentation and wrapping. |
| 74 | +// Implements the StringWriter interface. |
| 75 | +func (w *formattingWriter) WriteString(s string) (int, error) { |
| 76 | + w.written = 0 |
| 77 | + tokenScanner := bufio.NewScanner(strings.NewReader(s)) |
| 78 | + tokenScanner.Split(wordsAndDelimeters) |
| 79 | + var err error |
| 80 | + for err == nil && tokenScanner.Scan() { |
| 81 | + token := tokenScanner.Text() |
| 82 | + length := len(token) |
| 83 | + switch token { |
| 84 | + case "\t": |
| 85 | + length = defaultTabWidth |
| 86 | + case "\n": |
| 87 | + if w.currentLine == 0 { |
| 88 | + w.indent.popNotLast() |
| 89 | + w.indentReset = false |
| 90 | + } |
| 91 | + err = w.ln() |
| 92 | + continue |
| 93 | + } |
| 94 | + ln := false |
| 95 | + if w.currentLine == 0 || w.indentReset { |
| 96 | + if ln, err = w.writePadding(); err != nil { |
| 97 | + break |
| 98 | + } |
| 99 | + } |
| 100 | + // Write a new line and a padding in case of line overflow: |
| 101 | + if ln || w.currentLine+length > w.width { |
| 102 | + if err = w.ln(); err != nil { |
| 103 | + break |
| 104 | + } |
| 105 | + if token == " " { |
| 106 | + // Do not write space token that caused line break. |
| 107 | + continue |
| 108 | + } |
| 109 | + if _, err = w.writePadding(); err != nil { |
| 110 | + break |
| 111 | + } |
| 112 | + } |
| 113 | + err = w.write(token) |
| 114 | + } |
| 115 | + return w.written, err |
| 116 | +} |
0 commit comments