Skip to content
9 changes: 9 additions & 0 deletions cmd/present/static/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -532,3 +532,12 @@ figcaption {
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
}

blockquote {
padding: 5px 20px;
border-left: 5px solid #999;
}
blockquote cite {
font-size: 85%;
color: #999;
}
7 changes: 7 additions & 0 deletions cmd/present/templates/action.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,10 @@ It determines how the formatting actions are rendered.
{{define "html"}}{{.HTML}}{{end}}

{{define "caption"}}<figcaption>{{style .Text}}</figcaption>{{end}}

{{define "quote"}}
<blockquote>
<p>{{style .Text}}</p>
{{with .Citation}}<cite>&mdash; {{style .}}</cite>{{end}}
</blockquote>
{{end}}
11 changes: 11 additions & 0 deletions present/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ After that come slides/sections, each after a blank line:
.link http://foo label
.html file.html
.caption _Gopher_ by [[https://www.instagram.com/reneefrench/][Renée French]]
.quote Never memorize something that you can look up //CITATION: Albert Einstein

Again, more text

Expand Down Expand Up @@ -214,6 +215,16 @@ processing styling and links as in standard text lines.

.caption _Gopher_ by [[http://www.reneefrench.com][Renée French]]

quote:

The template uses the function "quote" to inject quotes and citations.

The text after ".quote" is embedded in a blockquote element after
processing styling. The text after the optional "//CITATION:" is treated
as a citation and embedded in a cite element after processing styling.

.quote Never memorize something that you can look up //CITATION: Albert Einstein

iframe:

The function "iframe" injects iframes (pages inside pages).
Expand Down
50 changes: 50 additions & 0 deletions present/quote.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package present

import (
"fmt"
"strings"
)

func init() {
Register("quote", parseQuote)
}

type Quote struct {
Text string
Citation string
}

func (c Quote) TemplateName() string { return "quote" }

const citationToken = "//CITATION:"

// parseQuote parses a quote present directive. Its syntax:
// .quote <text> [citation]
func parseQuote(_ *Context, sourceFile string, sourceLine int, cmd string) (Elem, error) {

cmd = strings.TrimSpace(strings.TrimPrefix(cmd, ".quote"))

tokens := strings.Split(cmd, citationToken)

text := strings.TrimSpace(strings.TrimPrefix(tokens[0], ".quote"))
citation := ""

if text == "" {
return nil, fmt.Errorf("%s:%d invalid quote syntax", sourceFile, sourceLine)
}

if len(tokens) == 1 {
return Quote{text, citation}, nil
}

citation = strings.TrimSpace(tokens[1])
if citation == "" || len(tokens) > 2 {
return nil, fmt.Errorf("%s:%d invalid citation syntax", sourceFile, sourceLine)
}

return Quote{text, citation}, nil
}
78 changes: 78 additions & 0 deletions present/quote_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package present

import (
"strings"
"testing"
)

func TestQuoteParsing(t *testing.T) {
var tests = []struct {
name string
cmd string
err string
Quote
}{
{
name: "quote with invalid citation syntax",
cmd: ".quote a quote //CITATION: ",
err: "invalid citation syntax",
},
{
name: "quote without text and valid citation",
cmd: ".quote //CITATION: citation",
err: "invalid quote syntax",
},
{
name: "quote with text",
cmd: ".quote some text",
Quote: Quote{
Text: "some text",
Citation: "",
},
},
{
name: "quote with text and citation",
cmd: ".quote some text //CITATION: other text",
Quote: Quote{
Text: "some text",
Citation: "other text",
},
},
}

for _, test := range tests {
element, err := parseQuote(nil, "test.slide", 0, test.cmd)

if err != nil {
if test.err == "" {
t.Errorf("%s: unexpected error %v", test.name, err)
} else if !strings.Contains(err.Error(), test.err) {
t.Errorf("%s: expected error %s; got %v", test.name, test.err, err)
}
continue
}

if test.err != "" {
t.Errorf("%s: expected error %s; but got none", test.name, test.err)
continue
}

quote, ok := element.(Quote)
if !ok {
t.Errorf("%s: expected a Code value; got %T", test.name, quote)
continue
}

if quote.Text != test.Text {
t.Errorf("%s: expected Text %s; got %s", test.name, test.Text, quote.Text)
}

if quote.Citation != test.Citation {
t.Errorf("%s: expected Citation %s; got %s", test.name, test.Citation, quote.Citation)
}
}
}