diff --git a/cmd/gold/dark.json b/cmd/gold/dark.json new file mode 100644 index 0000000..e760449 --- /dev/null +++ b/cmd/gold/dark.json @@ -0,0 +1,10 @@ +{ + "link": { + "color": "123", + "underline": true + }, + "heading": { + "bold": true, + "inverse": true + } +} diff --git a/cmd/gold/main.go b/cmd/gold/main.go index 09b73fe..fc0eab9 100644 --- a/cmd/gold/main.go +++ b/cmd/gold/main.go @@ -1,7 +1,9 @@ package main import ( + "flag" "fmt" + "os" "github.com/magicnumbers/gold" bf "gopkg.in/russross/blackfriday.v2" @@ -22,7 +24,13 @@ This is a test yo. ` func main() { - r := gold.TermRenderer{} - out := bf.Run([]byte(tmd), bf.WithRenderer(&r)) + s := flag.String("s", "", "style json path") + flag.Parse() + r, err := gold.NewTermRenderer(*s) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + out := bf.Run([]byte(tmd), bf.WithRenderer(r)) fmt.Println(string(out)) } diff --git a/gold.go b/gold.go index 530fbe6..a0319c0 100644 --- a/gold.go +++ b/gold.go @@ -1,8 +1,11 @@ package gold import ( + "encoding/json" "fmt" "io" + "io/ioutil" + "os" "strconv" "strings" @@ -26,134 +29,171 @@ const ( Blink ) -type TermRenderer struct { - style map[bf.NodeType]map[RuleKey]string +type ElementStyle struct { + Color string `json:"color"` + BackgroundColor string `json:"background_color"` + Underline bool `json:"underline"` + Bold bool `json:"bold"` + Italic bool `json:"italic"` + CrossedOut bool `json:"crossed_out"` + Faint bool `json:"faint"` + Conceal bool `json:"conceal"` + Overlined bool `json:"overlined"` + Inverse bool `json:"inverse"` + Blink bool `json:"blink"` } -func BaseString(node *bf.Node) string { +type Element struct { + BaseString string +} + +type TermRenderer struct { + style map[bf.NodeType]*ElementStyle +} + +func NewElement(node *bf.Node) Element { switch node.Type { case bf.Document: - return "" + return Element{""} case bf.BlockQuote: - return fmt.Sprintf("```\n%s\n```", string(node.Literal)) + return Element{fmt.Sprintf("```\n%s\n```", string(node.Literal))} case bf.List: - return "" + return Element{""} case bf.Item: - return fmt.Sprintf("* %s", string(node.Literal)) + return Element{fmt.Sprintf("* %s", string(node.Literal))} case bf.Paragraph: if node.Prev != nil && node.Prev.Type != bf.Link { - return "\n" + return Element{"\n"} } else { - return "" + return Element{""} } case bf.Heading: - return fmt.Sprintf("%s ", strings.Repeat("#", node.HeadingData.Level)) + return Element{fmt.Sprintf("%s ", strings.Repeat("#", node.HeadingData.Level))} case bf.HorizontalRule: - return "---\n" + return Element{"---\n"} case bf.Emph: - return fmt.Sprintf("%s\n", string(node.Literal)) + return Element{fmt.Sprintf("%s\n", string(node.Literal))} case bf.Strong: - return fmt.Sprintf("%s\n", string(node.Literal)) + return Element{fmt.Sprintf("%s\n", string(node.Literal))} case bf.Del: - return fmt.Sprintf("%s\n", string(node.Literal)) + return Element{fmt.Sprintf("%s\n", string(node.Literal))} case bf.Link: - return fmt.Sprintf("[%s](%s)\n", string(node.LastChild.Literal), string(node.LinkData.Destination)) + return Element{fmt.Sprintf("[%s](%s)\n", string(node.LastChild.Literal), string(node.LinkData.Destination))} case bf.Image: - return fmt.Sprintf("%s\n", string(node.Literal)) + return Element{fmt.Sprintf("%s\n", string(node.Literal))} case bf.Text: if node.Parent != nil && node.Parent.Type != bf.Link { - return fmt.Sprintf("%s\n", string(node.Literal)) + return Element{fmt.Sprintf("%s\n", string(node.Literal))} } else { - return "" + return Element{""} } case bf.HTMLBlock: - return fmt.Sprintf("%s\n", string(node.Literal)) + return Element{fmt.Sprintf("%s\n", string(node.Literal))} case bf.CodeBlock: - return fmt.Sprintf("%s\n", string(node.Literal)) + return Element{fmt.Sprintf("%s\n", string(node.Literal))} case bf.Softbreak: - return fmt.Sprintf("%s\n", string(node.Literal)) + return Element{fmt.Sprintf("%s\n", string(node.Literal))} case bf.Hardbreak: - return fmt.Sprintf("%s\n", string(node.Literal)) + return Element{fmt.Sprintf("%s\n", string(node.Literal))} case bf.Code: - return fmt.Sprintf("%s\n", string(node.Literal)) + return Element{fmt.Sprintf("%s\n", string(node.Literal))} case bf.HTMLSpan: - return fmt.Sprintf("%s\n", string(node.Literal)) + return Element{fmt.Sprintf("%s\n", string(node.Literal))} case bf.Table: - return fmt.Sprintf("%s\n", string(node.Literal)) + return Element{fmt.Sprintf("%s\n", string(node.Literal))} case bf.TableCell: - return fmt.Sprintf("%s\n", string(node.Literal)) + return Element{fmt.Sprintf("%s\n", string(node.Literal))} case bf.TableHead: - return fmt.Sprintf("%s\n", string(node.Literal)) + return Element{fmt.Sprintf("%s\n", string(node.Literal))} case bf.TableBody: - return fmt.Sprintf("%s\n", string(node.Literal)) + return Element{fmt.Sprintf("%s\n", string(node.Literal))} case bf.TableRow: - return fmt.Sprintf("%s\n", string(node.Literal)) + return Element{fmt.Sprintf("%s\n", string(node.Literal))} default: - return "" + return Element{""} } } +func NewTermRenderer(stylePath string) (*TermRenderer, error) { + if stylePath == "" { + return NewTermRendererFromBytes([]byte("{}")) + } + f, err := os.Open(stylePath) + if err != nil { + return nil, err + } + defer f.Close() + b, _ := ioutil.ReadAll(f) + return NewTermRendererFromBytes(b) +} + +func NewTermRendererFromBytes(b []byte) (*TermRenderer, error) { + e := make(map[string]*ElementStyle, 0) + err := json.Unmarshal(b, &e) + if err != nil { + return nil, err + } + tr := &TermRenderer{} + tr.style = make(map[bf.NodeType]*ElementStyle) + + for k, v := range e { + t, err := keyToType(k) + if err != nil { + fmt.Println(err) + continue + } + tr.style[t] = v + } + return tr, nil +} + func (tr *TermRenderer) RenderNode(w io.Writer, node *bf.Node, entering bool) bf.WalkStatus { if !entering { return bf.GoToNext } - text := BaseString(node) - if text == "" { + el := NewElement(node) + if el.BaseString == "" { return bf.GoToNext } rules := tr.style[node.Type] - if len(rules) == 0 { - fmt.Fprintf(w, "%s", text) + if rules == nil { + fmt.Fprintf(w, "%s", el.BaseString) return bf.GoToNext } - out := aurora.Reset(text) - if r, ok := rules[Color]; ok { - i, err := strconv.Atoi(r) + out := aurora.Reset(el.BaseString) + if rules.Color != "" { + i, err := strconv.Atoi(rules.Color) if err == nil && i >= 0 && i <= 255 { out = out.Index(uint8(i)) } } - if r, ok := rules[BackgroundColor]; ok { - i, err := strconv.Atoi(r) + if rules.BackgroundColor != "" { + i, err := strconv.Atoi(rules.BackgroundColor) if err == nil && i >= 0 && i <= 255 { out = out.Index(uint8(i)) } } - if r, ok := rules[Underline]; ok { - if r == "true" { - out = out.Underline() - } + if rules.Underline { + out = out.Underline() } - if r, ok := rules[Bold]; ok { - if r == "true" { - out = out.Bold() - } + if rules.Bold { + out = out.Bold() } - if r, ok := rules[Italic]; ok { - if r == "true" { - out = out.Italic() - } + if rules.Italic { + out = out.Italic() } - if r, ok := rules[CrossedOut]; ok { - if r == "true" { - out = out.CrossedOut() - } + if rules.CrossedOut { + out = out.CrossedOut() } - if r, ok := rules[Overlined]; ok { - if r == "true" { - out = out.Overlined() - } + if rules.Overlined { + out = out.Overlined() } - if r, ok := rules[Inverse]; ok { - if r == "true" { - out = out.Reverse() - } + if rules.Inverse { + out = out.Reverse() } - if r, ok := rules[Blink]; ok { - if r == "true" { - out = out.Blink() - } + if rules.Blink { + out = out.Blink() } w.Write([]byte(aurora.Sprintf("%s", out))) return bf.GoToNext @@ -164,3 +204,58 @@ func (tr *TermRenderer) RenderHeader(w io.Writer, ast *bf.Node) { func (tr *TermRenderer) RenderFooter(w io.Writer, ast *bf.Node) { } + +func keyToType(key string) (bf.NodeType, error) { + switch key { + case "document": + return bf.Document, nil + case "block_quote": + return bf.BlockQuote, nil + case "list": + return bf.List, nil + case "item": + return bf.Item, nil + case "paragraph": + return bf.Paragraph, nil + case "heading": + return bf.Heading, nil + case "hr": + return bf.HorizontalRule, nil + case "emph": + return bf.Emph, nil + case "strong": + return bf.Strong, nil + case "del": + return bf.Del, nil + case "link": + return bf.Link, nil + case "image": + return bf.Image, nil + case "text": + return bf.Text, nil + case "html_block": + return bf.HTMLBlock, nil + case "code_block": + return bf.CodeBlock, nil + case "softbreak": + return bf.Softbreak, nil + case "hardbreak": + return bf.Hardbreak, nil + case "code": + return bf.Code, nil + case "html_span": + return bf.HTMLSpan, nil + case "table": + return bf.Table, nil + case "table_cel": + return bf.TableCell, nil + case "table_head": + return bf.TableHead, nil + case "table_body": + return bf.TableBody, nil + case "table_row": + return bf.TableRow, nil + default: + return 0, fmt.Errorf("Invalid element type: %s", key) + } +}