Skip to content

Commit f9d9470

Browse files
committed
Merge remote-tracking branch 'giteaofficial/main'
* giteaofficial/main: Avoid emoji mismatch and allow to only enable chosen emojis (go-gitea#35692) feat(diff): Enable commenting on expanded lines in PR diffs (go-gitea#35662) Fix various bugs (go-gitea#35684) Fix workflow run event status while rerunning a failed job (go-gitea#35689) Use gitrepo.Repository instead of wikipath (go-gitea#35398) [skip ci] Updated translations via Crowdin Bump `actions/labeler` to v6 (go-gitea#35681) Use LFS object size instead of blob size when viewing a LFS file (go-gitea#35679)
2 parents c2a80d6 + 66ee8f3 commit f9d9470

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+1187
-479
lines changed

.github/workflows/pull-labeler.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ jobs:
1515
contents: read
1616
pull-requests: write
1717
steps:
18-
- uses: actions/labeler@v5
18+
- uses: actions/labeler@v6
1919
with:
2020
sync-labels: true

custom/conf/app.example.ini

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1343,6 +1343,10 @@ LEVEL = Info
13431343
;; Dont mistake it for Reactions.
13441344
;CUSTOM_EMOJIS = gitea, codeberg, gitlab, git, github, gogs
13451345
;;
1346+
;; Comma separated list of enabled emojis, for example: smile, thumbsup, thumbsdown
1347+
;; Leave it empty to enable all emojis.
1348+
;ENABLED_EMOJIS =
1349+
;;
13461350
;; Whether the full name of the users should be shown where possible. If the full name isn't set, the username will be used.
13471351
;DEFAULT_SHOW_FULL_NAME = false
13481352
;;

models/issues/issue_search.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@ func applySubscribedCondition(sess *xorm.Session, subscriberID int64) {
476476
),
477477
builder.Eq{"issue.poster_id": subscriberID},
478478
builder.In("issue.repo_id", builder.
479-
Select("id").
479+
Select("repo_id").
480480
From("watch").
481481
Where(builder.And(builder.Eq{"user_id": subscriberID},
482482
builder.In("mode", repo_model.WatchModeNormal, repo_model.WatchModeAuto))),

models/issues/issue_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,12 @@ func TestIssues(t *testing.T) {
197197
},
198198
[]int64{2},
199199
},
200+
{
201+
issues_model.IssuesOptions{
202+
SubscriberID: 11,
203+
},
204+
[]int64{11, 5, 9, 8, 3, 2, 1},
205+
},
200206
} {
201207
issues, err := issues_model.Issues(t.Context(), &test.Opts)
202208
assert.NoError(t, err)

models/repo/repo.go

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -229,10 +229,6 @@ func RelativePath(ownerName, repoName string) string {
229229
return strings.ToLower(ownerName) + "/" + strings.ToLower(repoName) + ".git"
230230
}
231231

232-
func RelativeWikiPath(ownerName, repoName string) string {
233-
return strings.ToLower(ownerName) + "/" + strings.ToLower(repoName) + ".wiki.git"
234-
}
235-
236232
// RelativePath should be an unix style path like username/reponame.git
237233
func (repo *Repository) RelativePath() string {
238234
return RelativePath(repo.OwnerName, repo.Name)
@@ -245,12 +241,6 @@ func (sr StorageRepo) RelativePath() string {
245241
return string(sr)
246242
}
247243

248-
// WikiStorageRepo returns the storage repo for the wiki
249-
// The wiki repository should have the same object format as the code repository
250-
func (repo *Repository) WikiStorageRepo() StorageRepo {
251-
return StorageRepo(RelativeWikiPath(repo.OwnerName, repo.Name))
252-
}
253-
254244
// SanitizedOriginalURL returns a sanitized OriginalURL
255245
func (repo *Repository) SanitizedOriginalURL() string {
256246
if repo.OriginalURL == "" {

models/repo/wiki.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ package repo
77
import (
88
"context"
99
"fmt"
10-
"path/filepath"
1110
"strings"
1211

1312
user_model "code.gitea.io/gitea/models/user"
@@ -76,12 +75,12 @@ func (repo *Repository) WikiCloneLink(ctx context.Context, doer *user_model.User
7675
return repo.cloneLink(ctx, doer, repo.Name+".wiki")
7776
}
7877

79-
// WikiPath returns wiki data path by given user and repository name.
80-
func WikiPath(userName, repoName string) string {
81-
return filepath.Join(user_model.UserPath(userName), strings.ToLower(repoName)+".wiki.git")
78+
func RelativeWikiPath(ownerName, repoName string) string {
79+
return strings.ToLower(ownerName) + "/" + strings.ToLower(repoName) + ".wiki.git"
8280
}
8381

84-
// WikiPath returns wiki data path for given repository.
85-
func (repo *Repository) WikiPath() string {
86-
return WikiPath(repo.OwnerName, repo.Name)
82+
// WikiStorageRepo returns the storage repo for the wiki
83+
// The wiki repository should have the same object format as the code repository
84+
func (repo *Repository) WikiStorageRepo() StorageRepo {
85+
return StorageRepo(RelativeWikiPath(repo.OwnerName, repo.Name))
8786
}

models/repo/wiki_test.go

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,10 @@
44
package repo_test
55

66
import (
7-
"path/filepath"
87
"testing"
98

109
repo_model "code.gitea.io/gitea/models/repo"
1110
"code.gitea.io/gitea/models/unittest"
12-
"code.gitea.io/gitea/modules/setting"
1311

1412
"github.com/stretchr/testify/assert"
1513
)
@@ -23,15 +21,10 @@ func TestRepository_WikiCloneLink(t *testing.T) {
2321
assert.Equal(t, "https://try.gitea.io/user2/repo1.wiki.git", cloneLink.HTTPS)
2422
}
2523

26-
func TestWikiPath(t *testing.T) {
24+
func TestRepository_RelativeWikiPath(t *testing.T) {
2725
assert.NoError(t, unittest.PrepareTestDatabase())
28-
expected := filepath.Join(setting.RepoRootPath, "user2/repo1.wiki.git")
29-
assert.Equal(t, expected, repo_model.WikiPath("user2", "repo1"))
30-
}
3126

32-
func TestRepository_WikiPath(t *testing.T) {
33-
assert.NoError(t, unittest.PrepareTestDatabase())
3427
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
35-
expected := filepath.Join(setting.RepoRootPath, "user2/repo1.wiki.git")
36-
assert.Equal(t, expected, repo.WikiPath())
28+
assert.Equal(t, "user2/repo1.wiki.git", repo_model.RelativeWikiPath(repo.OwnerName, repo.Name))
29+
assert.Equal(t, "user2/repo1.wiki.git", repo.WikiStorageRepo().RelativePath())
3730
}

modules/emoji/emoji.go

Lines changed: 65 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import (
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").
9198
func 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").
104110
func 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:").
121126
func 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.
128132
func ReplaceAliases(s string) string {
129-
loadMap()
130-
return aliasReplacer.Replace(s)
133+
return globalVars().aliasReplacer.Replace(s)
131134
}
132135

133136
type 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
165168
func 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 {

modules/emoji/emoji_test.go

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@ package emoji
77
import (
88
"testing"
99

10+
"code.gitea.io/gitea/modules/container"
11+
"code.gitea.io/gitea/modules/setting"
12+
"code.gitea.io/gitea/modules/test"
13+
1014
"github.com/stretchr/testify/assert"
1115
)
1216

13-
func TestDumpInfo(t *testing.T) {
14-
t.Logf("codes: %d", len(codeMap))
15-
t.Logf("aliases: %d", len(aliasMap))
16-
}
17-
1817
func TestLookup(t *testing.T) {
1918
a := FromCode("\U0001f37a")
2019
b := FromCode("🍺")
@@ -24,15 +23,27 @@ func TestLookup(t *testing.T) {
2423
assert.Equal(t, a, b)
2524
assert.Equal(t, b, c)
2625
assert.Equal(t, c, d)
27-
assert.Equal(t, a, d)
2826

2927
m := FromCode("\U0001f44d")
3028
n := FromAlias(":thumbsup:")
3129
o := FromAlias("+1")
3230

3331
assert.Equal(t, m, n)
3432
assert.Equal(t, m, o)
35-
assert.Equal(t, n, o)
33+
34+
defer test.MockVariableValue(&setting.UI.EnabledEmojisSet, container.SetOf("thumbsup"))()
35+
defer globalVarsStore.Store(nil)
36+
globalVarsStore.Store(nil)
37+
a = FromCode("\U0001f37a")
38+
c = FromAlias(":beer:")
39+
m = FromCode("\U0001f44d")
40+
n = FromAlias(":thumbsup:")
41+
o = FromAlias("+1")
42+
assert.Nil(t, a)
43+
assert.Nil(t, c)
44+
assert.NotNil(t, m)
45+
assert.NotNil(t, n)
46+
assert.Nil(t, o)
3647
}
3748

3849
func TestReplacers(t *testing.T) {

0 commit comments

Comments
 (0)