ref: 3fd6c1a24e4f159ee300f0b6dbc177615455e5d6
parent: 13b067b5064cc1c59ade383612906fce944dcf33
author: spf13 <[email protected]>
date: Thu Jan 9 12:33:20 EST 2014
Adding some embedded short codes (including code highlighting)
--- a/hugolib/shortcode.go
+++ b/hugolib/shortcode.go
@@ -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 @@
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()
}
--- /dev/null
+++ b/template/bundle/embedded.go
@@ -1,0 +1,45 @@
+// Copyright © 2013 Steve Francia <[email protected]>.
+//
+// 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 -->`)
+
+}
--- a/template/bundle/template.go
+++ b/template/bundle/template.go
@@ -173,6 +173,7 @@
}
func (t *GoHtmlTemplate) LoadEmbedded() {
+ t.EmbedShortcodes()
}
func (t *GoHtmlTemplate) AddInternalTemplate(prefix, name, tpl string) error {