From dd136858f1ea40ad3c94191d647487fa4f31926c Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 18 Oct 2024 20:33:49 +0200 Subject: Adding upstream version 9.0.0. Signed-off-by: Daniel Baumann --- modules/markup/markdown/callout/ast.go | 37 ++++++ modules/markup/markdown/callout/github.go | 141 +++++++++++++++++++++++ modules/markup/markdown/callout/github_legacy.go | 70 +++++++++++ 3 files changed, 248 insertions(+) create mode 100644 modules/markup/markdown/callout/ast.go create mode 100644 modules/markup/markdown/callout/github.go create mode 100644 modules/markup/markdown/callout/github_legacy.go (limited to 'modules/markup/markdown/callout') diff --git a/modules/markup/markdown/callout/ast.go b/modules/markup/markdown/callout/ast.go new file mode 100644 index 0000000..a5b1bbc --- /dev/null +++ b/modules/markup/markdown/callout/ast.go @@ -0,0 +1,37 @@ +// Copyright 2020 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package callout + +import ( + "github.com/yuin/goldmark/ast" +) + +// Attention is an inline for an attention +type Attention struct { + ast.BaseInline + AttentionType string +} + +// Dump implements Node.Dump. +func (n *Attention) Dump(source []byte, level int) { + m := map[string]string{} + m["AttentionType"] = n.AttentionType + ast.DumpHelper(n, source, level, m, nil) +} + +// KindAttention is the NodeKind for Attention +var KindAttention = ast.NewNodeKind("Attention") + +// Kind implements Node.Kind. +func (n *Attention) Kind() ast.NodeKind { + return KindAttention +} + +// NewAttention returns a new Attention node. +func NewAttention(attentionType string) *Attention { + return &Attention{ + BaseInline: ast.BaseInline{}, + AttentionType: attentionType, + } +} diff --git a/modules/markup/markdown/callout/github.go b/modules/markup/markdown/callout/github.go new file mode 100644 index 0000000..9b8b611 --- /dev/null +++ b/modules/markup/markdown/callout/github.go @@ -0,0 +1,141 @@ +// 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" + + "code.gitea.io/gitea/modules/svg" + + "github.com/yuin/goldmark/ast" + "github.com/yuin/goldmark/parser" + "github.com/yuin/goldmark/renderer" + "github.com/yuin/goldmark/renderer/html" + "github.com/yuin/goldmark/text" + "github.com/yuin/goldmark/util" +) + +type GitHubCalloutTransformer struct{} + +// Transform transforms the given AST tree. +func (g *GitHubCalloutTransformer) Transform(node *ast.Document, reader text.Reader, pc parser.Context) { + supportedAttentionTypes := map[string]bool{ + "note": true, + "tip": true, + "important": true, + "warning": true, + "caution": true, + } + + _ = ast.Walk(node, func(n ast.Node, entering bool) (ast.WalkStatus, error) { + if !entering { + return ast.WalkContinue, nil + } + + if v, ok := n.(*ast.Blockquote); ok { + if v.ChildCount() == 0 { + return ast.WalkContinue, nil + } + + // We only want attention blockquotes when the AST looks like: + // Text: "[" + // Text: "!TYPE" + // Text(SoftLineBreak): "]" + + // grab these nodes and make sure we adhere to the attention blockquote structure + firstParagraph := v.FirstChild() + if firstParagraph.ChildCount() < 3 { + return ast.WalkContinue, nil + } + firstTextNode, ok := firstParagraph.FirstChild().(*ast.Text) + if !ok || string(firstTextNode.Text(reader.Source())) != "[" { + return ast.WalkContinue, nil + } + secondTextNode, ok := firstTextNode.NextSibling().(*ast.Text) + if !ok { + return ast.WalkContinue, nil + } + // If the second node's text isn't one of the supported attention + // types, continue walking. + secondTextNodeText := secondTextNode.Text(reader.Source()) + attentionType := strings.ToLower(strings.TrimPrefix(string(secondTextNodeText), "!")) + if _, has := supportedAttentionTypes[attentionType]; !has { + return ast.WalkContinue, nil + } + + thirdTextNode, ok := secondTextNode.NextSibling().(*ast.Text) + if !ok || string(thirdTextNode.Text(reader.Source())) != "]" { + return ast.WalkContinue, nil + } + + // color the blockquote + v.SetAttributeString("class", []byte("attention-header attention-"+attentionType)) + + // create an emphasis to make it bold + attentionParagraph := ast.NewParagraph() + attentionParagraph.SetAttributeString("class", []byte("attention-title")) + emphasis := ast.NewEmphasis(2) + emphasis.SetAttributeString("class", []byte("attention-"+attentionType)) + firstParagraph.InsertBefore(firstParagraph, firstTextNode, emphasis) + + // capitalize first letter + attentionText := ast.NewString([]byte(strings.ToUpper(string(attentionType[0])) + attentionType[1:])) + + // replace the ![TYPE] with a dedicated paragraph of icon+Type + emphasis.AppendChild(emphasis, attentionText) + attentionParagraph.AppendChild(attentionParagraph, NewAttention(attentionType)) + attentionParagraph.AppendChild(attentionParagraph, emphasis) + firstParagraph.Parent().InsertBefore(firstParagraph.Parent(), firstParagraph, attentionParagraph) + firstParagraph.RemoveChild(firstParagraph, firstTextNode) + firstParagraph.RemoveChild(firstParagraph, secondTextNode) + firstParagraph.RemoveChild(firstParagraph, thirdTextNode) + } + return ast.WalkContinue, nil + }) +} + +type GitHubCalloutHTMLRenderer struct { + html.Config +} + +// RegisterFuncs implements renderer.NodeRenderer.RegisterFuncs. +func (r *GitHubCalloutHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) { + reg.Register(KindAttention, r.renderAttention) +} + +// renderAttention renders a quote marked with i.e. "> **Note**" or "> **Warning**" with a corresponding svg +func (r *GitHubCalloutHTMLRenderer) renderAttention(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { + if entering { + n := node.(*Attention) + + var octiconName string + switch n.AttentionType { + case "note": + octiconName = "info" + case "tip": + octiconName = "light-bulb" + case "important": + octiconName = "report" + case "warning": + octiconName = "alert" + case "caution": + octiconName = "stop" + default: + octiconName = "info" + } + _, _ = w.WriteString(string(svg.RenderHTML("octicon-"+octiconName, 16, "attention-icon attention-"+n.AttentionType))) + } + return ast.WalkContinue, nil +} + +func NewGitHubCalloutHTMLRenderer(opts ...html.Option) renderer.NodeRenderer { + r := &GitHubCalloutHTMLRenderer{ + Config: html.NewConfig(), + } + for _, opt := range opts { + opt.SetHTMLOption(&r.Config) + } + return r +} diff --git a/modules/markup/markdown/callout/github_legacy.go b/modules/markup/markdown/callout/github_legacy.go new file mode 100644 index 0000000..32a278b --- /dev/null +++ b/modules/markup/markdown/callout/github_legacy.go @@ -0,0 +1,70 @@ +// 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 + } + + if v, ok := n.(*ast.Blockquote); ok { + if v.ChildCount() == 0 { + return ast.WalkContinue, nil + } + + // 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("attention-header attention-"+calloutType)) + + // Create new paragraph. + attentionParagraph := ast.NewParagraph() + attentionParagraph.SetAttributeString("class", []byte("attention-title")) + + // Move the callout node to the paragraph and insert the paragraph. + attentionParagraph.AppendChild(attentionParagraph, NewAttention(calloutType)) + attentionParagraph.AppendChild(attentionParagraph, calloutNode) + firstParagraph.Parent().InsertBefore(firstParagraph.Parent(), firstParagraph, attentionParagraph) + firstParagraph.RemoveChild(firstParagraph, calloutNode) + } + + return ast.WalkContinue, nil + }) +} -- cgit v1.2.3