github.com/huner2/gomarkdoc@v0.3.6/renderer.go (about) 1 package gomarkdoc 2 3 import ( 4 "fmt" 5 "strings" 6 "text/template" 7 8 "github.com/huner2/gomarkdoc/format" 9 "github.com/huner2/gomarkdoc/lang" 10 ) 11 12 type ( 13 // Renderer provides capabilities for rendering various types of 14 // documentation with the configured format and templates. 15 Renderer struct { 16 templateOverrides map[string]string 17 tmpl *template.Template 18 format format.Format 19 } 20 21 // RendererOption configures the renderer's behavior. 22 RendererOption func(renderer *Renderer) error 23 ) 24 25 //go:generate ./gentmpl.sh templates templates 26 27 // NewRenderer initializes a Renderer configured using the provided options. If 28 // nothing special is provided, the created renderer will use the default set of 29 // templates and the GitHubFlavoredMarkdown. 30 func NewRenderer(opts ...RendererOption) (*Renderer, error) { 31 renderer := &Renderer{ 32 templateOverrides: make(map[string]string), 33 format: &format.GitHubFlavoredMarkdown{}, 34 } 35 36 for _, opt := range opts { 37 if err := opt(renderer); err != nil { 38 return nil, err 39 } 40 } 41 42 for name, tmplStr := range templates { 43 // Use the override if present 44 if val, ok := renderer.templateOverrides[name]; ok { 45 tmplStr = val 46 } 47 48 if renderer.tmpl == nil { 49 tmpl := template.New(name) 50 tmpl.Funcs(map[string]interface{}{ 51 "add": func(n1, n2 int) int { 52 return n1 + n2 53 }, 54 "spacer": func() string { 55 return "\n\n" 56 }, 57 58 "bold": renderer.format.Bold, 59 "header": renderer.format.Header, 60 "rawHeader": renderer.format.RawHeader, 61 "codeBlock": renderer.format.CodeBlock, 62 "link": renderer.format.Link, 63 "listEntry": renderer.format.ListEntry, 64 "accordion": renderer.format.Accordion, 65 "accordionHeader": renderer.format.AccordionHeader, 66 "accordionTerminator": renderer.format.AccordionTerminator, 67 "localHref": renderer.format.LocalHref, 68 "codeHref": renderer.format.CodeHref, 69 "paragraph": renderer.format.Paragraph, 70 "escape": renderer.format.Escape, 71 }) 72 73 if _, err := tmpl.Parse(tmplStr); err != nil { 74 return nil, err 75 } 76 77 renderer.tmpl = tmpl 78 } else if _, err := renderer.tmpl.New(name).Parse(tmplStr); err != nil { 79 return nil, err 80 } 81 } 82 83 return renderer, nil 84 } 85 86 // WithTemplateOverride adds a template that overrides the template with the 87 // provided name using the value provided in the tmpl parameter. 88 func WithTemplateOverride(name, tmpl string) RendererOption { 89 return func(renderer *Renderer) error { 90 if _, ok := templates[name]; !ok { 91 return fmt.Errorf(`gomarkdoc: invalid template name "%s"`, name) 92 } 93 94 renderer.templateOverrides[name] = tmpl 95 96 return nil 97 } 98 } 99 100 // WithFormat changes the renderer to use the format provided instead of the 101 // default format. 102 func WithFormat(format format.Format) RendererOption { 103 return func(renderer *Renderer) error { 104 renderer.format = format 105 return nil 106 } 107 } 108 109 // File renders a file containing one or more packages to document to a string. 110 // You can change the rendering of the file by overriding the "file" template 111 // or one of the templates it references. 112 func (out *Renderer) File(file *lang.File) (string, error) { 113 return out.writeTemplate("file", file) 114 } 115 116 // Package renders a package's documentation to a string. You can change the 117 // rendering of the package by overriding the "package" template or one of the 118 // templates it references. 119 func (out *Renderer) Package(pkg *lang.Package) (string, error) { 120 return out.writeTemplate("package", pkg) 121 } 122 123 // Func renders a function's documentation to a string. You can change the 124 // rendering of the package by overriding the "func" template or one of the 125 // templates it references. 126 func (out *Renderer) Func(fn *lang.Func) (string, error) { 127 return out.writeTemplate("func", fn) 128 } 129 130 // Type renders a type's documentation to a string. You can change the 131 // rendering of the type by overriding the "type" template or one of the 132 // templates it references. 133 func (out *Renderer) Type(typ *lang.Type) (string, error) { 134 return out.writeTemplate("type", typ) 135 } 136 137 // Example renders an example's documentation to a string. You can change the 138 // rendering of the example by overriding the "example" template or one of the 139 // templates it references. 140 func (out *Renderer) Example(ex *lang.Example) (string, error) { 141 return out.writeTemplate("example", ex) 142 } 143 144 // writeTemplate renders the template of the provided name using the provided 145 // data object to a string. It uses the set of templates provided to the 146 // renderer as a template library. 147 func (out *Renderer) writeTemplate(name string, data interface{}) (string, error) { 148 var result strings.Builder 149 if err := out.tmpl.ExecuteTemplate(&result, name, data); err != nil { 150 return "", err 151 } 152 153 return result.String(), nil 154 }