@@ -11,6 +11,7 @@ import (
11
11
"strings"
12
12
"sync"
13
13
14
+ "code.gitea.io/gitea/modules/log"
14
15
"code.gitea.io/gitea/modules/markup/mdstripper"
15
16
"code.gitea.io/gitea/modules/setting"
16
17
)
35
36
// e.g. gogits/gogs#12345
36
37
crossReferenceIssueNumericPattern = regexp .MustCompile (`(?:\s|^|\(|\[)([0-9a-zA-Z-_\.]+/[0-9a-zA-Z-_\.]+#[0-9]+)(?:\s|$|\)|\]|\.(\s|$))` )
37
38
38
- // Same as GitHub. See
39
- // https://help.github.com/articles/closing-issues-via-commit-messages
40
- issueCloseKeywords = []string {"close" , "closes" , "closed" , "fix" , "fixes" , "fixed" , "resolve" , "resolves" , "resolved" }
41
- issueReopenKeywords = []string {"reopen" , "reopens" , "reopened" }
42
-
43
39
issueCloseKeywordsPat , issueReopenKeywordsPat * regexp.Regexp
40
+ issueKeywordsOnce sync.Once
44
41
45
42
giteaHostInit sync.Once
46
43
giteaHost string
@@ -107,13 +104,40 @@ type RefSpan struct {
107
104
End int
108
105
}
109
106
110
- func makeKeywordsPat (keywords []string ) * regexp.Regexp {
111
- return regexp .MustCompile (`(?i)(?:\s|^|\(|\[)(` + strings .Join (keywords , `|` ) + `):? $` )
107
+ func makeKeywordsPat (words []string ) * regexp.Regexp {
108
+ acceptedWords := parseKeywords (words )
109
+ if len (acceptedWords ) == 0 {
110
+ // Never match
111
+ return nil
112
+ }
113
+ return regexp .MustCompile (`(?i)(?:\s|^|\(|\[)(` + strings .Join (acceptedWords , `|` ) + `):? $` )
112
114
}
113
115
114
- func init () {
115
- issueCloseKeywordsPat = makeKeywordsPat (issueCloseKeywords )
116
- issueReopenKeywordsPat = makeKeywordsPat (issueReopenKeywords )
116
+ func parseKeywords (words []string ) []string {
117
+ acceptedWords := make ([]string , 0 , 5 )
118
+ wordPat := regexp .MustCompile (`^[\pL]+$` )
119
+ for _ , word := range words {
120
+ word = strings .ToLower (strings .TrimSpace (word ))
121
+ // Accept Unicode letter class runes (a-z, á, à, ä, )
122
+ if wordPat .MatchString (word ) {
123
+ acceptedWords = append (acceptedWords , word )
124
+ } else {
125
+ log .Info ("Invalid keyword: %s" , word )
126
+ }
127
+ }
128
+ return acceptedWords
129
+ }
130
+
131
+ func newKeywords () {
132
+ issueKeywordsOnce .Do (func () {
133
+ // Delay initialization until after the settings module is initialized
134
+ doNewKeywords (setting .Repository .PullRequest .CloseKeywords , setting .Repository .PullRequest .ReopenKeywords )
135
+ })
136
+ }
137
+
138
+ func doNewKeywords (close []string , reopen []string ) {
139
+ issueCloseKeywordsPat = makeKeywordsPat (close )
140
+ issueReopenKeywordsPat = makeKeywordsPat (reopen )
117
141
}
118
142
119
143
// getGiteaHostName returns a normalized string with the local host name, with no scheme or port information
@@ -310,13 +334,19 @@ func getCrossReference(content []byte, start, end int, fromLink bool) *rawRefere
310
334
}
311
335
312
336
func findActionKeywords (content []byte , start int ) (XRefAction , * RefSpan ) {
313
- m := issueCloseKeywordsPat .FindSubmatchIndex (content [:start ])
314
- if m != nil {
315
- return XRefActionCloses , & RefSpan {Start : m [2 ], End : m [3 ]}
337
+ newKeywords ()
338
+ var m []int
339
+ if issueCloseKeywordsPat != nil {
340
+ m = issueCloseKeywordsPat .FindSubmatchIndex (content [:start ])
341
+ if m != nil {
342
+ return XRefActionCloses , & RefSpan {Start : m [2 ], End : m [3 ]}
343
+ }
316
344
}
317
- m = issueReopenKeywordsPat .FindSubmatchIndex (content [:start ])
318
- if m != nil {
319
- return XRefActionReopens , & RefSpan {Start : m [2 ], End : m [3 ]}
345
+ if issueReopenKeywordsPat != nil {
346
+ m = issueReopenKeywordsPat .FindSubmatchIndex (content [:start ])
347
+ if m != nil {
348
+ return XRefActionReopens , & RefSpan {Start : m [2 ], End : m [3 ]}
349
+ }
320
350
}
321
351
return XRefActionNone , nil
322
352
}
0 commit comments