2024-03-28 10:26:13 +08:00
|
|
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
|
|
|
package markdown
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"code.gitea.io/gitea/modules/markup"
|
|
|
|
|
|
|
|
"github.com/microcosm-cc/bluemonday/css"
|
|
|
|
"github.com/yuin/goldmark/ast"
|
|
|
|
"github.com/yuin/goldmark/renderer/html"
|
|
|
|
"github.com/yuin/goldmark/text"
|
|
|
|
"github.com/yuin/goldmark/util"
|
|
|
|
)
|
|
|
|
|
|
|
|
// renderCodeSpan renders CodeSpan elements (like goldmark upstream does) but also renders ColorPreview elements.
|
|
|
|
// See #21474 for reference
|
|
|
|
func (r *HTMLRenderer) renderCodeSpan(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
|
|
|
|
if entering {
|
|
|
|
if n.Attributes() != nil {
|
|
|
|
_, _ = w.WriteString("<code")
|
|
|
|
html.RenderAttributes(w, n, html.CodeAttributeFilter)
|
|
|
|
_ = w.WriteByte('>')
|
|
|
|
} else {
|
|
|
|
_, _ = w.WriteString("<code>")
|
|
|
|
}
|
|
|
|
for c := n.FirstChild(); c != nil; c = c.NextSibling() {
|
|
|
|
switch v := c.(type) {
|
|
|
|
case *ast.Text:
|
|
|
|
segment := v.Segment
|
|
|
|
value := segment.Value(source)
|
|
|
|
if bytes.HasSuffix(value, []byte("\n")) {
|
|
|
|
r.Writer.RawWrite(w, value[:len(value)-1])
|
|
|
|
r.Writer.RawWrite(w, []byte(" "))
|
|
|
|
} else {
|
|
|
|
r.Writer.RawWrite(w, value)
|
|
|
|
}
|
|
|
|
case *ColorPreview:
|
|
|
|
_, _ = w.WriteString(fmt.Sprintf(`<span class="color-preview" style="background-color: %v"></span>`, string(v.Color)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ast.WalkSkipChildren, nil
|
|
|
|
}
|
|
|
|
_, _ = w.WriteString("</code>")
|
|
|
|
return ast.WalkContinue, nil
|
|
|
|
}
|
|
|
|
|
2024-03-31 19:17:34 +08:00
|
|
|
// cssColorHandler checks if a string is a render-able CSS color value.
|
|
|
|
// The code is from "github.com/microcosm-cc/bluemonday/css.ColorHandler", except that it doesn't handle color words like "red".
|
|
|
|
func cssColorHandler(value string) bool {
|
|
|
|
value = strings.ToLower(value)
|
|
|
|
if css.HexRGB.MatchString(value) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if css.RGB.MatchString(value) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if css.RGBA.MatchString(value) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if css.HSL.MatchString(value) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return css.HSLA.MatchString(value)
|
|
|
|
}
|
|
|
|
|
2024-04-29 16:47:56 +08:00
|
|
|
func (g *ASTTransformer) transformCodeSpan(_ *markup.RenderContext, v *ast.CodeSpan, reader text.Reader) {
|
2024-03-28 10:26:13 +08:00
|
|
|
colorContent := v.Text(reader.Source())
|
2024-03-31 19:17:34 +08:00
|
|
|
if cssColorHandler(string(colorContent)) {
|
2024-03-28 10:26:13 +08:00
|
|
|
v.AppendChild(v, NewColorPreview(colorContent))
|
|
|
|
}
|
|
|
|
}
|