Move ANSIRenderer into a separate package

This commit is contained in:
Christian Muehlhaeuser
2019-12-14 00:11:29 +01:00
parent 3018ad9a32
commit 4b65ed22c2
21 changed files with 245 additions and 227 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
package gold package ansi
import ( import (
"io" "io"
+1 -1
View File
@@ -1,4 +1,4 @@
package gold package ansi
import ( import (
"bytes" "bytes"
+1 -1
View File
@@ -1,4 +1,4 @@
package gold package ansi
import ( import (
"bytes" "bytes"
+1 -1
View File
@@ -1,4 +1,4 @@
package gold package ansi
import ( import (
"bytes" "bytes"
+2 -2
View File
@@ -1,4 +1,4 @@
package gold package ansi
import ( import (
"io" "io"
@@ -16,7 +16,7 @@ func (e *CodeBlockElement) Render(w io.Writer, ctx RenderContext) error {
var indent uint var indent uint
var margin uint var margin uint
rules := ctx.styles.CodeBlock rules := ctx.options.Styles.CodeBlock
if rules.Indent != nil { if rules.Indent != nil {
indent = *rules.Indent indent = *rules.Indent
} }
+1 -2
View File
@@ -1,4 +1,4 @@
package gold package ansi
import ( import (
"html" "html"
@@ -9,7 +9,6 @@ import (
type RenderContext struct { type RenderContext struct {
options Options options Options
styles StyleConfig
blockStack *BlockStack blockStack *BlockStack
table *TableElement table *TableElement
+17 -17
View File
@@ -1,4 +1,4 @@
package gold package ansi
import ( import (
"bytes" "bytes"
@@ -25,7 +25,7 @@ type Element struct {
Finisher ElementFinisher Finisher ElementFinisher
} }
func (tr *TermRenderer) NewElement(node ast.Node, source []byte) Element { func (tr *ANSIRenderer) NewElement(node ast.Node, source []byte) Element {
ctx := tr.context ctx := tr.context
// fmt.Print(strings.Repeat(" ", ctx.blockStack.Len()), node.Type(), node.Kind()) // fmt.Print(strings.Repeat(" ", ctx.blockStack.Len()), node.Type(), node.Kind())
// defer fmt.Println() // defer fmt.Println()
@@ -35,7 +35,7 @@ func (tr *TermRenderer) NewElement(node ast.Node, source []byte) Element {
case ast.KindDocument: case ast.KindDocument:
e := &BlockElement{ e := &BlockElement{
Block: &bytes.Buffer{}, Block: &bytes.Buffer{},
Style: ctx.styles.Document, Style: ctx.options.Styles.Document,
Margin: true, Margin: true,
} }
return Element{ return Element{
@@ -70,7 +70,7 @@ func (tr *TermRenderer) NewElement(node ast.Node, source []byte) Element {
case ast.KindBlockquote: case ast.KindBlockquote:
e := &BlockElement{ e := &BlockElement{
Block: &bytes.Buffer{}, Block: &bytes.Buffer{},
Style: cascadeStyle(ctx.blockStack.Current().Style, ctx.styles.BlockQuote, true), Style: cascadeStyle(ctx.blockStack.Current().Style, ctx.options.Styles.BlockQuote, true),
Margin: true, Margin: true,
Newline: true, Newline: true,
} }
@@ -82,7 +82,7 @@ func (tr *TermRenderer) NewElement(node ast.Node, source []byte) Element {
// Lists // Lists
case ast.KindList: case ast.KindList:
s := ctx.styles.List.StyleBlock s := ctx.options.Styles.List.StyleBlock
if s.Indent == nil { if s.Indent == nil {
var i uint var i uint
s.Indent = &i s.Indent = &i
@@ -90,7 +90,7 @@ func (tr *TermRenderer) NewElement(node ast.Node, source []byte) Element {
n := node.Parent() n := node.Parent()
for n != nil { for n != nil {
if n.Kind() == ast.KindList { if n.Kind() == ast.KindList {
i := ctx.styles.List.LevelIndent i := ctx.options.Styles.List.LevelIndent
s.Indent = &i s.Indent = &i
break break
} }
@@ -156,16 +156,16 @@ func (tr *TermRenderer) NewElement(node ast.Node, source []byte) Element {
return Element{ return Element{
Renderer: &BaseElement{ Renderer: &BaseElement{
Token: ctx.SanitizeHTML(s, false), Token: ctx.SanitizeHTML(s, false),
Style: ctx.styles.Text, Style: ctx.options.Styles.Text,
}, },
} }
case ast.KindEmphasis: case ast.KindEmphasis:
n := node.(*ast.Emphasis) n := node.(*ast.Emphasis)
s := string(n.Text(source)) s := string(n.Text(source))
style := ctx.styles.Emph style := ctx.options.Styles.Emph
if n.Level > 1 { if n.Level > 1 {
style = ctx.styles.Strong style = ctx.options.Styles.Strong
} }
return Element{ return Element{
@@ -178,7 +178,7 @@ func (tr *TermRenderer) NewElement(node ast.Node, source []byte) Element {
case astext.KindStrikethrough: case astext.KindStrikethrough:
n := node.(*astext.Strikethrough) n := node.(*astext.Strikethrough)
s := string(n.Text(source)) s := string(n.Text(source))
style := ctx.styles.Strikethrough style := ctx.options.Styles.Strikethrough
return Element{ return Element{
Renderer: &BaseElement{ Renderer: &BaseElement{
@@ -192,7 +192,7 @@ func (tr *TermRenderer) NewElement(node ast.Node, source []byte) Element {
Entering: "", Entering: "",
Exiting: "", Exiting: "",
Renderer: &BaseElement{ Renderer: &BaseElement{
Style: ctx.styles.HorizontalRule, Style: ctx.options.Styles.HorizontalRule,
}, },
} }
@@ -271,7 +271,7 @@ func (tr *TermRenderer) NewElement(node ast.Node, source []byte) Element {
// n := node.(*ast.CodeSpan) // n := node.(*ast.CodeSpan)
e := &BlockElement{ e := &BlockElement{
Block: &bytes.Buffer{}, Block: &bytes.Buffer{},
Style: cascadeStyle(ctx.blockStack.Current().Style, ctx.styles.Code, true), Style: cascadeStyle(ctx.blockStack.Current().Style, ctx.options.Styles.Code, true),
} }
return Element{ return Element{
Renderer: e, Renderer: e,
@@ -318,7 +318,7 @@ func (tr *TermRenderer) NewElement(node ast.Node, source []byte) Element {
return Element{ return Element{
Renderer: &BaseElement{ Renderer: &BaseElement{
Token: ctx.SanitizeHTML(string(n.Text(source)), true) + "\n", Token: ctx.SanitizeHTML(string(n.Text(source)), true) + "\n",
Style: ctx.styles.HTMLBlock.StylePrimitive, Style: ctx.options.Styles.HTMLBlock.StylePrimitive,
}, },
} }
case ast.KindRawHTML: case ast.KindRawHTML:
@@ -326,7 +326,7 @@ func (tr *TermRenderer) NewElement(node ast.Node, source []byte) Element {
return Element{ return Element{
Renderer: &BaseElement{ Renderer: &BaseElement{
Token: ctx.SanitizeHTML(string(n.Text(source)), true) + "\n", Token: ctx.SanitizeHTML(string(n.Text(source)), true) + "\n",
Style: ctx.styles.HTMLSpan.StylePrimitive, Style: ctx.options.Styles.HTMLSpan.StylePrimitive,
}, },
} }
@@ -334,7 +334,7 @@ func (tr *TermRenderer) NewElement(node ast.Node, source []byte) Element {
case astext.KindDefinitionList: case astext.KindDefinitionList:
e := &BlockElement{ e := &BlockElement{
Block: &bytes.Buffer{}, Block: &bytes.Buffer{},
Style: cascadeStyle(ctx.blockStack.Current().Style, ctx.styles.DefinitionList, true), Style: cascadeStyle(ctx.blockStack.Current().Style, ctx.options.Styles.DefinitionList, true),
Margin: true, Margin: true,
Newline: true, Newline: true,
} }
@@ -347,14 +347,14 @@ func (tr *TermRenderer) NewElement(node ast.Node, source []byte) Element {
case astext.KindDefinitionTerm: case astext.KindDefinitionTerm:
return Element{ return Element{
Renderer: &BaseElement{ Renderer: &BaseElement{
Style: ctx.styles.DefinitionTerm, Style: ctx.options.Styles.DefinitionTerm,
}, },
} }
case astext.KindDefinitionDescription: case astext.KindDefinitionDescription:
return Element{ return Element{
Renderer: &BaseElement{ Renderer: &BaseElement{
Style: ctx.styles.DefinitionDescription, Style: ctx.options.Styles.DefinitionDescription,
}, },
} }
+8 -8
View File
@@ -1,4 +1,4 @@
package gold package ansi
import ( import (
"bytes" "bytes"
@@ -14,21 +14,21 @@ type HeadingElement struct {
func (e *HeadingElement) Render(w io.Writer, ctx RenderContext) error { func (e *HeadingElement) Render(w io.Writer, ctx RenderContext) error {
bs := ctx.blockStack bs := ctx.blockStack
rules := ctx.styles.Heading rules := ctx.options.Styles.Heading
switch e.Level { switch e.Level {
case 1: case 1:
rules = cascadeStyles(false, rules, ctx.styles.H1) rules = cascadeStyles(false, rules, ctx.options.Styles.H1)
case 2: case 2:
rules = cascadeStyles(false, rules, ctx.styles.H2) rules = cascadeStyles(false, rules, ctx.options.Styles.H2)
case 3: case 3:
rules = cascadeStyles(false, rules, ctx.styles.H3) rules = cascadeStyles(false, rules, ctx.options.Styles.H3)
case 4: case 4:
rules = cascadeStyles(false, rules, ctx.styles.H4) rules = cascadeStyles(false, rules, ctx.options.Styles.H4)
case 5: case 5:
rules = cascadeStyles(false, rules, ctx.styles.H5) rules = cascadeStyles(false, rules, ctx.options.Styles.H5)
case 6: case 6:
rules = cascadeStyles(false, rules, ctx.styles.H6) rules = cascadeStyles(false, rules, ctx.options.Styles.H6)
} }
if !e.First { if !e.First {
+3 -3
View File
@@ -1,4 +1,4 @@
package gold package ansi
import ( import (
"io" "io"
@@ -15,7 +15,7 @@ func (e *ImageElement) Render(w io.Writer, ctx RenderContext) error {
if len(e.Text) > 0 { if len(e.Text) > 0 {
el := &BaseElement{ el := &BaseElement{
Token: e.Text, Token: e.Text,
Style: ctx.styles.ImageText, Style: ctx.options.Styles.ImageText,
} }
err := el.Render(w, ctx) err := el.Render(w, ctx)
if err != nil { if err != nil {
@@ -26,7 +26,7 @@ func (e *ImageElement) Render(w io.Writer, ctx RenderContext) error {
el := &BaseElement{ el := &BaseElement{
Token: resolveRelativeURL(e.BaseURL, e.URL), Token: resolveRelativeURL(e.BaseURL, e.URL),
Prefix: " ", Prefix: " ",
Style: ctx.styles.Image, Style: ctx.options.Styles.Image,
} }
err := el.Render(w, ctx) err := el.Render(w, ctx)
if err != nil { if err != nil {
+1 -1
View File
@@ -1,4 +1,4 @@
package gold package ansi
import ( import (
"io" "io"
+3 -3
View File
@@ -1,4 +1,4 @@
package gold package ansi
import ( import (
"io" "io"
@@ -19,7 +19,7 @@ func (e *LinkElement) Render(w io.Writer, ctx RenderContext) error {
el := &BaseElement{ el := &BaseElement{
Token: e.Text, Token: e.Text,
Style: ctx.styles.LinkText, Style: ctx.options.Styles.LinkText,
} }
err := el.Render(w, ctx) err := el.Render(w, ctx)
if err != nil { if err != nil {
@@ -53,7 +53,7 @@ func (e *LinkElement) Render(w io.Writer, ctx RenderContext) error {
if len(e.URL) > 0 { if len(e.URL) > 0 {
pre := " " pre := " "
style := ctx.styles.Link style := ctx.options.Styles.Link
if !textRendered { if !textRendered {
pre = "" pre = ""
style.BlockPrefix = "" style.BlockPrefix = ""
+3 -3
View File
@@ -1,4 +1,4 @@
package gold package ansi
import ( import (
"io" "io"
@@ -13,12 +13,12 @@ func (e *ItemElement) Render(w io.Writer, ctx RenderContext) error {
var el *BaseElement var el *BaseElement
if e.Enumeration > 0 { if e.Enumeration > 0 {
el = &BaseElement{ el = &BaseElement{
Style: ctx.styles.Enumeration, Style: ctx.options.Styles.Enumeration,
Prefix: strconv.FormatInt(int64(e.Enumeration), 10), Prefix: strconv.FormatInt(int64(e.Enumeration), 10),
} }
} else { } else {
el = &BaseElement{ el = &BaseElement{
Style: ctx.styles.Item, Style: ctx.options.Styles.Item,
} }
} }
+1 -1
View File
@@ -1,4 +1,4 @@
package gold package ansi
import ( import (
"io" "io"
+1 -1
View File
@@ -1,4 +1,4 @@
package gold package ansi
import ( import (
"io" "io"
+2 -2
View File
@@ -1,4 +1,4 @@
package gold package ansi
import ( import (
"bytes" "bytes"
@@ -13,7 +13,7 @@ type ParagraphElement struct {
func (e *ParagraphElement) Render(w io.Writer, ctx RenderContext) error { func (e *ParagraphElement) Render(w io.Writer, ctx RenderContext) error {
bs := ctx.blockStack bs := ctx.blockStack
rules := ctx.styles.Paragraph rules := ctx.options.Styles.Paragraph
_, _ = w.Write([]byte("\n")) _, _ = w.Write([]byte("\n"))
be := BlockElement{ be := BlockElement{
+158
View File
@@ -0,0 +1,158 @@
package ansi
import (
"io"
"log"
"net/url"
"strings"
"github.com/yuin/goldmark/ast"
astext "github.com/yuin/goldmark/extension/ast"
"github.com/yuin/goldmark/renderer"
"github.com/yuin/goldmark/util"
)
type Options struct {
BaseURL string
WordWrap int
Styles StyleConfig
}
type ANSIRenderer struct {
context RenderContext
}
// NewANSIRenderer returns a new ANSIRenderer with style and options set.
func NewRenderer(options Options) *ANSIRenderer {
return &ANSIRenderer{
context: NewRenderContext(options),
}
}
// RegisterFuncs implements NodeRenderer.RegisterFuncs.
func (r *ANSIRenderer) 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 *ANSIRenderer) renderNode(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
// _, _ = w.Write([]byte(node.Type.String()))
writeTo := io.Writer(w)
bs := tr.context.blockStack
// children get rendered by their parent
if isChild(node) {
return ast.WalkContinue, nil
}
e := tr.NewElement(node, source)
if entering {
// everything below the Document element gets rendered into a block buffer
if bs.Len() > 0 {
writeTo = io.Writer(bs.Current().Block)
}
_, _ = writeTo.Write([]byte(e.Entering))
if e.Renderer != nil {
err := e.Renderer.Render(writeTo, tr.context)
if err != nil {
return ast.WalkStop, err
}
}
} else {
// everything below the Document element gets rendered into a block buffer
if bs.Len() > 0 {
writeTo = io.Writer(bs.Parent().Block)
}
// if we're finished rendering the entire document,
// flush to the real writer
if node.Type() == ast.TypeDocument {
writeTo = w
}
if e.Finisher != nil {
err := e.Finisher.Finish(writeTo, tr.context)
if err != nil {
return ast.WalkStop, err
}
}
_, _ = bs.Current().Block.Write([]byte(e.Exiting))
}
return ast.WalkContinue, nil
}
func isChild(node ast.Node) bool {
if node.Parent() == nil {
return false
}
// These types are already rendered by their parent
switch node.Parent().Kind() {
case ast.KindLink, ast.KindImage, ast.KindEmphasis, astext.KindStrikethrough, ast.KindBlockquote, astext.KindTableCell:
return true
default:
return false
}
}
func resolveRelativeURL(baseURL string, rel string) string {
u, err := url.Parse(rel)
if err != nil {
log.Fatal(err)
}
if u.IsAbs() {
return rel
}
u.Path = strings.TrimPrefix(u.Path, "/")
base, err := url.Parse(baseURL)
if err != nil {
return rel
}
return base.ResolveReference(u).String()
}
+1 -29
View File
@@ -1,14 +1,7 @@
package gold package ansi
import ( import (
"io"
"io/ioutil"
"os"
"github.com/lucasb-eyer/go-colorful" "github.com/lucasb-eyer/go-colorful"
"github.com/rakyll/statik/fs"
_ "github.com/charmbracelet/gold/statik"
) )
type StylePrimitive struct { type StylePrimitive struct {
@@ -95,27 +88,6 @@ type StyleConfig struct {
HTMLSpan StyleBlock `json:"html_span"` HTMLSpan StyleBlock `json:"html_span"`
} }
func loadStyle(f string) ([]byte, error) {
var r io.ReadCloser
var err error
r, err = os.Open(f)
if err != nil {
statikFS, err := fs.New()
if err != nil {
return nil, err
}
r, err = statikFS.Open("/" + f + ".json")
if err != nil {
return nil, err
}
}
defer r.Close()
return ioutil.ReadAll(r)
}
func cascadeStyles(onlyColors bool, s ...StyleBlock) StyleBlock { func cascadeStyles(onlyColors bool, s ...StyleBlock) StyleBlock {
var r StyleBlock var r StyleBlock
+3 -3
View File
@@ -1,4 +1,4 @@
package gold package ansi
import ( import (
"io" "io"
@@ -29,7 +29,7 @@ func (e *TableElement) Render(w io.Writer, ctx RenderContext) error {
var indent uint var indent uint
var margin uint var margin uint
rules := ctx.styles.Table rules := ctx.options.Styles.Table
if rules.Indent != nil { if rules.Indent != nil {
indent = *rules.Indent indent = *rules.Indent
} }
@@ -56,7 +56,7 @@ func (e *TableElement) Finish(w io.Writer, ctx RenderContext) error {
ctx.table.writer.Render() ctx.table.writer.Render()
ctx.table.writer = nil ctx.table.writer = nil
rules := ctx.styles.Table rules := ctx.options.Styles.Table
renderText(ctx.table.indentWriter, ctx.blockStack.Current().Style.StylePrimitive, rules.BlockSuffix) renderText(ctx.table.indentWriter, ctx.blockStack.Current().Style.StylePrimitive, rules.BlockSuffix)
return nil return nil
} }
+4 -4
View File
@@ -1,4 +1,4 @@
package gold package ansi
import ( import (
"io" "io"
@@ -11,14 +11,14 @@ type TaskElement struct {
func (e *TaskElement) Render(w io.Writer, ctx RenderContext) error { func (e *TaskElement) Render(w io.Writer, ctx RenderContext) error {
var el *BaseElement var el *BaseElement
pre := ctx.styles.Task.Unticked pre := ctx.options.Styles.Task.Unticked
if e.Checked { if e.Checked {
pre = ctx.styles.Task.Ticked pre = ctx.options.Styles.Task.Ticked
} }
el = &BaseElement{ el = &BaseElement{
Prefix: pre, Prefix: pre,
Style: ctx.styles.Task.StylePrimitive, Style: ctx.options.Styles.Task.StylePrimitive,
} }
return el.Render(w, ctx) return el.Render(w, ctx)
+1 -1
View File
@@ -1,4 +1,4 @@
package gold package ansi
import ( import (
"regexp" "regexp"
+32 -143
View File
@@ -4,26 +4,22 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"io" "io"
"log" "io/ioutil"
"net/url" "os"
"strings"
"github.com/rakyll/statik/fs"
"github.com/yuin/goldmark" "github.com/yuin/goldmark"
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/extension" "github.com/yuin/goldmark/extension"
astext "github.com/yuin/goldmark/extension/ast"
"github.com/yuin/goldmark/parser" "github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer" "github.com/yuin/goldmark/renderer"
"github.com/yuin/goldmark/util" "github.com/yuin/goldmark/util"
"github.com/charmbracelet/gold/ansi"
_ "github.com/charmbracelet/gold/statik"
) )
type Options struct {
BaseURL string
WordWrap int
}
type TermRenderer struct { type TermRenderer struct {
context RenderContext Options ansi.Options
} }
// Render initializes a new TermRenderer and renders a markdown with a specific // Render initializes a new TermRenderer and renders a markdown with a specific
@@ -35,7 +31,7 @@ func Render(in string, stylePath string) ([]byte, error) {
// RenderBytes initializes a new TermRenderer and renders a markdown with a // RenderBytes initializes a new TermRenderer and renders a markdown with a
// specific style. // specific style.
func RenderBytes(in []byte, stylePath string) ([]byte, error) { func RenderBytes(in []byte, stylePath string) ([]byte, error) {
r, err := NewTermRenderer(stylePath, Options{ r, err := NewTermRenderer(stylePath, ansi.Options{
WordWrap: 80, WordWrap: 80,
}) })
if err != nil { if err != nil {
@@ -45,7 +41,7 @@ func RenderBytes(in []byte, stylePath string) ([]byte, error) {
} }
// NewTermRenderer returns a new TermRenderer with style and options set. // NewTermRenderer returns a new TermRenderer with style and options set.
func NewTermRenderer(stylePath string, options Options) (*TermRenderer, error) { func NewTermRenderer(stylePath string, options ansi.Options) (*TermRenderer, error) {
if stylePath == "" { if stylePath == "" {
return NewTermRendererFromBytes([]byte("{}"), options) return NewTermRendererFromBytes([]byte("{}"), options)
} }
@@ -59,17 +55,15 @@ func NewTermRenderer(stylePath string, options Options) (*TermRenderer, error) {
// NewTermRendererFromBytes returns a new TermRenderer with style and options // NewTermRendererFromBytes returns a new TermRenderer with style and options
// set. // set.
func NewTermRendererFromBytes(b []byte, options Options) (*TermRenderer, error) { func NewTermRendererFromBytes(b []byte, options ansi.Options) (*TermRenderer, error) {
tr := &TermRenderer{ err := json.Unmarshal(b, &options.Styles)
context: NewRenderContext(options),
}
err := json.Unmarshal(b, &tr.context.styles)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return tr, nil return &TermRenderer{
Options: options,
}, nil
} }
// Render returns the markdown rendered into a string. // Render returns the markdown rendered into a string.
@@ -89,139 +83,34 @@ func (tr *TermRenderer) RenderBytes(in []byte) ([]byte, error) {
parser.WithAutoHeadingID(), parser.WithAutoHeadingID(),
), ),
) )
ar := ansi.NewRenderer(tr.Options)
md.SetRenderer( md.SetRenderer(
renderer.NewRenderer( renderer.NewRenderer(
renderer.WithNodeRenderers(util.Prioritized(tr, 1000)))) renderer.WithNodeRenderers(util.Prioritized(ar, 1000))))
var buf bytes.Buffer var buf bytes.Buffer
err := md.Convert(in, &buf) err := md.Convert(in, &buf)
return buf.Bytes(), err return buf.Bytes(), err
} }
// RegisterFuncs implements NodeRenderer.RegisterFuncs. func loadStyle(f string) ([]byte, error) {
func (r *TermRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) { var r io.ReadCloser
// blocks var err error
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 r, err = os.Open(f)
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 := io.Writer(w)
bs := tr.context.blockStack
// children get rendered by their parent
if isChild(node) {
return ast.WalkContinue, nil
}
e := tr.NewElement(node, source)
if entering {
// everything below the Document element gets rendered into a block buffer
if bs.Len() > 0 {
writeTo = io.Writer(bs.Current().Block)
}
_, _ = writeTo.Write([]byte(e.Entering))
if e.Renderer != nil {
err := e.Renderer.Render(writeTo, tr.context)
if err != nil {
return ast.WalkStop, err
}
}
} else {
// everything below the Document element gets rendered into a block buffer
if bs.Len() > 0 {
writeTo = io.Writer(bs.Parent().Block)
}
// if we're finished rendering the entire document,
// flush to the real writer
if node.Type() == ast.TypeDocument {
writeTo = w
}
if e.Finisher != nil {
err := e.Finisher.Finish(writeTo, tr.context)
if err != nil {
return ast.WalkStop, err
}
}
_, _ = bs.Current().Block.Write([]byte(e.Exiting))
}
return ast.WalkContinue, nil
}
func isChild(node ast.Node) bool {
if node.Parent() == nil {
return false
}
// These types are already rendered by their parent
switch node.Parent().Kind() {
case ast.KindLink, ast.KindImage, ast.KindEmphasis, astext.KindStrikethrough, ast.KindBlockquote, astext.KindTableCell:
return true
default:
return false
}
}
func resolveRelativeURL(baseURL string, rel string) string {
u, err := url.Parse(rel)
if err != nil { if err != nil {
log.Fatal(err) statikFS, err := fs.New()
} if err != nil {
if u.IsAbs() { return nil, err
return rel }
}
u.Path = strings.TrimPrefix(u.Path, "/")
base, err := url.Parse(baseURL) r, err = statikFS.Open("/" + f + ".json")
if err != nil { if err != nil {
return rel return nil, err
}
} }
return base.ResolveReference(u).String()
defer r.Close()
return ioutil.ReadAll(r)
} }