emoji.go

  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}