Skip to content

Commit d0d257b

Browse files
KN4CK3Rlunny
andauthored
Add support for commit cross references (#22645)
Fixes #22628 This PR adds cross references for commits by using the format `owner/repo@commit` . References are rendered like [go-gitea/lgtm@6fe88302](#dummy). --------- Co-authored-by: Lunny Xiao <[email protected]>
1 parent 3ff5a6a commit d0d257b

File tree

3 files changed

+101
-0
lines changed

3 files changed

+101
-0
lines changed

modules/markup/html.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ var defaultProcessors = []processor{
164164
linkProcessor,
165165
mentionProcessor,
166166
issueIndexPatternProcessor,
167+
commitCrossReferencePatternProcessor,
167168
sha1CurrentPatternProcessor,
168169
emailAddressProcessor,
169170
emojiProcessor,
@@ -190,6 +191,7 @@ var commitMessageProcessors = []processor{
190191
linkProcessor,
191192
mentionProcessor,
192193
issueIndexPatternProcessor,
194+
commitCrossReferencePatternProcessor,
193195
sha1CurrentPatternProcessor,
194196
emailAddressProcessor,
195197
emojiProcessor,
@@ -221,6 +223,7 @@ var commitMessageSubjectProcessors = []processor{
221223
linkProcessor,
222224
mentionProcessor,
223225
issueIndexPatternProcessor,
226+
commitCrossReferencePatternProcessor,
224227
sha1CurrentPatternProcessor,
225228
emojiShortCodeProcessor,
226229
emojiProcessor,
@@ -257,6 +260,7 @@ func RenderIssueTitle(
257260
) (string, error) {
258261
return renderProcessString(ctx, []processor{
259262
issueIndexPatternProcessor,
263+
commitCrossReferencePatternProcessor,
260264
sha1CurrentPatternProcessor,
261265
emojiShortCodeProcessor,
262266
emojiProcessor,
@@ -907,6 +911,23 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
907911
}
908912
}
909913

914+
func commitCrossReferencePatternProcessor(ctx *RenderContext, node *html.Node) {
915+
next := node.NextSibling
916+
917+
for node != nil && node != next {
918+
found, ref := references.FindRenderizableCommitCrossReference(node.Data)
919+
if !found {
920+
return
921+
}
922+
923+
reftext := ref.Owner + "/" + ref.Name + "@" + base.ShortSha(ref.CommitSha)
924+
link := createLink(util.URLJoin(setting.AppSubURL, ref.Owner, ref.Name, "commit", ref.CommitSha), reftext, "commit")
925+
926+
replaceContent(node, ref.RefLocation.Start, ref.RefLocation.End, link)
927+
node = node.NextSibling.NextSibling
928+
}
929+
}
930+
910931
// fullSha1PatternProcessor renders SHA containing URLs
911932
func fullSha1PatternProcessor(ctx *RenderContext, node *html.Node) {
912933
if ctx.Metas == nil {

modules/references/references.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ var (
3737
// crossReferenceIssueNumericPattern matches string that references a numeric issue in a different repository
3838
// e.g. gogits/gogs#12345
3939
crossReferenceIssueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([0-9a-zA-Z-_\.]+/[0-9a-zA-Z-_\.]+[#!][0-9]+)(?:\s|$|\)|\]|[:;,.?!]\s|[:;,.?!]$)`)
40+
// crossReferenceCommitPattern matches a string that references a commit in a different repository
41+
// e.g. go-gitea/gitea@d8a994ef, go-gitea/gitea@d8a994ef243349f321568f9e36d5c3f444b99cae (7-40 characters)
42+
crossReferenceCommitPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([0-9a-zA-Z-_\.]+)/([0-9a-zA-Z-_\.]+)@([0-9a-f]{7,40})(?:\s|$|\)|\]|[:;,.?!]\s|[:;,.?!]$)`)
4043
// spaceTrimmedPattern let's us find the trailing space
4144
spaceTrimmedPattern = regexp.MustCompile(`(?:.*[0-9a-zA-Z-_])\s`)
4245
// timeLogPattern matches string for time tracking
@@ -92,6 +95,7 @@ type RenderizableReference struct {
9295
Issue string
9396
Owner string
9497
Name string
98+
CommitSha string
9599
IsPull bool
96100
RefLocation *RefSpan
97101
Action XRefAction
@@ -350,6 +354,21 @@ func FindRenderizableReferenceNumeric(content string, prOnly bool) (bool, *Rende
350354
}
351355
}
352356

357+
// FindRenderizableCommitCrossReference returns the first unvalidated commit cross reference found in a string.
358+
func FindRenderizableCommitCrossReference(content string) (bool, *RenderizableReference) {
359+
m := crossReferenceCommitPattern.FindStringSubmatchIndex(content)
360+
if len(m) < 8 {
361+
return false, nil
362+
}
363+
364+
return true, &RenderizableReference{
365+
Owner: content[m[2]:m[3]],
366+
Name: content[m[4]:m[5]],
367+
CommitSha: content[m[6]:m[7]],
368+
RefLocation: &RefSpan{Start: m[0], End: m[1]},
369+
}
370+
}
371+
353372
// FindRenderizableReferenceRegexp returns the first regexp unvalidated references found in a string.
354373
func FindRenderizableReferenceRegexp(content string, pattern *regexp.Regexp) (bool, *RenderizableReference) {
355374
match := pattern.FindStringSubmatchIndex(content)

modules/references/references_test.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,67 @@ func TestFindAllMentions(t *testing.T) {
303303
}, res)
304304
}
305305

306+
func TestFindRenderizableCommitCrossReference(t *testing.T) {
307+
cases := []struct {
308+
Input string
309+
Expected *RenderizableReference
310+
}{
311+
{
312+
Input: "",
313+
Expected: nil,
314+
},
315+
{
316+
Input: "test",
317+
Expected: nil,
318+
},
319+
{
320+
Input: "go-gitea/gitea@test",
321+
Expected: nil,
322+
},
323+
{
324+
Input: "go-gitea/gitea@ab1234",
325+
Expected: nil,
326+
},
327+
{
328+
Input: "go-gitea/gitea@abcd1234",
329+
Expected: &RenderizableReference{
330+
Owner: "go-gitea",
331+
Name: "gitea",
332+
CommitSha: "abcd1234",
333+
RefLocation: &RefSpan{Start: 0, End: 23},
334+
},
335+
},
336+
{
337+
Input: "go-gitea/gitea@abcd1234abcd1234abcd1234abcd1234abcd1234",
338+
Expected: &RenderizableReference{
339+
Owner: "go-gitea",
340+
Name: "gitea",
341+
CommitSha: "abcd1234abcd1234abcd1234abcd1234abcd1234",
342+
RefLocation: &RefSpan{Start: 0, End: 55},
343+
},
344+
},
345+
{
346+
Input: "go-gitea/gitea@abcd1234abcd1234abcd1234abcd1234abcd12340", // longer than 40 characters
347+
Expected: nil,
348+
},
349+
{
350+
Input: "test go-gitea/gitea@abcd1234 test",
351+
Expected: &RenderizableReference{
352+
Owner: "go-gitea",
353+
Name: "gitea",
354+
CommitSha: "abcd1234",
355+
RefLocation: &RefSpan{Start: 4, End: 29},
356+
},
357+
},
358+
}
359+
360+
for _, c := range cases {
361+
found, ref := FindRenderizableCommitCrossReference(c.Input)
362+
assert.Equal(t, ref != nil, found)
363+
assert.Equal(t, c.Expected, ref)
364+
}
365+
}
366+
306367
func TestRegExp_mentionPattern(t *testing.T) {
307368
trueTestCases := []struct {
308369
pat string

0 commit comments

Comments
 (0)