mirror of
https://github.com/rwinkhart/glamour-temp-MUTN.git
synced 2026-09-02 15:07:28 -04:00
Switch to goldmark parser
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
package gold
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
type CheckedItemElement struct {
|
||||
Checked bool
|
||||
}
|
||||
|
||||
func (e *CheckedItemElement) Render(w io.Writer, ctx RenderContext) error {
|
||||
var el *BaseElement
|
||||
|
||||
pre := "✗ "
|
||||
if e.Checked {
|
||||
pre = "✓ "
|
||||
}
|
||||
|
||||
el = &BaseElement{
|
||||
Prefix: pre,
|
||||
Style: ctx.style[CheckedItem],
|
||||
}
|
||||
|
||||
return el.Render(w, ctx)
|
||||
}
|
||||
+202
-130
@@ -1,9 +1,12 @@
|
||||
package gold
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
bf "gopkg.in/russross/blackfriday.v2"
|
||||
"github.com/yuin/goldmark/ast"
|
||||
astext "github.com/yuin/goldmark/extension/ast"
|
||||
)
|
||||
|
||||
type ElementRenderer interface {
|
||||
@@ -21,210 +24,279 @@ type Element struct {
|
||||
Finisher ElementFinisher
|
||||
}
|
||||
|
||||
func (tr *TermRenderer) NewElement(node *bf.Node) Element {
|
||||
func (tr *TermRenderer) NewElement(node ast.Node, source []byte) Element {
|
||||
ctx := tr.context
|
||||
// fmt.Print(strings.Repeat(" ", ctx.blockStack.Len()), node.Type(), node.Kind())
|
||||
// defer fmt.Println()
|
||||
|
||||
switch node.Type {
|
||||
case bf.Document:
|
||||
de := &DocumentElement{}
|
||||
switch node.Kind() {
|
||||
// Document
|
||||
case ast.KindDocument:
|
||||
return Element{
|
||||
Renderer: de,
|
||||
Finisher: de,
|
||||
Renderer: &DocumentElement{},
|
||||
Finisher: &DocumentElement{},
|
||||
}
|
||||
|
||||
// Heading
|
||||
case ast.KindHeading:
|
||||
n := node.(*ast.Heading)
|
||||
he := &HeadingElement{
|
||||
Level: n.Level,
|
||||
First: node.PreviousSibling() == nil,
|
||||
}
|
||||
return Element{
|
||||
Exiting: "",
|
||||
Renderer: he,
|
||||
Finisher: he,
|
||||
}
|
||||
|
||||
// Paragraph
|
||||
case ast.KindParagraph:
|
||||
return Element{
|
||||
Renderer: &ParagraphElement{},
|
||||
Finisher: &ParagraphElement{},
|
||||
}
|
||||
|
||||
// Blockquote
|
||||
case ast.KindBlockquote:
|
||||
e := &BlockElement{
|
||||
Block: &bytes.Buffer{},
|
||||
Style: cascadeStyle(ctx.blockStack.Current().Style, ctx.style[BlockQuote], true),
|
||||
Margin: true,
|
||||
}
|
||||
case bf.BlockQuote:
|
||||
return Element{
|
||||
Entering: "\n",
|
||||
Exiting: "\n",
|
||||
Renderer: &BaseElement{
|
||||
Token: string(node.Literal),
|
||||
Style: ctx.style[BlockQuote],
|
||||
},
|
||||
Renderer: e,
|
||||
Finisher: e,
|
||||
}
|
||||
case bf.List:
|
||||
le := &ListElement{
|
||||
Nested: node.Parent.Type == bf.Item,
|
||||
|
||||
// Lists
|
||||
case ast.KindList:
|
||||
e := &BlockElement{
|
||||
Block: &bytes.Buffer{},
|
||||
Style: cascadeStyle(ctx.blockStack.Current().Style, ctx.style[List], true),
|
||||
Margin: true,
|
||||
}
|
||||
return Element{
|
||||
Renderer: le,
|
||||
Finisher: le,
|
||||
Entering: "\n",
|
||||
Renderer: e,
|
||||
Finisher: e,
|
||||
}
|
||||
case bf.Item:
|
||||
|
||||
case ast.KindListItem:
|
||||
var l uint
|
||||
if node.ListData.ListFlags&bf.ListTypeOrdered > 0 {
|
||||
l = 1
|
||||
n := node
|
||||
for n.Prev != nil && (n.Prev.Type == bf.Item) {
|
||||
l++
|
||||
n = n.Prev
|
||||
var e uint
|
||||
l = 1
|
||||
n := node
|
||||
for n.PreviousSibling() != nil && (n.PreviousSibling().Kind() == ast.KindListItem) {
|
||||
l++
|
||||
n = n.PreviousSibling()
|
||||
}
|
||||
if node.Parent().(*ast.List).IsOrdered() {
|
||||
e = l
|
||||
}
|
||||
|
||||
post := "\n"
|
||||
if node.LastChild().Kind() == ast.KindList || node.NextSibling() == nil {
|
||||
post = ""
|
||||
}
|
||||
|
||||
if node.FirstChild().FirstChild().Kind() == astext.KindTaskCheckBox {
|
||||
nc := node.FirstChild().FirstChild().(*astext.TaskCheckBox)
|
||||
|
||||
return Element{
|
||||
Exiting: post,
|
||||
Renderer: &CheckedItemElement{
|
||||
Checked: nc.IsChecked,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return Element{
|
||||
Exiting: post,
|
||||
Renderer: &ItemElement{
|
||||
Text: string(node.Literal),
|
||||
Enumeration: l,
|
||||
Enumeration: e,
|
||||
},
|
||||
}
|
||||
case bf.Paragraph:
|
||||
pe := &ParagraphElement{
|
||||
InsideList: node.Parent != nil && node.Parent.Type == bf.Item,
|
||||
|
||||
// Text Elements
|
||||
case ast.KindText:
|
||||
n := node.(*ast.Text)
|
||||
s := string(n.Segment.Value(source))
|
||||
|
||||
if n.HardLineBreak() || (n.SoftLineBreak()) {
|
||||
s += "\n"
|
||||
}
|
||||
return Element{
|
||||
Renderer: pe,
|
||||
Finisher: pe,
|
||||
}
|
||||
case bf.Heading:
|
||||
return Element{
|
||||
Exiting: "\n",
|
||||
Renderer: &HeadingElement{
|
||||
Text: string(node.FirstChild.Literal),
|
||||
Level: node.HeadingData.Level,
|
||||
First: node.Prev == nil,
|
||||
Renderer: &BaseElement{
|
||||
Token: ctx.SanitizeHTML(s, false),
|
||||
Style: ctx.style[Text],
|
||||
},
|
||||
}
|
||||
case bf.HorizontalRule:
|
||||
|
||||
case ast.KindEmphasis:
|
||||
n := node.(*ast.Emphasis)
|
||||
s := string(n.Text(source))
|
||||
style := ctx.style[Emph]
|
||||
if n.Level > 1 {
|
||||
style = ctx.style[Strong]
|
||||
}
|
||||
|
||||
return Element{
|
||||
Renderer: &BaseElement{
|
||||
Token: ctx.SanitizeHTML(s, false),
|
||||
Style: style,
|
||||
},
|
||||
}
|
||||
|
||||
case ast.KindThematicBreak:
|
||||
return Element{
|
||||
Entering: "",
|
||||
Exiting: "\n",
|
||||
Exiting: "",
|
||||
Renderer: &BaseElement{
|
||||
Style: ctx.style[HorizontalRule],
|
||||
},
|
||||
}
|
||||
case bf.Emph:
|
||||
return Element{
|
||||
Renderer: &BaseElement{
|
||||
Token: string(node.FirstChild.Literal),
|
||||
Style: ctx.style[Emph],
|
||||
},
|
||||
}
|
||||
case bf.Strong:
|
||||
return Element{
|
||||
Renderer: &BaseElement{
|
||||
Token: string(node.FirstChild.Literal),
|
||||
Style: ctx.style[Strong],
|
||||
},
|
||||
}
|
||||
case bf.Del:
|
||||
return Element{
|
||||
Renderer: &BaseElement{
|
||||
Token: string(node.Literal),
|
||||
Style: ctx.style[Del],
|
||||
},
|
||||
}
|
||||
case bf.Link:
|
||||
var text string
|
||||
if node.LastChild != nil {
|
||||
text = string(node.LastChild.Literal)
|
||||
}
|
||||
|
||||
// Links
|
||||
case ast.KindLink:
|
||||
n := node.(*ast.Link)
|
||||
text := string(n.Text(source))
|
||||
return Element{
|
||||
Renderer: &LinkElement{
|
||||
Text: text,
|
||||
BaseURL: tr.context.options.BaseURL,
|
||||
URL: string(node.LinkData.Destination),
|
||||
BaseURL: ctx.options.BaseURL,
|
||||
URL: string(n.Destination),
|
||||
},
|
||||
}
|
||||
case bf.Image:
|
||||
var text string
|
||||
if node.LastChild != nil {
|
||||
text = string(node.LastChild.Literal)
|
||||
case ast.KindAutoLink:
|
||||
n := node.(*ast.AutoLink)
|
||||
text := string(n.Text(source))
|
||||
return Element{
|
||||
Renderer: &LinkElement{
|
||||
Text: text,
|
||||
BaseURL: ctx.options.BaseURL,
|
||||
URL: string(n.URL(source)),
|
||||
},
|
||||
}
|
||||
|
||||
// Images
|
||||
case ast.KindImage:
|
||||
n := node.(*ast.Image)
|
||||
text := string(n.Text(source))
|
||||
return Element{
|
||||
Renderer: &ImageElement{
|
||||
Text: text,
|
||||
BaseURL: tr.context.options.BaseURL,
|
||||
URL: string(node.LinkData.Destination),
|
||||
BaseURL: ctx.options.BaseURL,
|
||||
URL: string(n.Destination),
|
||||
},
|
||||
}
|
||||
case bf.Text:
|
||||
return Element{
|
||||
Renderer: &BaseElement{
|
||||
Token: tr.context.SanitizeHTML(string(node.Literal), false),
|
||||
Style: ctx.style[Text],
|
||||
},
|
||||
|
||||
// Code
|
||||
case ast.KindFencedCodeBlock:
|
||||
n := node.(*ast.FencedCodeBlock)
|
||||
l := n.Lines().Len()
|
||||
s := ""
|
||||
for i := 0; i < l; i++ {
|
||||
line := n.Lines().At(i)
|
||||
s += string(line.Value(source))
|
||||
}
|
||||
case bf.HTMLBlock:
|
||||
return Element{
|
||||
Renderer: &BaseElement{
|
||||
Token: tr.context.SanitizeHTML(string(node.Literal), true) + "\n",
|
||||
Style: ctx.style[HTMLBlock],
|
||||
},
|
||||
}
|
||||
case bf.CodeBlock:
|
||||
return Element{
|
||||
Entering: "\n",
|
||||
Renderer: &CodeBlockElement{
|
||||
Code: string(node.Literal),
|
||||
Language: string(node.CodeBlockData.Info),
|
||||
Code: s,
|
||||
Language: string(n.Language(source)),
|
||||
},
|
||||
}
|
||||
case bf.Softbreak:
|
||||
|
||||
case ast.KindCodeBlock:
|
||||
n := node.(*ast.CodeBlock)
|
||||
l := n.Lines().Len()
|
||||
s := ""
|
||||
for i := 0; i < l; i++ {
|
||||
line := n.Lines().At(i)
|
||||
s += string(line.Value(source))
|
||||
}
|
||||
return Element{
|
||||
Exiting: "\n",
|
||||
Renderer: &BaseElement{
|
||||
Token: string(node.Literal),
|
||||
Style: ctx.style[Softbreak],
|
||||
Entering: "\n",
|
||||
Renderer: &CodeBlockElement{
|
||||
Code: s,
|
||||
},
|
||||
}
|
||||
case bf.Hardbreak:
|
||||
|
||||
case ast.KindCodeSpan:
|
||||
// n := node.(*ast.CodeSpan)
|
||||
e := &BlockElement{
|
||||
Block: &bytes.Buffer{},
|
||||
Style: cascadeStyle(ctx.blockStack.Current().Style, ctx.style[Code], true),
|
||||
}
|
||||
return Element{
|
||||
Exiting: "\n",
|
||||
Renderer: &BaseElement{
|
||||
Token: string(node.Literal),
|
||||
Style: ctx.style[Hardbreak],
|
||||
},
|
||||
Renderer: e,
|
||||
Finisher: e,
|
||||
}
|
||||
case bf.Code:
|
||||
return Element{
|
||||
Renderer: &BaseElement{
|
||||
Token: string(node.Literal),
|
||||
Style: ctx.style[Code],
|
||||
},
|
||||
}
|
||||
case bf.HTMLSpan:
|
||||
return Element{
|
||||
Renderer: &BaseElement{
|
||||
Token: tr.context.SanitizeHTML(string(node.Literal), true) + "\n",
|
||||
Style: ctx.style[HTMLSpan],
|
||||
},
|
||||
}
|
||||
case bf.Table:
|
||||
|
||||
// Tables
|
||||
case astext.KindTable:
|
||||
te := &TableElement{}
|
||||
return Element{
|
||||
Entering: "\n",
|
||||
Renderer: te,
|
||||
Finisher: te,
|
||||
}
|
||||
case bf.TableCell:
|
||||
|
||||
case astext.KindTableCell:
|
||||
s := ""
|
||||
n := node.FirstChild
|
||||
n := node.FirstChild()
|
||||
for n != nil {
|
||||
s += string(n.Literal)
|
||||
s += string(n.LinkData.Destination)
|
||||
n = n.Next
|
||||
s += string(n.Text(source))
|
||||
// s += string(n.LinkData.Destination)
|
||||
n = n.NextSibling()
|
||||
}
|
||||
|
||||
return Element{
|
||||
Renderer: &TableCellElement{
|
||||
Text: s,
|
||||
Head: node.Parent.Parent.Type == bf.TableHead,
|
||||
Head: node.Parent().Kind() == astext.KindTableHeader,
|
||||
},
|
||||
}
|
||||
case bf.TableHead:
|
||||
|
||||
case astext.KindTableHeader:
|
||||
return Element{
|
||||
Finisher: &TableHeadElement{},
|
||||
}
|
||||
case bf.TableBody:
|
||||
return Element{}
|
||||
case bf.TableRow:
|
||||
case astext.KindTableRow:
|
||||
return Element{
|
||||
Finisher: &TableRowElement{},
|
||||
}
|
||||
|
||||
default:
|
||||
// HTML Elements
|
||||
case ast.KindHTMLBlock:
|
||||
n := node.(*ast.HTMLBlock)
|
||||
return Element{
|
||||
Renderer: &BaseElement{
|
||||
Token: string(node.Literal),
|
||||
Token: ctx.SanitizeHTML(string(n.Text(source)), true) + "\n",
|
||||
Style: ctx.style[HTMLBlock],
|
||||
},
|
||||
}
|
||||
case ast.KindRawHTML:
|
||||
n := node.(*ast.RawHTML)
|
||||
return Element{
|
||||
Renderer: &BaseElement{
|
||||
Token: ctx.SanitizeHTML(string(n.Text(source)), true) + "\n",
|
||||
Style: ctx.style[HTMLSpan],
|
||||
},
|
||||
}
|
||||
|
||||
// Handled by parents
|
||||
case astext.KindTaskCheckBox:
|
||||
// handled by KindListItem
|
||||
return Element{}
|
||||
case ast.KindTextBlock:
|
||||
return Element{}
|
||||
|
||||
// Unknown case
|
||||
default:
|
||||
fmt.Println("Warning: unhandled element", node.Kind().String())
|
||||
return Element{}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,5 +14,6 @@ require (
|
||||
github.com/rakyll/statik v0.1.6
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||
github.com/spf13/cobra v0.0.5
|
||||
github.com/yuin/goldmark v1.1.14
|
||||
gopkg.in/russross/blackfriday.v2 v2.0.0
|
||||
)
|
||||
|
||||
@@ -87,6 +87,8 @@ github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljT
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.1.14 h1:9/OvYI+gdtQ5EAZY0y4kuVnuKjlE03BRqTw/njWYRNo=
|
||||
github.com/yuin/goldmark v1.1.14/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3 h1:eH6Eip3UpmR+yM/qI9Ijluzb1bNv/cAU/n+6l8tRSis=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package gold
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -8,6 +9,13 @@ import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/yuin/goldmark"
|
||||
"github.com/yuin/goldmark/ast"
|
||||
"github.com/yuin/goldmark/extension"
|
||||
astext "github.com/yuin/goldmark/extension/ast"
|
||||
"github.com/yuin/goldmark/parser"
|
||||
"github.com/yuin/goldmark/renderer"
|
||||
"github.com/yuin/goldmark/util"
|
||||
bf "gopkg.in/russross/blackfriday.v2"
|
||||
)
|
||||
|
||||
@@ -83,21 +91,83 @@ func (tr *TermRenderer) Render(in string) string {
|
||||
|
||||
// RenderBytes returns the markdown rendered into a byte slice.
|
||||
func (tr *TermRenderer) RenderBytes(in []byte) []byte {
|
||||
return bf.Run(in, bf.WithRenderer(tr))
|
||||
md := goldmark.New(
|
||||
goldmark.WithExtensions(extension.GFM),
|
||||
goldmark.WithParserOptions(
|
||||
parser.WithAutoHeadingID(),
|
||||
),
|
||||
)
|
||||
md.SetRenderer(
|
||||
renderer.NewRenderer(
|
||||
renderer.WithNodeRenderers(util.Prioritized(tr, 1000))))
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := md.Convert(in, &buf); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// RenderNode renders a single markdown node.
|
||||
func (tr *TermRenderer) RenderNode(w io.Writer, node *bf.Node, entering bool) bf.WalkStatus {
|
||||
// RegisterFuncs implements NodeRenderer.RegisterFuncs .
|
||||
func (r *TermRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
|
||||
// blocks
|
||||
reg.Register(ast.KindDocument, r.renderNode)
|
||||
reg.Register(ast.KindHeading, r.renderNode)
|
||||
reg.Register(ast.KindBlockquote, r.renderNode)
|
||||
reg.Register(ast.KindCodeBlock, r.renderNode)
|
||||
reg.Register(ast.KindFencedCodeBlock, r.renderNode)
|
||||
reg.Register(ast.KindHTMLBlock, r.renderNode)
|
||||
reg.Register(ast.KindList, r.renderNode)
|
||||
reg.Register(ast.KindListItem, r.renderNode)
|
||||
reg.Register(ast.KindParagraph, r.renderNode)
|
||||
reg.Register(ast.KindTextBlock, r.renderNode)
|
||||
reg.Register(ast.KindThematicBreak, r.renderNode)
|
||||
|
||||
// inlines
|
||||
reg.Register(ast.KindAutoLink, r.renderNode)
|
||||
reg.Register(ast.KindCodeSpan, r.renderNode)
|
||||
reg.Register(ast.KindEmphasis, r.renderNode)
|
||||
reg.Register(ast.KindImage, r.renderNode)
|
||||
reg.Register(ast.KindLink, r.renderNode)
|
||||
reg.Register(ast.KindRawHTML, r.renderNode)
|
||||
reg.Register(ast.KindText, r.renderNode)
|
||||
reg.Register(ast.KindString, r.renderNode)
|
||||
|
||||
// tables
|
||||
reg.Register(astext.KindTable, r.renderNode)
|
||||
reg.Register(astext.KindTableHeader, r.renderNode)
|
||||
reg.Register(astext.KindTableRow, r.renderNode)
|
||||
reg.Register(astext.KindTableCell, r.renderNode)
|
||||
|
||||
// definitions
|
||||
reg.Register(astext.KindDefinitionList, r.renderNode)
|
||||
reg.Register(astext.KindDefinitionTerm, r.renderNode)
|
||||
reg.Register(astext.KindDefinitionDescription, r.renderNode)
|
||||
|
||||
// footnotes
|
||||
reg.Register(astext.KindFootnote, r.renderNode)
|
||||
reg.Register(astext.KindFootnoteList, r.renderNode)
|
||||
reg.Register(astext.KindFootnoteLink, r.renderNode)
|
||||
reg.Register(astext.KindFootnoteBackLink, r.renderNode)
|
||||
|
||||
// checkboxes
|
||||
reg.Register(astext.KindTaskCheckBox, r.renderNode)
|
||||
|
||||
// strikethrough
|
||||
reg.Register(astext.KindStrikethrough, r.renderNode)
|
||||
}
|
||||
|
||||
func (tr *TermRenderer) renderNode(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
|
||||
// _, _ = w.Write([]byte(node.Type.String()))
|
||||
writeTo := w
|
||||
writeTo := io.Writer(w)
|
||||
bs := tr.context.blockStack
|
||||
|
||||
// children get rendered by their parent
|
||||
if isChild(node) {
|
||||
return bf.GoToNext
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
e := tr.NewElement(node)
|
||||
e := tr.NewElement(node, source)
|
||||
if entering {
|
||||
// everything below the Document element gets rendered into a block buffer
|
||||
if bs.Len() > 0 {
|
||||
@@ -108,8 +178,7 @@ func (tr *TermRenderer) RenderNode(w io.Writer, node *bf.Node, entering bool) bf
|
||||
if e.Renderer != nil {
|
||||
err := e.Renderer.Render(writeTo, tr.context)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return bf.Terminate
|
||||
return ast.WalkStop, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -120,21 +189,20 @@ func (tr *TermRenderer) RenderNode(w io.Writer, node *bf.Node, entering bool) bf
|
||||
|
||||
// if we're finished rendering the entire document,
|
||||
// flush to the real writer
|
||||
if node.Type == bf.Document {
|
||||
if node.Type() == ast.TypeDocument {
|
||||
writeTo = w
|
||||
}
|
||||
|
||||
if e.Finisher != nil {
|
||||
err := e.Finisher.Finish(writeTo, tr.context)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return bf.Terminate
|
||||
return ast.WalkStop, err
|
||||
}
|
||||
}
|
||||
_, _ = writeTo.Write([]byte(e.Exiting))
|
||||
_, _ = bs.Current().Block.Write([]byte(e.Exiting))
|
||||
}
|
||||
|
||||
return bf.GoToNext
|
||||
return ast.WalkContinue, nil
|
||||
}
|
||||
|
||||
// RenderHeader renders the markdown's header.
|
||||
@@ -145,14 +213,14 @@ func (tr *TermRenderer) RenderHeader(w io.Writer, ast *bf.Node) {
|
||||
func (tr *TermRenderer) RenderFooter(w io.Writer, ast *bf.Node) {
|
||||
}
|
||||
|
||||
func isChild(node *bf.Node) bool {
|
||||
if node.Parent == nil {
|
||||
func isChild(node ast.Node) bool {
|
||||
if node.Parent() == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// These types are already rendered by their parent
|
||||
switch node.Parent.Type {
|
||||
case bf.Heading, bf.Link, bf.Image, bf.TableCell, bf.Emph, bf.Strong:
|
||||
switch node.Parent().Kind() {
|
||||
case ast.KindLink, ast.KindImage, ast.KindEmphasis, ast.KindBlockquote, astext.KindTableCell:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
|
||||
+33
-15
@@ -1,13 +1,13 @@
|
||||
package gold
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
|
||||
"github.com/muesli/reflow"
|
||||
)
|
||||
|
||||
type HeadingElement struct {
|
||||
Text string
|
||||
Level int
|
||||
First bool
|
||||
}
|
||||
@@ -31,6 +31,25 @@ func (e *HeadingElement) Render(w io.Writer, ctx RenderContext) error {
|
||||
rules = cascadeStyles(false, rules, ctx.style[H6])
|
||||
}
|
||||
|
||||
if !e.First {
|
||||
renderText(w, bs.Current().Style, "\n")
|
||||
}
|
||||
|
||||
be := BlockElement{
|
||||
Block: &bytes.Buffer{},
|
||||
Style: cascadeStyle(bs.Current().Style, rules, true),
|
||||
}
|
||||
bs.Push(be)
|
||||
|
||||
renderText(w, bs.Parent().Style, rules.Prefix)
|
||||
renderText(w, bs.Current().Style, rules.StyledPrefix)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *HeadingElement) Finish(w io.Writer, ctx RenderContext) error {
|
||||
bs := ctx.blockStack
|
||||
rules := bs.Current().Style
|
||||
|
||||
var indent uint
|
||||
var margin uint
|
||||
if rules.Indent != nil {
|
||||
@@ -43,7 +62,7 @@ func (e *HeadingElement) Render(w io.Writer, ctx RenderContext) error {
|
||||
iw := &IndentWriter{
|
||||
Indent: indent + margin,
|
||||
IndentFunc: func(wr io.Writer) {
|
||||
renderText(w, bs.Current().Style, " ")
|
||||
renderText(w, bs.Parent().Style, " ")
|
||||
},
|
||||
Forward: &AnsiWriter{
|
||||
Forward: w,
|
||||
@@ -51,22 +70,21 @@ func (e *HeadingElement) Render(w io.Writer, ctx RenderContext) error {
|
||||
}
|
||||
|
||||
flow := reflow.NewReflow(int(bs.Width(ctx) - indent - margin*2))
|
||||
_, err := flow.Write(bs.Current().Block.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
flow.Close()
|
||||
|
||||
var pre string
|
||||
if !e.First {
|
||||
pre = "\n"
|
||||
}
|
||||
el := &BaseElement{
|
||||
Prefix: pre,
|
||||
Token: e.Text,
|
||||
Style: rules,
|
||||
}
|
||||
err := el.Render(flow, ctx)
|
||||
_, err = iw.Write(flow.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
flow.Close()
|
||||
_, err = iw.Write(flow.Bytes())
|
||||
return err
|
||||
renderText(w, bs.Current().Style, rules.StyledSuffix)
|
||||
renderText(w, bs.Parent().Style, rules.Suffix)
|
||||
|
||||
bs.Current().Block.Reset()
|
||||
bs.Pop()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
package gold
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
|
||||
"github.com/muesli/reflow"
|
||||
)
|
||||
|
||||
type ListElement struct {
|
||||
Nested bool
|
||||
}
|
||||
|
||||
func (e *ListElement) Render(w io.Writer, ctx RenderContext) error {
|
||||
bs := ctx.blockStack
|
||||
rules := ctx.style[List]
|
||||
|
||||
if !e.Nested {
|
||||
_, _ = w.Write([]byte("\n"))
|
||||
}
|
||||
|
||||
be := BlockElement{
|
||||
Block: &bytes.Buffer{},
|
||||
Style: cascadeStyle(bs.Current().Style, rules, true),
|
||||
}
|
||||
bs.Push(be)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *ListElement) Finish(w io.Writer, ctx RenderContext) error {
|
||||
bs := ctx.blockStack
|
||||
rules := bs.Current().Style
|
||||
|
||||
renderText(bs.Current().Block, rules, rules.Suffix)
|
||||
|
||||
_, err := NewMarginWriter(ctx, w, rules).Write(
|
||||
reflow.Bytes(bs.Current().Block.Bytes(), int(bs.Width(ctx))))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bs.Current().Block.Reset()
|
||||
bs.Pop()
|
||||
return nil
|
||||
}
|
||||
+7
-20
@@ -9,25 +9,19 @@ import (
|
||||
)
|
||||
|
||||
type ParagraphElement struct {
|
||||
InsideList bool
|
||||
}
|
||||
|
||||
func (e *ParagraphElement) Render(w io.Writer, ctx RenderContext) error {
|
||||
bs := ctx.blockStack
|
||||
var rules ElementStyle
|
||||
|
||||
if e.InsideList {
|
||||
// list item
|
||||
rules = ctx.style[List]
|
||||
} else {
|
||||
rules = ctx.style[Paragraph]
|
||||
_, _ = w.Write([]byte("\n"))
|
||||
be := BlockElement{
|
||||
Block: &bytes.Buffer{},
|
||||
Style: cascadeStyle(bs.Current().Style, rules, true),
|
||||
}
|
||||
bs.Push(be)
|
||||
rules = ctx.style[Paragraph]
|
||||
_, _ = w.Write([]byte("\n"))
|
||||
be := BlockElement{
|
||||
Block: &bytes.Buffer{},
|
||||
Style: cascadeStyle(bs.Current().Style, rules, true),
|
||||
}
|
||||
bs.Push(be)
|
||||
|
||||
renderText(w, bs.Current().Style, rules.Prefix)
|
||||
return nil
|
||||
@@ -38,11 +32,6 @@ func (e *ParagraphElement) Finish(w io.Writer, ctx RenderContext) error {
|
||||
rules := bs.Current().Style
|
||||
|
||||
keepNewlines := false
|
||||
if e.InsideList {
|
||||
// remove indent & margin for list items
|
||||
rules = bs.Current().Style
|
||||
keepNewlines = true
|
||||
}
|
||||
|
||||
renderText(bs.Current().Block, rules, rules.Suffix)
|
||||
|
||||
@@ -61,8 +50,6 @@ func (e *ParagraphElement) Finish(w io.Writer, ctx RenderContext) error {
|
||||
}
|
||||
|
||||
bs.Current().Block.Reset()
|
||||
if !e.InsideList {
|
||||
bs.Pop()
|
||||
}
|
||||
bs.Pop()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -16,38 +16,54 @@ type StyleType int
|
||||
|
||||
const (
|
||||
Document StyleType = iota
|
||||
|
||||
// Block elements
|
||||
Heading
|
||||
Paragraph
|
||||
BlockQuote
|
||||
List
|
||||
|
||||
// Text elements
|
||||
Text
|
||||
Emph
|
||||
Strong
|
||||
HorizontalRule
|
||||
|
||||
// ListItem styles
|
||||
Item
|
||||
CheckedItem
|
||||
Enumeration
|
||||
Paragraph
|
||||
Heading
|
||||
|
||||
// Links
|
||||
Link
|
||||
LinkText
|
||||
|
||||
// Images
|
||||
Image
|
||||
ImageText
|
||||
|
||||
// Code styles
|
||||
CodeBlock
|
||||
Code
|
||||
|
||||
// Table styles
|
||||
Table
|
||||
TableCell
|
||||
TableHead
|
||||
TableBody
|
||||
TableRow
|
||||
|
||||
// HTML styles
|
||||
HTMLBlock
|
||||
HTMLSpan
|
||||
|
||||
// Style definitions for individual heading levels
|
||||
H1
|
||||
H2
|
||||
H3
|
||||
H4
|
||||
H5
|
||||
H6
|
||||
HorizontalRule
|
||||
Emph
|
||||
Strong
|
||||
Del
|
||||
Link
|
||||
LinkText
|
||||
Image
|
||||
ImageText
|
||||
Text
|
||||
HTMLBlock
|
||||
CodeBlock
|
||||
Softbreak
|
||||
Hardbreak
|
||||
Code
|
||||
HTMLSpan
|
||||
Table
|
||||
TableCell
|
||||
TableHead
|
||||
TableBody
|
||||
TableRow
|
||||
)
|
||||
|
||||
type ElementStyle struct {
|
||||
@@ -67,6 +83,8 @@ type ElementStyle struct {
|
||||
Theme string `json:"theme"`
|
||||
Prefix string `json:"prefix"`
|
||||
Suffix string `json:"suffix"`
|
||||
StyledPrefix string `json:"styled_prefix"`
|
||||
StyledSuffix string `json:"styled_suffix"`
|
||||
Format string `json:"format"`
|
||||
}
|
||||
|
||||
@@ -235,6 +253,8 @@ func keyToType(key string) (StyleType, error) {
|
||||
return List, nil
|
||||
case "item":
|
||||
return Item, nil
|
||||
case "checked_item":
|
||||
return CheckedItem, nil
|
||||
case "enumeration":
|
||||
return Enumeration, nil
|
||||
case "paragraph":
|
||||
@@ -259,8 +279,6 @@ func keyToType(key string) (StyleType, error) {
|
||||
return Emph, nil
|
||||
case "strong":
|
||||
return Strong, nil
|
||||
case "del":
|
||||
return Del, nil
|
||||
case "link":
|
||||
return Link, nil
|
||||
case "link_text":
|
||||
@@ -275,10 +293,6 @@ func keyToType(key string) (StyleType, error) {
|
||||
return HTMLBlock, nil
|
||||
case "code_block":
|
||||
return CodeBlock, nil
|
||||
case "softbreak":
|
||||
return Softbreak, nil
|
||||
case "hardbreak":
|
||||
return Hardbreak, nil
|
||||
case "code":
|
||||
return Code, nil
|
||||
case "html_span":
|
||||
|
||||
Reference in New Issue
Block a user