diff --git a/docs/content/extras/shortcodes.md b/docs/content/extras/shortcodes.md
index 5464b1a57..c0b6e0b04 100644
--- a/docs/content/extras/shortcodes.md
+++ b/docs/content/extras/shortcodes.md
@@ -29,8 +29,8 @@ want a [partial template](/templates/partial) instead.
## Using a shortcode
-In your content files, a shortcode can be called by using '`{{% name parameters
-%}}`' respectively. Shortcodes are space delimited (parameters with spaces
+In your content files, a shortcode can be called by using '`{{%/* name parameters
+*/%}}`' respectively. Shortcodes are space delimited (parameters with spaces
can be quoted).
The first word is always the name of the shortcode. Parameters follow the name.
@@ -43,7 +43,7 @@ shortcodes match (name only), the closing being prepended with a slash.
Example of a paired shortcode:
- {{ % highlight go %}} A bunch of code here {{ % /highlight %}}
+ {{%/* highlight go */%}} A bunch of code here {{%/* /highlight */%}}
## Hugo Shortcodes
@@ -60,9 +60,8 @@ HTML. Read more on [highlighting](/extras/highlighting).
closing shortcode.
#### Example
-The example has an extra space between the “`{{`” and “`%`” characters to prevent rendering here.
- {{ % highlight html %}}
+ {{%/* highlight html */%}}
{{ .Title }}
@@ -71,7 +70,7 @@ The example has an extra space between the “`{{`” and “`%`” characters t
{{ end }}
Summary Next Line. {{% img src=“/not/real” %}}.\nMore text here.
\n\nSome more text
\n") + checkPageContent(t, p, "Summary Next Line. \n\n \n \n
Some more text
\n") checkPageSummary(t, p, "Summary Next Line. . More text here. Some more text") checkPageType(t, p, "page") checkPageLayout(t, p, "page/single.html", "_default/single.html", "theme/page/single.html", "theme/_default/single.html") diff --git a/hugolib/shortcode.go b/hugolib/shortcode.go index ef413bfb3..6dfc4ef02 100644 --- a/hugolib/shortcode.go +++ b/hugolib/shortcode.go @@ -1,4 +1,4 @@ -// Copyright © 2013 Steve Francia") { - idx := strings.LastIndex(str, "
") - str = str[:idx] +func extractShortcodes(stringToParse string, p *Page, t Template) (string, map[string]shortcode, error) { + + shortCodes := make(map[string]shortcode) + + startIdx := strings.Index(stringToParse, "{{") + + // short cut for docs with no shortcodes + if startIdx < 0 { + return stringToParse, shortCodes, nil } - if strings.HasPrefix(strings.TrimSpace(str), "
") { - str = str[strings.Index(str, "")+5:] - } + // the parser takes a string; + // since this is an internal API, it could make sense to use the mutable []byte all the way, but + // it seems that the time isn't really spent in the byte copy operations, and the impl. gets a lot cleaner + pt := &pageTokens{lexer: newShortcodeLexer("parse-page", stringToParse, pos(startIdx))} - return str -} + id := 1 // incremented id, will be appended onto temp. shortcode placeholders + var result bytes.Buffer -func FindEnd(str string, name string) (int, int) { - var endPos int - var startPos int - var try []string + // the parser is guaranteed to return items in proper order or fail, so … + // … it's safe to keep some "global" state + var currItem item + var currShortcode shortcode + var err error - try = append(try, "{{% /"+name+" %}}") - try = append(try, "{{% /"+name+"%}}") - try = append(try, "{{%/"+name+"%}}") - try = append(try, "{{%/"+name+" %}}") +Loop: + for { + currItem = pt.next() - lowest := len(str) - for _, x := range try { - start := strings.Index(str, x) - if start < lowest && start > 0 { - startPos = start - endPos = startPos + len(x) + switch currItem.typ { + case tText: + result.WriteString(currItem.val) + case tLeftDelimScWithMarkup, tLeftDelimScNoMarkup: + // let extractShortcode handle left delim (will do so recursively) + pt.backup() + if currShortcode, err = extractShortcode(pt, p, t); err != nil { + return result.String(), shortCodes, err + } + + if currShortcode.params == nil { + currShortcode.params = make([]string, 0) + } + + // wrap it in a block level element to let it be left alone by the markup engine + placeHolder := createShortcodePlaceholder(id) + result.WriteString(placeHolder) + shortCodes[placeHolder] = currShortcode + id++ + case tEOF: + break Loop + case tError: + err := fmt.Errorf("%s:%d: %s", + p.BaseFileName(), (p.lineNumRawContentStart() + pt.lexer.lineNum() - 1), currItem) + currShortcode.err = err + return result.String(), shortCodes, err } } - return startPos, endPos + return result.String(), shortCodes, nil + +} + +// Replace prefixed shortcode tokens (HUGOSHORTCODE-1, HUGOSHORTCODE-2) with the real content. +// This assumes that all tokens exist in the input string and that they are in order. +// numReplacements = -1 will do len(replacements), and it will always start from the beginning (1) +// wrappendInDiv = true means that the token is wrapped in a +func replaceShortcodeTokens(source []byte, prefix string, numReplacements int, wrappedInDiv bool, replacements map[string]string) ([]byte, error) { + + if numReplacements < 0 { + numReplacements = len(replacements) + } + + if numReplacements == 0 { + return source, nil + } + + newLen := len(source) + + for i := 1; i <= numReplacements; i++ { + key := prefix + "-" + strconv.Itoa(i) + + if wrappedInDiv { + key = "