Remove superfluous p-tags around shortcodes

This commit replaces the regexp driven `replaceShortcodeTokens` with a handwritten one.

It wasnt't possible to handle the p-tags case without breaking performance.

This fix actually improves in that area:

```
benchmark                           old ns/op     new ns/op     delta
BenchmarkParsePage                  142738        142667        -0.05%
BenchmarkReplaceShortcodeTokens     665590        575645        -13.51%
BenchmarkShortcodeLexer             176038        181074        +2.86%

benchmark                           old allocs     new allocs     delta
BenchmarkParsePage                  87             87             +0.00%
BenchmarkReplaceShortcodeTokens     9631           9424           -2.15%
BenchmarkShortcodeLexer             274            274            +0.00%

benchmark                           old bytes     new bytes     delta
BenchmarkParsePage                  141830        141830        +0.00%
BenchmarkReplaceShortcodeTokens     52275         35219         -32.63%
BenchmarkShortcodeLexer             30177         30178         +0.00%
```

Fixes #1148
This commit is contained in:
Bjørn Erik Pedersen
2015-06-21 13:08:30 +02:00
parent ae4f72b091
commit 004fcddc80
5 changed files with 97 additions and 59 deletions

View File

@@ -14,6 +14,8 @@
package hugolib
import (
"bytes"
"errors"
"fmt"
"html/template"
"reflect"
@@ -132,7 +134,7 @@ func HandleShortcodes(stringToParse string, page *Page, t tpl.Template) (string,
}
if len(tmpShortcodes) > 0 {
tmpContentWithTokensReplaced, err := replaceShortcodeTokens([]byte(tmpContent), shortcodePlaceholderPrefix, true, tmpShortcodes)
tmpContentWithTokensReplaced, err := replaceShortcodeTokens([]byte(tmpContent), shortcodePlaceholderPrefix, tmpShortcodes)
if err != nil {
return "", fmt.Errorf("Fail to replace short code tokens in %s:\n%s", page.BaseFileName(), err.Error())
@@ -436,10 +438,10 @@ Loop:
}
// replaceShortcodeTokensInsources calls replaceShortcodeTokens for every source given.
func replaceShortcodeTokensInsources(prefix string, wrapped bool, replacements map[string]string, sources ...[]byte) (b [][]byte, err error) {
func replaceShortcodeTokensInsources(prefix string, replacements map[string]string, sources ...[]byte) (b [][]byte, err error) {
result := make([][]byte, len(sources))
for i, s := range sources {
b, err := replaceShortcodeTokens(s, prefix, wrapped, replacements)
b, err := replaceShortcodeTokens(s, prefix, replacements)
if err != nil {
return nil, err
@@ -450,43 +452,65 @@ func replaceShortcodeTokensInsources(prefix string, wrapped bool, replacements m
}
// Replace prefixed shortcode tokens (HUGOSHORTCODE-1, HUGOSHORTCODE-2) with the real content.
// wrapped = true means that the token has been wrapped in {@{@/@}@}
func replaceShortcodeTokens(source []byte, prefix string, wrapped bool, replacements map[string]string) (b []byte, err error) {
var re *regexp.Regexp
func replaceShortcodeTokens(source []byte, prefix string, replacements map[string]string) ([]byte, error) {
if wrapped {
re, err = regexp.Compile(`\{@\{@` + regexp.QuoteMeta(prefix) + `-\d+@\}@\}`)
if err != nil {
return nil, err
}
} else {
re, err = regexp.Compile(regexp.QuoteMeta(prefix) + `-(\d+)`)
if err != nil {
return nil, err
}
if len(replacements) == 0 {
return source, nil
}
// use panic/recover for reporting if an unknown
defer func() {
if r := recover(); r != nil {
var ok bool
b = nil
err, ok = r.(error)
if !ok {
err = fmt.Errorf("unexpected panic during replaceShortcodeTokens: %v", r)
var buff bytes.Buffer
sourceLen := len(source)
width := 0
start := 0
pre := []byte("{@{@" + prefix)
post := []byte("@}@}")
pStart := []byte("<p>")
pEnd := []byte("</p>")
k := bytes.Index(source[start:], pre)
for k != -1 {
j := start + k
postIdx := bytes.Index(source[j:], post)
if postIdx < 0 {
// this should never happen, but let the caller decide to panic or not
return nil, errors.New("illegal state in content; shortcode token missing end delim")
}
end := j + postIdx + 4
newVal := []byte(replacements[string(source[j:end])])
// Issue #1148: Check for wrapping p-tags <p>
if j >= 3 && bytes.Equal(source[j-3:j], pStart) {
if (k+4) < sourceLen && bytes.Equal(source[end:end+4], pEnd) {
j -= 3
end += 4
}
}
}()
b = re.ReplaceAllFunc(source, func(m []byte) []byte {
key := string(m)
if val, ok := replacements[key]; ok {
return []byte(val)
oldVal := source[j:end]
w, err := buff.Write(source[start:j])
if err != nil {
return nil, errors.New("buff write failed")
}
panic(fmt.Errorf("unknown shortcode token %q", key))
})
width += w
w, err = buff.Write(newVal)
if err != nil {
return nil, errors.New("buff write failed")
}
width += w
start = j + len(oldVal)
return b, err
k = bytes.Index(source[start:], pre)
}
_, err := buff.Write(source[start:])
if err != nil {
return nil, errors.New("buff write failed")
}
return buff.Bytes(), nil
}
func getShortcodeTemplate(name string, t tpl.Template) *template.Template {