2021-05-30 07:22:04 +02:00
|
|
|
package utils
|
|
|
|
|
2021-10-24 07:26:03 +02:00
|
|
|
import (
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/mattn/go-runewidth"
|
2022-03-19 10:12:58 +02:00
|
|
|
"github.com/samber/lo"
|
2021-10-24 07:26:03 +02:00
|
|
|
)
|
2021-05-30 07:22:04 +02:00
|
|
|
|
2023-05-21 04:06:22 +02:00
|
|
|
type Alignment int
|
|
|
|
|
|
|
|
const (
|
|
|
|
AlignLeft Alignment = iota
|
|
|
|
AlignRight
|
|
|
|
)
|
|
|
|
|
|
|
|
type ColumnConfig struct {
|
|
|
|
Width int
|
|
|
|
Alignment Alignment
|
|
|
|
}
|
|
|
|
|
2021-05-30 07:22:04 +02:00
|
|
|
// WithPadding pads a string as much as you want
|
2023-05-21 04:06:22 +02:00
|
|
|
func WithPadding(str string, padding int, alignment Alignment) string {
|
2021-05-30 07:22:04 +02:00
|
|
|
uncoloredStr := Decolorise(str)
|
2021-10-24 09:18:08 +02:00
|
|
|
width := runewidth.StringWidth(uncoloredStr)
|
|
|
|
if padding < width {
|
2021-05-30 07:22:04 +02:00
|
|
|
return str
|
|
|
|
}
|
2023-05-21 04:06:22 +02:00
|
|
|
space := strings.Repeat(" ", padding-width)
|
|
|
|
if alignment == AlignLeft {
|
|
|
|
return str + space
|
|
|
|
} else {
|
|
|
|
return space + str
|
|
|
|
}
|
2021-05-30 07:22:04 +02:00
|
|
|
}
|
|
|
|
|
2023-05-21 04:06:22 +02:00
|
|
|
// defaults to left-aligning each column. If you want to set the alignment of
|
|
|
|
// each column, pass in a slice of Alignment values.
|
|
|
|
func RenderDisplayStrings(displayStringsArr [][]string, columnAlignments []Alignment) string {
|
2021-10-31 13:29:43 +02:00
|
|
|
displayStringsArr = excludeBlankColumns(displayStringsArr)
|
2021-05-30 07:22:04 +02:00
|
|
|
padWidths := getPadWidths(displayStringsArr)
|
2023-05-21 04:06:22 +02:00
|
|
|
columnConfigs := make([]ColumnConfig, len(padWidths))
|
|
|
|
for i, padWidth := range padWidths {
|
|
|
|
// gracefully handle when columnAlignments is shorter than padWidths
|
|
|
|
alignment := AlignLeft
|
|
|
|
if len(columnAlignments) > i {
|
|
|
|
alignment = columnAlignments[i]
|
|
|
|
}
|
|
|
|
|
|
|
|
columnConfigs[i] = ColumnConfig{
|
|
|
|
Width: padWidth,
|
|
|
|
Alignment: alignment,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
output := getPaddedDisplayStrings(displayStringsArr, columnConfigs)
|
2021-05-30 07:22:04 +02:00
|
|
|
|
2021-10-31 13:29:43 +02:00
|
|
|
return output
|
2021-05-30 07:22:04 +02:00
|
|
|
}
|
|
|
|
|
2021-10-31 13:29:43 +02:00
|
|
|
// NOTE: this mutates the input slice for the sake of performance
|
|
|
|
func excludeBlankColumns(displayStringsArr [][]string) [][]string {
|
|
|
|
if len(displayStringsArr) == 0 {
|
|
|
|
return displayStringsArr
|
|
|
|
}
|
|
|
|
|
|
|
|
// if all rows share a blank column, we want to remove that column
|
|
|
|
toRemove := []int{}
|
|
|
|
outer:
|
|
|
|
for i := range displayStringsArr[0] {
|
|
|
|
for _, strings := range displayStringsArr {
|
|
|
|
if strings[i] != "" {
|
|
|
|
continue outer
|
|
|
|
}
|
|
|
|
}
|
|
|
|
toRemove = append(toRemove, i)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(toRemove) == 0 {
|
|
|
|
return displayStringsArr
|
|
|
|
}
|
|
|
|
|
|
|
|
// remove the columns
|
|
|
|
for i, strings := range displayStringsArr {
|
|
|
|
for j := len(toRemove) - 1; j >= 0; j-- {
|
|
|
|
strings = append(strings[:toRemove[j]], strings[toRemove[j]+1:]...)
|
|
|
|
}
|
|
|
|
displayStringsArr[i] = strings
|
|
|
|
}
|
|
|
|
|
|
|
|
return displayStringsArr
|
|
|
|
}
|
|
|
|
|
2023-05-21 04:06:22 +02:00
|
|
|
func getPaddedDisplayStrings(stringArrays [][]string, columnConfigs []ColumnConfig) string {
|
2021-10-31 13:29:43 +02:00
|
|
|
builder := strings.Builder{}
|
2021-05-30 07:22:04 +02:00
|
|
|
for i, stringArray := range stringArrays {
|
|
|
|
if len(stringArray) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
2023-05-21 04:06:22 +02:00
|
|
|
for j, columnConfig := range columnConfigs {
|
2021-05-30 07:22:04 +02:00
|
|
|
if len(stringArray)-1 < j {
|
|
|
|
continue
|
|
|
|
}
|
2023-05-21 04:06:22 +02:00
|
|
|
builder.WriteString(WithPadding(stringArray[j], columnConfig.Width, columnConfig.Alignment))
|
2021-10-31 13:29:43 +02:00
|
|
|
builder.WriteString(" ")
|
2021-05-30 07:22:04 +02:00
|
|
|
}
|
2023-05-21 04:06:22 +02:00
|
|
|
if len(stringArray)-1 < len(columnConfigs) {
|
2021-05-30 07:22:04 +02:00
|
|
|
continue
|
|
|
|
}
|
2023-05-21 04:06:22 +02:00
|
|
|
builder.WriteString(stringArray[len(columnConfigs)])
|
2021-10-31 13:29:43 +02:00
|
|
|
|
|
|
|
if i < len(stringArrays)-1 {
|
|
|
|
builder.WriteString("\n")
|
|
|
|
}
|
2021-05-30 07:22:04 +02:00
|
|
|
}
|
2021-10-31 13:29:43 +02:00
|
|
|
return builder.String()
|
2021-05-30 07:22:04 +02:00
|
|
|
}
|
2021-10-24 07:26:03 +02:00
|
|
|
|
|
|
|
func getPadWidths(stringArrays [][]string) []int {
|
2023-07-24 05:06:42 +02:00
|
|
|
maxWidth := MaxFn(stringArrays, func(stringArray []string) int {
|
2022-03-19 10:12:58 +02:00
|
|
|
return len(stringArray)
|
|
|
|
})
|
|
|
|
|
2021-10-24 07:26:03 +02:00
|
|
|
if maxWidth-1 < 0 {
|
|
|
|
return []int{}
|
|
|
|
}
|
2023-07-24 05:06:42 +02:00
|
|
|
return lo.Map(lo.Range(maxWidth-1), func(i int, _ int) int {
|
|
|
|
return MaxFn(stringArrays, func(stringArray []string) int {
|
2022-03-19 10:12:58 +02:00
|
|
|
uncoloredStr := Decolorise(stringArray[i])
|
|
|
|
|
|
|
|
return runewidth.StringWidth(uncoloredStr)
|
|
|
|
})
|
|
|
|
})
|
2021-10-24 07:26:03 +02:00
|
|
|
}
|
2021-10-30 11:15:50 +02:00
|
|
|
|
2023-07-24 05:06:42 +02:00
|
|
|
func MaxFn[T any](items []T, fn func(T) int) int {
|
|
|
|
max := 0
|
|
|
|
for _, item := range items {
|
|
|
|
if fn(item) > max {
|
|
|
|
max = fn(item)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return max
|
|
|
|
}
|
|
|
|
|
2021-10-30 11:15:50 +02:00
|
|
|
// TruncateWithEllipsis returns a string, truncated to a certain length, with an ellipsis
|
|
|
|
func TruncateWithEllipsis(str string, limit int) string {
|
|
|
|
if runewidth.StringWidth(str) > limit && limit <= 3 {
|
|
|
|
return strings.Repeat(".", limit)
|
|
|
|
}
|
|
|
|
return runewidth.Truncate(str, limit, "...")
|
|
|
|
}
|
|
|
|
|
|
|
|
func SafeTruncate(str string, limit int) string {
|
|
|
|
if len(str) > limit {
|
|
|
|
return str[0:limit]
|
|
|
|
} else {
|
|
|
|
return str
|
|
|
|
}
|
|
|
|
}
|
2022-01-19 09:32:27 +02:00
|
|
|
|
2022-11-07 07:35:36 +02:00
|
|
|
const COMMIT_HASH_SHORT_SIZE = 8
|
|
|
|
|
2022-01-19 09:32:27 +02:00
|
|
|
func ShortSha(sha string) string {
|
2022-11-07 07:35:36 +02:00
|
|
|
if len(sha) < COMMIT_HASH_SHORT_SIZE {
|
2022-01-19 09:32:27 +02:00
|
|
|
return sha
|
|
|
|
}
|
2022-11-07 07:35:36 +02:00
|
|
|
return sha[:COMMIT_HASH_SHORT_SIZE]
|
2022-01-19 09:32:27 +02:00
|
|
|
}
|