1// Package emoji terminal output. 2package emoji 3 4import ( 5 "bytes" 6 "errors" 7 "fmt" 8 "io" 9 "regexp" 10 "unicode" 11) 12 13//go:generate generateEmojiCodeMap -pkg emoji -o emoji_codemap.go 14 15// Replace Padding character for emoji. 16var ( 17 ReplacePadding = " " 18) 19 20// CodeMap gets the underlying map of emoji. 21func CodeMap() map[string]string { 22 return emojiCode() 23} 24 25// RevCodeMap gets the underlying map of emoji. 26func RevCodeMap() map[string][]string { 27 return emojiRevCode() 28} 29 30func AliasList(shortCode string) []string { 31 return emojiRevCode()[emojiCode()[shortCode]] 32} 33 34// HasAlias flags if the given `shortCode` has multiple aliases with other 35// codes. 36func HasAlias(shortCode string) bool { 37 return len(AliasList(shortCode)) > 1 38} 39 40// NormalizeShortCode normalizes a given `shortCode` to a deterministic alias. 41func NormalizeShortCode(shortCode string) string { 42 shortLists := AliasList(shortCode) 43 if len(shortLists) == 0 { 44 return shortCode 45 } 46 return shortLists[0] 47} 48 49// regular expression that matches :flag-[countrycode]: 50var flagRegexp = regexp.MustCompile(":flag-([a-z]{2}):") 51 52func emojize(x string) string { 53 str, ok := emojiCode()[x] 54 if ok { 55 return str + ReplacePadding 56 } 57 if match := flagRegexp.FindStringSubmatch(x); len(match) == 2 { 58 return regionalIndicator(match[1][0]) + regionalIndicator(match[1][1]) 59 } 60 return x 61} 62 63// regionalIndicator maps a lowercase letter to a unicode regional indicator 64func regionalIndicator(i byte) string { 65 return string('\U0001F1E6' + rune(i) - 'a') 66} 67 68func replaseEmoji(input *bytes.Buffer) string { 69 emoji := bytes.NewBufferString(":") 70 for { 71 i, _, err := input.ReadRune() 72 if err != nil { 73 // not replase 74 return emoji.String() 75 } 76 77 if i == ':' && emoji.Len() == 1 { 78 return emoji.String() + replaseEmoji(input) 79 } 80 81 emoji.WriteRune(i) 82 switch { 83 case unicode.IsSpace(i): 84 return emoji.String() 85 case i == ':': 86 return emojize(emoji.String()) 87 } 88 } 89} 90 91func compile(x string) string { 92 if x == "" { 93 return "" 94 } 95 96 input := bytes.NewBufferString(x) 97 output := bytes.NewBufferString("") 98 99 for { 100 i, _, err := input.ReadRune() 101 if err != nil { 102 break 103 } 104 switch i { 105 default: 106 output.WriteRune(i) 107 case ':': 108 output.WriteString(replaseEmoji(input)) 109 } 110 } 111 return output.String() 112} 113 114// Print is fmt.Print which supports emoji 115func Print(a ...interface{}) (int, error) { 116 return fmt.Print(compile(fmt.Sprint(a...))) 117} 118 119// Println is fmt.Println which supports emoji 120func Println(a ...interface{}) (int, error) { 121 return fmt.Println(compile(fmt.Sprint(a...))) 122} 123 124// Printf is fmt.Printf which supports emoji 125func Printf(format string, a ...interface{}) (int, error) { 126 return fmt.Print(compile(fmt.Sprintf(format, a...))) 127} 128 129// Fprint is fmt.Fprint which supports emoji 130func Fprint(w io.Writer, a ...interface{}) (int, error) { 131 return fmt.Fprint(w, compile(fmt.Sprint(a...))) 132} 133 134// Fprintln is fmt.Fprintln which supports emoji 135func Fprintln(w io.Writer, a ...interface{}) (int, error) { 136 return fmt.Fprintln(w, compile(fmt.Sprint(a...))) 137} 138 139// Fprintf is fmt.Fprintf which supports emoji 140func Fprintf(w io.Writer, format string, a ...interface{}) (int, error) { 141 return fmt.Fprint(w, compile(fmt.Sprintf(format, a...))) 142} 143 144// Sprint is fmt.Sprint which supports emoji 145func Sprint(a ...interface{}) string { 146 return compile(fmt.Sprint(a...)) 147} 148 149// Sprintf is fmt.Sprintf which supports emoji 150func Sprintf(format string, a ...interface{}) string { 151 return compile(fmt.Sprintf(format, a...)) 152} 153 154// Errorf is fmt.Errorf which supports emoji 155func Errorf(format string, a ...interface{}) error { 156 return errors.New(compile(Sprintf(format, a...))) 157} 158