forked from gitea/gitea
		
	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
					
				| @ -164,6 +164,7 @@ var defaultProcessors = []processor{ | ||||
| 	linkProcessor, | ||||
| 	mentionProcessor, | ||||
| 	issueIndexPatternProcessor, | ||||
| 	commitCrossReferencePatternProcessor, | ||||
| 	sha1CurrentPatternProcessor, | ||||
| 	emailAddressProcessor, | ||||
| 	emojiProcessor, | ||||
| @ -190,6 +191,7 @@ var commitMessageProcessors = []processor{ | ||||
| 	linkProcessor, | ||||
| 	mentionProcessor, | ||||
| 	issueIndexPatternProcessor, | ||||
| 	commitCrossReferencePatternProcessor, | ||||
| 	sha1CurrentPatternProcessor, | ||||
| 	emailAddressProcessor, | ||||
| 	emojiProcessor, | ||||
| @ -221,6 +223,7 @@ var commitMessageSubjectProcessors = []processor{ | ||||
| 	linkProcessor, | ||||
| 	mentionProcessor, | ||||
| 	issueIndexPatternProcessor, | ||||
| 	commitCrossReferencePatternProcessor, | ||||
| 	sha1CurrentPatternProcessor, | ||||
| 	emojiShortCodeProcessor, | ||||
| 	emojiProcessor, | ||||
| @ -257,6 +260,7 @@ func RenderIssueTitle( | ||||
| ) (string, error) { | ||||
| 	return renderProcessString(ctx, []processor{ | ||||
| 		issueIndexPatternProcessor, | ||||
| 		commitCrossReferencePatternProcessor, | ||||
| 		sha1CurrentPatternProcessor, | ||||
| 		emojiShortCodeProcessor, | ||||
| 		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 | ||||
| func fullSha1PatternProcessor(ctx *RenderContext, node *html.Node) { | ||||
| 	if ctx.Metas == nil { | ||||
|  | ||||
| @ -37,6 +37,9 @@ var ( | ||||
| 	// crossReferenceIssueNumericPattern matches string that references a numeric issue in a different repository | ||||
| 	// e.g. gogits/gogs#12345 | ||||
| 	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 = regexp.MustCompile(`(?:.*[0-9a-zA-Z-_])\s`) | ||||
| 	// timeLogPattern matches string for time tracking | ||||
| @ -92,6 +95,7 @@ type RenderizableReference struct { | ||||
| 	Issue          string | ||||
| 	Owner          string | ||||
| 	Name           string | ||||
| 	CommitSha      string | ||||
| 	IsPull         bool | ||||
| 	RefLocation    *RefSpan | ||||
| 	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. | ||||
| func FindRenderizableReferenceRegexp(content string, pattern *regexp.Regexp) (bool, *RenderizableReference) { | ||||
| 	match := pattern.FindStringSubmatchIndex(content) | ||||
|  | ||||
| @ -303,6 +303,67 @@ func TestFindAllMentions(t *testing.T) { | ||||
| 	}, 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) { | ||||
| 	trueTestCases := []struct { | ||||
| 		pat string | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 KN4CK3R
						KN4CK3R