You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

emoji.go 3.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. // Copyright 2020 The Gitea Authors. All rights reserved.
  2. // Copyright 2015 Kenneth Shaw
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. package emoji
  6. import (
  7. "sort"
  8. "strings"
  9. "sync"
  10. )
  11. // Gemoji is a set of emoji data.
  12. type Gemoji []Emoji
  13. // Emoji represents a single emoji and associated data.
  14. type Emoji struct {
  15. Emoji string
  16. Description string
  17. Aliases []string
  18. UnicodeVersion string
  19. SkinTones bool
  20. }
  21. var (
  22. // codeMap provides a map of the emoji unicode code to its emoji data.
  23. codeMap map[string]int
  24. // aliasMap provides a map of the alias to its emoji data.
  25. aliasMap map[string]int
  26. // codeReplacer is the string replacer for emoji codes.
  27. codeReplacer *strings.Replacer
  28. // aliasReplacer is the string replacer for emoji aliases.
  29. aliasReplacer *strings.Replacer
  30. once sync.Once
  31. )
  32. func loadMap() {
  33. once.Do(func() {
  34. // initialize
  35. codeMap = make(map[string]int, len(GemojiData))
  36. aliasMap = make(map[string]int, len(GemojiData))
  37. // process emoji codes and aliases
  38. codePairs := make([]string, 0)
  39. aliasPairs := make([]string, 0)
  40. // sort from largest to small so we match combined emoji first
  41. sort.Slice(GemojiData, func(i, j int) bool {
  42. return len(GemojiData[i].Emoji) > len(GemojiData[j].Emoji)
  43. })
  44. for i, e := range GemojiData {
  45. if e.Emoji == "" || len(e.Aliases) == 0 {
  46. continue
  47. }
  48. // setup codes
  49. codeMap[e.Emoji] = i
  50. codePairs = append(codePairs, e.Emoji, ":"+e.Aliases[0]+":")
  51. // setup aliases
  52. for _, a := range e.Aliases {
  53. if a == "" {
  54. continue
  55. }
  56. aliasMap[a] = i
  57. aliasPairs = append(aliasPairs, ":"+a+":", e.Emoji)
  58. }
  59. }
  60. // create replacers
  61. codeReplacer = strings.NewReplacer(codePairs...)
  62. aliasReplacer = strings.NewReplacer(aliasPairs...)
  63. })
  64. }
  65. // FromCode retrieves the emoji data based on the provided unicode code (ie,
  66. // "\u2618" will return the Gemoji data for "shamrock").
  67. func FromCode(code string) *Emoji {
  68. loadMap()
  69. i, ok := codeMap[code]
  70. if !ok {
  71. return nil
  72. }
  73. return &GemojiData[i]
  74. }
  75. // FromAlias retrieves the emoji data based on the provided alias in the form
  76. // "alias" or ":alias:" (ie, "shamrock" or ":shamrock:" will return the Gemoji
  77. // data for "shamrock").
  78. func FromAlias(alias string) *Emoji {
  79. loadMap()
  80. if strings.HasPrefix(alias, ":") && strings.HasSuffix(alias, ":") {
  81. alias = alias[1 : len(alias)-1]
  82. }
  83. i, ok := aliasMap[alias]
  84. if !ok {
  85. return nil
  86. }
  87. return &GemojiData[i]
  88. }
  89. // ReplaceCodes replaces all emoji codes with the first corresponding emoji
  90. // alias (in the form of ":alias:") (ie, "\u2618" will be converted to
  91. // ":shamrock:").
  92. func ReplaceCodes(s string) string {
  93. loadMap()
  94. return codeReplacer.Replace(s)
  95. }
  96. // ReplaceAliases replaces all aliases of the form ":alias:" with its
  97. // corresponding unicode value.
  98. func ReplaceAliases(s string) string {
  99. loadMap()
  100. return aliasReplacer.Replace(s)
  101. }
  102. // FindEmojiSubmatchIndex returns index pair of longest emoji in a string
  103. func FindEmojiSubmatchIndex(s string) []int {
  104. loadMap()
  105. //see if there are any emoji in string before looking for position of specific ones
  106. //no performance difference when there is a match but 10x faster when there are not
  107. if s == ReplaceCodes(s) {
  108. return nil
  109. }
  110. for j := range GemojiData {
  111. i := strings.Index(s, GemojiData[j].Emoji)
  112. if i != -1 {
  113. return []int{i, i + len(GemojiData[j].Emoji)}
  114. }
  115. }
  116. return nil
  117. }