Handling of ASCII HT "\t" in OutputLinePrefix
I was having trouble with the handling of tabs in OutputLinePrefix. This patch adds a TabWidth option which defaults to 8.
// TabWidth sets the perceived size of a `\t` (horizontal tab).
// This is used in the computation of the width of OutputLinePrefix
// (only). Setting TabWidth to 0 disables TabWidth calculations.
// Default: 8
TabWidth int
The handling of tabs has always been a problem in UNIX. It starts going bad at the tty driver and goes downhill from there. I assume it's just as bad in Linux, BSD, and Mach based systems like OS-X.
Here's the patch with all of the debugging code left in, but turned off. It's based on "github.com/bbrks/wrap/[email protected]".
*** ./wrapper.go 2025/07/26 13:49:43 1.1
--- ./wrapper.go 2025/07/26 21:18:22
***************
*** 1,6 ****
--- 1,7 ----
package wrap
import (
+ "log"
"strings"
"unicode/utf8"
)
***************
*** 8,13 ****
--- 9,15 ----
const (
defaultBreakpoints = " -"
defaultNewline = "\n"
+ defaultTabWidth = 8
)
// Wrapper contains settings for customisable word-wrapping.
***************
*** 30,37 ****
// Default: ""
OutputLineSuffix string
! // LimitIncludesPrefixSuffix can be set to false if you don't want prefixes
! // and suffixes to be included in the length limits.
// Default: true
LimitIncludesPrefixSuffix bool
--- 32,40 ----
// Default: ""
OutputLineSuffix string
! // LimitIncludesPrefixSuffix can be set to false if you don't
! // want prefixes and suffixes to be included in the length
! // limits.
// Default: true
LimitIncludesPrefixSuffix bool
***************
*** 50,57 ****
// Default: false
StripTrailingNewline bool
! // CutLongWords will cause a hard-wrap in the middle of a word if the word's length exceeds the given limit.
CutLongWords bool
}
// NewWrapper returns a new instance of a Wrapper initialised with defaults.
--- 53,68 ----
// Default: false
StripTrailingNewline bool
! // CutLongWords will cause a hard-wrap in the middle of a word if
! // the word's length exceeds the given limit.
! // Default: false
CutLongWords bool
+
+ // TabWidth sets the perceived size of a `\t` (horizontal tab).
+ // This is used in the computation of the width of OutputLinePrefix
+ // (only). Setting TabWidth to 0 disables TabWidth calculations.
+ // Default: 8
+ TabWidth int
}
// NewWrapper returns a new instance of a Wrapper initialised with defaults.
***************
*** 59,64 ****
--- 70,76 ----
return Wrapper{
Breakpoints: defaultBreakpoints,
Newline: defaultNewline,
+ TabWidth: defaultTabWidth,
LimitIncludesPrefixSuffix: true,
}
}
***************
*** 75,81 ****
// Subtract the length of the prefix and suffix from the limit
// so we don't break length limits when using them.
if w.LimitIncludesPrefixSuffix {
! limit -= utf8.RuneCountInString(w.OutputLinePrefix) + utf8.RuneCountInString(w.OutputLineSuffix)
}
var ret string
--- 87,115 ----
// Subtract the length of the prefix and suffix from the limit
// so we don't break length limits when using them.
if w.LimitIncludesPrefixSuffix {
! totalTabWidth := 0
! effectiveOutputLinePrefixWidth :=
! utf8.RuneCountInString(w.OutputLinePrefix)
! if w.TabWidth > 0 {
! if false {
! log.Printf("Build #3a effectiveOutputLinePrefixWidth=%d\n",
! effectiveOutputLinePrefixWidth)
! }
! totalTabWidth =
! strings.Count(w.OutputLinePrefix, "\t") *
! (w.TabWidth - 1) // Minus 1 for the rune itself.
! effectiveOutputLinePrefixWidth += totalTabWidth
! if false {
! log.Printf("Build #3b totalTabWidth=%d\n", totalTabWidth)
! log.Printf("Build #3c effectiveOutputLinePrefixWidth(updated)=%d\n",
! effectiveOutputLinePrefixWidth)
! log.Printf("Build #3d strings.Count(w.OutputLinePrefix, '\\t')=%d\n",
! strings.Count(w.OutputLinePrefix, "\t"))
! log.Printf("Build #3e:\t %v \n", []byte(w.OutputLinePrefix))
! }
! }
! limit -= effectiveOutputLinePrefixWidth +
! utf8.RuneCountInString(w.OutputLineSuffix)
}
var ret string
It'll misbehave if you set OutputLinePrefix to something like #####\t######\t. I hadn't thought of that. It'll assume 13 chars + 14 tab-induced phantom-chars (27) of prefix length. It won't break; it'll just look funny.
It's not easy to address because the width of a tab is set in the tty driver.
Dang it.
-E