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 (
"io"
+1 -1
View File
@@ -1,4 +1,4 @@
package gold
package ansi
import (
"bytes"
+1 -1
View File
@@ -1,4 +1,4 @@
package gold
package ansi
import (
"bytes"
+1 -1
View File
@@ -1,4 +1,4 @@
package gold
package ansi
import (
"bytes"
+2 -2
View File
@@ -1,4 +1,4 @@
package gold
package ansi
import (
"io"
@@ -16,7 +16,7 @@ func (e *CodeBlockElement) Render(w io.Writer, ctx RenderContext) error {
var indent uint
var margin uint
rules := ctx.styles.CodeBlock
rules := ctx.options.Styles.CodeBlock
if rules.Indent != nil {
indent = *rules.Indent
}
+1 -2
View File
@@ -1,4 +1,4 @@
package gold
package ansi
import (
"html"
@@ -9,7 +9,6 @@ import (
type RenderContext struct {
options Options
styles StyleConfig
blockStack *BlockStack
table *TableElement
+17 -17
View File
@@ -1,4 +1,4 @@
package gold
package ansi
import (
"bytes"
@@ -25,7 +25,7 @@ type Element struct {
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
// fmt.Print(strings.Repeat(" ", ctx.blockStack.Len()), node.Type(), node.Kind())
// defer fmt.Println()
@@ -35,7 +35,7 @@ func (tr *TermRenderer) NewElement(node ast.Node, source []byte) Element {
case ast.KindDocument:
e := &BlockElement{
Block: &bytes.Buffer{},
Style: ctx.styles.Document,
Style: ctx.options.Styles.Document,
Margin: true,
}
return Element{
@@ -70,7 +70,7 @@ func (tr *TermRenderer) NewElement(node ast.Node, source []byte) Element {
case ast.KindBlockquote:
e := &BlockElement{
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,
Newline: true,
}
@@ -82,7 +82,7 @@ func (tr *TermRenderer) NewElement(node ast.Node, source []byte) Element {
// Lists
case ast.KindList:
s := ctx.styles.List.StyleBlock
s := ctx.options.Styles.List.StyleBlock
if s.Indent == nil {
var i uint
s.Indent = &i
@@ -90,7 +90,7 @@ func (tr *TermRenderer) NewElement(node ast.Node, source []byte) Element {
n := node.Parent()
for n != nil {
if n.Kind() == ast.KindList {
i := ctx.styles.List.LevelIndent
i := ctx.options.Styles.List.LevelIndent
s.Indent = &i
break
}
@@ -156,16 +156,16 @@ func (tr *TermRenderer) NewElement(node ast.Node, source []byte) Element {
return Element{
Renderer: &BaseElement{
Token: ctx.SanitizeHTML(s, false),
Style: ctx.styles.Text,
Style: ctx.options.Styles.Text,
},
}
case ast.KindEmphasis:
n := node.(*ast.Emphasis)
s := string(n.Text(source))
style := ctx.styles.Emph
style := ctx.options.Styles.Emph
if n.Level > 1 {
style = ctx.styles.Strong
style = ctx.options.Styles.Strong
}
return Element{
@@ -178,7 +178,7 @@ func (tr *TermRenderer) NewElement(node ast.Node, source []byte) Element {
case astext.KindStrikethrough:
n := node.(*astext.Strikethrough)
s := string(n.Text(source))
style := ctx.styles.Strikethrough
style := ctx.options.Styles.Strikethrough
return Element{
Renderer: &BaseElement{
@@ -192,7 +192,7 @@ func (tr *TermRenderer) NewElement(node ast.Node, source []byte) Element {
Entering: "",
Exiting: "",
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)
e := &BlockElement{
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{
Renderer: e,
@@ -318,7 +318,7 @@ func (tr *TermRenderer) NewElement(node ast.Node, source []byte) Element {
return Element{
Renderer: &BaseElement{
Token: ctx.SanitizeHTML(string(n.Text(source)), true) + "\n",
Style: ctx.styles.HTMLBlock.StylePrimitive,
Style: ctx.options.Styles.HTMLBlock.StylePrimitive,
},
}
case ast.KindRawHTML:
@@ -326,7 +326,7 @@ func (tr *TermRenderer) NewElement(node ast.Node, source []byte) Element {
return Element{
Renderer: &BaseElement{
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:
e := &BlockElement{
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,
Newline: true,
}
@@ -347,14 +347,14 @@ func (tr *TermRenderer) NewElement(node ast.Node, source []byte) Element {
case astext.KindDefinitionTerm:
return Element{
Renderer: &BaseElement{
Style: ctx.styles.DefinitionTerm,
Style: ctx.options.Styles.DefinitionTerm,
},
}
case astext.KindDefinitionDescription:
return Element{
Renderer: &BaseElement{
Style: ctx.styles.DefinitionDescription,
Style: ctx.options.Styles.DefinitionDescription,
},
}
+8 -8
View File
@@ -1,4 +1,4 @@
package gold
package ansi
import (
"bytes"
@@ -14,21 +14,21 @@ type HeadingElement struct {
func (e *HeadingElement) Render(w io.Writer, ctx RenderContext) error {
bs := ctx.blockStack
rules := ctx.styles.Heading
rules := ctx.options.Styles.Heading
switch e.Level {
case 1:
rules = cascadeStyles(false, rules, ctx.styles.H1)
rules = cascadeStyles(false, rules, ctx.options.Styles.H1)
case 2:
rules = cascadeStyles(false, rules, ctx.styles.H2)
rules = cascadeStyles(false, rules, ctx.options.Styles.H2)
case 3:
rules = cascadeStyles(false, rules, ctx.styles.H3)
rules = cascadeStyles(false, rules, ctx.options.Styles.H3)
case 4:
rules = cascadeStyles(false, rules, ctx.styles.H4)
rules = cascadeStyles(false, rules, ctx.options.Styles.H4)
case 5:
rules = cascadeStyles(false, rules, ctx.styles.H5)
rules = cascadeStyles(false, rules, ctx.options.Styles.H5)
case 6:
rules = cascadeStyles(false, rules, ctx.styles.H6)
rules = cascadeStyles(false, rules, ctx.options.Styles.H6)
}
if !e.First {
+3 -3
View File
@@ -1,4 +1,4 @@
package gold
package ansi
import (
"io"
@@ -15,7 +15,7 @@ func (e *ImageElement) Render(w io.Writer, ctx RenderContext) error {
if len(e.Text) > 0 {
el := &BaseElement{
Token: e.Text,
Style: ctx.styles.ImageText,
Style: ctx.options.Styles.ImageText,
}
err := el.Render(w, ctx)
if err != nil {
@@ -26,7 +26,7 @@ func (e *ImageElement) Render(w io.Writer, ctx RenderContext) error {
el := &BaseElement{
Token: resolveRelativeURL(e.BaseURL, e.URL),
Prefix: " ",
Style: ctx.styles.Image,
Style: ctx.options.Styles.Image,
}
err := el.Render(w, ctx)
if err != nil {
+1 -1
View File
@@ -1,4 +1,4 @@
package gold
package ansi
import (
"io"
+3 -3
View File
@@ -1,4 +1,4 @@
package gold
package ansi
import (
"io"
@@ -19,7 +19,7 @@ func (e *LinkElement) Render(w io.Writer, ctx RenderContext) error {
el := &BaseElement{
Token: e.Text,
Style: ctx.styles.LinkText,
Style: ctx.options.Styles.LinkText,
}
err := el.Render(w, ctx)
if err != nil {
@@ -53,7 +53,7 @@ func (e *LinkElement) Render(w io.Writer, ctx RenderContext) error {
if len(e.URL) > 0 {
pre := " "
style := ctx.styles.Link
style := ctx.options.Styles.Link
if !textRendered {
pre = ""
style.BlockPrefix = ""
+3 -3
View File
@@ -1,4 +1,4 @@
package gold
package ansi
import (
"io"
@@ -13,12 +13,12 @@ func (e *ItemElement) Render(w io.Writer, ctx RenderContext) error {
var el *BaseElement
if e.Enumeration > 0 {
el = &BaseElement{
Style: ctx.styles.Enumeration,
Style: ctx.options.Styles.Enumeration,
Prefix: strconv.FormatInt(int64(e.Enumeration), 10),
}
} else {
el = &BaseElement{
Style: ctx.styles.Item,
Style: ctx.options.Styles.Item,
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
package gold
package ansi
import (
"io"
+1 -1
View File
@@ -1,4 +1,4 @@
package gold
package ansi
import (
"io"
+2 -2
View File
@@ -1,4 +1,4 @@
package gold
package ansi
import (
"bytes"
@@ -13,7 +13,7 @@ type ParagraphElement struct {
func (e *ParagraphElement) Render(w io.Writer, ctx RenderContext) error {
bs := ctx.blockStack
rules := ctx.styles.Paragraph
rules := ctx.options.Styles.Paragraph
_, _ = w.Write([]byte("\n"))
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 (
"io"
"io/ioutil"
"os"
"github.com/lucasb-eyer/go-colorful"
"github.com/rakyll/statik/fs"
_ "github.com/charmbracelet/gold/statik"
)
type StylePrimitive struct {
@@ -95,27 +88,6 @@ type StyleConfig struct {
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 {
var r StyleBlock
+3 -3
View File
@@ -1,4 +1,4 @@
package gold
package ansi
import (
"io"
@@ -29,7 +29,7 @@ func (e *TableElement) Render(w io.Writer, ctx RenderContext) error {
var indent uint
var margin uint
rules := ctx.styles.Table
rules := ctx.options.Styles.Table
if rules.Indent != nil {
indent = *rules.Indent
}
@@ -56,7 +56,7 @@ func (e *TableElement) Finish(w io.Writer, ctx RenderContext) error {
ctx.table.writer.Render()
ctx.table.writer = nil
rules := ctx.styles.Table
rules := ctx.options.Styles.Table
renderText(ctx.table.indentWriter, ctx.blockStack.Current().Style.StylePrimitive, rules.BlockSuffix)
return nil
}
+4 -4
View File
@@ -1,4 +1,4 @@
package gold
package ansi
import (
"io"
@@ -11,14 +11,14 @@ type TaskElement struct {
func (e *TaskElement) Render(w io.Writer, ctx RenderContext) error {
var el *BaseElement
pre := ctx.styles.Task.Unticked
pre := ctx.options.Styles.Task.Unticked
if e.Checked {
pre = ctx.styles.Task.Ticked
pre = ctx.options.Styles.Task.Ticked
}
el = &BaseElement{
Prefix: pre,
Style: ctx.styles.Task.StylePrimitive,
Style: ctx.options.Styles.Task.StylePrimitive,
}
return el.Render(w, ctx)
+1 -1
View File
@@ -1,4 +1,4 @@
package gold
package ansi
import (
"regexp"
+32 -143
View File
@@ -4,26 +4,22 @@ import (
"bytes"
"encoding/json"
"io"
"log"
"net/url"
"strings"
"io/ioutil"
"os"
"github.com/rakyll/statik/fs"
"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"
"github.com/charmbracelet/gold/ansi"
_ "github.com/charmbracelet/gold/statik"
)
type Options struct {
BaseURL string
WordWrap int
}
type TermRenderer struct {
context RenderContext
Options ansi.Options
}
// 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
// specific style.
func RenderBytes(in []byte, stylePath string) ([]byte, error) {
r, err := NewTermRenderer(stylePath, Options{
r, err := NewTermRenderer(stylePath, ansi.Options{
WordWrap: 80,
})
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.
func NewTermRenderer(stylePath string, options Options) (*TermRenderer, error) {
func NewTermRenderer(stylePath string, options ansi.Options) (*TermRenderer, error) {
if stylePath == "" {
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
// set.
func NewTermRendererFromBytes(b []byte, options Options) (*TermRenderer, error) {
tr := &TermRenderer{
context: NewRenderContext(options),
}
err := json.Unmarshal(b, &tr.context.styles)
func NewTermRendererFromBytes(b []byte, options ansi.Options) (*TermRenderer, error) {
err := json.Unmarshal(b, &options.Styles)
if err != nil {
return nil, err
}
return tr, nil
return &TermRenderer{
Options: options,
}, nil
}
// Render returns the markdown rendered into a string.
@@ -89,139 +83,34 @@ func (tr *TermRenderer) RenderBytes(in []byte) ([]byte, error) {
parser.WithAutoHeadingID(),
),
)
ar := ansi.NewRenderer(tr.Options)
md.SetRenderer(
renderer.NewRenderer(
renderer.WithNodeRenderers(util.Prioritized(tr, 1000))))
renderer.WithNodeRenderers(util.Prioritized(ar, 1000))))
var buf bytes.Buffer
err := md.Convert(in, &buf)
return buf.Bytes(), err
}
// 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)
func loadStyle(f string) ([]byte, error) {
var r io.ReadCloser
var err error
// 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 := 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)
r, err = os.Open(f)
if err != nil {
log.Fatal(err)
}
if u.IsAbs() {
return rel
}
u.Path = strings.TrimPrefix(u.Path, "/")
statikFS, err := fs.New()
if err != nil {
return nil, err
}
base, err := url.Parse(baseURL)
if err != nil {
return rel
r, err = statikFS.Open("/" + f + ".json")
if err != nil {
return nil, err
}
}
return base.ResolveReference(u).String()
defer r.Close()
return ioutil.ReadAll(r)
}