92d78dea72
text/ gh-style display-width string helpers (Truncate / FuzzyAgo / Pluralize)
compat/ server-CLI version skew probe (kubectl) + 24h TTL cache (gh internal/update)
acceptance/contract/ helpers_test.go for in-process cobra exec + golden assert
(uses iostreams.SetForTest singleton replacement; no t.Parallel)
cmdutil/errors.go: AllCodes() + ClassifyHTTPErrorOutputs() bridge for the
contract test suite (no behavior change in error handling)
go-runewidth promoted from indirect to direct (used by text.Truncate for
display-width-aware CJK truncation).
acceptance/contract/helpers_test.go forward-references cmd.NewRootCmd which
is exported in PR-7 — that file does not compile until then by design.
37 lines
828 B
Go
37 lines
828 B
Go
package text
|
|
|
|
import "github.com/mattn/go-runewidth"
|
|
|
|
const ellipsis = "…"
|
|
|
|
// Truncate cuts s to at most maxWidth display columns (CJK / emoji 占 2 列;
|
|
// ASCII 占 1 列), appending "…" if truncated. Mirrors gh
|
|
// internal/text/text.go Truncate semantics — display width, not rune count.
|
|
//
|
|
// Edge cases:
|
|
//
|
|
// maxWidth ≤ 0 → ""
|
|
// s 已 ≤ maxWidth → s 原样返回
|
|
// ellipsis 占 1 列, 所以 truncate 时 budget = maxWidth - 1
|
|
func Truncate(maxWidth int, s string) string {
|
|
if maxWidth <= 0 {
|
|
return ""
|
|
}
|
|
if runewidth.StringWidth(s) <= maxWidth {
|
|
return s
|
|
}
|
|
if maxWidth == 1 {
|
|
return ellipsis
|
|
}
|
|
budget := maxWidth - 1 // reserve for ellipsis
|
|
w := 0
|
|
for i, r := range s {
|
|
rw := runewidth.RuneWidth(r)
|
|
if w+rw > budget {
|
|
return s[:i] + ellipsis
|
|
}
|
|
w += rw
|
|
}
|
|
return s + ellipsis
|
|
}
|