mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-26 12:27:06 +00: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,23 +4,22 @@ | |||||||
| 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 { | ||||||
| 		if meta == nil { | 		meta = meta.Content[0] | ||||||
| 			return nil | 	} | ||||||
| 		} | 	if meta == nil { | ||||||
| 		switch meta.Kind { | 		return nil | ||||||
| 		case yaml.DocumentNode: |  | ||||||
| 			meta = meta.Content[0] |  | ||||||
| 			continue |  | ||||||
| 		default: |  | ||||||
| 		} |  | ||||||
| 		break |  | ||||||
| 	} | 	} | ||||||
| 	switch meta.Kind { | 	switch meta.Kind { | ||||||
| 	case yaml.MappingNode: | 	case yaml.MappingNode: | ||||||
| @@ -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
	 wxiaoguang
					wxiaoguang