1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
|
package authors
import (
"crypto/md5"
"strings"
"github.com/gookit/color"
"github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/lucasb-eyer/go-colorful"
"github.com/mattn/go-runewidth"
)
type authorNameCacheKey struct {
authorName string
truncateTo int
}
// if these being global variables causes trouble we can wrap them in a struct
// attached to the gui state.
var (
authorInitialCache = make(map[string]string)
authorNameCache = make(map[authorNameCacheKey]string)
authorStyleCache = make(map[string]*style.TextStyle)
)
const authorNameWildcard = "*"
func ShortAuthor(authorName string) string {
if value, ok := authorInitialCache[authorName]; ok {
return value
}
initials := getInitials(authorName)
if initials == "" {
return ""
}
value := AuthorStyle(authorName).Sprint(initials)
authorInitialCache[authorName] = value
return value
}
func LongAuthor(authorName string, length int) string {
cacheKey := authorNameCacheKey{authorName: authorName, truncateTo: length}
if value, ok := authorNameCache[cacheKey]; ok {
return value
}
paddedAuthorName := utils.WithPadding(authorName, length, utils.AlignLeft)
truncatedName := utils.TruncateWithEllipsis(paddedAuthorName, length)
value := AuthorStyle(authorName).Sprint(truncatedName)
authorNameCache[cacheKey] = value
return value
}
// AuthorWithLength returns a representation of the author that fits into a
// given maximum length:
// - if the length is less than 2, it returns an empty string
// - if the length is 2, it returns the initials
// - otherwise, it returns the author name truncated to the maximum length
func AuthorWithLength(authorName string, length int) string {
if length < 2 {
return ""
}
if length == 2 {
return ShortAuthor(authorName)
}
return LongAuthor(authorName, length)
}
func AuthorStyle(authorName string) *style.TextStyle {
if value, ok := authorStyleCache[authorName]; ok {
return value
}
// use the unified style whatever the author name is
if value, ok := authorStyleCache[authorNameWildcard]; ok {
return value
}
value := trueColorStyle(authorName)
authorStyleCache[authorName] = &value
return &value
}
func trueColorStyle(str string) style.TextStyle {
hash := md5.Sum([]byte(str))
c := colorful.Hsl(randFloat(hash[0:4])*360.0, 0.6+0.4*randFloat(hash[4:8]), 0.4+randFloat(hash[8:12])*0.2)
return style.New().SetFg(style.NewRGBColor(color.RGB(uint8(c.R*255), uint8(c.G*255), uint8(c.B*255))))
}
func randFloat(hash []byte) float64 {
return float64(randInt(hash, 100)) / 100
}
func randInt(hash []byte, max int) int {
sum := 0
for _, b := range hash {
sum = (sum + int(b)) % max
}
return sum
}
func getInitials(authorName string) string {
if authorName == "" {
return authorName
}
firstRune := getFirstRune(authorName)
if runewidth.RuneWidth(firstRune) > 1 {
return string(firstRune)
}
split := strings.Split(authorName, " ")
if len(split) == 1 {
return utils.LimitStr(authorName, 2)
}
return utils.LimitStr(split[0], 1) + utils.LimitStr(split[1], 1)
}
func getFirstRune(str string) rune {
// just using the loop for the sake of getting the first rune
for _, r := range str {
return r
}
// should never land here
return 0
}
func SetCustomAuthors(customAuthorColors map[string]string) {
authorStyleCache = utils.SetCustomColors(customAuthorColors)
}
|