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.
This commit is contained in:
Martin Atkins 2021-01-13 09:03:02 -08:00
parent 0c84a56700
commit 1f1af87dea
4 changed files with 12 additions and 8 deletions

View File

@ -183,11 +183,11 @@ func Diagnostic(diag tfdiags.Diagnostic, sources map[string][]byte, color *color
}
if desc.Detail != "" {
if width != 0 {
if width > 1 {
lines := strings.Split(desc.Detail, "\n")
for _, line := range lines {
if !strings.HasPrefix(line, " ") {
line = wordwrap.WrapString(line, uint(width))
line = wordwrap.WrapString(line, uint(width-1))
}
fmt.Fprintf(&buf, "%s\n", line)
}

View File

@ -403,8 +403,8 @@ wrap onto multiple lines. Thank-you very much for listening.
To fix this, run this very long command:
terraform read-my-mind -please -thanks -but-do-not-wrap-this-line-because-it-is-prefixed-with-spaces
Here is a coda which is also long enough to wrap and so it should eventually
make it onto multiple lines. THE END
Here is a coda which is also long enough to wrap and so it should
eventually make it onto multiple lines. THE END
`
output := Diagnostic(diags[0], nil, color, 76)

View File

@ -16,7 +16,10 @@ import (
// 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 {
rule := strings.Repeat("─", width)
if width <= 1 {
return "\n"
}
rule := strings.Repeat("─", width-1)
if color == nil { // sometimes unit tests don't populate this properly
return "\n" + rule
}
@ -34,7 +37,7 @@ func HorizontalRule(color *colorstring.Colorize, width int) string {
// 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 == 0 {
if width <= 1 {
// Silly edge case. We'll just return the original string to avoid
// panicking or doing other weird stuff.
return str
@ -44,7 +47,7 @@ func WordWrap(str string, width int) string {
lines := strings.Split(str, "\n")
for i, line := range lines {
if !strings.HasPrefix(line, " ") {
line = wordwrap.WrapString(line, uint(width))
line = wordwrap.WrapString(line, uint(width-1))
}
if i > 0 {
buf.WriteByte('\n') // reintroduce the newlines we skipped in Scan

View File

@ -503,7 +503,8 @@ func TestStateMv_differentResourceTypes(t *testing.T) {
t.Fatalf("expected error output, got:\n%s", ui.OutputWriter.String())
}
if !strings.Contains(ui.ErrorWriter.String(), "resource types don't match") {
errOutput := strings.Replace(ui.ErrorWriter.String(), "\n", " ", -1)
if !strings.Contains(errOutput, "resource types don't match") {
t.Fatalf("expected initialization error, got:\n%s", ui.ErrorWriter.String())
}
}