mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-03 20:36:07 +01:00 
			
		
		
		
	@@ -4,6 +4,7 @@
 | 
				
			|||||||
package markdown
 | 
					package markdown
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"html/template"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/yuin/goldmark/ast"
 | 
						"github.com/yuin/goldmark/ast"
 | 
				
			||||||
@@ -29,9 +30,7 @@ func (n *Details) Kind() ast.NodeKind {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// NewDetails returns a new Paragraph node.
 | 
					// NewDetails returns a new Paragraph node.
 | 
				
			||||||
func NewDetails() *Details {
 | 
					func NewDetails() *Details {
 | 
				
			||||||
	return &Details{
 | 
						return &Details{}
 | 
				
			||||||
		BaseBlock: ast.BaseBlock{},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Summary is a block that contains the summary of details block
 | 
					// Summary is a block that contains the summary of details block
 | 
				
			||||||
@@ -54,9 +53,7 @@ func (n *Summary) Kind() ast.NodeKind {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// NewSummary returns a new Summary node.
 | 
					// NewSummary returns a new Summary node.
 | 
				
			||||||
func NewSummary() *Summary {
 | 
					func NewSummary() *Summary {
 | 
				
			||||||
	return &Summary{
 | 
						return &Summary{}
 | 
				
			||||||
		BaseBlock: ast.BaseBlock{},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TaskCheckBoxListItem is a block that represents a list item of a markdown block with a checkbox
 | 
					// TaskCheckBoxListItem is a block that represents a list item of a markdown block with a checkbox
 | 
				
			||||||
@@ -95,29 +92,6 @@ type Icon struct {
 | 
				
			|||||||
	Name []byte
 | 
						Name []byte
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Dump implements Node.Dump .
 | 
					 | 
				
			||||||
func (n *Icon) Dump(source []byte, level int) {
 | 
					 | 
				
			||||||
	m := map[string]string{}
 | 
					 | 
				
			||||||
	m["Name"] = string(n.Name)
 | 
					 | 
				
			||||||
	ast.DumpHelper(n, source, level, m, nil)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// KindIcon is the NodeKind for Icon
 | 
					 | 
				
			||||||
var KindIcon = ast.NewNodeKind("Icon")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Kind implements Node.Kind.
 | 
					 | 
				
			||||||
func (n *Icon) Kind() ast.NodeKind {
 | 
					 | 
				
			||||||
	return KindIcon
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// NewIcon returns a new Paragraph node.
 | 
					 | 
				
			||||||
func NewIcon(name string) *Icon {
 | 
					 | 
				
			||||||
	return &Icon{
 | 
					 | 
				
			||||||
		BaseInline: ast.BaseInline{},
 | 
					 | 
				
			||||||
		Name:       []byte(name),
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ColorPreview is an inline for a color preview
 | 
					// ColorPreview is an inline for a color preview
 | 
				
			||||||
type ColorPreview struct {
 | 
					type ColorPreview struct {
 | 
				
			||||||
	ast.BaseInline
 | 
						ast.BaseInline
 | 
				
			||||||
@@ -175,3 +149,24 @@ func NewAttention(attentionType string) *Attention {
 | 
				
			|||||||
		AttentionType: attentionType,
 | 
							AttentionType: attentionType,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var KindRawHTML = ast.NewNodeKind("RawHTML")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type RawHTML struct {
 | 
				
			||||||
 | 
						ast.BaseBlock
 | 
				
			||||||
 | 
						rawHTML template.HTML
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (n *RawHTML) Dump(source []byte, level int) {
 | 
				
			||||||
 | 
						m := map[string]string{}
 | 
				
			||||||
 | 
						m["RawHTML"] = string(n.rawHTML)
 | 
				
			||||||
 | 
						ast.DumpHelper(n, source, level, m, nil)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (n *RawHTML) Kind() ast.NodeKind {
 | 
				
			||||||
 | 
						return KindRawHTML
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewRawHTML(rawHTML template.HTML) *RawHTML {
 | 
				
			||||||
 | 
						return &RawHTML{rawHTML: rawHTML}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,25 +4,24 @@
 | 
				
			|||||||
package markdown
 | 
					package markdown
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/htmlutil"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/svg"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/yuin/goldmark/ast"
 | 
						"github.com/yuin/goldmark/ast"
 | 
				
			||||||
	east "github.com/yuin/goldmark/extension/ast"
 | 
						east "github.com/yuin/goldmark/extension/ast"
 | 
				
			||||||
	"gopkg.in/yaml.v3"
 | 
						"gopkg.in/yaml.v3"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func nodeToTable(meta *yaml.Node) ast.Node {
 | 
					func nodeToTable(meta *yaml.Node) ast.Node {
 | 
				
			||||||
	for {
 | 
						for meta != nil && meta.Kind == yaml.DocumentNode {
 | 
				
			||||||
 | 
							meta = meta.Content[0]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if meta == nil {
 | 
						if meta == nil {
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	switch meta.Kind {
 | 
						switch meta.Kind {
 | 
				
			||||||
		case yaml.DocumentNode:
 | 
					 | 
				
			||||||
			meta = meta.Content[0]
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		default:
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		break
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	switch meta.Kind {
 | 
					 | 
				
			||||||
	case yaml.MappingNode:
 | 
						case yaml.MappingNode:
 | 
				
			||||||
		return mappingNodeToTable(meta)
 | 
							return mappingNodeToTable(meta)
 | 
				
			||||||
	case yaml.SequenceNode:
 | 
						case yaml.SequenceNode:
 | 
				
			||||||
@@ -72,12 +71,28 @@ func sequenceNodeToTable(meta *yaml.Node) ast.Node {
 | 
				
			|||||||
	return table
 | 
						return table
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func nodeToDetails(meta *yaml.Node, icon string) ast.Node {
 | 
					func nodeToDetails(g *ASTTransformer, meta *yaml.Node) ast.Node {
 | 
				
			||||||
 | 
						for meta != nil && meta.Kind == yaml.DocumentNode {
 | 
				
			||||||
 | 
							meta = meta.Content[0]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if meta == nil {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if meta.Kind != yaml.MappingNode {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var keys []string
 | 
				
			||||||
 | 
						for i := 0; i < len(meta.Content); i += 2 {
 | 
				
			||||||
 | 
							if meta.Content[i].Kind == yaml.ScalarNode {
 | 
				
			||||||
 | 
								keys = append(keys, meta.Content[i].Value)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	details := NewDetails()
 | 
						details := NewDetails()
 | 
				
			||||||
 | 
						details.SetAttributeString(g.renderInternal.SafeAttr("class"), g.renderInternal.SafeValue("frontmatter-content"))
 | 
				
			||||||
	summary := NewSummary()
 | 
						summary := NewSummary()
 | 
				
			||||||
	summary.AppendChild(summary, NewIcon(icon))
 | 
						summaryInnerHTML := htmlutil.HTMLFormat("%s %s", svg.RenderHTML("octicon-table", 12), strings.Join(keys, ", "))
 | 
				
			||||||
 | 
						summary.AppendChild(summary, NewRawHTML(summaryInnerHTML))
 | 
				
			||||||
	details.AppendChild(details, summary)
 | 
						details.AppendChild(details, summary)
 | 
				
			||||||
	details.AppendChild(details, nodeToTable(meta))
 | 
						details.AppendChild(details, nodeToTable(meta))
 | 
				
			||||||
 | 
					 | 
				
			||||||
	return details
 | 
						return details
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,9 +5,6 @@ package markdown
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"regexp"
 | 
					 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
	"sync"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/container"
 | 
						"code.gitea.io/gitea/modules/container"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/markup"
 | 
						"code.gitea.io/gitea/modules/markup"
 | 
				
			||||||
@@ -51,7 +48,7 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	tocList := make([]Header, 0, 20)
 | 
						tocList := make([]Header, 0, 20)
 | 
				
			||||||
	if rc.yamlNode != nil {
 | 
						if rc.yamlNode != nil {
 | 
				
			||||||
		metaNode := rc.toMetaNode()
 | 
							metaNode := rc.toMetaNode(g)
 | 
				
			||||||
		if metaNode != nil {
 | 
							if metaNode != nil {
 | 
				
			||||||
			node.InsertBefore(node, firstChild, metaNode)
 | 
								node.InsertBefore(node, firstChild, metaNode)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -112,11 +109,6 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// it is copied from old code, which is quite doubtful whether it is correct
 | 
					 | 
				
			||||||
var reValidIconName = sync.OnceValue(func() *regexp.Regexp {
 | 
					 | 
				
			||||||
	return regexp.MustCompile(`^[-\w]+$`) // old: regexp.MustCompile("^[a-z ]+$")
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// NewHTMLRenderer creates a HTMLRenderer to render in the gitea form.
 | 
					// NewHTMLRenderer creates a HTMLRenderer to render in the gitea form.
 | 
				
			||||||
func NewHTMLRenderer(renderInternal *internal.RenderInternal, opts ...html.Option) renderer.NodeRenderer {
 | 
					func NewHTMLRenderer(renderInternal *internal.RenderInternal, opts ...html.Option) renderer.NodeRenderer {
 | 
				
			||||||
	r := &HTMLRenderer{
 | 
						r := &HTMLRenderer{
 | 
				
			||||||
@@ -141,11 +133,11 @@ func (r *HTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
 | 
				
			|||||||
	reg.Register(ast.KindDocument, r.renderDocument)
 | 
						reg.Register(ast.KindDocument, r.renderDocument)
 | 
				
			||||||
	reg.Register(KindDetails, r.renderDetails)
 | 
						reg.Register(KindDetails, r.renderDetails)
 | 
				
			||||||
	reg.Register(KindSummary, r.renderSummary)
 | 
						reg.Register(KindSummary, r.renderSummary)
 | 
				
			||||||
	reg.Register(KindIcon, r.renderIcon)
 | 
					 | 
				
			||||||
	reg.Register(ast.KindCodeSpan, r.renderCodeSpan)
 | 
						reg.Register(ast.KindCodeSpan, r.renderCodeSpan)
 | 
				
			||||||
	reg.Register(KindAttention, r.renderAttention)
 | 
						reg.Register(KindAttention, r.renderAttention)
 | 
				
			||||||
	reg.Register(KindTaskCheckBoxListItem, r.renderTaskCheckBoxListItem)
 | 
						reg.Register(KindTaskCheckBoxListItem, r.renderTaskCheckBoxListItem)
 | 
				
			||||||
	reg.Register(east.KindTaskCheckBox, r.renderTaskCheckBox)
 | 
						reg.Register(east.KindTaskCheckBox, r.renderTaskCheckBox)
 | 
				
			||||||
 | 
						reg.Register(KindRawHTML, r.renderRawHTML)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *HTMLRenderer) renderDocument(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
 | 
					func (r *HTMLRenderer) renderDocument(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
 | 
				
			||||||
@@ -207,30 +199,14 @@ func (r *HTMLRenderer) renderSummary(w util.BufWriter, source []byte, node ast.N
 | 
				
			|||||||
	return ast.WalkContinue, nil
 | 
						return ast.WalkContinue, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *HTMLRenderer) renderIcon(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
 | 
					func (r *HTMLRenderer) renderRawHTML(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
 | 
				
			||||||
	if !entering {
 | 
						if !entering {
 | 
				
			||||||
		return ast.WalkContinue, nil
 | 
							return ast.WalkContinue, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						n := node.(*RawHTML)
 | 
				
			||||||
	n := node.(*Icon)
 | 
						_, err := w.WriteString(string(r.renderInternal.ProtectSafeAttrs(n.rawHTML)))
 | 
				
			||||||
 | 
					 | 
				
			||||||
	name := strings.TrimSpace(strings.ToLower(string(n.Name)))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if len(name) == 0 {
 | 
					 | 
				
			||||||
		// skip this
 | 
					 | 
				
			||||||
		return ast.WalkContinue, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if !reValidIconName().MatchString(name) {
 | 
					 | 
				
			||||||
		// skip this
 | 
					 | 
				
			||||||
		return ast.WalkContinue, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// FIXME: the "icon xxx" is from Fomantic UI, it's really questionable whether it still works correctly
 | 
					 | 
				
			||||||
	err := r.renderInternal.FormatWithSafeAttrs(w, `<i class="icon %s"></i>`, name)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return ast.WalkStop, err
 | 
							return ast.WalkStop, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	return ast.WalkContinue, nil
 | 
						return ast.WalkContinue, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -184,11 +184,7 @@ func render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error
 | 
				
			|||||||
	// Preserve original length.
 | 
						// Preserve original length.
 | 
				
			||||||
	bufWithMetadataLength := len(buf)
 | 
						bufWithMetadataLength := len(buf)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rc := &RenderConfig{
 | 
						rc := &RenderConfig{Meta: markup.RenderMetaAsDetails}
 | 
				
			||||||
		Meta: markup.RenderMetaAsDetails,
 | 
					 | 
				
			||||||
		Icon: "table",
 | 
					 | 
				
			||||||
		Lang: "",
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	buf, _ = ExtractMetadataBytes(buf, rc)
 | 
						buf, _ = ExtractMetadataBytes(buf, rc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	metaLength := bufWithMetadataLength - len(buf)
 | 
						metaLength := bufWithMetadataLength - len(buf)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -383,18 +383,74 @@ func TestColorPreview(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestTaskList(t *testing.T) {
 | 
					func TestMarkdownFrontmatter(t *testing.T) {
 | 
				
			||||||
	testcases := []struct {
 | 
						testcases := []struct {
 | 
				
			||||||
		testcase string
 | 
							name     string
 | 
				
			||||||
 | 
							input    string
 | 
				
			||||||
		expected string
 | 
							expected string
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"MapInFrontmatter",
 | 
				
			||||||
 | 
								`---
 | 
				
			||||||
 | 
					key1: val1
 | 
				
			||||||
 | 
					key2: val2
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					test
 | 
				
			||||||
 | 
					`,
 | 
				
			||||||
 | 
								`<details class="frontmatter-content"><summary><span>octicon-table(12/)</span> key1, key2</summary><table>
 | 
				
			||||||
 | 
					<thead>
 | 
				
			||||||
 | 
					<tr>
 | 
				
			||||||
 | 
					<th>key1</th>
 | 
				
			||||||
 | 
					<th>key2</th>
 | 
				
			||||||
 | 
					</tr>
 | 
				
			||||||
 | 
					</thead>
 | 
				
			||||||
 | 
					<tbody>
 | 
				
			||||||
 | 
					<tr>
 | 
				
			||||||
 | 
					<td>val1</td>
 | 
				
			||||||
 | 
					<td>val2</td>
 | 
				
			||||||
 | 
					</tr>
 | 
				
			||||||
 | 
					</tbody>
 | 
				
			||||||
 | 
					</table>
 | 
				
			||||||
 | 
					</details><p>test</p>
 | 
				
			||||||
 | 
					`,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"ListInFrontmatter",
 | 
				
			||||||
 | 
								`---
 | 
				
			||||||
 | 
					- item1
 | 
				
			||||||
 | 
					- item2
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					test
 | 
				
			||||||
 | 
					`,
 | 
				
			||||||
 | 
								`- item1
 | 
				
			||||||
 | 
					- item2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<p>test</p>
 | 
				
			||||||
 | 
					`,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"StringInFrontmatter",
 | 
				
			||||||
 | 
								`---
 | 
				
			||||||
 | 
					anything
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					test
 | 
				
			||||||
 | 
					`,
 | 
				
			||||||
 | 
								`anything
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<p>test</p>
 | 
				
			||||||
 | 
					`,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			// data-source-position should take into account YAML frontmatter.
 | 
								// data-source-position should take into account YAML frontmatter.
 | 
				
			||||||
 | 
								"ListAfterFrontmatter",
 | 
				
			||||||
			`---
 | 
								`---
 | 
				
			||||||
foo: bar
 | 
					foo: bar
 | 
				
			||||||
---
 | 
					---
 | 
				
			||||||
- [ ] task 1`,
 | 
					- [ ] task 1`,
 | 
				
			||||||
			`<details><summary><i class="icon table"></i></summary><table>
 | 
								`<details class="frontmatter-content"><summary><span>octicon-table(12/)</span> foo</summary><table>
 | 
				
			||||||
<thead>
 | 
					<thead>
 | 
				
			||||||
<tr>
 | 
					<tr>
 | 
				
			||||||
<th>foo</th>
 | 
					<th>foo</th>
 | 
				
			||||||
@@ -414,9 +470,9 @@ foo: bar
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, test := range testcases {
 | 
						for _, test := range testcases {
 | 
				
			||||||
		res, err := markdown.RenderString(markup.NewTestRenderContext(), test.testcase)
 | 
							res, err := markdown.RenderString(markup.NewTestRenderContext(), test.input)
 | 
				
			||||||
		assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase)
 | 
							assert.NoError(t, err, "Unexpected error in testcase: %q", test.name)
 | 
				
			||||||
		assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase)
 | 
							assert.Equal(t, test.expected, string(res), "Unexpected result in testcase %q", test.name)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,7 +16,6 @@ import (
 | 
				
			|||||||
// RenderConfig represents rendering configuration for this file
 | 
					// RenderConfig represents rendering configuration for this file
 | 
				
			||||||
type RenderConfig struct {
 | 
					type RenderConfig struct {
 | 
				
			||||||
	Meta     markup.RenderMetaMode
 | 
						Meta     markup.RenderMetaMode
 | 
				
			||||||
	Icon     string
 | 
					 | 
				
			||||||
	TOC      string // "false": hide,  "side"/empty: in sidebar,  "main"/"true": in main view
 | 
						TOC      string // "false": hide,  "side"/empty: in sidebar,  "main"/"true": in main view
 | 
				
			||||||
	Lang     string
 | 
						Lang     string
 | 
				
			||||||
	yamlNode *yaml.Node
 | 
						yamlNode *yaml.Node
 | 
				
			||||||
@@ -74,7 +73,7 @@ func (rc *RenderConfig) UnmarshalYAML(value *yaml.Node) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	type yamlRenderConfig struct {
 | 
						type yamlRenderConfig struct {
 | 
				
			||||||
		Meta *string `yaml:"meta"`
 | 
							Meta *string `yaml:"meta"`
 | 
				
			||||||
		Icon *string `yaml:"details_icon"`
 | 
							Icon *string `yaml:"details_icon"` // deprecated, because there is no font icon, so no custom icon
 | 
				
			||||||
		TOC  *string `yaml:"include_toc"`
 | 
							TOC  *string `yaml:"include_toc"`
 | 
				
			||||||
		Lang *string `yaml:"lang"`
 | 
							Lang *string `yaml:"lang"`
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -96,10 +95,6 @@ func (rc *RenderConfig) UnmarshalYAML(value *yaml.Node) error {
 | 
				
			|||||||
		rc.Meta = renderMetaModeFromString(*cfg.Gitea.Meta)
 | 
							rc.Meta = renderMetaModeFromString(*cfg.Gitea.Meta)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if cfg.Gitea.Icon != nil {
 | 
					 | 
				
			||||||
		rc.Icon = strings.TrimSpace(strings.ToLower(*cfg.Gitea.Icon))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if cfg.Gitea.Lang != nil && *cfg.Gitea.Lang != "" {
 | 
						if cfg.Gitea.Lang != nil && *cfg.Gitea.Lang != "" {
 | 
				
			||||||
		rc.Lang = *cfg.Gitea.Lang
 | 
							rc.Lang = *cfg.Gitea.Lang
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -111,7 +106,7 @@ func (rc *RenderConfig) UnmarshalYAML(value *yaml.Node) error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (rc *RenderConfig) toMetaNode() ast.Node {
 | 
					func (rc *RenderConfig) toMetaNode(g *ASTTransformer) ast.Node {
 | 
				
			||||||
	if rc.yamlNode == nil {
 | 
						if rc.yamlNode == nil {
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -119,7 +114,7 @@ func (rc *RenderConfig) toMetaNode() ast.Node {
 | 
				
			|||||||
	case markup.RenderMetaAsTable:
 | 
						case markup.RenderMetaAsTable:
 | 
				
			||||||
		return nodeToTable(rc.yamlNode)
 | 
							return nodeToTable(rc.yamlNode)
 | 
				
			||||||
	case markup.RenderMetaAsDetails:
 | 
						case markup.RenderMetaAsDetails:
 | 
				
			||||||
		return nodeToDetails(rc.yamlNode, rc.Icon)
 | 
							return nodeToDetails(g, rc.yamlNode)
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,42 +21,36 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
 | 
				
			|||||||
		{
 | 
							{
 | 
				
			||||||
			"empty", &RenderConfig{
 | 
								"empty", &RenderConfig{
 | 
				
			||||||
				Meta: "table",
 | 
									Meta: "table",
 | 
				
			||||||
				Icon: "table",
 | 
					 | 
				
			||||||
				Lang: "",
 | 
									Lang: "",
 | 
				
			||||||
			}, "",
 | 
								}, "",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"lang", &RenderConfig{
 | 
								"lang", &RenderConfig{
 | 
				
			||||||
				Meta: "table",
 | 
									Meta: "table",
 | 
				
			||||||
				Icon: "table",
 | 
					 | 
				
			||||||
				Lang: "test",
 | 
									Lang: "test",
 | 
				
			||||||
			}, "lang: test",
 | 
								}, "lang: test",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"metatable", &RenderConfig{
 | 
								"metatable", &RenderConfig{
 | 
				
			||||||
				Meta: "table",
 | 
									Meta: "table",
 | 
				
			||||||
				Icon: "table",
 | 
					 | 
				
			||||||
				Lang: "",
 | 
									Lang: "",
 | 
				
			||||||
			}, "gitea: table",
 | 
								}, "gitea: table",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"metanone", &RenderConfig{
 | 
								"metanone", &RenderConfig{
 | 
				
			||||||
				Meta: "none",
 | 
									Meta: "none",
 | 
				
			||||||
				Icon: "table",
 | 
					 | 
				
			||||||
				Lang: "",
 | 
									Lang: "",
 | 
				
			||||||
			}, "gitea: none",
 | 
								}, "gitea: none",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"metadetails", &RenderConfig{
 | 
								"metadetails", &RenderConfig{
 | 
				
			||||||
				Meta: "details",
 | 
									Meta: "details",
 | 
				
			||||||
				Icon: "table",
 | 
					 | 
				
			||||||
				Lang: "",
 | 
									Lang: "",
 | 
				
			||||||
			}, "gitea: details",
 | 
								}, "gitea: details",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"metawrong", &RenderConfig{
 | 
								"metawrong", &RenderConfig{
 | 
				
			||||||
				Meta: "details",
 | 
									Meta: "details",
 | 
				
			||||||
				Icon: "table",
 | 
					 | 
				
			||||||
				Lang: "",
 | 
									Lang: "",
 | 
				
			||||||
			}, "gitea: wrong",
 | 
								}, "gitea: wrong",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
@@ -64,7 +58,6 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
 | 
				
			|||||||
			"toc", &RenderConfig{
 | 
								"toc", &RenderConfig{
 | 
				
			||||||
				TOC:  "true",
 | 
									TOC:  "true",
 | 
				
			||||||
				Meta: "table",
 | 
									Meta: "table",
 | 
				
			||||||
				Icon: "table",
 | 
					 | 
				
			||||||
				Lang: "",
 | 
									Lang: "",
 | 
				
			||||||
			}, "include_toc: true",
 | 
								}, "include_toc: true",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
@@ -72,14 +65,12 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
 | 
				
			|||||||
			"tocfalse", &RenderConfig{
 | 
								"tocfalse", &RenderConfig{
 | 
				
			||||||
				TOC:  "false",
 | 
									TOC:  "false",
 | 
				
			||||||
				Meta: "table",
 | 
									Meta: "table",
 | 
				
			||||||
				Icon: "table",
 | 
					 | 
				
			||||||
				Lang: "",
 | 
									Lang: "",
 | 
				
			||||||
			}, "include_toc: false",
 | 
								}, "include_toc: false",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"toclang", &RenderConfig{
 | 
								"toclang", &RenderConfig{
 | 
				
			||||||
				Meta: "table",
 | 
									Meta: "table",
 | 
				
			||||||
				Icon: "table",
 | 
					 | 
				
			||||||
				TOC:  "true",
 | 
									TOC:  "true",
 | 
				
			||||||
				Lang: "testlang",
 | 
									Lang: "testlang",
 | 
				
			||||||
			}, `
 | 
								}, `
 | 
				
			||||||
@@ -90,7 +81,6 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
 | 
				
			|||||||
		{
 | 
							{
 | 
				
			||||||
			"complexlang", &RenderConfig{
 | 
								"complexlang", &RenderConfig{
 | 
				
			||||||
				Meta: "table",
 | 
									Meta: "table",
 | 
				
			||||||
				Icon: "table",
 | 
					 | 
				
			||||||
				Lang: "testlang",
 | 
									Lang: "testlang",
 | 
				
			||||||
			}, `
 | 
								}, `
 | 
				
			||||||
				gitea:
 | 
									gitea:
 | 
				
			||||||
@@ -100,7 +90,6 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
 | 
				
			|||||||
		{
 | 
							{
 | 
				
			||||||
			"complexlang2", &RenderConfig{
 | 
								"complexlang2", &RenderConfig{
 | 
				
			||||||
				Meta: "table",
 | 
									Meta: "table",
 | 
				
			||||||
				Icon: "table",
 | 
					 | 
				
			||||||
				Lang: "testlang",
 | 
									Lang: "testlang",
 | 
				
			||||||
			}, `
 | 
								}, `
 | 
				
			||||||
	lang: notright
 | 
						lang: notright
 | 
				
			||||||
@@ -111,7 +100,6 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
 | 
				
			|||||||
		{
 | 
							{
 | 
				
			||||||
			"complexlang", &RenderConfig{
 | 
								"complexlang", &RenderConfig{
 | 
				
			||||||
				Meta: "table",
 | 
									Meta: "table",
 | 
				
			||||||
				Icon: "table",
 | 
					 | 
				
			||||||
				Lang: "testlang",
 | 
									Lang: "testlang",
 | 
				
			||||||
			}, `
 | 
								}, `
 | 
				
			||||||
	gitea:
 | 
						gitea:
 | 
				
			||||||
@@ -123,7 +111,6 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
 | 
				
			|||||||
				Lang: "two",
 | 
									Lang: "two",
 | 
				
			||||||
				Meta: "table",
 | 
									Meta: "table",
 | 
				
			||||||
				TOC:  "true",
 | 
									TOC:  "true",
 | 
				
			||||||
				Icon: "smiley",
 | 
					 | 
				
			||||||
			}, `
 | 
								}, `
 | 
				
			||||||
	lang: one
 | 
						lang: one
 | 
				
			||||||
	include_toc: true
 | 
						include_toc: true
 | 
				
			||||||
@@ -139,14 +126,12 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
 | 
				
			|||||||
		t.Run(tt.name, func(t *testing.T) {
 | 
							t.Run(tt.name, func(t *testing.T) {
 | 
				
			||||||
			got := &RenderConfig{
 | 
								got := &RenderConfig{
 | 
				
			||||||
				Meta: "table",
 | 
									Meta: "table",
 | 
				
			||||||
				Icon: "table",
 | 
					 | 
				
			||||||
				Lang: "",
 | 
									Lang: "",
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			err := yaml.Unmarshal([]byte(strings.ReplaceAll(tt.args, "\t", "    ")), got)
 | 
								err := yaml.Unmarshal([]byte(strings.ReplaceAll(tt.args, "\t", "    ")), got)
 | 
				
			||||||
			require.NoError(t, err)
 | 
								require.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			assert.Equal(t, tt.expected.Meta, got.Meta)
 | 
								assert.Equal(t, tt.expected.Meta, got.Meta)
 | 
				
			||||||
			assert.Equal(t, tt.expected.Icon, got.Icon)
 | 
					 | 
				
			||||||
			assert.Equal(t, tt.expected.Lang, got.Lang)
 | 
								assert.Equal(t, tt.expected.Lang, got.Lang)
 | 
				
			||||||
			assert.Equal(t, tt.expected.TOC, got.TOC)
 | 
								assert.Equal(t, tt.expected.TOC, got.TOC)
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -511,6 +511,18 @@
 | 
				
			|||||||
  padding-left: 2em;
 | 
					  padding-left: 2em;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markup details.frontmatter-content summary {
 | 
				
			||||||
 | 
					  text-overflow: ellipsis;
 | 
				
			||||||
 | 
					  overflow: hidden;
 | 
				
			||||||
 | 
					  white-space: nowrap;
 | 
				
			||||||
 | 
					  margin-bottom: 0.25em;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.markup details.frontmatter-content svg {
 | 
				
			||||||
 | 
					  vertical-align: middle;
 | 
				
			||||||
 | 
					  margin: 0 0.25em;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.file-revisions-btn {
 | 
					.file-revisions-btn {
 | 
				
			||||||
  display: block;
 | 
					  display: block;
 | 
				
			||||||
  float: left;
 | 
					  float: left;
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user