mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-11-29 03:38:52 +03:00
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 <xiaolunwen@gmail.com>
This commit is contained in:
parent
3ff5a6a365
commit
d0d257b243
3 changed files with 101 additions and 0 deletions
|
@ -164,6 +164,7 @@ var defaultProcessors = []processor{
|
||||||
linkProcessor,
|
linkProcessor,
|
||||||
mentionProcessor,
|
mentionProcessor,
|
||||||
issueIndexPatternProcessor,
|
issueIndexPatternProcessor,
|
||||||
|
commitCrossReferencePatternProcessor,
|
||||||
sha1CurrentPatternProcessor,
|
sha1CurrentPatternProcessor,
|
||||||
emailAddressProcessor,
|
emailAddressProcessor,
|
||||||
emojiProcessor,
|
emojiProcessor,
|
||||||
|
@ -190,6 +191,7 @@ var commitMessageProcessors = []processor{
|
||||||
linkProcessor,
|
linkProcessor,
|
||||||
mentionProcessor,
|
mentionProcessor,
|
||||||
issueIndexPatternProcessor,
|
issueIndexPatternProcessor,
|
||||||
|
commitCrossReferencePatternProcessor,
|
||||||
sha1CurrentPatternProcessor,
|
sha1CurrentPatternProcessor,
|
||||||
emailAddressProcessor,
|
emailAddressProcessor,
|
||||||
emojiProcessor,
|
emojiProcessor,
|
||||||
|
@ -221,6 +223,7 @@ var commitMessageSubjectProcessors = []processor{
|
||||||
linkProcessor,
|
linkProcessor,
|
||||||
mentionProcessor,
|
mentionProcessor,
|
||||||
issueIndexPatternProcessor,
|
issueIndexPatternProcessor,
|
||||||
|
commitCrossReferencePatternProcessor,
|
||||||
sha1CurrentPatternProcessor,
|
sha1CurrentPatternProcessor,
|
||||||
emojiShortCodeProcessor,
|
emojiShortCodeProcessor,
|
||||||
emojiProcessor,
|
emojiProcessor,
|
||||||
|
@ -257,6 +260,7 @@ func RenderIssueTitle(
|
||||||
) (string, error) {
|
) (string, error) {
|
||||||
return renderProcessString(ctx, []processor{
|
return renderProcessString(ctx, []processor{
|
||||||
issueIndexPatternProcessor,
|
issueIndexPatternProcessor,
|
||||||
|
commitCrossReferencePatternProcessor,
|
||||||
sha1CurrentPatternProcessor,
|
sha1CurrentPatternProcessor,
|
||||||
emojiShortCodeProcessor,
|
emojiShortCodeProcessor,
|
||||||
emojiProcessor,
|
emojiProcessor,
|
||||||
|
@ -907,6 +911,23 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func commitCrossReferencePatternProcessor(ctx *RenderContext, node *html.Node) {
|
||||||
|
next := node.NextSibling
|
||||||
|
|
||||||
|
for node != nil && node != next {
|
||||||
|
found, ref := references.FindRenderizableCommitCrossReference(node.Data)
|
||||||
|
if !found {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
reftext := ref.Owner + "/" + ref.Name + "@" + base.ShortSha(ref.CommitSha)
|
||||||
|
link := createLink(util.URLJoin(setting.AppSubURL, ref.Owner, ref.Name, "commit", ref.CommitSha), reftext, "commit")
|
||||||
|
|
||||||
|
replaceContent(node, ref.RefLocation.Start, ref.RefLocation.End, link)
|
||||||
|
node = node.NextSibling.NextSibling
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// fullSha1PatternProcessor renders SHA containing URLs
|
// fullSha1PatternProcessor renders SHA containing URLs
|
||||||
func fullSha1PatternProcessor(ctx *RenderContext, node *html.Node) {
|
func fullSha1PatternProcessor(ctx *RenderContext, node *html.Node) {
|
||||||
if ctx.Metas == nil {
|
if ctx.Metas == nil {
|
||||||
|
|
|
@ -37,6 +37,9 @@ var (
|
||||||
// crossReferenceIssueNumericPattern matches string that references a numeric issue in a different repository
|
// crossReferenceIssueNumericPattern matches string that references a numeric issue in a different repository
|
||||||
// e.g. gogits/gogs#12345
|
// e.g. gogits/gogs#12345
|
||||||
crossReferenceIssueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([0-9a-zA-Z-_\.]+/[0-9a-zA-Z-_\.]+[#!][0-9]+)(?:\s|$|\)|\]|[:;,.?!]\s|[:;,.?!]$)`)
|
crossReferenceIssueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([0-9a-zA-Z-_\.]+/[0-9a-zA-Z-_\.]+[#!][0-9]+)(?:\s|$|\)|\]|[:;,.?!]\s|[:;,.?!]$)`)
|
||||||
|
// crossReferenceCommitPattern matches a string that references a commit in a different repository
|
||||||
|
// e.g. go-gitea/gitea@d8a994ef, go-gitea/gitea@d8a994ef243349f321568f9e36d5c3f444b99cae (7-40 characters)
|
||||||
|
crossReferenceCommitPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([0-9a-zA-Z-_\.]+)/([0-9a-zA-Z-_\.]+)@([0-9a-f]{7,40})(?:\s|$|\)|\]|[:;,.?!]\s|[:;,.?!]$)`)
|
||||||
// spaceTrimmedPattern let's us find the trailing space
|
// spaceTrimmedPattern let's us find the trailing space
|
||||||
spaceTrimmedPattern = regexp.MustCompile(`(?:.*[0-9a-zA-Z-_])\s`)
|
spaceTrimmedPattern = regexp.MustCompile(`(?:.*[0-9a-zA-Z-_])\s`)
|
||||||
// timeLogPattern matches string for time tracking
|
// timeLogPattern matches string for time tracking
|
||||||
|
@ -92,6 +95,7 @@ type RenderizableReference struct {
|
||||||
Issue string
|
Issue string
|
||||||
Owner string
|
Owner string
|
||||||
Name string
|
Name string
|
||||||
|
CommitSha string
|
||||||
IsPull bool
|
IsPull bool
|
||||||
RefLocation *RefSpan
|
RefLocation *RefSpan
|
||||||
Action XRefAction
|
Action XRefAction
|
||||||
|
@ -350,6 +354,21 @@ func FindRenderizableReferenceNumeric(content string, prOnly bool) (bool, *Rende
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FindRenderizableCommitCrossReference returns the first unvalidated commit cross reference found in a string.
|
||||||
|
func FindRenderizableCommitCrossReference(content string) (bool, *RenderizableReference) {
|
||||||
|
m := crossReferenceCommitPattern.FindStringSubmatchIndex(content)
|
||||||
|
if len(m) < 8 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, &RenderizableReference{
|
||||||
|
Owner: content[m[2]:m[3]],
|
||||||
|
Name: content[m[4]:m[5]],
|
||||||
|
CommitSha: content[m[6]:m[7]],
|
||||||
|
RefLocation: &RefSpan{Start: m[0], End: m[1]},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// FindRenderizableReferenceRegexp returns the first regexp unvalidated references found in a string.
|
// FindRenderizableReferenceRegexp returns the first regexp unvalidated references found in a string.
|
||||||
func FindRenderizableReferenceRegexp(content string, pattern *regexp.Regexp) (bool, *RenderizableReference) {
|
func FindRenderizableReferenceRegexp(content string, pattern *regexp.Regexp) (bool, *RenderizableReference) {
|
||||||
match := pattern.FindStringSubmatchIndex(content)
|
match := pattern.FindStringSubmatchIndex(content)
|
||||||
|
|
|
@ -303,6 +303,67 @@ func TestFindAllMentions(t *testing.T) {
|
||||||
}, res)
|
}, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFindRenderizableCommitCrossReference(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
Input string
|
||||||
|
Expected *RenderizableReference
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Input: "",
|
||||||
|
Expected: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Input: "test",
|
||||||
|
Expected: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Input: "go-gitea/gitea@test",
|
||||||
|
Expected: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Input: "go-gitea/gitea@ab1234",
|
||||||
|
Expected: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Input: "go-gitea/gitea@abcd1234",
|
||||||
|
Expected: &RenderizableReference{
|
||||||
|
Owner: "go-gitea",
|
||||||
|
Name: "gitea",
|
||||||
|
CommitSha: "abcd1234",
|
||||||
|
RefLocation: &RefSpan{Start: 0, End: 23},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Input: "go-gitea/gitea@abcd1234abcd1234abcd1234abcd1234abcd1234",
|
||||||
|
Expected: &RenderizableReference{
|
||||||
|
Owner: "go-gitea",
|
||||||
|
Name: "gitea",
|
||||||
|
CommitSha: "abcd1234abcd1234abcd1234abcd1234abcd1234",
|
||||||
|
RefLocation: &RefSpan{Start: 0, End: 55},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Input: "go-gitea/gitea@abcd1234abcd1234abcd1234abcd1234abcd12340", // longer than 40 characters
|
||||||
|
Expected: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Input: "test go-gitea/gitea@abcd1234 test",
|
||||||
|
Expected: &RenderizableReference{
|
||||||
|
Owner: "go-gitea",
|
||||||
|
Name: "gitea",
|
||||||
|
CommitSha: "abcd1234",
|
||||||
|
RefLocation: &RefSpan{Start: 4, End: 29},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
found, ref := FindRenderizableCommitCrossReference(c.Input)
|
||||||
|
assert.Equal(t, ref != nil, found)
|
||||||
|
assert.Equal(t, c.Expected, ref)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRegExp_mentionPattern(t *testing.T) {
|
func TestRegExp_mentionPattern(t *testing.T) {
|
||||||
trueTestCases := []struct {
|
trueTestCases := []struct {
|
||||||
pat string
|
pat string
|
||||||
|
|
Loading…
Reference in a new issue