mirror of
https://github.com/rwinkhart/glamour-temp-MUTN.git
synced 2026-09-06 00:47:15 -04:00
Use a Renderer interface instead of rendering fragments
This commit is contained in:
@@ -0,0 +1,72 @@
|
|||||||
|
package gold
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/logrusorgru/aurora"
|
||||||
|
bf "gopkg.in/russross/blackfriday.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BaseElement struct {
|
||||||
|
Token string
|
||||||
|
Pre string
|
||||||
|
Post string
|
||||||
|
Style StyleType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *BaseElement) Render(w io.Writer, node *bf.Node, tr *TermRenderer) error {
|
||||||
|
if e.Pre != "" {
|
||||||
|
fmt.Fprintf(w, "%s", e.Pre)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if e.Post != "" {
|
||||||
|
fmt.Fprintf(w, "%s", e.Post)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
rules := tr.style[e.Style]
|
||||||
|
if rules == nil {
|
||||||
|
fmt.Fprintf(w, "%s", e.Token)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
out := aurora.Reset(e.Token)
|
||||||
|
if rules.Color != "" {
|
||||||
|
i, err := strconv.Atoi(rules.Color)
|
||||||
|
if err == nil && i >= 0 && i <= 255 {
|
||||||
|
out = out.Index(uint8(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if rules.BackgroundColor != "" {
|
||||||
|
i, err := strconv.Atoi(rules.BackgroundColor)
|
||||||
|
if err == nil && i >= 0 && i <= 255 {
|
||||||
|
out = out.Index(uint8(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if rules.Underline {
|
||||||
|
out = out.Underline()
|
||||||
|
}
|
||||||
|
if rules.Bold {
|
||||||
|
out = out.Bold()
|
||||||
|
}
|
||||||
|
if rules.Italic {
|
||||||
|
out = out.Italic()
|
||||||
|
}
|
||||||
|
if rules.CrossedOut {
|
||||||
|
out = out.CrossedOut()
|
||||||
|
}
|
||||||
|
if rules.Overlined {
|
||||||
|
out = out.Overlined()
|
||||||
|
}
|
||||||
|
if rules.Inverse {
|
||||||
|
out = out.Reverse()
|
||||||
|
}
|
||||||
|
if rules.Blink {
|
||||||
|
out = out.Blink()
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write([]byte(aurora.Sprintf("%s", out)))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package gold
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/alecthomas/chroma/quick"
|
||||||
|
bf "gopkg.in/russross/blackfriday.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CodeBlockElement struct {
|
||||||
|
Code string
|
||||||
|
Language string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *CodeBlockElement) Render(w io.Writer, node *bf.Node, tr *TermRenderer) error {
|
||||||
|
var theme string
|
||||||
|
rules := tr.style[CodeBlock]
|
||||||
|
if rules != nil {
|
||||||
|
theme = rules.Theme
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(theme) > 0 {
|
||||||
|
return quick.Highlight(w, e.Code, e.Language, "terminal16m", theme)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback rendering
|
||||||
|
el := &BaseElement{
|
||||||
|
Token: string(node.Literal),
|
||||||
|
Style: CodeBlock,
|
||||||
|
}
|
||||||
|
return el.Render(w, node, tr)
|
||||||
|
}
|
||||||
+171
@@ -0,0 +1,171 @@
|
|||||||
|
package gold
|
||||||
|
|
||||||
|
import (
|
||||||
|
"html"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
bf "gopkg.in/russross/blackfriday.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ElementRenderer interface {
|
||||||
|
Render(w io.Writer, node *bf.Node, tr *TermRenderer) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Element struct {
|
||||||
|
Pre string
|
||||||
|
Post string
|
||||||
|
Renderer ElementRenderer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr *TermRenderer) NewElement(node *bf.Node) Element {
|
||||||
|
switch node.Type {
|
||||||
|
case bf.Document:
|
||||||
|
return Element{
|
||||||
|
Renderer: &BaseElement{
|
||||||
|
Style: Document,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case bf.BlockQuote:
|
||||||
|
return Element{
|
||||||
|
Pre: "\n",
|
||||||
|
Post: "\n",
|
||||||
|
Renderer: &BaseElement{
|
||||||
|
Token: string(node.Literal),
|
||||||
|
Style: BlockQuote,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case bf.List:
|
||||||
|
return Element{
|
||||||
|
Renderer: &ListElement{},
|
||||||
|
}
|
||||||
|
case bf.Item:
|
||||||
|
return Element{
|
||||||
|
Renderer: &ItemElement{},
|
||||||
|
}
|
||||||
|
case bf.Paragraph:
|
||||||
|
return Element{
|
||||||
|
Post: "\n",
|
||||||
|
Renderer: &ParagraphElement{},
|
||||||
|
}
|
||||||
|
case bf.Heading:
|
||||||
|
return Element{
|
||||||
|
Post: "\n",
|
||||||
|
Renderer: &HeadingElement{},
|
||||||
|
}
|
||||||
|
case bf.HorizontalRule:
|
||||||
|
return Element{
|
||||||
|
Pre: "\n",
|
||||||
|
Post: "\n",
|
||||||
|
Renderer: &BaseElement{
|
||||||
|
Token: "---",
|
||||||
|
Style: HorizontalRule,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case bf.Emph:
|
||||||
|
return Element{
|
||||||
|
Renderer: &BaseElement{
|
||||||
|
Token: string(node.FirstChild.Literal),
|
||||||
|
Style: Emph,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case bf.Strong:
|
||||||
|
return Element{
|
||||||
|
Renderer: &BaseElement{
|
||||||
|
Token: string(node.FirstChild.Literal),
|
||||||
|
Style: Strong,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case bf.Del:
|
||||||
|
return Element{
|
||||||
|
Renderer: &BaseElement{
|
||||||
|
Token: string(node.Literal),
|
||||||
|
Style: Del,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case bf.Link:
|
||||||
|
return Element{
|
||||||
|
Renderer: &LinkElement{},
|
||||||
|
}
|
||||||
|
case bf.Image:
|
||||||
|
return Element{
|
||||||
|
Renderer: &ImageElement{},
|
||||||
|
}
|
||||||
|
case bf.Text:
|
||||||
|
return Element{
|
||||||
|
Renderer: &BaseElement{
|
||||||
|
Token: html.UnescapeString(stripper.Sanitize(string(node.Literal))),
|
||||||
|
Style: Text,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case bf.HTMLBlock:
|
||||||
|
return Element{
|
||||||
|
Renderer: &BaseElement{
|
||||||
|
Token: html.UnescapeString(strings.TrimSpace(stripper.Sanitize(string(node.Literal)))) + "\n",
|
||||||
|
Style: HTMLBlock,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case bf.CodeBlock:
|
||||||
|
return Element{
|
||||||
|
Pre: "\n",
|
||||||
|
Post: "\n",
|
||||||
|
Renderer: &CodeBlockElement{
|
||||||
|
Code: string(node.Literal),
|
||||||
|
Language: string(node.CodeBlockData.Info),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case bf.Softbreak:
|
||||||
|
return Element{
|
||||||
|
Post: "\n",
|
||||||
|
Renderer: &BaseElement{
|
||||||
|
Token: string(node.Literal),
|
||||||
|
Style: Softbreak,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case bf.Hardbreak:
|
||||||
|
return Element{
|
||||||
|
Post: "\n",
|
||||||
|
Renderer: &BaseElement{
|
||||||
|
Token: string(node.Literal),
|
||||||
|
Style: Hardbreak,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case bf.Code:
|
||||||
|
return Element{
|
||||||
|
Renderer: &BaseElement{
|
||||||
|
Token: string(node.Literal),
|
||||||
|
Style: Code,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case bf.HTMLSpan:
|
||||||
|
return Element{
|
||||||
|
Renderer: &BaseElement{
|
||||||
|
Token: html.UnescapeString(strings.TrimSpace(stripper.Sanitize(string(node.Literal)))) + "\n",
|
||||||
|
Style: HTMLSpan,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case bf.Table:
|
||||||
|
return Element{
|
||||||
|
Pre: "\n",
|
||||||
|
Post: "\n",
|
||||||
|
}
|
||||||
|
case bf.TableCell:
|
||||||
|
return Element{}
|
||||||
|
case bf.TableHead:
|
||||||
|
return Element{}
|
||||||
|
case bf.TableBody:
|
||||||
|
return Element{}
|
||||||
|
case bf.TableRow:
|
||||||
|
return Element{
|
||||||
|
Pre: "\n",
|
||||||
|
Post: "\n",
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return Element{
|
||||||
|
Renderer: &BaseElement{
|
||||||
|
Token: string(node.Literal),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,17 +3,12 @@ package gold
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/alecthomas/chroma/quick"
|
|
||||||
"github.com/logrusorgru/aurora"
|
|
||||||
"github.com/microcosm-cc/bluemonday"
|
"github.com/microcosm-cc/bluemonday"
|
||||||
bf "gopkg.in/russross/blackfriday.v2"
|
bf "gopkg.in/russross/blackfriday.v2"
|
||||||
)
|
)
|
||||||
@@ -22,19 +17,6 @@ var (
|
|||||||
stripper = bluemonday.StrictPolicy()
|
stripper = bluemonday.StrictPolicy()
|
||||||
)
|
)
|
||||||
|
|
||||||
type Fragment struct {
|
|
||||||
Token string
|
|
||||||
Pre string
|
|
||||||
Post string
|
|
||||||
Style StyleType
|
|
||||||
}
|
|
||||||
|
|
||||||
type Element struct {
|
|
||||||
Pre string
|
|
||||||
Post string
|
|
||||||
Fragments []Fragment
|
|
||||||
}
|
|
||||||
|
|
||||||
type TermRenderer struct {
|
type TermRenderer struct {
|
||||||
BaseURL string
|
BaseURL string
|
||||||
style map[StyleType]*ElementStyle
|
style map[StyleType]*ElementStyle
|
||||||
@@ -89,313 +71,6 @@ func NewTermRendererFromBytes(b []byte) (*TermRenderer, error) {
|
|||||||
return tr, nil
|
return tr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveRelativeURL(baseURL string, rel string) string {
|
|
||||||
u, err := url.Parse(rel)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
if u.IsAbs() {
|
|
||||||
return rel
|
|
||||||
}
|
|
||||||
if u.Path[0] == '/' {
|
|
||||||
u.Path = u.Path[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
base, err := url.Parse(baseURL)
|
|
||||||
if err != nil {
|
|
||||||
return rel
|
|
||||||
}
|
|
||||||
return base.ResolveReference(u).String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *TermRenderer) NewElement(node *bf.Node) Element {
|
|
||||||
switch node.Type {
|
|
||||||
case bf.Document:
|
|
||||||
return Element{
|
|
||||||
Pre: "",
|
|
||||||
Post: "",
|
|
||||||
Fragments: []Fragment{{
|
|
||||||
Token: "",
|
|
||||||
Style: Document,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
case bf.BlockQuote:
|
|
||||||
return Element{
|
|
||||||
Pre: "\n",
|
|
||||||
Post: "\n",
|
|
||||||
Fragments: []Fragment{{
|
|
||||||
Token: string(node.Literal),
|
|
||||||
Style: BlockQuote,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
case bf.List:
|
|
||||||
pre := ""
|
|
||||||
if node.Parent.Type != bf.Item {
|
|
||||||
pre = "\n"
|
|
||||||
}
|
|
||||||
return Element{
|
|
||||||
Pre: pre,
|
|
||||||
Post: "",
|
|
||||||
Fragments: []Fragment{{
|
|
||||||
Token: string(node.Literal),
|
|
||||||
Style: List,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
case bf.Item:
|
|
||||||
l := 0
|
|
||||||
n := node
|
|
||||||
for n.Parent != nil && (n.Parent.Type == bf.List || n.Parent.Type == bf.Item) {
|
|
||||||
if n.Parent.Type == bf.List {
|
|
||||||
l++
|
|
||||||
}
|
|
||||||
n = n.Parent
|
|
||||||
}
|
|
||||||
return Element{
|
|
||||||
Pre: strings.Repeat(" ", l-1) + "• ",
|
|
||||||
Post: "",
|
|
||||||
Fragments: []Fragment{{
|
|
||||||
Token: string(node.Literal),
|
|
||||||
Style: Item,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
case bf.Paragraph:
|
|
||||||
pre := "\n"
|
|
||||||
if node.Prev == nil || (node.Parent != nil && node.Parent.Type == bf.Item) {
|
|
||||||
pre = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return Element{
|
|
||||||
Pre: pre,
|
|
||||||
Post: "\n",
|
|
||||||
Fragments: []Fragment{{
|
|
||||||
Token: string(node.Literal),
|
|
||||||
Style: Paragraph,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
case bf.Heading:
|
|
||||||
var pre string
|
|
||||||
if node.Prev != nil {
|
|
||||||
pre = "\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
return Element{
|
|
||||||
Pre: pre,
|
|
||||||
Post: "\n",
|
|
||||||
Fragments: []Fragment{{
|
|
||||||
Token: fmt.Sprintf("%s %s", strings.Repeat("#", node.HeadingData.Level), node.FirstChild.Literal),
|
|
||||||
Style: Heading,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
case bf.HorizontalRule:
|
|
||||||
return Element{
|
|
||||||
Pre: "\n",
|
|
||||||
Post: "\n",
|
|
||||||
Fragments: []Fragment{{
|
|
||||||
Token: "---",
|
|
||||||
Style: HorizontalRule,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
case bf.Emph:
|
|
||||||
return Element{
|
|
||||||
Pre: "",
|
|
||||||
Post: "",
|
|
||||||
Fragments: []Fragment{{
|
|
||||||
Token: string(node.FirstChild.Literal),
|
|
||||||
Style: Emph,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
case bf.Strong:
|
|
||||||
return Element{
|
|
||||||
Pre: "",
|
|
||||||
Post: "",
|
|
||||||
Fragments: []Fragment{{
|
|
||||||
Token: string(node.FirstChild.Literal),
|
|
||||||
Style: Strong,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
case bf.Del:
|
|
||||||
return Element{
|
|
||||||
Pre: "",
|
|
||||||
Post: "",
|
|
||||||
Fragments: []Fragment{{
|
|
||||||
Token: string(node.Literal),
|
|
||||||
Style: Del,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
|
|
||||||
case bf.Link:
|
|
||||||
f := []Fragment{}
|
|
||||||
|
|
||||||
if node.LastChild != nil {
|
|
||||||
if node.LastChild.Type == bf.Image {
|
|
||||||
el := tr.NewElement(node.LastChild)
|
|
||||||
f = el.Fragments
|
|
||||||
}
|
|
||||||
if len(node.LastChild.Literal) > 0 {
|
|
||||||
f = append(f, Fragment{
|
|
||||||
Token: string(node.LastChild.Literal),
|
|
||||||
Style: LinkText,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(node.LinkData.Destination) > 0 {
|
|
||||||
f = append(f, Fragment{
|
|
||||||
Token: resolveRelativeURL(tr.BaseURL, string(node.LinkData.Destination)),
|
|
||||||
Pre: " (",
|
|
||||||
Post: ")",
|
|
||||||
Style: Link,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return Element{
|
|
||||||
Pre: "",
|
|
||||||
Post: "",
|
|
||||||
Fragments: f,
|
|
||||||
}
|
|
||||||
|
|
||||||
case bf.Image:
|
|
||||||
f := []Fragment{}
|
|
||||||
if len(node.LastChild.Literal) > 0 {
|
|
||||||
f = append(f, Fragment{
|
|
||||||
Token: string(node.LastChild.Literal),
|
|
||||||
Style: Image,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if len(node.LinkData.Destination) > 0 {
|
|
||||||
f = append(f, Fragment{
|
|
||||||
Token: resolveRelativeURL(tr.BaseURL, string(node.LinkData.Destination)),
|
|
||||||
Pre: " [Image: ",
|
|
||||||
Post: "]",
|
|
||||||
Style: Link,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return Element{
|
|
||||||
Pre: "",
|
|
||||||
Post: "",
|
|
||||||
Fragments: f,
|
|
||||||
}
|
|
||||||
|
|
||||||
case bf.Text:
|
|
||||||
return Element{
|
|
||||||
Pre: "",
|
|
||||||
Post: "",
|
|
||||||
Fragments: []Fragment{{
|
|
||||||
Token: html.UnescapeString(stripper.Sanitize(string(node.Literal))),
|
|
||||||
Style: Text,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
case bf.HTMLBlock:
|
|
||||||
return Element{
|
|
||||||
Pre: "",
|
|
||||||
Post: "",
|
|
||||||
Fragments: []Fragment{{
|
|
||||||
Token: html.UnescapeString(strings.TrimSpace(stripper.Sanitize(string(node.Literal)))) + "\n",
|
|
||||||
Style: HTMLBlock,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
case bf.CodeBlock:
|
|
||||||
return Element{
|
|
||||||
Pre: "\n",
|
|
||||||
Post: "\n",
|
|
||||||
Fragments: []Fragment{{
|
|
||||||
Token: string(node.Literal),
|
|
||||||
Style: CodeBlock,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
case bf.Softbreak:
|
|
||||||
return Element{
|
|
||||||
Pre: "",
|
|
||||||
Post: "\n",
|
|
||||||
Fragments: []Fragment{{
|
|
||||||
Token: string(node.Literal),
|
|
||||||
Style: Softbreak,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
case bf.Hardbreak:
|
|
||||||
return Element{
|
|
||||||
Pre: "",
|
|
||||||
Post: "\n",
|
|
||||||
Fragments: []Fragment{{
|
|
||||||
Token: string(node.Literal),
|
|
||||||
Style: Hardbreak,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
case bf.Code:
|
|
||||||
return Element{
|
|
||||||
Pre: "",
|
|
||||||
Post: "",
|
|
||||||
Fragments: []Fragment{{
|
|
||||||
Token: string(node.Literal),
|
|
||||||
Style: Code,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
case bf.HTMLSpan:
|
|
||||||
return Element{
|
|
||||||
Pre: "",
|
|
||||||
Post: "",
|
|
||||||
Fragments: []Fragment{{
|
|
||||||
Token: html.UnescapeString(strings.TrimSpace(stripper.Sanitize(string(node.Literal)))) + "\n",
|
|
||||||
Style: HTMLSpan,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
case bf.Table:
|
|
||||||
return Element{
|
|
||||||
Pre: "\n",
|
|
||||||
Post: "\n",
|
|
||||||
Fragments: []Fragment{{
|
|
||||||
Token: string(node.Literal),
|
|
||||||
Style: Table,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
case bf.TableCell:
|
|
||||||
return Element{
|
|
||||||
Pre: "",
|
|
||||||
Post: "",
|
|
||||||
Fragments: []Fragment{{
|
|
||||||
Token: string(node.Literal),
|
|
||||||
Style: TableCell,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
case bf.TableHead:
|
|
||||||
return Element{
|
|
||||||
Pre: "",
|
|
||||||
Post: "",
|
|
||||||
Fragments: []Fragment{{
|
|
||||||
Token: string(node.Literal),
|
|
||||||
Style: TableHead,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
case bf.TableBody:
|
|
||||||
return Element{
|
|
||||||
Pre: "",
|
|
||||||
Post: "",
|
|
||||||
Fragments: []Fragment{{
|
|
||||||
Token: string(node.Literal),
|
|
||||||
Style: TableBody,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
case bf.TableRow:
|
|
||||||
return Element{
|
|
||||||
Pre: "\n",
|
|
||||||
Post: "\n",
|
|
||||||
Fragments: []Fragment{{
|
|
||||||
Token: string(node.Literal),
|
|
||||||
Style: TableRow,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return Element{
|
|
||||||
Pre: "",
|
|
||||||
Post: "",
|
|
||||||
Fragments: []Fragment{{
|
|
||||||
Token: string(node.Literal),
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *TermRenderer) Render(in string) string {
|
func (tr *TermRenderer) Render(in string) string {
|
||||||
return string(tr.RenderBytes([]byte(in)))
|
return string(tr.RenderBytes([]byte(in)))
|
||||||
}
|
}
|
||||||
@@ -404,51 +79,6 @@ func (tr *TermRenderer) RenderBytes(in []byte) []byte {
|
|||||||
return bf.Run(in, bf.WithRenderer(tr))
|
return bf.Run(in, bf.WithRenderer(tr))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *TermRenderer) renderFragment(w io.Writer, f Fragment) {
|
|
||||||
rules := tr.style[f.Style]
|
|
||||||
if rules == nil {
|
|
||||||
fmt.Fprintf(w, "%s", f.Token)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
out := aurora.Reset(f.Token)
|
|
||||||
if rules.Color != "" {
|
|
||||||
i, err := strconv.Atoi(rules.Color)
|
|
||||||
if err == nil && i >= 0 && i <= 255 {
|
|
||||||
out = out.Index(uint8(i))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if rules.BackgroundColor != "" {
|
|
||||||
i, err := strconv.Atoi(rules.BackgroundColor)
|
|
||||||
if err == nil && i >= 0 && i <= 255 {
|
|
||||||
out = out.Index(uint8(i))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if rules.Underline {
|
|
||||||
out = out.Underline()
|
|
||||||
}
|
|
||||||
if rules.Bold {
|
|
||||||
out = out.Bold()
|
|
||||||
}
|
|
||||||
if rules.Italic {
|
|
||||||
out = out.Italic()
|
|
||||||
}
|
|
||||||
if rules.CrossedOut {
|
|
||||||
out = out.CrossedOut()
|
|
||||||
}
|
|
||||||
if rules.Overlined {
|
|
||||||
out = out.Overlined()
|
|
||||||
}
|
|
||||||
if rules.Inverse {
|
|
||||||
out = out.Reverse()
|
|
||||||
}
|
|
||||||
if rules.Blink {
|
|
||||||
out = out.Blink()
|
|
||||||
}
|
|
||||||
|
|
||||||
w.Write([]byte(aurora.Sprintf("%s", out)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tr *TermRenderer) RenderNode(w io.Writer, node *bf.Node, entering bool) bf.WalkStatus {
|
func (tr *TermRenderer) RenderNode(w io.Writer, node *bf.Node, entering bool) bf.WalkStatus {
|
||||||
// fmt.Fprintf(w, "%s %t", node.Type, entering)
|
// fmt.Fprintf(w, "%s %t", node.Type, entering)
|
||||||
e := tr.NewElement(node)
|
e := tr.NewElement(node)
|
||||||
@@ -465,33 +95,10 @@ func (tr *TermRenderer) RenderNode(w io.Writer, node *bf.Node, entering bool) bf
|
|||||||
return bf.GoToNext
|
return bf.GoToNext
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, f := range e.Fragments {
|
if e.Renderer != nil {
|
||||||
if node.Type == bf.CodeBlock {
|
err := e.Renderer.Render(w, node, tr)
|
||||||
var theme string
|
|
||||||
if rules, ok := tr.style[f.Style]; ok {
|
|
||||||
if len(rules.Theme) > 0 {
|
|
||||||
theme = rules.Theme
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(theme) > 0 {
|
|
||||||
err := quick.Highlight(w, f.Token, string(node.CodeBlockData.Info), "terminal16m", theme)
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
continue
|
return bf.GoToNext
|
||||||
}
|
|
||||||
// if chroma failed, render the fragment as text below
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.Token == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.Pre != "" {
|
|
||||||
fmt.Fprintf(w, "%s", f.Pre)
|
|
||||||
}
|
|
||||||
tr.renderFragment(w, f)
|
|
||||||
if f.Post != "" {
|
|
||||||
fmt.Fprintf(w, "%s", f.Post)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -515,3 +122,22 @@ func isChild(node *bf.Node) bool {
|
|||||||
return false
|
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
|
||||||
|
}
|
||||||
|
if u.Path[0] == '/' {
|
||||||
|
u.Path = u.Path[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
base, err := url.Parse(baseURL)
|
||||||
|
if err != nil {
|
||||||
|
return rel
|
||||||
|
}
|
||||||
|
return base.ResolveReference(u).String()
|
||||||
|
}
|
||||||
|
|||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
package gold
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
bf "gopkg.in/russross/blackfriday.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HeadingElement struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *HeadingElement) Render(w io.Writer, node *bf.Node, tr *TermRenderer) error {
|
||||||
|
var pre string
|
||||||
|
if node.Prev != nil {
|
||||||
|
pre = "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
el := &BaseElement{
|
||||||
|
Pre: pre,
|
||||||
|
Token: fmt.Sprintf("%s %s", strings.Repeat("#", node.HeadingData.Level), node.FirstChild.Literal),
|
||||||
|
Style: Heading,
|
||||||
|
}
|
||||||
|
return el.Render(w, node, tr)
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package gold
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
bf "gopkg.in/russross/blackfriday.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ImageElement struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ImageElement) Render(w io.Writer, node *bf.Node, tr *TermRenderer) error {
|
||||||
|
if len(node.LastChild.Literal) > 0 {
|
||||||
|
el := &BaseElement{
|
||||||
|
Token: string(node.LastChild.Literal),
|
||||||
|
Style: Image,
|
||||||
|
}
|
||||||
|
el.Render(w, node.LastChild, tr)
|
||||||
|
}
|
||||||
|
if len(node.LinkData.Destination) > 0 {
|
||||||
|
el := &BaseElement{
|
||||||
|
Token: resolveRelativeURL(tr.BaseURL, string(node.LinkData.Destination)),
|
||||||
|
Pre: " [Image: ",
|
||||||
|
Post: "]",
|
||||||
|
Style: Link,
|
||||||
|
}
|
||||||
|
el.Render(w, node, tr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package gold
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
bf "gopkg.in/russross/blackfriday.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LinkElement struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *LinkElement) Render(w io.Writer, node *bf.Node, tr *TermRenderer) error {
|
||||||
|
if node.LastChild != nil {
|
||||||
|
if node.LastChild.Type == bf.Image {
|
||||||
|
el := tr.NewElement(node.LastChild)
|
||||||
|
el.Renderer.Render(w, node.LastChild, tr)
|
||||||
|
}
|
||||||
|
if len(node.LastChild.Literal) > 0 {
|
||||||
|
el := &BaseElement{
|
||||||
|
Token: string(node.LastChild.Literal),
|
||||||
|
Style: LinkText,
|
||||||
|
}
|
||||||
|
el.Render(w, node.LastChild, tr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(node.LinkData.Destination) > 0 {
|
||||||
|
el := &BaseElement{
|
||||||
|
Token: resolveRelativeURL(tr.BaseURL, string(node.LinkData.Destination)),
|
||||||
|
Pre: " (",
|
||||||
|
Post: ")",
|
||||||
|
Style: Link,
|
||||||
|
}
|
||||||
|
el.Render(w, node, tr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package gold
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
bf "gopkg.in/russross/blackfriday.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ListElement struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ListElement) Render(w io.Writer, node *bf.Node, tr *TermRenderer) error {
|
||||||
|
pre := ""
|
||||||
|
if node.Parent.Type != bf.Item {
|
||||||
|
pre = "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
el := &BaseElement{
|
||||||
|
Pre: pre,
|
||||||
|
Token: string(node.Literal),
|
||||||
|
Style: List,
|
||||||
|
}
|
||||||
|
return el.Render(w, node, tr)
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
package gold
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
bf "gopkg.in/russross/blackfriday.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ItemElement struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ItemElement) Render(w io.Writer, node *bf.Node, tr *TermRenderer) error {
|
||||||
|
l := 0
|
||||||
|
n := node
|
||||||
|
for n.Parent != nil && (n.Parent.Type == bf.List || n.Parent.Type == bf.Item) {
|
||||||
|
if n.Parent.Type == bf.List {
|
||||||
|
l++
|
||||||
|
}
|
||||||
|
n = n.Parent
|
||||||
|
}
|
||||||
|
|
||||||
|
el := &BaseElement{
|
||||||
|
Pre: strings.Repeat(" ", l-1) + "• ",
|
||||||
|
Token: string(node.Literal),
|
||||||
|
Style: Item,
|
||||||
|
}
|
||||||
|
return el.Render(w, node, tr)
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package gold
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
bf "gopkg.in/russross/blackfriday.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ParagraphElement struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ParagraphElement) Render(w io.Writer, node *bf.Node, tr *TermRenderer) error {
|
||||||
|
pre := "\n"
|
||||||
|
if node.Prev == nil || (node.Parent != nil && node.Parent.Type == bf.Item) {
|
||||||
|
pre = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
el := &BaseElement{
|
||||||
|
Pre: pre,
|
||||||
|
Token: string(node.Literal),
|
||||||
|
Style: Paragraph,
|
||||||
|
}
|
||||||
|
return el.Render(w, node, tr)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user