mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-07 20:30:36 -05:00
Adding some embedded short codes (including code highlighting)
This commit is contained in:
parent
13b067b506
commit
3fd6c1a24e
3 changed files with 201 additions and 149 deletions
|
@ -14,12 +14,12 @@
|
|||
package hugolib
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/spf13/hugo/template/bundle"
|
||||
"html/template"
|
||||
"strings"
|
||||
"unicode"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/spf13/hugo/template/bundle"
|
||||
"html/template"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
var _ = fmt.Println
|
||||
|
@ -27,195 +27,201 @@ var _ = fmt.Println
|
|||
type ShortcodeFunc func([]string) string
|
||||
|
||||
type Shortcode struct {
|
||||
Name string
|
||||
Func ShortcodeFunc
|
||||
Name string
|
||||
Func ShortcodeFunc
|
||||
}
|
||||
|
||||
type ShortcodeWithPage struct {
|
||||
Params interface{}
|
||||
Inner template.HTML
|
||||
Page *Page
|
||||
Params interface{}
|
||||
Inner template.HTML
|
||||
Page *Page
|
||||
}
|
||||
|
||||
type Shortcodes map[string]ShortcodeFunc
|
||||
|
||||
func ShortcodesHandle(stringToParse string, p *Page, t bundle.Template) string {
|
||||
leadStart := strings.Index(stringToParse, `{{%`)
|
||||
if leadStart >= 0 {
|
||||
leadEnd := strings.Index(stringToParse[leadStart:], `%}}`) + leadStart
|
||||
if leadEnd > leadStart {
|
||||
name, par := SplitParams(stringToParse[leadStart+3 : leadEnd])
|
||||
tmpl := GetTemplate(name, t)
|
||||
if tmpl == nil {
|
||||
return stringToParse
|
||||
}
|
||||
params := Tokenize(par)
|
||||
// Always look for closing tag.
|
||||
endStart, endEnd := FindEnd(stringToParse[leadEnd:], name)
|
||||
var data = &ShortcodeWithPage{Params: params, Page: p}
|
||||
if endStart > 0 {
|
||||
s := stringToParse[leadEnd+3 : leadEnd+endStart]
|
||||
data.Inner = template.HTML(CleanP(ShortcodesHandle(s, p, t)))
|
||||
remainder := CleanP(stringToParse[leadEnd+endEnd:])
|
||||
leadStart := strings.Index(stringToParse, `{{%`)
|
||||
if leadStart >= 0 {
|
||||
leadEnd := strings.Index(stringToParse[leadStart:], `%}}`) + leadStart
|
||||
if leadEnd > leadStart {
|
||||
name, par := SplitParams(stringToParse[leadStart+3 : leadEnd])
|
||||
tmpl := GetTemplate(name, t)
|
||||
if tmpl == nil {
|
||||
return stringToParse
|
||||
}
|
||||
params := Tokenize(par)
|
||||
// Always look for closing tag.
|
||||
endStart, endEnd := FindEnd(stringToParse[leadEnd:], name)
|
||||
var data = &ShortcodeWithPage{Params: params, Page: p}
|
||||
if endStart > 0 {
|
||||
s := stringToParse[leadEnd+3 : leadEnd+endStart]
|
||||
data.Inner = template.HTML(CleanP(ShortcodesHandle(s, p, t)))
|
||||
remainder := CleanP(stringToParse[leadEnd+endEnd:])
|
||||
|
||||
return CleanP(stringToParse[:leadStart]) +
|
||||
ShortcodeRender(tmpl, data) +
|
||||
CleanP(ShortcodesHandle(remainder, p, t))
|
||||
}
|
||||
return CleanP(stringToParse[:leadStart]) +
|
||||
ShortcodeRender(tmpl, data) +
|
||||
CleanP(ShortcodesHandle(stringToParse[leadEnd+3:], p,
|
||||
t))
|
||||
}
|
||||
}
|
||||
return stringToParse
|
||||
return CleanP(stringToParse[:leadStart]) +
|
||||
ShortcodeRender(tmpl, data) +
|
||||
CleanP(ShortcodesHandle(remainder, p, t))
|
||||
}
|
||||
return CleanP(stringToParse[:leadStart]) +
|
||||
ShortcodeRender(tmpl, data) +
|
||||
CleanP(ShortcodesHandle(stringToParse[leadEnd+3:], p,
|
||||
t))
|
||||
}
|
||||
}
|
||||
return stringToParse
|
||||
}
|
||||
|
||||
// Clean up odd behavior when closing tag is on first line
|
||||
// or opening tag is on the last line due to extra line in markdown file
|
||||
func CleanP(str string) string {
|
||||
if strings.HasSuffix(strings.TrimSpace(str), "<p>") {
|
||||
idx := strings.LastIndex(str, "<p>")
|
||||
str = str[:idx]
|
||||
}
|
||||
if strings.HasSuffix(strings.TrimSpace(str), "<p>") {
|
||||
idx := strings.LastIndex(str, "<p>")
|
||||
str = str[:idx]
|
||||
}
|
||||
|
||||
if strings.HasPrefix(strings.TrimSpace(str), "</p>") {
|
||||
str = str[strings.Index(str, "</p>")+5:]
|
||||
}
|
||||
if strings.HasPrefix(strings.TrimSpace(str), "</p>") {
|
||||
str = str[strings.Index(str, "</p>")+5:]
|
||||
}
|
||||
|
||||
return str
|
||||
return str
|
||||
}
|
||||
|
||||
func FindEnd(str string, name string) (int, int) {
|
||||
var endPos int
|
||||
var startPos int
|
||||
var try []string
|
||||
var endPos int
|
||||
var startPos int
|
||||
var try []string
|
||||
|
||||
try = append(try, "{{% /"+name+" %}}")
|
||||
try = append(try, "{{% /"+name+"%}}")
|
||||
try = append(try, "{{%/"+name+"%}}")
|
||||
try = append(try, "{{%/"+name+" %}}")
|
||||
try = append(try, "{{% /"+name+" %}}")
|
||||
try = append(try, "{{% /"+name+"%}}")
|
||||
try = append(try, "{{%/"+name+"%}}")
|
||||
try = append(try, "{{%/"+name+" %}}")
|
||||
|
||||
lowest := len(str)
|
||||
for _, x := range try {
|
||||
start := strings.Index(str, x)
|
||||
if start < lowest && start > 0 {
|
||||
startPos = start
|
||||
endPos = startPos + len(x)
|
||||
}
|
||||
}
|
||||
lowest := len(str)
|
||||
for _, x := range try {
|
||||
start := strings.Index(str, x)
|
||||
if start < lowest && start > 0 {
|
||||
startPos = start
|
||||
endPos = startPos + len(x)
|
||||
}
|
||||
}
|
||||
|
||||
return startPos, endPos
|
||||
return startPos, endPos
|
||||
}
|
||||
|
||||
func GetTemplate(name string, t bundle.Template) *template.Template {
|
||||
return t.Lookup("shortcodes/" + name + ".html")
|
||||
if x := t.Lookup("shortcodes/" + name + ".html"); x != nil {
|
||||
return x
|
||||
}
|
||||
return t.Lookup("_internal/shortcodes/" + name + ".html")
|
||||
}
|
||||
|
||||
func StripShortcodes(stringToParse string) string {
|
||||
posStart := strings.Index(stringToParse, "{{%")
|
||||
if posStart > 0 {
|
||||
posEnd := strings.Index(stringToParse[posStart:], "%}}") + posStart
|
||||
if posEnd > posStart {
|
||||
newString := stringToParse[:posStart] + StripShortcodes(stringToParse[posEnd+3:])
|
||||
return newString
|
||||
}
|
||||
}
|
||||
return stringToParse
|
||||
posStart := strings.Index(stringToParse, "{{%")
|
||||
if posStart > 0 {
|
||||
posEnd := strings.Index(stringToParse[posStart:], "%}}") + posStart
|
||||
if posEnd > posStart {
|
||||
newString := stringToParse[:posStart] + StripShortcodes(stringToParse[posEnd+3:])
|
||||
return newString
|
||||
}
|
||||
}
|
||||
return stringToParse
|
||||
}
|
||||
|
||||
func Tokenize(in string) interface{} {
|
||||
first := strings.Fields(in)
|
||||
var final = make([]string, 0)
|
||||
first := strings.Fields(in)
|
||||
var final = make([]string, 0)
|
||||
|
||||
// if don't need to parse, don't parse.
|
||||
if strings.Index(in, " ") < 0 && strings.Index(in, "=") < 1 {
|
||||
return append(final, in)
|
||||
}
|
||||
// if don't need to parse, don't parse.
|
||||
if strings.Index(in, " ") < 0 && strings.Index(in, "=") < 1 {
|
||||
return append(final, in)
|
||||
}
|
||||
|
||||
var keys = make([]string, 0)
|
||||
inQuote := false
|
||||
start := 0
|
||||
var keys = make([]string, 0)
|
||||
inQuote := false
|
||||
start := 0
|
||||
|
||||
for i, v := range first {
|
||||
index := strings.Index(v, "=")
|
||||
for i, v := range first {
|
||||
index := strings.Index(v, "=")
|
||||
|
||||
if !inQuote {
|
||||
if index > 1 {
|
||||
keys = append(keys, v[:index])
|
||||
v = v[index+1:]
|
||||
}
|
||||
}
|
||||
if !inQuote {
|
||||
if index > 1 {
|
||||
keys = append(keys, v[:index])
|
||||
v = v[index+1:]
|
||||
}
|
||||
}
|
||||
|
||||
// Adjusted to handle htmlencoded and non htmlencoded input
|
||||
if !strings.HasPrefix(v, "“") && !strings.HasPrefix(v, "\"") && !inQuote {
|
||||
final = append(final, v)
|
||||
} else if inQuote && (strings.HasSuffix(v, "”") ||
|
||||
strings.HasSuffix(v, "\"")) && !strings.HasSuffix(v, "\\\"") {
|
||||
if strings.HasSuffix(v, "\"") {
|
||||
first[i] = v[:len(v)-1]
|
||||
} else {
|
||||
first[i] = v[:len(v)-7]
|
||||
}
|
||||
final = append(final, strings.Join(first[start:i+1], " "))
|
||||
inQuote = false
|
||||
} else if (strings.HasPrefix(v, "“") ||
|
||||
strings.HasPrefix(v, "\"")) && !inQuote {
|
||||
if strings.HasSuffix(v, "”") || strings.HasSuffix(v,
|
||||
"\"") {
|
||||
if strings.HasSuffix(v, "\"") {
|
||||
if len(v) > 1 {
|
||||
final = append(final, v[1:len(v)-1])
|
||||
} else {
|
||||
final = append(final, "")
|
||||
}
|
||||
} else {
|
||||
final = append(final, v[7:len(v)-7])
|
||||
}
|
||||
} else {
|
||||
start = i
|
||||
if strings.HasPrefix(v, "\"") {
|
||||
first[i] = v[1:]
|
||||
} else {
|
||||
first[i] = v[7:]
|
||||
}
|
||||
inQuote = true
|
||||
}
|
||||
}
|
||||
// Adjusted to handle htmlencoded and non htmlencoded input
|
||||
if !strings.HasPrefix(v, "“") && !strings.HasPrefix(v, "\"") && !inQuote {
|
||||
final = append(final, v)
|
||||
} else if inQuote && (strings.HasSuffix(v, "”") ||
|
||||
strings.HasSuffix(v, "\"")) && !strings.HasSuffix(v, "\\\"") {
|
||||
if strings.HasSuffix(v, "\"") {
|
||||
first[i] = v[:len(v)-1]
|
||||
} else {
|
||||
first[i] = v[:len(v)-7]
|
||||
}
|
||||
final = append(final, strings.Join(first[start:i+1], " "))
|
||||
inQuote = false
|
||||
} else if (strings.HasPrefix(v, "“") ||
|
||||
strings.HasPrefix(v, "\"")) && !inQuote {
|
||||
if strings.HasSuffix(v, "”") || strings.HasSuffix(v,
|
||||
"\"") {
|
||||
if strings.HasSuffix(v, "\"") {
|
||||
if len(v) > 1 {
|
||||
final = append(final, v[1:len(v)-1])
|
||||
} else {
|
||||
final = append(final, "")
|
||||
}
|
||||
} else {
|
||||
final = append(final, v[7:len(v)-7])
|
||||
}
|
||||
} else {
|
||||
start = i
|
||||
if strings.HasPrefix(v, "\"") {
|
||||
first[i] = v[1:]
|
||||
} else {
|
||||
first[i] = v[7:]
|
||||
}
|
||||
inQuote = true
|
||||
}
|
||||
}
|
||||
|
||||
// No closing "... just make remainder the final token
|
||||
if inQuote && i == len(first) {
|
||||
final = append(final, first[start:]...)
|
||||
}
|
||||
}
|
||||
// No closing "... just make remainder the final token
|
||||
if inQuote && i == len(first) {
|
||||
final = append(final, first[start:]...)
|
||||
}
|
||||
}
|
||||
|
||||
if len(keys) > 0 && (len(keys) != len(final)) {
|
||||
panic("keys and final different lengths")
|
||||
}
|
||||
if len(keys) > 0 && (len(keys) != len(final)) {
|
||||
panic("keys and final different lengths")
|
||||
}
|
||||
|
||||
if len(keys) > 0 {
|
||||
var m = make(map[string]string)
|
||||
for i, k := range keys {
|
||||
m[k] = final[i]
|
||||
}
|
||||
if len(keys) > 0 {
|
||||
var m = make(map[string]string)
|
||||
for i, k := range keys {
|
||||
m[k] = final[i]
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
return final
|
||||
return final
|
||||
}
|
||||
|
||||
func SplitParams(in string) (name string, par2 string) {
|
||||
i := strings.IndexFunc(strings.TrimSpace(in), unicode.IsSpace)
|
||||
if i < 1 {
|
||||
return strings.TrimSpace(in), ""
|
||||
}
|
||||
i := strings.IndexFunc(strings.TrimSpace(in), unicode.IsSpace)
|
||||
if i < 1 {
|
||||
return strings.TrimSpace(in), ""
|
||||
}
|
||||
|
||||
return strings.TrimSpace(in[:i+1]), strings.TrimSpace(in[i+1:])
|
||||
return strings.TrimSpace(in[:i+1]), strings.TrimSpace(in[i+1:])
|
||||
}
|
||||
|
||||
func ShortcodeRender(tmpl *template.Template, data *ShortcodeWithPage) string {
|
||||
buffer := new(bytes.Buffer)
|
||||
tmpl.Execute(buffer, data)
|
||||
return buffer.String()
|
||||
buffer := new(bytes.Buffer)
|
||||
err := tmpl.Execute(buffer, data)
|
||||
if err != nil {
|
||||
fmt.Println("error processing shortcode", tmpl.Name(), "\n ERR:", err)
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
|
45
template/bundle/embedded.go
Normal file
45
template/bundle/embedded.go
Normal file
|
@ -0,0 +1,45 @@
|
|||
// Copyright © 2013 Steve Francia <spf@spf13.com>.
|
||||
//
|
||||
// Licensed under the Simple Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// http://opensource.org/licenses/Simple-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package bundle
|
||||
|
||||
type Tmpl struct {
|
||||
Name string
|
||||
Data string
|
||||
}
|
||||
|
||||
func (t *GoHtmlTemplate) EmbedShortcodes() {
|
||||
const k = "shortcodes"
|
||||
|
||||
t.AddInternalTemplate(k, "highlight.html", `{{ $lang := index .Params 0 }}{{ highlight .Inner $lang }}`)
|
||||
t.AddInternalTemplate(k, "test.html", `This is a simple Test`)
|
||||
t.AddInternalTemplate(k, "figure.html", `<!-- image -->
|
||||
<figure {{ if isset .Params "class" }}class="{{ index .Params "class" }}"{{ end }}>
|
||||
{{ if isset .Params "link"}}<a href="{{ index .Params "link"}}">{{ end }}
|
||||
<img src="{{ index .Params "src" }}" {{ if or (isset .Params "alt") (isset .Params "caption") }}alt="{{ if isset .Params "alt"}}{{ index .Params "alt"}}{{else}}{{ index .Params "caption" }}{{ end }}"{{ end }} />
|
||||
{{ if isset .Params "link"}}</a>{{ end }}
|
||||
{{ if or (or (isset .Params "title") (isset .Params "caption")) (isset .Params "attr")}}
|
||||
<figcaption>{{ if isset .Params "title" }}
|
||||
<h4>{{ index .Params "title" }}</h4>{{ end }}
|
||||
{{ if or (isset .Params "caption") (isset .Params "attr")}}<p>
|
||||
{{ index .Params "caption" }}
|
||||
{{ if isset .Params "attrlink"}}<a href="{{ index .Params "attrlink"}}"> {{ end }}
|
||||
{{ index .Params "attr" }}
|
||||
{{ if isset .Params "attrlink"}}</a> {{ end }}
|
||||
</p> {{ end }}
|
||||
</figcaption>
|
||||
{{ end }}
|
||||
</figure>
|
||||
<!-- image -->`)
|
||||
|
||||
}
|
|
@ -173,6 +173,7 @@ func NewTemplate() Template {
|
|||
}
|
||||
|
||||
func (t *GoHtmlTemplate) LoadEmbedded() {
|
||||
t.EmbedShortcodes()
|
||||
}
|
||||
|
||||
func (t *GoHtmlTemplate) AddInternalTemplate(prefix, name, tpl string) error {
|
||||
|
|
Loading…
Reference in a new issue