summaryrefslogtreecommitdiffstats
path: root/modules/markup
diff options
context:
space:
mode:
authorSandro Santilli <strk@kbt.io>2022-06-10 07:39:53 +0200
committerGitHub <noreply@github.com>2022-06-10 13:39:53 +0800
commit52c2e82813c2519cd08c9ecd66ef7b0815682770 (patch)
tree1120ca6d9b6534e33554f0b1c74529e62feba5b2 /modules/markup
parent5f618248a98ffbb7066100ffe1955faf791d0205 (diff)
downloadgitea-52c2e82813c2519cd08c9ecd66ef7b0815682770.tar.gz
gitea-52c2e82813c2519cd08c9ecd66ef7b0815682770.zip
Custom regexp external issues (#17624)
* Implement custom regular expression for external issue tracking. Signed-off-by: Alexander Beyn <malex@fatelectrons.org> * Fix syntax/style * Update repo.go * Set metas['regexp'] * gofmt * fix some tests * fix more tests * refactor frontend * use LRU cache for regexp * Update modules/markup/html_internal_test.go Co-authored-by: Alexander Beyn <malex@fatelectrons.org> Co-authored-by: techknowlogick <techknowlogick@gitea.io> Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Diffstat (limited to 'modules/markup')
-rw-r--r--modules/markup/html.go40
-rw-r--r--modules/markup/html_internal_test.go52
2 files changed, 77 insertions, 15 deletions
diff --git a/modules/markup/html.go b/modules/markup/html.go
index c5d36e701f..69d9ba3ef2 100644
--- a/modules/markup/html.go
+++ b/modules/markup/html.go
@@ -20,6 +20,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup/common"
"code.gitea.io/gitea/modules/references"
+ "code.gitea.io/gitea/modules/regexplru"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates/vars"
"code.gitea.io/gitea/modules/util"
@@ -33,6 +34,7 @@ import (
const (
IssueNameStyleNumeric = "numeric"
IssueNameStyleAlphanumeric = "alphanumeric"
+ IssueNameStyleRegexp = "regexp"
)
var (
@@ -815,19 +817,35 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
)
next := node.NextSibling
+
for node != nil && node != next {
- _, exttrack := ctx.Metas["format"]
- alphanum := ctx.Metas["style"] == IssueNameStyleAlphanumeric
+ _, hasExtTrackFormat := ctx.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
- found, ref = references.FindRenderizableReferenceNumeric(node.Data, exttrack && alphanum)
- if exttrack && alphanum {
- if found2, ref2 := references.FindRenderizableReferenceAlphanumeric(node.Data); found2 {
- if !found || ref2.RefLocation.Start < ref.RefLocation.Start {
- found = true
- ref = ref2
- }
+ isNumericStyle := ctx.Metas["style"] == "" || ctx.Metas["style"] == IssueNameStyleNumeric
+ foundNumeric, refNumeric := references.FindRenderizableReferenceNumeric(node.Data, hasExtTrackFormat && !isNumericStyle)
+
+ switch ctx.Metas["style"] {
+ case "", IssueNameStyleNumeric:
+ found, ref = foundNumeric, refNumeric
+ case IssueNameStyleAlphanumeric:
+ found, ref = references.FindRenderizableReferenceAlphanumeric(node.Data)
+ case IssueNameStyleRegexp:
+ pattern, err := regexplru.GetCompiled(ctx.Metas["regexp"])
+ if err != nil {
+ return
+ }
+ found, ref = references.FindRenderizableReferenceRegexp(node.Data, pattern)
+ }
+
+ // 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
+ if hasExtTrackFormat && !isNumericStyle {
+ // If numeric (PR) was found, and it was BEFORE the non-numeric pattern, use that
+ if foundNumeric && refNumeric.RefLocation.Start < ref.RefLocation.Start {
+ found = foundNumeric
+ ref = refNumeric
}
}
if !found {
@@ -836,7 +854,7 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
var link *html.Node
reftext := node.Data[ref.RefLocation.Start:ref.RefLocation.End]
- if exttrack && !ref.IsPull {
+ if hasExtTrackFormat && !ref.IsPull {
ctx.Metas["index"] = ref.Issue
res, err := vars.Expand(ctx.Metas["format"], ctx.Metas)
@@ -869,7 +887,7 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
// Decorate action keywords if actionable
var keyword *html.Node
- if references.IsXrefActionable(ref, exttrack, alphanum) {
+ if references.IsXrefActionable(ref, hasExtTrackFormat) {
keyword = createKeyword(node.Data[ref.ActionLocation.Start:ref.ActionLocation.End])
} else {
keyword = &html.Node{
diff --git a/modules/markup/html_internal_test.go b/modules/markup/html_internal_test.go
index f0eb3253e1..25b0f7b7a5 100644
--- a/modules/markup/html_internal_test.go
+++ b/modules/markup/html_internal_test.go
@@ -21,8 +21,8 @@ const (
TestRepoURL = TestAppURL + TestOrgRepo + "/"
)
-// alphanumLink an HTML link to an alphanumeric-style issue
-func alphanumIssueLink(baseURL, class, name string) string {
+// externalIssueLink an HTML link to an alphanumeric-style issue
+func externalIssueLink(baseURL, class, name string) string {
return link(util.URLJoin(baseURL, name), class, name)
}
@@ -54,6 +54,13 @@ var alphanumericMetas = map[string]string{
"style": IssueNameStyleAlphanumeric,
}
+var regexpMetas = map[string]string{
+ "format": "https://someurl.com/{user}/{repo}/{index}",
+ "user": "someUser",
+ "repo": "someRepo",
+ "style": IssueNameStyleRegexp,
+}
+
// these values should match the TestOrgRepo const above
var localMetas = map[string]string{
"user": "gogits",
@@ -184,7 +191,7 @@ func TestRender_IssueIndexPattern4(t *testing.T) {
test := func(s, expectedFmt string, names ...string) {
links := make([]interface{}, len(names))
for i, name := range names {
- links[i] = alphanumIssueLink("https://someurl.com/someUser/someRepo/", "ref-issue ref-external-issue", name)
+ links[i] = externalIssueLink("https://someurl.com/someUser/someRepo/", "ref-issue ref-external-issue", name)
}
expected := fmt.Sprintf(expectedFmt, links...)
testRenderIssueIndexPattern(t, s, expected, &RenderContext{Metas: alphanumericMetas})
@@ -194,6 +201,43 @@ func TestRender_IssueIndexPattern4(t *testing.T) {
test("test issue ABCDEFGHIJ-1234567890", "test issue %s", "ABCDEFGHIJ-1234567890")
}
+func TestRender_IssueIndexPattern5(t *testing.T) {
+ setting.AppURL = TestAppURL
+
+ // regexp: render inputs without valid mentions
+ test := func(s, expectedFmt, pattern string, ids, names []string) {
+ metas := regexpMetas
+ metas["regexp"] = pattern
+ links := make([]interface{}, len(ids))
+ for i, id := range ids {
+ links[i] = link(util.URLJoin("https://someurl.com/someUser/someRepo/", id), "ref-issue ref-external-issue", names[i])
+ }
+
+ expected := fmt.Sprintf(expectedFmt, links...)
+ testRenderIssueIndexPattern(t, s, expected, &RenderContext{Metas: metas})
+ }
+
+ test("abc ISSUE-123 def", "abc %s def",
+ "ISSUE-(\\d+)",
+ []string{"123"},
+ []string{"ISSUE-123"},
+ )
+
+ test("abc (ISSUE 123) def", "abc %s def",
+ "\\(ISSUE (\\d+)\\)",
+ []string{"123"},
+ []string{"(ISSUE 123)"},
+ )
+
+ test("abc ISSUE-123 def", "abc %s def",
+ "(ISSUE-(\\d+))",
+ []string{"ISSUE-123"},
+ []string{"ISSUE-123"},
+ )
+
+ testRenderIssueIndexPattern(t, "will not match", "will not match", &RenderContext{Metas: regexpMetas})
+}
+
func testRenderIssueIndexPattern(t *testing.T, input, expected string, ctx *RenderContext) {
if ctx.URLPrefix == "" {
ctx.URLPrefix = TestAppURL
@@ -202,7 +246,7 @@ func testRenderIssueIndexPattern(t *testing.T, input, expected string, ctx *Rend
var buf strings.Builder
err := postProcess(ctx, []processor{issueIndexPatternProcessor}, strings.NewReader(input), &buf)
assert.NoError(t, err)
- assert.Equal(t, expected, buf.String())
+ assert.Equal(t, expected, buf.String(), "input=%q", input)
}
func TestRender_AutoLink(t *testing.T) {