diff --git a/modules/references/references.go b/modules/references/references.go index d3d451269fd..0d470d479ae 100644 --- a/modules/references/references.go +++ b/modules/references/references.go @@ -7,9 +7,11 @@ import ( "bytes" "net/url" "regexp" + "slices" "strconv" "strings" "sync" + "unicode/utf8" "gitea.dev/modules/log" "gitea.dev/modules/markup/mdstripper" @@ -45,6 +47,7 @@ var ( timeLogPattern = regexp.MustCompile(`(?:\s|^|\(|\[)(@([0-9]+([\.,][0-9]+)?(w|d|m|h))+)(?:\s|$|\)|\]|[:;,.?!]\s|[:;,.?!]$)`) issueCloseKeywordsPat, issueReopenKeywordsPat *regexp.Regexp + issueKeywordWindow int issueKeywordsOnce sync.Once giteaHostInit sync.Once @@ -167,6 +170,10 @@ func newKeywords() { func doNewKeywords(closeKeywords, reopenKeywords []string) { issueCloseKeywordsPat = makeKeywordsPat(closeKeywords) issueReopenKeywordsPat = makeKeywordsPat(reopenKeywords) + issueKeywordWindow = 0 + for _, word := range slices.Concat(closeKeywords, reopenKeywords) { + issueKeywordWindow = max(issueKeywordWindow, utf8.RuneCountInString(word)*utf8.UTFMax+3) // delimiter, keyword with (?i)-folded runes like ſ for s, ": " + } } // getGiteaHostName returns a normalized string with the local host name, with no scheme or port information @@ -599,17 +606,16 @@ func getCrossReference(content []byte, start, end int, fromLink, prOnly bool) *r func findActionKeywords(content []byte, start int) (XRefAction, *RefSpan) { newKeywords() - var m []int + windowStart := max(start-issueKeywordWindow, 0) + prefix := content[windowStart:start] if issueCloseKeywordsPat != nil { - m = issueCloseKeywordsPat.FindSubmatchIndex(content[:start]) - if m != nil { - return XRefActionCloses, &RefSpan{Start: m[2], End: m[3]} + if m := issueCloseKeywordsPat.FindSubmatchIndex(prefix); m != nil { + return XRefActionCloses, &RefSpan{Start: windowStart + m[2], End: windowStart + m[3]} } } if issueReopenKeywordsPat != nil { - m = issueReopenKeywordsPat.FindSubmatchIndex(content[:start]) - if m != nil { - return XRefActionReopens, &RefSpan{Start: m[2], End: m[3]} + if m := issueReopenKeywordsPat.FindSubmatchIndex(prefix); m != nil { + return XRefActionReopens, &RefSpan{Start: windowStart + m[2], End: windowStart + m[3]} } } return XRefActionNone, nil diff --git a/modules/references/references_test.go b/modules/references/references_test.go index 222b93d4092..53cd98a931c 100644 --- a/modules/references/references_test.go +++ b/modules/references/references_test.go @@ -162,6 +162,13 @@ func TestFindAllIssueReferences(t *testing.T) { {1235, "", "", "1235", false, XRefActionNone, &RefSpan{Start: 8, End: 13}, nil, ""}, }, }, + { + "After a long investigation into the root cause, this resolves #1, unlike the still unresolved #2", + []testResult{ + {1, "", "", "1", false, XRefActionCloses, &RefSpan{Start: 62, End: 64}, &RefSpan{Start: 53, End: 61}, ""}, + {2, "", "", "2", false, XRefActionNone, &RefSpan{Start: 94, End: 96}, nil, ""}, + }, + }, { "For [!123] yes", []testResult{