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