github.com/phsym/gomarkdoc@v0.5.4/renderer.go (about)

     1  package gomarkdoc
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"text/template"
     7  
     8  	"github.com/phsym/gomarkdoc/format"
     9  	"github.com/phsym/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  }