88 "io"
99 "sort"
1010 "strings"
11- "sync"
11+ "sync/atomic"
12+
13+ "code.gitea.io/gitea/modules/setting"
1214)
1315
1416// Gemoji is a set of emoji data.
@@ -23,74 +25,78 @@ type Emoji struct {
2325 SkinTones bool
2426}
2527
26- var (
27- // codeMap provides a map of the emoji unicode code to its emoji data.
28- codeMap map [string ]int
29-
30- // aliasMap provides a map of the alias to its emoji data.
31- aliasMap map [string ]int
32-
33- // emptyReplacer is the string replacer for emoji codes.
34- emptyReplacer * strings.Replacer
35-
36- // codeReplacer is the string replacer for emoji codes.
37- codeReplacer * strings.Replacer
38-
39- // aliasReplacer is the string replacer for emoji aliases.
40- aliasReplacer * strings.Replacer
41-
42- once sync.Once
43- )
28+ type globalVarsStruct struct {
29+ codeMap map [string ]int // emoji unicode code to its emoji data.
30+ aliasMap map [string ]int // the alias to its emoji data.
31+ emptyReplacer * strings.Replacer // string replacer for emoji codes, used for finding emoji positions.
32+ codeReplacer * strings.Replacer // string replacer for emoji codes.
33+ aliasReplacer * strings.Replacer // string replacer for emoji aliases.
34+ }
4435
45- func loadMap () {
46- once .Do (func () {
47- // initialize
48- codeMap = make (map [string ]int , len (GemojiData ))
49- aliasMap = make (map [string ]int , len (GemojiData ))
36+ var globalVarsStore atomic.Pointer [globalVarsStruct ]
5037
51- // process emoji codes and aliases
52- codePairs := make ([]string , 0 )
53- emptyPairs := make ([]string , 0 )
54- aliasPairs := make ([]string , 0 )
38+ func globalVars () * globalVarsStruct {
39+ vars := globalVarsStore .Load ()
40+ if vars != nil {
41+ return vars
42+ }
43+ // although there can be concurrent calls, the result should be the same, and there is no performance problem
44+ vars = & globalVarsStruct {}
45+ vars .codeMap = make (map [string ]int , len (GemojiData ))
46+ vars .aliasMap = make (map [string ]int , len (GemojiData ))
47+
48+ // process emoji codes and aliases
49+ codePairs := make ([]string , 0 )
50+ emptyPairs := make ([]string , 0 )
51+ aliasPairs := make ([]string , 0 )
52+
53+ // sort from largest to small so we match combined emoji first
54+ sort .Slice (GemojiData , func (i , j int ) bool {
55+ return len (GemojiData [i ].Emoji ) > len (GemojiData [j ].Emoji )
56+ })
5557
56- // sort from largest to small so we match combined emoji first
57- sort . Slice ( GemojiData , func ( i , j int ) bool {
58- return len ( GemojiData [ i ]. Emoji ) > len ( GemojiData [ j ]. Emoji )
59- })
58+ for idx , emoji := range GemojiData {
59+ if emoji . Emoji == "" || len ( emoji . Aliases ) == 0 {
60+ continue
61+ }
6062
61- for i , e := range GemojiData {
62- if e .Emoji == "" || len (e .Aliases ) == 0 {
63+ // process aliases
64+ firstAlias := ""
65+ for _ , alias := range emoji .Aliases {
66+ if alias == "" {
6367 continue
6468 }
65-
66- // setup codes
67- codeMap [e .Emoji ] = i
68- codePairs = append (codePairs , e .Emoji , ":" + e .Aliases [0 ]+ ":" )
69- emptyPairs = append (emptyPairs , e .Emoji , e .Emoji )
70-
71- // setup aliases
72- for _ , a := range e .Aliases {
73- if a == "" {
74- continue
75- }
76-
77- aliasMap [a ] = i
78- aliasPairs = append (aliasPairs , ":" + a + ":" , e .Emoji )
69+ enabled := len (setting .UI .EnabledEmojisSet ) == 0 || setting .UI .EnabledEmojisSet .Contains (alias )
70+ if ! enabled {
71+ continue
7972 }
73+ if firstAlias == "" {
74+ firstAlias = alias
75+ }
76+ vars .aliasMap [alias ] = idx
77+ aliasPairs = append (aliasPairs , ":" + alias + ":" , emoji .Emoji )
8078 }
8179
82- // create replacers
83- emptyReplacer = strings .NewReplacer (emptyPairs ... )
84- codeReplacer = strings .NewReplacer (codePairs ... )
85- aliasReplacer = strings .NewReplacer (aliasPairs ... )
86- })
80+ // process emoji code
81+ if firstAlias != "" {
82+ vars .codeMap [emoji .Emoji ] = idx
83+ codePairs = append (codePairs , emoji .Emoji , ":" + emoji .Aliases [0 ]+ ":" )
84+ emptyPairs = append (emptyPairs , emoji .Emoji , emoji .Emoji )
85+ }
86+ }
87+
88+ // create replacers
89+ vars .emptyReplacer = strings .NewReplacer (emptyPairs ... )
90+ vars .codeReplacer = strings .NewReplacer (codePairs ... )
91+ vars .aliasReplacer = strings .NewReplacer (aliasPairs ... )
92+ globalVarsStore .Store (vars )
93+ return vars
8794}
8895
8996// FromCode retrieves the emoji data based on the provided unicode code (ie,
9097// "\u2618" will return the Gemoji data for "shamrock").
9198func FromCode (code string ) * Emoji {
92- loadMap ()
93- i , ok := codeMap [code ]
99+ i , ok := globalVars ().codeMap [code ]
94100 if ! ok {
95101 return nil
96102 }
@@ -102,12 +108,11 @@ func FromCode(code string) *Emoji {
102108// "alias" or ":alias:" (ie, "shamrock" or ":shamrock:" will return the Gemoji
103109// data for "shamrock").
104110func FromAlias (alias string ) * Emoji {
105- loadMap ()
106111 if strings .HasPrefix (alias , ":" ) && strings .HasSuffix (alias , ":" ) {
107112 alias = alias [1 : len (alias )- 1 ]
108113 }
109114
110- i , ok := aliasMap [alias ]
115+ i , ok := globalVars (). aliasMap [alias ]
111116 if ! ok {
112117 return nil
113118 }
@@ -119,15 +124,13 @@ func FromAlias(alias string) *Emoji {
119124// alias (in the form of ":alias:") (ie, "\u2618" will be converted to
120125// ":shamrock:").
121126func ReplaceCodes (s string ) string {
122- loadMap ()
123- return codeReplacer .Replace (s )
127+ return globalVars ().codeReplacer .Replace (s )
124128}
125129
126130// ReplaceAliases replaces all aliases of the form ":alias:" with its
127131// corresponding unicode value.
128132func ReplaceAliases (s string ) string {
129- loadMap ()
130- return aliasReplacer .Replace (s )
133+ return globalVars ().aliasReplacer .Replace (s )
131134}
132135
133136type rememberSecondWriteWriter struct {
@@ -163,7 +166,6 @@ func (n *rememberSecondWriteWriter) WriteString(s string) (int, error) {
163166
164167// FindEmojiSubmatchIndex returns index pair of longest emoji in a string
165168func FindEmojiSubmatchIndex (s string ) []int {
166- loadMap ()
167169 secondWriteWriter := rememberSecondWriteWriter {}
168170
169171 // A faster and clean implementation would copy the trie tree formation in strings.NewReplacer but
@@ -175,7 +177,7 @@ func FindEmojiSubmatchIndex(s string) []int {
175177 // Therefore we can simply take the index of the second write as our first emoji
176178 //
177179 // FIXME: just copy the trie implementation from strings.NewReplacer
178- _ , _ = emptyReplacer .WriteString (& secondWriteWriter , s )
180+ _ , _ = globalVars (). emptyReplacer .WriteString (& secondWriteWriter , s )
179181
180182 // if we wrote less than twice then we never "replaced"
181183 if secondWriteWriter .writecount < 2 {
0 commit comments