From b594188623f2afd0bc61eb69fb3cc26b5c47859d Mon Sep 17 00:00:00 2001 From: Gergely Nagy 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 --- .../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 000000000..add6b0a84 --- /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 92c0e786e..00d01a2f5 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), ),