Skip to content

Commit

Permalink
Support custom external formats/renderers
Browse files Browse the repository at this point in the history
Add the configuration variable externalFormats, which maps formats to a
custom external executable or list of an executable and arguments. Shell
out to these executables to render content of those formats.

Fixes gohugoio#234.
  • Loading branch information
betaveros committed Nov 5, 2017
1 parent b88a105 commit 441f17c
Showing 1 changed file with 51 additions and 0 deletions.
51 changes: 51 additions & 0 deletions helpers/content.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ type ContentSpec struct {
footnoteReturnLinkContents string
// SummaryLength is the length of the summary that Hugo extracts from a content.
summaryLength int
// map of format to executable name, or to list of executable name and following args
externalFormats map[string]interface{}

Highlight func(code, lang, optsStr string) (string, error)
defatultPygmentsOpts map[string]string
Expand All @@ -61,6 +63,7 @@ func NewContentSpec(cfg config.Provider) (*ContentSpec, error) {
footnoteAnchorPrefix: cfg.GetString("footnoteAnchorPrefix"),
footnoteReturnLinkContents: cfg.GetString("footnoteReturnLinkContents"),
summaryLength: cfg.GetInt("summaryLength"),
externalFormats: cfg.GetStringMap("externalFormats"),

cfg: cfg,
}
Expand Down Expand Up @@ -441,6 +444,20 @@ type RenderingContext struct {

// RenderBytes renders a []byte.
func (c ContentSpec) RenderBytes(ctx *RenderingContext) []byte {
external := c.externalFormats[ctx.PageFmt]
if external != nil {
switch external.(type) {
case []string:
externalList := external.([]string)
return externallyRenderContent(ctx, externalList[0], externalList[1:])
case string:
externalString := external.(string)
return externallyRenderContent(ctx, externalString, nil)
default:
jww.ERROR.Printf("Could not understand external format for %s", ctx.PageFmt)
jww.ERROR.Println("Falling back to normal rendering methods")
}
}
switch ctx.PageFmt {
default:
return c.markdownRender(ctx)
Expand Down Expand Up @@ -730,3 +747,37 @@ func orgRender(ctx *RenderingContext, c ContentSpec) []byte {
return goorgeous.Org(cleanContent,
c.getHTMLRenderer(blackfriday.HTML_TOC, ctx))
}

func externallyRenderContent(ctx *RenderingContext, externalProgram string, args []string) []byte {
content := ctx.Content
cleanContent := bytes.Replace(content, SummaryDivider, []byte(""), 1)

path, err := exec.LookPath(externalProgram)

if err != nil {
jww.ERROR.Println(externalProgram, "not found in $PATH. Leaving content unrendered.")
return content
}

jww.INFO.Println("Rendering", ctx.DocumentName, "with", path, "...")
cmd := exec.Command(path, args...)
cmd.Stdin = bytes.NewReader(cleanContent)
var out, cmderr bytes.Buffer
cmd.Stdout = &out
cmd.Stderr = &cmderr
err = cmd.Run()
// like in rst2html and AsciiDoc, log stderr output regardless of state of err
for _, item := range strings.Split(string(cmderr.Bytes()), "\n") {
item := strings.TrimSpace(item)
if item != "" {
jww.ERROR.Printf("%s:%s", ctx.DocumentName, item)
}
}
if err != nil {
jww.ERROR.Printf("%s rendering %s: %v", path, ctx.DocumentName, err)
}

result := normalizeExternalHelperLineFeeds(out.Bytes())

return result
}

0 comments on commit 441f17c

Please sign in to comment.