diff --git a/checkeditem.go b/checkeditem.go new file mode 100644 index 0000000..1031ba3 --- /dev/null +++ b/checkeditem.go @@ -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) +} diff --git a/elements.go b/elements.go index c3d6f0e..fc70aaf 100644 --- a/elements.go +++ b/elements.go @@ -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{} } } diff --git a/go.mod b/go.mod index 504f86f..c40397c 100644 --- a/go.mod +++ b/go.mod @@ -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 ) diff --git a/go.sum b/go.sum index 7a44b7a..877a365 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/gold.go b/gold.go index f0f2425..844a7a3 100644 --- a/gold.go +++ b/gold.go @@ -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 diff --git a/heading.go b/heading.go index a55e5f0..62f6471 100644 --- a/heading.go +++ b/heading.go @@ -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 } diff --git a/list.go b/list.go deleted file mode 100644 index 251e090..0000000 --- a/list.go +++ /dev/null @@ -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 -} diff --git a/paragraph.go b/paragraph.go index edd6470..ef123ed 100644 --- a/paragraph.go +++ b/paragraph.go @@ -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 } diff --git a/style.go b/style.go index 088d267..089ced0 100644 --- a/style.go +++ b/style.go @@ -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":