@@ -11,6 +11,7 @@ import (
1111 "strings"
1212 "sync"
1313
14+ "code.gitea.io/gitea/modules/log"
1415 "code.gitea.io/gitea/modules/markup/mdstripper"
1516 "code.gitea.io/gitea/modules/setting"
1617)
3536 // e.g. gogits/gogs#12345
3637 crossReferenceIssueNumericPattern = regexp .MustCompile (`(?:\s|^|\(|\[)([0-9a-zA-Z-_\.]+/[0-9a-zA-Z-_\.]+#[0-9]+)(?:\s|$|\)|\]|\.(\s|$))` )
3738
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-
4339 issueCloseKeywordsPat , issueReopenKeywordsPat * regexp.Regexp
40+ issueKeywordsOnce sync.Once
4441
4542 giteaHostInit sync.Once
4643 giteaHost string
@@ -107,13 +104,40 @@ type RefSpan struct {
107104 End int
108105}
109106
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 , `|` ) + `):? $` )
112114}
113115
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 )
117141}
118142
119143// 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
310334}
311335
312336func 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+ }
316344 }
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+ }
320350 }
321351 return XRefActionNone , nil
322352}
0 commit comments