mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-29 10:57:44 +09:00 
			
		
		
		
	Add sub issue list support (#32940)
Just like GitHub, show issue icon/title when the issue number is in a list
This commit is contained in:
		| @@ -4,9 +4,9 @@ | ||||
| package markup | ||||
|  | ||||
| import ( | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
|  | ||||
| 	"code.gitea.io/gitea/modules/base" | ||||
| 	"code.gitea.io/gitea/modules/httplib" | ||||
| 	"code.gitea.io/gitea/modules/log" | ||||
| 	"code.gitea.io/gitea/modules/references" | ||||
| @@ -16,8 +16,16 @@ import ( | ||||
| 	"code.gitea.io/gitea/modules/util" | ||||
|  | ||||
| 	"golang.org/x/net/html" | ||||
| 	"golang.org/x/net/html/atom" | ||||
| ) | ||||
|  | ||||
| type RenderIssueIconTitleOptions struct { | ||||
| 	OwnerName  string | ||||
| 	RepoName   string | ||||
| 	LinkHref   string | ||||
| 	IssueIndex int64 | ||||
| } | ||||
|  | ||||
| func fullIssuePatternProcessor(ctx *RenderContext, node *html.Node) { | ||||
| 	if ctx.RenderOptions.Metas == nil { | ||||
| 		return | ||||
| @@ -66,6 +74,27 @@ func fullIssuePatternProcessor(ctx *RenderContext, node *html.Node) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func createIssueLinkContentWithSummary(ctx *RenderContext, linkHref string, ref *references.RenderizableReference) *html.Node { | ||||
| 	if DefaultRenderHelperFuncs.RenderRepoIssueIconTitle == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	issueIndex, _ := strconv.ParseInt(ref.Issue, 10, 64) | ||||
| 	h, err := DefaultRenderHelperFuncs.RenderRepoIssueIconTitle(ctx, RenderIssueIconTitleOptions{ | ||||
| 		OwnerName:  ref.Owner, | ||||
| 		RepoName:   ref.Name, | ||||
| 		LinkHref:   linkHref, | ||||
| 		IssueIndex: issueIndex, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		log.Error("RenderRepoIssueIconTitle failed: %v", err) | ||||
| 		return nil | ||||
| 	} | ||||
| 	if h == "" { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return &html.Node{Type: html.RawNode, Data: string(ctx.RenderInternal.ProtectSafeAttrs(h))} | ||||
| } | ||||
|  | ||||
| func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) { | ||||
| 	if ctx.RenderOptions.Metas == nil { | ||||
| 		return | ||||
| @@ -76,32 +105,28 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) { | ||||
| 	// old logic: crossLinkOnly := ctx.RenderOptions.Metas["mode"] == "document" && !ctx.IsWiki | ||||
| 	crossLinkOnly := ctx.RenderOptions.Metas["markupAllowShortIssuePattern"] != "true" | ||||
|  | ||||
| 	var ( | ||||
| 		found bool | ||||
| 		ref   *references.RenderizableReference | ||||
| 	) | ||||
| 	var ref *references.RenderizableReference | ||||
|  | ||||
| 	next := node.NextSibling | ||||
|  | ||||
| 	for node != nil && node != next { | ||||
| 		_, hasExtTrackFormat := ctx.RenderOptions.Metas["format"] | ||||
|  | ||||
| 		// Repos with external issue trackers might still need to reference local PRs | ||||
| 		// We need to concern with the first one that shows up in the text, whichever it is | ||||
| 		isNumericStyle := ctx.RenderOptions.Metas["style"] == "" || ctx.RenderOptions.Metas["style"] == IssueNameStyleNumeric | ||||
| 		foundNumeric, refNumeric := references.FindRenderizableReferenceNumeric(node.Data, hasExtTrackFormat && !isNumericStyle, crossLinkOnly) | ||||
| 		refNumeric := references.FindRenderizableReferenceNumeric(node.Data, hasExtTrackFormat && !isNumericStyle, crossLinkOnly) | ||||
|  | ||||
| 		switch ctx.RenderOptions.Metas["style"] { | ||||
| 		case "", IssueNameStyleNumeric: | ||||
| 			found, ref = foundNumeric, refNumeric | ||||
| 			ref = refNumeric | ||||
| 		case IssueNameStyleAlphanumeric: | ||||
| 			found, ref = references.FindRenderizableReferenceAlphanumeric(node.Data) | ||||
| 			ref = references.FindRenderizableReferenceAlphanumeric(node.Data) | ||||
| 		case IssueNameStyleRegexp: | ||||
| 			pattern, err := regexplru.GetCompiled(ctx.RenderOptions.Metas["regexp"]) | ||||
| 			if err != nil { | ||||
| 				return | ||||
| 			} | ||||
| 			found, ref = references.FindRenderizableReferenceRegexp(node.Data, pattern) | ||||
| 			ref = references.FindRenderizableReferenceRegexp(node.Data, pattern) | ||||
| 		} | ||||
|  | ||||
| 		// Repos with external issue trackers might still need to reference local PRs | ||||
| @@ -109,17 +134,17 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) { | ||||
| 		if hasExtTrackFormat && !isNumericStyle && refNumeric != nil { | ||||
| 			// If numeric (PR) was found, and it was BEFORE the non-numeric pattern, use that | ||||
| 			// Allow a free-pass when non-numeric pattern wasn't found. | ||||
| 			if found && (ref == nil || refNumeric.RefLocation.Start < ref.RefLocation.Start) { | ||||
| 				found = foundNumeric | ||||
| 			if ref == nil || refNumeric.RefLocation.Start < ref.RefLocation.Start { | ||||
| 				ref = refNumeric | ||||
| 			} | ||||
| 		} | ||||
| 		if !found { | ||||
|  | ||||
| 		if ref == nil { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		var link *html.Node | ||||
| 		reftext := node.Data[ref.RefLocation.Start:ref.RefLocation.End] | ||||
| 		refText := node.Data[ref.RefLocation.Start:ref.RefLocation.End] | ||||
| 		if hasExtTrackFormat && !ref.IsPull { | ||||
| 			ctx.RenderOptions.Metas["index"] = ref.Issue | ||||
|  | ||||
| @@ -129,18 +154,23 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) { | ||||
| 				log.Error("unable to expand template vars for ref %s, err: %v", ref.Issue, err) | ||||
| 			} | ||||
|  | ||||
| 			link = createLink(ctx, res, reftext, "ref-issue ref-external-issue") | ||||
| 			link = createLink(ctx, res, refText, "ref-issue ref-external-issue") | ||||
| 		} else { | ||||
| 			// Path determines the type of link that will be rendered. It's unknown at this point whether | ||||
| 			// the linked item is actually a PR or an issue. Luckily it's of no real consequence because | ||||
| 			// Gitea will redirect on click as appropriate. | ||||
| 			issueOwner := util.Iif(ref.Owner == "", ctx.RenderOptions.Metas["user"], ref.Owner) | ||||
| 			issueRepo := util.Iif(ref.Owner == "", ctx.RenderOptions.Metas["repo"], ref.Name) | ||||
| 			issuePath := util.Iif(ref.IsPull, "pulls", "issues") | ||||
| 			if ref.Owner == "" { | ||||
| 				linkHref := ctx.RenderHelper.ResolveLink(util.URLJoin(ctx.RenderOptions.Metas["user"], ctx.RenderOptions.Metas["repo"], issuePath, ref.Issue), LinkTypeApp) | ||||
| 				link = createLink(ctx, linkHref, reftext, "ref-issue") | ||||
| 			} else { | ||||
| 				linkHref := ctx.RenderHelper.ResolveLink(util.URLJoin(ref.Owner, ref.Name, issuePath, ref.Issue), LinkTypeApp) | ||||
| 				link = createLink(ctx, linkHref, reftext, "ref-issue") | ||||
| 			linkHref := ctx.RenderHelper.ResolveLink(util.URLJoin(issueOwner, issueRepo, issuePath, ref.Issue), LinkTypeApp) | ||||
|  | ||||
| 			// at the moment, only render the issue index in a full line (or simple line) as icon+title | ||||
| 			// otherwise it would be too noisy for "take #1 as an example" in a sentence | ||||
| 			if node.Parent.DataAtom == atom.Li && ref.RefLocation.Start < 20 && ref.RefLocation.End == len(node.Data) { | ||||
| 				link = createIssueLinkContentWithSummary(ctx, linkHref, ref) | ||||
| 			} | ||||
| 			if link == nil { | ||||
| 				link = createLink(ctx, linkHref, refText, "ref-issue") | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| @@ -168,21 +198,3 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) { | ||||
| 		node = node.NextSibling.NextSibling.NextSibling.NextSibling | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 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) | ||||
| 		linkHref := ctx.RenderHelper.ResolveLink(util.URLJoin(ref.Owner, ref.Name, "commit", ref.CommitSha), LinkTypeApp) | ||||
| 		link := createLink(ctx, linkHref, reftext, "commit") | ||||
|  | ||||
| 		replaceContent(node, ref.RefLocation.Start, ref.RefLocation.End, link) | ||||
| 		node = node.NextSibling.NextSibling | ||||
| 	} | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user