mirror of
				https://github.com/go-gitea/gitea
				synced 2025-11-03 21:08:25 +00:00 
			
		
		
		
	Backport #31240 by wxiaoguang Fix #31214 Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
		@@ -1010,4 +1010,10 @@ func TestAttention(t *testing.T) {
 | 
			
		||||
	test(`> [!important]`, renderAttention("important", "octicon-report")+"\n</blockquote>")
 | 
			
		||||
	test(`> [!warning]`, renderAttention("warning", "octicon-alert")+"\n</blockquote>")
 | 
			
		||||
	test(`> [!caution]`, renderAttention("caution", "octicon-stop")+"\n</blockquote>")
 | 
			
		||||
 | 
			
		||||
	// escaped by mdformat
 | 
			
		||||
	test(`> \[!NOTE\]`, renderAttention("note", "octicon-info")+"\n</blockquote>")
 | 
			
		||||
 | 
			
		||||
	// legacy GitHub style
 | 
			
		||||
	test(`> **warning**`, renderAttention("warning", "octicon-alert")+"\n</blockquote>")
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -31,10 +31,16 @@ func (b *blockParser) Open(parent ast.Node, reader text.Reader, pc parser.Contex
 | 
			
		||||
		return nil, parser.NoChildren
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dollars := false
 | 
			
		||||
	var dollars bool
 | 
			
		||||
	if b.parseDollars && line[pos] == '$' && line[pos+1] == '$' {
 | 
			
		||||
		dollars = true
 | 
			
		||||
	} else if line[pos] != '\\' || line[pos+1] != '[' {
 | 
			
		||||
	} else if line[pos] == '\\' && line[pos+1] == '[' {
 | 
			
		||||
		if len(line[pos:]) >= 3 && line[pos+2] == '!' && bytes.Contains(line[pos:], []byte(`\]`)) {
 | 
			
		||||
			// do not process escaped attention block: "> \[!NOTE\]"
 | 
			
		||||
			return nil, parser.NoChildren
 | 
			
		||||
		}
 | 
			
		||||
		dollars = false
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil, parser.NoChildren
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ import (
 | 
			
		||||
	"golang.org/x/text/language"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// renderAttention renders a quote marked with i.e. "> **Note**" or "> **Warning**" with a corresponding svg
 | 
			
		||||
// renderAttention renders a quote marked with i.e. "> **Note**" or "> [!Warning]" with a corresponding svg
 | 
			
		||||
func (r *HTMLRenderer) renderAttention(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
 | 
			
		||||
	if entering {
 | 
			
		||||
		n := node.(*Attention)
 | 
			
		||||
@@ -37,38 +37,93 @@ func (r *HTMLRenderer) renderAttention(w util.BufWriter, source []byte, node ast
 | 
			
		||||
	return ast.WalkContinue, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *ASTTransformer) transformBlockquote(v *ast.Blockquote, reader text.Reader) (ast.WalkStatus, error) {
 | 
			
		||||
	// We only want attention blockquotes when the AST looks like:
 | 
			
		||||
	// > Text("[") Text("!TYPE") Text("]")
 | 
			
		||||
func (g *ASTTransformer) extractBlockquoteAttentionEmphasis(firstParagraph ast.Node, reader text.Reader) (string, []ast.Node) {
 | 
			
		||||
	if firstParagraph.ChildCount() < 1 {
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
	node1, ok := firstParagraph.FirstChild().(*ast.Emphasis)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
	val1 := string(node1.Text(reader.Source()))
 | 
			
		||||
	attentionType := strings.ToLower(val1)
 | 
			
		||||
	if g.attentionTypes.Contains(attentionType) {
 | 
			
		||||
		return attentionType, []ast.Node{node1}
 | 
			
		||||
	}
 | 
			
		||||
	return "", nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	// grab these nodes and make sure we adhere to the attention blockquote structure
 | 
			
		||||
	firstParagraph := v.FirstChild()
 | 
			
		||||
	g.applyElementDir(firstParagraph)
 | 
			
		||||
	if firstParagraph.ChildCount() < 3 {
 | 
			
		||||
		return ast.WalkContinue, nil
 | 
			
		||||
func (g *ASTTransformer) extractBlockquoteAttention2(firstParagraph ast.Node, reader text.Reader) (string, []ast.Node) {
 | 
			
		||||
	if firstParagraph.ChildCount() < 2 {
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
	node1, ok := firstParagraph.FirstChild().(*ast.Text)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return ast.WalkContinue, nil
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
	node2, ok := node1.NextSibling().(*ast.Text)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return ast.WalkContinue, nil
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
	val1 := string(node1.Segment.Value(reader.Source()))
 | 
			
		||||
	val2 := string(node2.Segment.Value(reader.Source()))
 | 
			
		||||
	if strings.HasPrefix(val1, `\[!`) && val2 == `\]` {
 | 
			
		||||
		attentionType := strings.ToLower(val1[3:])
 | 
			
		||||
		if g.attentionTypes.Contains(attentionType) {
 | 
			
		||||
			return attentionType, []ast.Node{node1, node2}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return "", nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *ASTTransformer) extractBlockquoteAttention3(firstParagraph ast.Node, reader text.Reader) (string, []ast.Node) {
 | 
			
		||||
	if firstParagraph.ChildCount() < 3 {
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
	node1, ok := firstParagraph.FirstChild().(*ast.Text)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
	node2, ok := node1.NextSibling().(*ast.Text)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
	node3, ok := node2.NextSibling().(*ast.Text)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return ast.WalkContinue, nil
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
	val1 := string(node1.Segment.Value(reader.Source()))
 | 
			
		||||
	val2 := string(node2.Segment.Value(reader.Source()))
 | 
			
		||||
	val3 := string(node3.Segment.Value(reader.Source()))
 | 
			
		||||
	if val1 != "[" || val3 != "]" || !strings.HasPrefix(val2, "!") {
 | 
			
		||||
		return ast.WalkContinue, nil
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// grab attention type from markdown source
 | 
			
		||||
	attentionType := strings.ToLower(val2[1:])
 | 
			
		||||
	if !g.attentionTypes.Contains(attentionType) {
 | 
			
		||||
	if g.attentionTypes.Contains(attentionType) {
 | 
			
		||||
		return attentionType, []ast.Node{node1, node2, node3}
 | 
			
		||||
	}
 | 
			
		||||
	return "", nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *ASTTransformer) transformBlockquote(v *ast.Blockquote, reader text.Reader) (ast.WalkStatus, error) {
 | 
			
		||||
	// We only want attention blockquotes when the AST looks like:
 | 
			
		||||
	// > Text("[") Text("!TYPE") Text("]")
 | 
			
		||||
	// > Text("\[!TYPE") TEXT("\]")
 | 
			
		||||
	// > Text("**TYPE**")
 | 
			
		||||
 | 
			
		||||
	// grab these nodes and make sure we adhere to the attention blockquote structure
 | 
			
		||||
	firstParagraph := v.FirstChild()
 | 
			
		||||
	g.applyElementDir(firstParagraph)
 | 
			
		||||
 | 
			
		||||
	attentionType, processedNodes := g.extractBlockquoteAttentionEmphasis(firstParagraph, reader)
 | 
			
		||||
	if attentionType == "" {
 | 
			
		||||
		attentionType, processedNodes = g.extractBlockquoteAttention2(firstParagraph, reader)
 | 
			
		||||
	}
 | 
			
		||||
	if attentionType == "" {
 | 
			
		||||
		attentionType, processedNodes = g.extractBlockquoteAttention3(firstParagraph, reader)
 | 
			
		||||
	}
 | 
			
		||||
	if attentionType == "" {
 | 
			
		||||
		return ast.WalkContinue, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -88,9 +143,9 @@ func (g *ASTTransformer) transformBlockquote(v *ast.Blockquote, reader text.Read
 | 
			
		||||
	attentionParagraph.AppendChild(attentionParagraph, NewAttention(attentionType))
 | 
			
		||||
	attentionParagraph.AppendChild(attentionParagraph, emphasis)
 | 
			
		||||
	firstParagraph.Parent().InsertBefore(firstParagraph.Parent(), firstParagraph, attentionParagraph)
 | 
			
		||||
	firstParagraph.RemoveChild(firstParagraph, node1)
 | 
			
		||||
	firstParagraph.RemoveChild(firstParagraph, node2)
 | 
			
		||||
	firstParagraph.RemoveChild(firstParagraph, node3)
 | 
			
		||||
	for _, processed := range processedNodes {
 | 
			
		||||
		firstParagraph.RemoveChild(firstParagraph, processed)
 | 
			
		||||
	}
 | 
			
		||||
	if firstParagraph.ChildCount() == 0 {
 | 
			
		||||
		firstParagraph.Parent().RemoveChild(firstParagraph.Parent(), firstParagraph)
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user