Files
opentf/command/format/trivia.go
Martin Atkins 1f1af87dea command/format: Don't try to fill the last terminal column
In some terminal emulators, writing a character into the last column on a
row causes the terminal to immediately wrap to the beginning of the next
line, even if the very next character in the stream is a hard newline.
That can then lead to errant blank lines in the final output which make
it harder to navigate the visual hierarchy.

As a compromise to avoid this, we'll format our horizontal rules and
paragraphs to one column less than the terminal width. That does mean that
our horizontal rules won't _quite_ cover the whole terminal width, but
it seems like a good compromise in order to get consistent behavior across
a wider variety of terminal implementations.
2021-01-13 15:37:04 -08:00

59 lines
1.9 KiB
Go

package format
import (
"strings"
"github.com/mitchellh/colorstring"
wordwrap "github.com/mitchellh/go-wordwrap"
)
// HorizontalRule returns a newline character followed by a number of
// horizontal line characters to fill the given width.
//
// If the given colorize has colors enabled, the rule will also be given a
// dark grey color to attempt to visually de-emphasize it for sighted users.
//
// This is intended for printing to the UI via mitchellh/cli.UI.Output, or
// similar, which will automatically append a trailing newline too.
func HorizontalRule(color *colorstring.Colorize, width int) string {
if width <= 1 {
return "\n"
}
rule := strings.Repeat("─", width-1)
if color == nil { // sometimes unit tests don't populate this properly
return "\n" + rule
}
return color.Color("[dark_gray]\n" + rule)
}
// WordWrap takes a string containing unbroken lines of text and inserts
// newline characters to try to make the text fit within the given width.
//
// The string can already contain newline characters, for example if you are
// trying to render multiple paragraphs of text. (In that case, our usual
// style would be to have _two_ newline characters as the paragraph separator.)
//
// As a special case, any line that begins with at least one space will be left
// unbroken. This allows including literal segments in the output, such as
// code snippets or filenames, where word wrapping would be confusing.
func WordWrap(str string, width int) string {
if width <= 1 {
// Silly edge case. We'll just return the original string to avoid
// panicking or doing other weird stuff.
return str
}
var buf strings.Builder
lines := strings.Split(str, "\n")
for i, line := range lines {
if !strings.HasPrefix(line, " ") {
line = wordwrap.WrapString(line, uint(width-1))
}
if i > 0 {
buf.WriteByte('\n') // reintroduce the newlines we skipped in Scan
}
buf.WriteString(line)
}
return buf.String()
}