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