Skip to content

Commit 526286b

Browse files
committed
refactor global vars
1 parent 863170a commit 526286b

File tree

7 files changed

+57
-63
lines changed

7 files changed

+57
-63
lines changed

modules/markup/html.go

Lines changed: 45 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,27 @@ const (
2525
IssueNameStyleRegexp = "regexp"
2626
)
2727

28-
var (
28+
// CSS class for action keywords (e.g. "closes: #1")
29+
const keywordClass = "issue-keyword"
30+
31+
type globalVarsType struct {
32+
hashCurrentPattern *regexp.Regexp
33+
shortLinkPattern *regexp.Regexp
34+
anyHashPattern *regexp.Regexp
35+
comparePattern *regexp.Regexp
36+
fullURLPattern *regexp.Regexp
37+
emailRegex *regexp.Regexp
38+
blackfridayExtRegex *regexp.Regexp
39+
emojiShortCodeRegex *regexp.Regexp
40+
issueFullPattern *regexp.Regexp
41+
filesChangedFullPattern *regexp.Regexp
42+
43+
tagCleaner *regexp.Regexp
44+
nulCleaner *strings.Replacer
45+
}
46+
47+
var globalVars = sync.OnceValue[*globalVarsType](func() *globalVarsType {
48+
v := &globalVarsType{}
2949
// NOTE: All below regex matching do not perform any extra validation.
3050
// Thus a link is produced even if the linked entity does not exist.
3151
// While fast, this is also incorrect and lead to false positives.
@@ -36,79 +56,58 @@ var (
3656
// hashCurrentPattern matches string that represents a commit SHA, e.g. d8a994ef243349f321568f9e36d5c3f444b99cae
3757
// Although SHA1 hashes are 40 chars long, SHA256 are 64, the regex matches the hash from 7 to 64 chars in length
3858
// so that abbreviated hash links can be used as well. This matches git and GitHub usability.
39-
hashCurrentPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([0-9a-f]{7,64})(?:\s|$|\)|\]|[.,:](\s|$))`)
59+
v.hashCurrentPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([0-9a-f]{7,64})(?:\s|$|\)|\]|[.,:](\s|$))`)
4060

4161
// shortLinkPattern matches short but difficult to parse [[name|link|arg=test]] syntax
42-
shortLinkPattern = regexp.MustCompile(`\[\[(.*?)\]\](\w*)`)
62+
v.shortLinkPattern = regexp.MustCompile(`\[\[(.*?)\]\](\w*)`)
4363

4464
// anyHashPattern splits url containing SHA into parts
45-
anyHashPattern = regexp.MustCompile(`https?://(?:\S+/){4,5}([0-9a-f]{40,64})(/[-+~%./\w]+)?(\?[-+~%.\w&=]+)?(#[-+~%.\w]+)?`)
65+
v.anyHashPattern = regexp.MustCompile(`https?://(?:\S+/){4,5}([0-9a-f]{40,64})(/[-+~%./\w]+)?(\?[-+~%.\w&=]+)?(#[-+~%.\w]+)?`)
4666

4767
// comparePattern matches "http://domain/org/repo/compare/COMMIT1...COMMIT2#hash"
48-
comparePattern = regexp.MustCompile(`https?://(?:\S+/){4,5}([0-9a-f]{7,64})(\.\.\.?)([0-9a-f]{7,64})?(#[-+~_%.a-zA-Z0-9]+)?`)
68+
v.comparePattern = regexp.MustCompile(`https?://(?:\S+/){4,5}([0-9a-f]{7,64})(\.\.\.?)([0-9a-f]{7,64})?(#[-+~_%.a-zA-Z0-9]+)?`)
4969

5070
// fullURLPattern matches full URL like "mailto:...", "https://..." and "ssh+git://..."
51-
fullURLPattern = regexp.MustCompile(`^[a-z][-+\w]+:`)
71+
v.fullURLPattern = regexp.MustCompile(`^[a-z][-+\w]+:`)
5272

5373
// emailRegex is definitely not perfect with edge cases,
5474
// it is still accepted by the CommonMark specification, as well as the HTML5 spec:
5575
// http://spec.commonmark.org/0.28/#email-address
5676
// https://html.spec.whatwg.org/multipage/input.html#e-mail-state-(type%3Demail)
57-
emailRegex = regexp.MustCompile("(?:\\s|^|\\(|\\[)([a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9]{2,}(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+)(?:\\s|$|\\)|\\]|;|,|\\?|!|\\.(\\s|$))")
77+
v.emailRegex = regexp.MustCompile("(?:\\s|^|\\(|\\[)([a-zA-Z0-9.!#$%&'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9]{2,}(?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+)(?:\\s|$|\\)|\\]|;|,|\\?|!|\\.(\\s|$))")
5878

5979
// blackfridayExtRegex is for blackfriday extensions create IDs like fn:user-content-footnote
60-
blackfridayExtRegex = regexp.MustCompile(`[^:]*:user-content-`)
80+
v.blackfridayExtRegex = regexp.MustCompile(`[^:]*:user-content-`)
6181

6282
// emojiShortCodeRegex find emoji by alias like :smile:
63-
emojiShortCodeRegex = regexp.MustCompile(`:[-+\w]+:`)
64-
)
83+
v.emojiShortCodeRegex = regexp.MustCompile(`:[-+\w]+:`)
6584

66-
// CSS class for action keywords (e.g. "closes: #1")
67-
const keywordClass = "issue-keyword"
85+
// example: https://domain/org/repo/pulls/27#hash
86+
v.issueFullPattern = regexp.MustCompile(regexp.QuoteMeta(setting.AppURL) +
87+
`[\w_.-]+/[\w_.-]+/(?:issues|pulls)/((?:\w{1,10}-)?[1-9][0-9]*)([\?|#](\S+)?)?\b`)
88+
89+
// example: https://domain/org/repo/pulls/27/files#hash
90+
v.filesChangedFullPattern = regexp.MustCompile(regexp.QuoteMeta(setting.AppURL) +
91+
`[\w_.-]+/[\w_.-]+/pulls/((?:\w{1,10}-)?[1-9][0-9]*)/files([\?|#](\S+)?)?\b`)
92+
93+
v.tagCleaner = regexp.MustCompile(`<((?:/?\w+/\w+)|(?:/[\w ]+/)|(/?[hH][tT][mM][lL]\b)|(/?[hH][eE][aA][dD]\b))`)
94+
v.nulCleaner = strings.NewReplacer("\000", "")
95+
return v
96+
})
6897

6998
// IsFullURLBytes reports whether link fits valid format.
7099
func IsFullURLBytes(link []byte) bool {
71-
return fullURLPattern.Match(link)
100+
return globalVars().fullURLPattern.Match(link)
72101
}
73102

74103
func IsFullURLString(link string) bool {
75-
return fullURLPattern.MatchString(link)
104+
return globalVars().fullURLPattern.MatchString(link)
76105
}
77106

78107
func IsNonEmptyRelativePath(link string) bool {
79108
return link != "" && !IsFullURLString(link) && link[0] != '/' && link[0] != '?' && link[0] != '#'
80109
}
81110

82-
// regexp for full links to issues/pulls
83-
var issueFullPattern *regexp.Regexp
84-
85-
// Once for to prevent races
86-
var issueFullPatternOnce sync.Once
87-
88-
// regexp for full links to hash comment in pull request files changed tab
89-
var filesChangedFullPattern *regexp.Regexp
90-
91-
// Once for to prevent races
92-
var filesChangedFullPatternOnce sync.Once
93-
94-
func getIssueFullPattern() *regexp.Regexp {
95-
issueFullPatternOnce.Do(func() {
96-
// example: https://domain/org/repo/pulls/27#hash
97-
issueFullPattern = regexp.MustCompile(regexp.QuoteMeta(setting.AppURL) +
98-
`[\w_.-]+/[\w_.-]+/(?:issues|pulls)/((?:\w{1,10}-)?[1-9][0-9]*)([\?|#](\S+)?)?\b`)
99-
})
100-
return issueFullPattern
101-
}
102-
103-
func getFilesChangedFullPattern() *regexp.Regexp {
104-
filesChangedFullPatternOnce.Do(func() {
105-
// example: https://domain/org/repo/pulls/27/files#hash
106-
filesChangedFullPattern = regexp.MustCompile(regexp.QuoteMeta(setting.AppURL) +
107-
`[\w_.-]+/[\w_.-]+/pulls/((?:\w{1,10}-)?[1-9][0-9]*)/files([\?|#](\S+)?)?\b`)
108-
})
109-
return filesChangedFullPattern
110-
}
111-
112111
// CustomLinkURLSchemes allows for additional schemes to be detected when parsing links within text
113112
func CustomLinkURLSchemes(schemes []string) {
114113
schemes = append(schemes, "http", "https")
@@ -286,11 +285,6 @@ func RenderEmoji(
286285
return renderProcessString(ctx, emojiProcessors, content)
287286
}
288287

289-
var (
290-
tagCleaner = regexp.MustCompile(`<((?:/?\w+/\w+)|(?:/[\w ]+/)|(/?[hH][tT][mM][lL]\b)|(/?[hH][eE][aA][dD]\b))`)
291-
nulCleaner = strings.NewReplacer("\000", "")
292-
)
293-
294288
func postProcess(ctx *RenderContext, procs []processor, input io.Reader, output io.Writer) error {
295289
defer ctx.Cancel()
296290
// FIXME: don't read all content to memory
@@ -304,7 +298,7 @@ func postProcess(ctx *RenderContext, procs []processor, input io.Reader, output
304298
// prepend "<html><body>"
305299
strings.NewReader("<html><body>"),
306300
// Strip out nuls - they're always invalid
307-
bytes.NewReader(tagCleaner.ReplaceAll([]byte(nulCleaner.Replace(string(rawHTML))), []byte("&lt;$1"))),
301+
bytes.NewReader(globalVars().tagCleaner.ReplaceAll([]byte(globalVars().nulCleaner.Replace(string(rawHTML))), []byte("&lt;$1"))),
308302
// close the tags
309303
strings.NewReader("</body></html>"),
310304
))
@@ -351,7 +345,7 @@ func visitNode(ctx *RenderContext, procs []processor, node *html.Node) *html.Nod
351345
// Add user-content- to IDs and "#" links if they don't already have them
352346
for idx, attr := range node.Attr {
353347
val := strings.TrimPrefix(attr.Val, "#")
354-
notHasPrefix := !(strings.HasPrefix(val, "user-content-") || blackfridayExtRegex.MatchString(val))
348+
notHasPrefix := !(strings.HasPrefix(val, "user-content-") || globalVars().blackfridayExtRegex.MatchString(val))
355349

356350
if attr.Key == "id" && notHasPrefix {
357351
node.Attr[idx].Val = "user-content-" + attr.Val

modules/markup/html_commit.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func createCodeLink(href, content, class string) *html.Node {
5454
}
5555

5656
func anyHashPatternExtract(s string) (ret anyHashPatternResult, ok bool) {
57-
m := anyHashPattern.FindStringSubmatchIndex(s)
57+
m := globalVars().anyHashPattern.FindStringSubmatchIndex(s)
5858
if m == nil {
5959
return ret, false
6060
}
@@ -120,7 +120,7 @@ func comparePatternProcessor(ctx *RenderContext, node *html.Node) {
120120
node = node.NextSibling
121121
continue
122122
}
123-
m := comparePattern.FindStringSubmatchIndex(node.Data)
123+
m := globalVars().comparePattern.FindStringSubmatchIndex(node.Data)
124124
if m == nil || slices.Contains(m[:8], -1) { // ensure that every group (m[0]...m[7]) has a match
125125
node = node.NextSibling
126126
continue
@@ -173,7 +173,7 @@ func hashCurrentPatternProcessor(ctx *RenderContext, node *html.Node) {
173173
ctx.ShaExistCache = make(map[string]bool)
174174
}
175175
for node != nil && node != next && start < len(node.Data) {
176-
m := hashCurrentPattern.FindStringSubmatchIndex(node.Data[start:])
176+
m := globalVars().hashCurrentPattern.FindStringSubmatchIndex(node.Data[start:])
177177
if m == nil {
178178
return
179179
}

modules/markup/html_email.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import "golang.org/x/net/html"
99
func emailAddressProcessor(ctx *RenderContext, node *html.Node) {
1010
next := node.NextSibling
1111
for node != nil && node != next {
12-
m := emailRegex.FindStringSubmatchIndex(node.Data)
12+
m := globalVars().emailRegex.FindStringSubmatchIndex(node.Data)
1313
if m == nil {
1414
return
1515
}

modules/markup/html_emoji.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func emojiShortCodeProcessor(ctx *RenderContext, node *html.Node) {
6262
start := 0
6363
next := node.NextSibling
6464
for node != nil && node != next && start < len(node.Data) {
65-
m := emojiShortCodeRegex.FindStringSubmatchIndex(node.Data[start:])
65+
m := globalVars().emojiShortCodeRegex.FindStringSubmatchIndex(node.Data[start:])
6666
if m == nil {
6767
return
6868
}

modules/markup/html_internal_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -413,10 +413,10 @@ func TestRegExp_sha1CurrentPattern(t *testing.T) {
413413
}
414414

415415
for _, testCase := range trueTestCases {
416-
assert.True(t, hashCurrentPattern.MatchString(testCase))
416+
assert.True(t, globalVars().hashCurrentPattern.MatchString(testCase))
417417
}
418418
for _, testCase := range falseTestCases {
419-
assert.False(t, hashCurrentPattern.MatchString(testCase))
419+
assert.False(t, globalVars().hashCurrentPattern.MatchString(testCase))
420420
}
421421
}
422422

@@ -496,9 +496,9 @@ func TestRegExp_shortLinkPattern(t *testing.T) {
496496
}
497497

498498
for _, testCase := range trueTestCases {
499-
assert.True(t, shortLinkPattern.MatchString(testCase))
499+
assert.True(t, globalVars().shortLinkPattern.MatchString(testCase))
500500
}
501501
for _, testCase := range falseTestCases {
502-
assert.False(t, shortLinkPattern.MatchString(testCase))
502+
assert.False(t, globalVars().shortLinkPattern.MatchString(testCase))
503503
}
504504
}

modules/markup/html_issue.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@ func fullIssuePatternProcessor(ctx *RenderContext, node *html.Node) {
2323
}
2424
next := node.NextSibling
2525
for node != nil && node != next {
26-
m := getIssueFullPattern().FindStringSubmatchIndex(node.Data)
26+
m := globalVars().issueFullPattern.FindStringSubmatchIndex(node.Data)
2727
if m == nil {
2828
return
2929
}
3030

31-
mDiffView := getFilesChangedFullPattern().FindStringSubmatchIndex(node.Data)
31+
mDiffView := globalVars().filesChangedFullPattern.FindStringSubmatchIndex(node.Data)
3232
// leave it as it is if the link is from "Files Changed" tab in PR Diff View https://domain/org/repo/pulls/27/files
3333
if mDiffView != nil {
3434
return

modules/markup/html_link.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ func ResolveLink(ctx *RenderContext, link, userContentAnchorPrefix string) (resu
4040
func shortLinkProcessor(ctx *RenderContext, node *html.Node) {
4141
next := node.NextSibling
4242
for node != nil && node != next {
43-
m := shortLinkPattern.FindStringSubmatchIndex(node.Data)
43+
m := globalVars().shortLinkPattern.FindStringSubmatchIndex(node.Data)
4444
if m == nil {
4545
return
4646
}

0 commit comments

Comments
 (0)