@@ -29,12 +29,14 @@ var (
29
29
// mentionPattern matches all mentions in the form of "@user"
30
30
mentionPattern = regexp .MustCompile (`(?:\s|^|\(|\[)(@[0-9a-zA-Z-_]+|@[0-9a-zA-Z-_][0-9a-zA-Z-_.]+[0-9a-zA-Z-_])(?:\s|[:,;.?!]\s|[:,;.?!]?$|\)|\])` )
31
31
// issueNumericPattern matches string that references to a numeric issue, e.g. #1287
32
- issueNumericPattern = regexp .MustCompile (`(?:\s|^|\(|\[)([#!][0-9]+)(?:\s|$|\)|\]|:|\.( \s|$) )` )
32
+ issueNumericPattern = regexp .MustCompile (`(?:\s|^|\(|\[)([#!][0-9]+)(?:\s|$|\)|\]|[:;,.?!] \s|[:;,.?!]$ )` )
33
33
// issueAlphanumericPattern matches string that references to an alphanumeric issue, e.g. ABC-1234
34
34
issueAlphanumericPattern = regexp .MustCompile (`(?:\s|^|\(|\[)([A-Z]{1,10}-[1-9][0-9]*)(?:\s|$|\)|\]|:|\.(\s|$))` )
35
35
// crossReferenceIssueNumericPattern matches string that references a numeric issue in a different repository
36
36
// e.g. gogits/gogs#12345
37
- crossReferenceIssueNumericPattern = regexp .MustCompile (`(?:\s|^|\(|\[)([0-9a-zA-Z-_\.]+/[0-9a-zA-Z-_\.]+[#!][0-9]+)(?:\s|$|\)|\]|\.(\s|$))` )
37
+ crossReferenceIssueNumericPattern = regexp .MustCompile (`(?:\s|^|\(|\[)([0-9a-zA-Z-_\.]+/[0-9a-zA-Z-_\.]+[#!][0-9]+)(?:\s|$|\)|\]|[:;,.?!]\s|[:;,.?!]$)` )
38
+ // spaceTrimmedPattern let's us find the trailing space
39
+ spaceTrimmedPattern = regexp .MustCompile (`(?:.*[0-9a-zA-Z-_])\s` )
38
40
39
41
issueCloseKeywordsPat , issueReopenKeywordsPat * regexp.Regexp
40
42
issueKeywordsOnce sync.Once
@@ -172,10 +174,24 @@ func FindAllMentionsMarkdown(content string) []string {
172
174
// FindAllMentionsBytes matches mention patterns in given content
173
175
// and returns a list of locations for the unvalidated user names, including the @ prefix.
174
176
func FindAllMentionsBytes (content []byte ) []RefSpan {
175
- mentions := mentionPattern .FindAllSubmatchIndex (content , - 1 )
176
- ret := make ([]RefSpan , len (mentions ))
177
- for i , val := range mentions {
178
- ret [i ] = RefSpan {Start : val [2 ], End : val [3 ]}
177
+ // Sadly we can't use FindAllSubmatchIndex because our pattern checks for starting and
178
+ // trailing spaces (\s@mention,\s), so if we get two consecutive references, the space
179
+ // from the second reference will be "eaten" by the first one:
180
+ // ...\s@mention1\s@mention2\s... --> ...`\s@mention1\s`, (not) `@mention2,\s...`
181
+ ret := make ([]RefSpan , 0 , 5 )
182
+ pos := 0
183
+ for {
184
+ match := mentionPattern .FindSubmatchIndex (content [pos :])
185
+ if match == nil {
186
+ break
187
+ }
188
+ ret = append (ret , RefSpan {Start : match [2 ] + pos , End : match [3 ] + pos })
189
+ notrail := spaceTrimmedPattern .FindSubmatchIndex (content [match [2 ]+ pos : match [3 ]+ pos ])
190
+ if notrail == nil {
191
+ pos = match [3 ] + pos
192
+ } else {
193
+ pos = match [3 ] + pos + notrail [1 ] - notrail [3 ]
194
+ }
179
195
}
180
196
return ret
181
197
}
@@ -252,19 +268,44 @@ func FindRenderizableReferenceAlphanumeric(content string) (bool, *RenderizableR
252
268
func findAllIssueReferencesBytes (content []byte , links []string ) []* rawReference {
253
269
254
270
ret := make ([]* rawReference , 0 , 10 )
255
-
256
- matches := issueNumericPattern .FindAllSubmatchIndex (content , - 1 )
257
- for _ , match := range matches {
258
- if ref := getCrossReference (content , match [2 ], match [3 ], false , false ); ref != nil {
271
+ pos := 0
272
+
273
+ // Sadly we can't use FindAllSubmatchIndex because our pattern checks for starting and
274
+ // trailing spaces (\s#ref,\s), so if we get two consecutive references, the space
275
+ // from the second reference will be "eaten" by the first one:
276
+ // ...\s#ref1\s#ref2\s... --> ...`\s#ref1\s`, (not) `#ref2,\s...`
277
+ for {
278
+ match := issueNumericPattern .FindSubmatchIndex (content [pos :])
279
+ if match == nil {
280
+ break
281
+ }
282
+ if ref := getCrossReference (content , match [2 ]+ pos , match [3 ]+ pos , false , false ); ref != nil {
259
283
ret = append (ret , ref )
260
284
}
285
+ notrail := spaceTrimmedPattern .FindSubmatchIndex (content [match [2 ]+ pos : match [3 ]+ pos ])
286
+ if notrail == nil {
287
+ pos = match [3 ] + pos
288
+ } else {
289
+ pos = match [3 ] + pos + notrail [1 ] - notrail [3 ]
290
+ }
261
291
}
262
292
263
- matches = crossReferenceIssueNumericPattern .FindAllSubmatchIndex (content , - 1 )
264
- for _ , match := range matches {
265
- if ref := getCrossReference (content , match [2 ], match [3 ], false , false ); ref != nil {
293
+ pos = 0
294
+
295
+ for {
296
+ match := crossReferenceIssueNumericPattern .FindSubmatchIndex (content [pos :])
297
+ if match == nil {
298
+ break
299
+ }
300
+ if ref := getCrossReference (content , match [2 ]+ pos , match [3 ]+ pos , false , false ); ref != nil {
266
301
ret = append (ret , ref )
267
302
}
303
+ notrail := spaceTrimmedPattern .FindSubmatchIndex (content [match [2 ]+ pos : match [3 ]+ pos ])
304
+ if notrail == nil {
305
+ pos = match [3 ] + pos
306
+ } else {
307
+ pos = match [3 ] + pos + notrail [1 ] - notrail [3 ]
308
+ }
268
309
}
269
310
270
311
localhost := getGiteaHostName ()
0 commit comments