From b594188623f2afd0bc61eb69fb3cc26b5c47859d Mon Sep 17 00:00:00 2001
From: Gergely Nagy <forgejo@gergo.csillger.hu>
Date: Sat, 17 Feb 2024 17:36:36 +0100
Subject: [PATCH] Restore compatibility with the legacy GitHub callout syntax

Although GitHub removed support for the legacy callout syntax, we don't
have to! Restore this support via another AST transformer.

Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
---
 .../markup/markdown/callout/github_legacy.go  | 60 +++++++++++++++++++
 modules/markup/markdown/markdown.go           |  1 +
 2 files changed, 61 insertions(+)
 create mode 100644 modules/markup/markdown/callout/github_legacy.go

diff --git a/modules/markup/markdown/callout/github_legacy.go b/modules/markup/markdown/callout/github_legacy.go
new file mode 100644
index 0000000000..add6b0a847
--- /dev/null
+++ b/modules/markup/markdown/callout/github_legacy.go
@@ -0,0 +1,60 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Copyright 2024 The Forgejo Authors c/o Codeberg e.V.. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package callout
+
+import (
+	"strings"
+
+	"github.com/yuin/goldmark/ast"
+	"github.com/yuin/goldmark/parser"
+	"github.com/yuin/goldmark/text"
+)
+
+// Transformer for GitHub's legacy callout markup.
+type GitHubLegacyCalloutTransformer struct{}
+
+func (g *GitHubLegacyCalloutTransformer) Transform(node *ast.Document, reader text.Reader, pc parser.Context) {
+	supportedCalloutTypes := map[string]bool{"Note": true, "Warning": true}
+
+	_ = ast.Walk(node, func(n ast.Node, entering bool) (ast.WalkStatus, error) {
+		if !entering {
+			return ast.WalkContinue, nil
+		}
+
+		switch v := n.(type) {
+		case *ast.Blockquote:
+			// The first paragraph contains the callout type.
+			firstParagraph := v.FirstChild()
+			if firstParagraph.ChildCount() < 1 {
+				return ast.WalkContinue, nil
+			}
+
+			// In the legacy GitHub callout markup, the first node of the first
+			// paragraph should be an emphasis.
+			calloutNode, ok := firstParagraph.FirstChild().(*ast.Emphasis)
+			if !ok {
+				return ast.WalkContinue, nil
+			}
+			calloutText := string(calloutNode.Text(reader.Source()))
+			calloutType := strings.ToLower(calloutText)
+			// We only support "Note" and "Warning" callouts in legacy mode,
+			// match only those.
+			if _, has := supportedCalloutTypes[calloutText]; !has {
+				return ast.WalkContinue, nil
+			}
+
+			// Set the attention attribute on the emphasis
+			calloutNode.SetAttributeString("class", []byte("attention-"+calloutType))
+
+			// color the blockquote
+			v.SetAttributeString("class", []byte("gt-py-3 attention attention-"+calloutType))
+
+			// Prepend callout icon before the callout node itself
+			firstParagraph.InsertBefore(firstParagraph, calloutNode, NewAttention(calloutType))
+		}
+
+		return ast.WalkContinue, nil
+	})
+}
diff --git a/modules/markup/markdown/markdown.go b/modules/markup/markdown/markdown.go
index 92c0e786e9..00d01a2f55 100644
--- a/modules/markup/markdown/markdown.go
+++ b/modules/markup/markdown/markdown.go
@@ -125,6 +125,7 @@ func SpecializedMarkdown() goldmark.Markdown {
 				parser.WithAttribute(),
 				parser.WithAutoHeadingID(),
 				parser.WithASTTransformers(
+					util.Prioritized(&callout.GitHubLegacyCalloutTransformer{}, 8000),
 					util.Prioritized(&callout.GitHubCalloutTransformer{}, 9000),
 					util.Prioritized(&ASTTransformer{}, 10000),
 				),