+ Author Name
+ Job title, Company
+ joe@example.com
+ http://url/
+ @twitter_name
+
+The subtitle, date, and tags lines are optional.
+
+The date line may be written without a time:
+ 2 Jan 2006
+In this case, the time will be interpreted as 10am UTC on that date.
+
+The tags line is a comma-separated list of tags that may be used to categorize
+the document.
+
+The author section may contain a mixture of text, twitter names, and links.
+For slide presentations, only the plain text lines will be displayed on the
+first slide.
+
+Multiple presenters may be specified, separated by a blank line.
+
+After that come slides/sections, each after a blank line:
+
+ * Title of slide or section (must have asterisk)
+
+ Some Text
+
+ ** Subsection
+
+ - bullets
+ - more bullets
+ - a bullet with
+
+ *** Sub-subsection
+
+ Some More text
+
+ Preformatted text
+ is indented (however you like)
+
+ Further Text, including invocations like:
+
+ .code x.go /^func main/,/^}/
+ .play y.go
+ .image image.jpg
+ .background image.jpg
+ .iframe http://foo
+ .link http://foo label
+ .html file.html
+ .caption _Gopher_ by [[https://www.instagram.com/reneefrench/][Renée French]]
+
+ Again, more text
+
+Blank lines are OK (not mandatory) after the title and after the
+text. Text, bullets, and .code etc. are all optional; title is
+not.
+
+Lines starting with # in column 1 are commentary.
+
+Fonts:
+
+Within the input for plain text or lists, text bracketed by font
+markers will be presented in italic, bold, or program font.
+Marker characters are _ (italic), * (bold) and ` (program font).
+An opening marker must be preceded by a space or punctuation
+character or else be at start of a line; similarly, a closing
+marker must be followed by a space or punctuation character or
+else be at the end of a line. Unmatched markers appear as plain text.
+There must be no spaces between markers. Within marked text,
+a single marker character becomes a space and a doubled single
+marker quotes the marker character.
+
+ _italic_
+ *bold*
+ `program`
+ Markup—_especially_italic_text_—can easily be overused.
+ _Why_use_scoped__ptr_? Use plain ***ptr* instead.
+
+Inline links:
+
+Links can be included in any text with the form [[url][label]], or
+[[url]] to use the URL itself as the label.
+
+Functions:
+
+A number of template functions are available through invocations
+in the input text. Each such invocation contains a period as the
+first character on the line, followed immediately by the name of
+the function, followed by any arguments. A typical invocation might
+be
+ .play demo.go /^func show/,/^}/
+(except that the ".play" must be at the beginning of the line and
+not be indented like this.)
+
+Here follows a description of the functions:
+
+code:
+
+Injects program source into the output by extracting code from files
+and injecting them as HTML-escaped blocks. The argument is
+a file name followed by an optional address that specifies what
+section of the file to display. The address syntax is similar in
+its simplest form to that of ed, but comes from sam and is more
+general. See
+ https://plan9.io/sys/doc/sam/sam.html Table II
+for full details. The displayed block is always rounded out to a
+full line at both ends.
+
+If no pattern is present, the entire file is displayed.
+
+Any line in the program that ends with the four characters
+ OMIT
+is deleted from the source before inclusion, making it easy
+to write things like
+ .code test.go /START OMIT/,/END OMIT/
+to find snippets like this
+ tedious_code = boring_function()
+ // START OMIT
+ interesting_code = fascinating_function()
+ // END OMIT
+and see only this:
+ interesting_code = fascinating_function()
+
+Also, inside the displayed text a line that ends
+ // HL
+will be highlighted in the display. A highlighting mark may have a
+suffix word, such as
+ // HLxxx
+Such highlights are enabled only if the code invocation ends with
+"HL" followed by the word:
+ .code test.go /^type Foo/,/^}/ HLxxx
+
+The .code function may take one or more flags immediately preceding
+the filename. This command shows test.go in an editable text area:
+ .code -edit test.go
+This command shows test.go with line numbers:
+ .code -numbers test.go
+
+play:
+
+The function "play" is the same as "code" but puts a button
+on the displayed source so the program can be run from the browser.
+Although only the selected text is shown, all the source is included
+in the HTML output so it can be presented to the compiler.
+
+link:
+
+Create a hyperlink. The syntax is 1 or 2 space-separated arguments.
+The first argument is always the HTTP URL. If there is a second
+argument, it is the text label to display for this link.
+
+ .link http://golang.org golang.org
+
+image:
+
+The template uses the function "image" to inject picture files.
+
+The syntax is simple: 1 or 3 space-separated arguments.
+The first argument is always the file name.
+If there are more arguments, they are the height and width;
+both must be present, or substituted with an underscore.
+Replacing a dimension argument with the underscore parameter
+preserves the aspect ratio of the image when scaling.
+
+ .image images/betsy.jpg 100 200
+
+ .image images/janet.jpg _ 300
+
+video:
+
+The template uses the function "video" to inject video files.
+
+The syntax is simple: 2 or 4 space-separated arguments.
+The first argument is always the file name.
+The second argument is always the file content-type.
+If there are more arguments, they are the height and width;
+both must be present, or substituted with an underscore.
+Replacing a dimension argument with the underscore parameter
+preserves the aspect ratio of the video when scaling.
+
+ .video videos/evangeline.mp4 video/mp4 400 600
+
+ .video videos/mabel.ogg video/ogg 500 _
+
+background:
+
+The template uses the function "background" to set the background image for
+a slide. The only argument is the file name of the image.
+
+ .background images/susan.jpg
+
+caption:
+
+The template uses the function "caption" to inject figure captions.
+
+The text after ".caption" is embedded in a figcaption element after
+processing styling and links as in standard text lines.
+
+ .caption _Gopher_ by [[http://www.reneefrench.com][Renée French]]
+
+iframe:
+
+The function "iframe" injects iframes (pages inside pages).
+Its syntax is the same as that of image.
+
+html:
+
+The function html includes the contents of the specified file as
+unescaped HTML. This is useful for including custom HTML elements
+that cannot be created using only the slide format.
+It is your responsibility to make sure the included HTML is valid and safe.
+
+ .html file.html
+
+Presenter notes:
+
+Presenter notes may be enabled by appending the "-notes" flag when you run
+your "present" binary.
+
+This will allow you to open a second window by pressing 'N' from your browser
+displaying your slides. The second window is completely synced with your main
+window, except that presenter notes are only visible on the second window.
+
+Lines that begin with ": " are treated as presenter notes.
+
+ * Title of slide
+
+ Some Text
+
+ : Presenter notes (first paragraph)
+ : Presenter notes (subsequent paragraph(s))
+
+Notes may appear anywhere within the slide text. For example:
+
+ * Title of slide
+
+ : Presenter notes (first paragraph)
+
+ Some Text
+
+ : Presenter notes (subsequent paragraph(s))
+
+This has the same result as the example above.
+
+*/
+package present // import "golang.org/x/tools/present"
diff --git a/vendor/golang.org/x/tools/present/html.go b/vendor/golang.org/x/tools/present/html.go
new file mode 100644
index 00000000000..cca90ef4af1
--- /dev/null
+++ b/vendor/golang.org/x/tools/present/html.go
@@ -0,0 +1,31 @@
+package present
+
+import (
+ "errors"
+ "html/template"
+ "path/filepath"
+ "strings"
+)
+
+func init() {
+ Register("html", parseHTML)
+}
+
+func parseHTML(ctx *Context, fileName string, lineno int, text string) (Elem, error) {
+ p := strings.Fields(text)
+ if len(p) != 2 {
+ return nil, errors.New("invalid .html args")
+ }
+ name := filepath.Join(filepath.Dir(fileName), p[1])
+ b, err := ctx.ReadFile(name)
+ if err != nil {
+ return nil, err
+ }
+ return HTML{template.HTML(b)}, nil
+}
+
+type HTML struct {
+ template.HTML
+}
+
+func (s HTML) TemplateName() string { return "html" }
diff --git a/vendor/golang.org/x/tools/present/iframe.go b/vendor/golang.org/x/tools/present/iframe.go
new file mode 100644
index 00000000000..057d2295858
--- /dev/null
+++ b/vendor/golang.org/x/tools/present/iframe.go
@@ -0,0 +1,48 @@
+// Copyright 2013 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("iframe", parseIframe)
+}
+
+type Iframe struct {
+ URL string
+ Width int
+ Height int
+}
+
+func (i Iframe) TemplateName() string { return "iframe" }
+
+func parseIframe(ctx *Context, fileName string, lineno int, text string) (Elem, error) {
+ args := strings.Fields(text)
+ if len(args) < 2 {
+ return nil, fmt.Errorf("incorrect iframe invocation: %q", text)
+ }
+ i := Iframe{URL: args[1]}
+ a, err := parseArgs(fileName, lineno, args[2:])
+ if err != nil {
+ return nil, err
+ }
+ switch len(a) {
+ case 0:
+ // no size parameters
+ case 2:
+ if v, ok := a[0].(int); ok {
+ i.Height = v
+ }
+ if v, ok := a[1].(int); ok {
+ i.Width = v
+ }
+ default:
+ return nil, fmt.Errorf("incorrect iframe invocation: %q", text)
+ }
+ return i, nil
+}
diff --git a/vendor/golang.org/x/tools/present/image.go b/vendor/golang.org/x/tools/present/image.go
new file mode 100644
index 00000000000..84965cae544
--- /dev/null
+++ b/vendor/golang.org/x/tools/present/image.go
@@ -0,0 +1,53 @@
+// Copyright 2012 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("image", parseImage)
+}
+
+type Image struct {
+ URL string
+ Width int
+ Height int
+}
+
+func (i Image) TemplateName() string { return "image" }
+
+func parseImage(ctx *Context, fileName string, lineno int, text string) (Elem, error) {
+ args := strings.Fields(text)
+ if len(args) < 2 {
+ return nil, fmt.Errorf("incorrect image invocation: %q", text)
+ }
+ img := Image{URL: args[1]}
+ a, err := parseArgs(fileName, lineno, args[2:])
+ if err != nil {
+ return nil, err
+ }
+ switch len(a) {
+ case 0:
+ // no size parameters
+ case 2:
+ // If a parameter is empty (underscore) or invalid
+ // leave the field set to zero. The "image" action
+ // template will then omit that img tag attribute and
+ // the browser will calculate the value to preserve
+ // the aspect ratio.
+ if v, ok := a[0].(int); ok {
+ img.Height = v
+ }
+ if v, ok := a[1].(int); ok {
+ img.Width = v
+ }
+ default:
+ return nil, fmt.Errorf("incorrect image invocation: %q", text)
+ }
+ return img, nil
+}
diff --git a/vendor/golang.org/x/tools/present/link.go b/vendor/golang.org/x/tools/present/link.go
new file mode 100644
index 00000000000..2aead352e9c
--- /dev/null
+++ b/vendor/golang.org/x/tools/present/link.go
@@ -0,0 +1,100 @@
+// Copyright 2012 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"
+ "log"
+ "net/url"
+ "strings"
+)
+
+func init() {
+ Register("link", parseLink)
+}
+
+type Link struct {
+ URL *url.URL
+ Label string
+}
+
+func (l Link) TemplateName() string { return "link" }
+
+func parseLink(ctx *Context, fileName string, lineno int, text string) (Elem, error) {
+ args := strings.Fields(text)
+ if len(args) < 2 {
+ return nil, fmt.Errorf("link element must have at least 2 arguments")
+ }
+ url, err := url.Parse(args[1])
+ if err != nil {
+ return nil, err
+ }
+ label := ""
+ if len(args) > 2 {
+ label = strings.Join(args[2:], " ")
+ } else {
+ scheme := url.Scheme + "://"
+ if url.Scheme == "mailto" {
+ scheme = "mailto:"
+ }
+ label = strings.Replace(url.String(), scheme, "", 1)
+ }
+ return Link{url, label}, nil
+}
+
+func renderLink(href, text string) string {
+ text = font(text)
+ if text == "" {
+ text = href
+ }
+ // Open links in new window only when their url is absolute.
+ target := "_blank"
+ if u, err := url.Parse(href); err != nil {
+ log.Println("renderLink parsing url:", err)
+ } else if !u.IsAbs() || u.Scheme == "javascript" {
+ target = "_self"
+ }
+
+ return fmt.Sprintf(`%s`, href, target, text)
+}
+
+// parseInlineLink parses an inline link at the start of s, and returns
+// a rendered HTML link and the total length of the raw inline link.
+// If no inline link is present, it returns all zeroes.
+func parseInlineLink(s string) (link string, length int) {
+ if !strings.HasPrefix(s, "[[") {
+ return
+ }
+ end := strings.Index(s, "]]")
+ if end == -1 {
+ return
+ }
+ urlEnd := strings.Index(s, "]")
+ rawURL := s[2:urlEnd]
+ const badURLChars = `<>"{}|\^[] ` + "`" // per RFC2396 section 2.4.3
+ if strings.ContainsAny(rawURL, badURLChars) {
+ return
+ }
+ if urlEnd == end {
+ simpleUrl := ""
+ url, err := url.Parse(rawURL)
+ if err == nil {
+ // If the URL is http://foo.com, drop the http://
+ // In other words, render [[http://golang.org]] as:
+ // golang.org
+ if strings.HasPrefix(rawURL, url.Scheme+"://") {
+ simpleUrl = strings.TrimPrefix(rawURL, url.Scheme+"://")
+ } else if strings.HasPrefix(rawURL, url.Scheme+":") {
+ simpleUrl = strings.TrimPrefix(rawURL, url.Scheme+":")
+ }
+ }
+ return renderLink(rawURL, simpleUrl), end + 2
+ }
+ if s[urlEnd:urlEnd+2] != "][" {
+ return
+ }
+ text := s[urlEnd+2 : end]
+ return renderLink(rawURL, text), end + 2
+}
diff --git a/vendor/golang.org/x/tools/present/parse.go b/vendor/golang.org/x/tools/present/parse.go
new file mode 100644
index 00000000000..dd0f00b27ba
--- /dev/null
+++ b/vendor/golang.org/x/tools/present/parse.go
@@ -0,0 +1,568 @@
+// Copyright 2011 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 (
+ "bufio"
+ "bytes"
+ "errors"
+ "fmt"
+ "html/template"
+ "io"
+ "io/ioutil"
+ "log"
+ "net/url"
+ "regexp"
+ "strings"
+ "time"
+ "unicode"
+ "unicode/utf8"
+)
+
+var (
+ parsers = make(map[string]ParseFunc)
+ funcs = template.FuncMap{}
+)
+
+// Template returns an empty template with the action functions in its FuncMap.
+func Template() *template.Template {
+ return template.New("").Funcs(funcs)
+}
+
+// Render renders the doc to the given writer using the provided template.
+func (d *Doc) Render(w io.Writer, t *template.Template) error {
+ data := struct {
+ *Doc
+ Template *template.Template
+ PlayEnabled bool
+ NotesEnabled bool
+ }{d, t, PlayEnabled, NotesEnabled}
+ return t.ExecuteTemplate(w, "root", data)
+}
+
+// Render renders the section to the given writer using the provided template.
+func (s *Section) Render(w io.Writer, t *template.Template) error {
+ data := struct {
+ *Section
+ Template *template.Template
+ PlayEnabled bool
+ }{s, t, PlayEnabled}
+ return t.ExecuteTemplate(w, "section", data)
+}
+
+type ParseFunc func(ctx *Context, fileName string, lineNumber int, inputLine string) (Elem, error)
+
+// Register binds the named action, which does not begin with a period, to the
+// specified parser to be invoked when the name, with a period, appears in the
+// present input text.
+func Register(name string, parser ParseFunc) {
+ if len(name) == 0 || name[0] == ';' {
+ panic("bad name in Register: " + name)
+ }
+ parsers["."+name] = parser
+}
+
+// Doc represents an entire document.
+type Doc struct {
+ Title string
+ Subtitle string
+ Time time.Time
+ Authors []Author
+ TitleNotes []string
+ Sections []Section
+ Tags []string
+}
+
+// Author represents the person who wrote and/or is presenting the document.
+type Author struct {
+ Elem []Elem
+}
+
+// TextElem returns the first text elements of the author details.
+// This is used to display the author' name, job title, and company
+// without the contact details.
+func (p *Author) TextElem() (elems []Elem) {
+ for _, el := range p.Elem {
+ if _, ok := el.(Text); !ok {
+ break
+ }
+ elems = append(elems, el)
+ }
+ return
+}
+
+// Section represents a section of a document (such as a presentation slide)
+// comprising a title and a list of elements.
+type Section struct {
+ Number []int
+ Title string
+ Elem []Elem
+ Notes []string
+ Classes []string
+ Styles []string
+}
+
+// HTMLAttributes for the section
+func (s Section) HTMLAttributes() template.HTMLAttr {
+ if len(s.Classes) == 0 && len(s.Styles) == 0 {
+ return ""
+ }
+
+ var class string
+ if len(s.Classes) > 0 {
+ class = fmt.Sprintf(`class=%q`, strings.Join(s.Classes, " "))
+ }
+ var style string
+ if len(s.Styles) > 0 {
+ style = fmt.Sprintf(`style=%q`, strings.Join(s.Styles, " "))
+ }
+ return template.HTMLAttr(strings.Join([]string{class, style}, " "))
+}
+
+// Sections contained within the section.
+func (s Section) Sections() (sections []Section) {
+ for _, e := range s.Elem {
+ if section, ok := e.(Section); ok {
+ sections = append(sections, section)
+ }
+ }
+ return
+}
+
+// Level returns the level of the given section.
+// The document title is level 1, main section 2, etc.
+func (s Section) Level() int {
+ return len(s.Number) + 1
+}
+
+// FormattedNumber returns a string containing the concatenation of the
+// numbers identifying a Section.
+func (s Section) FormattedNumber() string {
+ b := &bytes.Buffer{}
+ for _, n := range s.Number {
+ fmt.Fprintf(b, "%v.", n)
+ }
+ return b.String()
+}
+
+func (s Section) TemplateName() string { return "section" }
+
+// Elem defines the interface for a present element. That is, something that
+// can provide the name of the template used to render the element.
+type Elem interface {
+ TemplateName() string
+}
+
+// renderElem implements the elem template function, used to render
+// sub-templates.
+func renderElem(t *template.Template, e Elem) (template.HTML, error) {
+ var data interface{} = e
+ if s, ok := e.(Section); ok {
+ data = struct {
+ Section
+ Template *template.Template
+ }{s, t}
+ }
+ return execTemplate(t, e.TemplateName(), data)
+}
+
+// pageNum derives a page number from a section.
+func pageNum(s Section, offset int) int {
+ if len(s.Number) == 0 {
+ return offset
+ }
+ return s.Number[0] + offset
+}
+
+func init() {
+ funcs["elem"] = renderElem
+ funcs["pagenum"] = pageNum
+}
+
+// execTemplate is a helper to execute a template and return the output as a
+// template.HTML value.
+func execTemplate(t *template.Template, name string, data interface{}) (template.HTML, error) {
+ b := new(bytes.Buffer)
+ err := t.ExecuteTemplate(b, name, data)
+ if err != nil {
+ return "", err
+ }
+ return template.HTML(b.String()), nil
+}
+
+// Text represents an optionally preformatted paragraph.
+type Text struct {
+ Lines []string
+ Pre bool
+}
+
+func (t Text) TemplateName() string { return "text" }
+
+// List represents a bulleted list.
+type List struct {
+ Bullet []string
+}
+
+func (l List) TemplateName() string { return "list" }
+
+// Lines is a helper for parsing line-based input.
+type Lines struct {
+ line int // 0 indexed, so has 1-indexed number of last line returned
+ text []string
+}
+
+func readLines(r io.Reader) (*Lines, error) {
+ var lines []string
+ s := bufio.NewScanner(r)
+ for s.Scan() {
+ lines = append(lines, s.Text())
+ }
+ if err := s.Err(); err != nil {
+ return nil, err
+ }
+ return &Lines{0, lines}, nil
+}
+
+func (l *Lines) next() (text string, ok bool) {
+ for {
+ current := l.line
+ l.line++
+ if current >= len(l.text) {
+ return "", false
+ }
+ text = l.text[current]
+ // Lines starting with # are comments.
+ if len(text) == 0 || text[0] != '#' {
+ ok = true
+ break
+ }
+ }
+ return
+}
+
+func (l *Lines) back() {
+ l.line--
+}
+
+func (l *Lines) nextNonEmpty() (text string, ok bool) {
+ for {
+ text, ok = l.next()
+ if !ok {
+ return
+ }
+ if len(text) > 0 {
+ break
+ }
+ }
+ return
+}
+
+// A Context specifies the supporting context for parsing a presentation.
+type Context struct {
+ // ReadFile reads the file named by filename and returns the contents.
+ ReadFile func(filename string) ([]byte, error)
+}
+
+// ParseMode represents flags for the Parse function.
+type ParseMode int
+
+const (
+ // If set, parse only the title and subtitle.
+ TitlesOnly ParseMode = 1
+)
+
+// Parse parses a document from r.
+func (ctx *Context) Parse(r io.Reader, name string, mode ParseMode) (*Doc, error) {
+ doc := new(Doc)
+ lines, err := readLines(r)
+ if err != nil {
+ return nil, err
+ }
+
+ for i := lines.line; i < len(lines.text); i++ {
+ if strings.HasPrefix(lines.text[i], "*") {
+ break
+ }
+
+ if isSpeakerNote(lines.text[i]) {
+ doc.TitleNotes = append(doc.TitleNotes, lines.text[i][2:])
+ }
+ }
+
+ err = parseHeader(doc, lines)
+ if err != nil {
+ return nil, err
+ }
+ if mode&TitlesOnly != 0 {
+ return doc, nil
+ }
+
+ // Authors
+ if doc.Authors, err = parseAuthors(lines); err != nil {
+ return nil, err
+ }
+ // Sections
+ if doc.Sections, err = parseSections(ctx, name, lines, []int{}); err != nil {
+ return nil, err
+ }
+ return doc, nil
+}
+
+// Parse parses a document from r. Parse reads assets used by the presentation
+// from the file system using ioutil.ReadFile.
+func Parse(r io.Reader, name string, mode ParseMode) (*Doc, error) {
+ ctx := Context{ReadFile: ioutil.ReadFile}
+ return ctx.Parse(r, name, mode)
+}
+
+// isHeading matches any section heading.
+var isHeading = regexp.MustCompile(`^\*+ `)
+
+// lesserHeading returns true if text is a heading of a lesser or equal level
+// than that denoted by prefix.
+func lesserHeading(text, prefix string) bool {
+ return isHeading.MatchString(text) && !strings.HasPrefix(text, prefix+"*")
+}
+
+// parseSections parses Sections from lines for the section level indicated by
+// number (a nil number indicates the top level).
+func parseSections(ctx *Context, name string, lines *Lines, number []int) ([]Section, error) {
+ var sections []Section
+ for i := 1; ; i++ {
+ // Next non-empty line is title.
+ text, ok := lines.nextNonEmpty()
+ for ok && text == "" {
+ text, ok = lines.next()
+ }
+ if !ok {
+ break
+ }
+ prefix := strings.Repeat("*", len(number)+1)
+ if !strings.HasPrefix(text, prefix+" ") {
+ lines.back()
+ break
+ }
+ section := Section{
+ Number: append(append([]int{}, number...), i),
+ Title: text[len(prefix)+1:],
+ }
+ text, ok = lines.nextNonEmpty()
+ for ok && !lesserHeading(text, prefix) {
+ var e Elem
+ r, _ := utf8.DecodeRuneInString(text)
+ switch {
+ case unicode.IsSpace(r):
+ i := strings.IndexFunc(text, func(r rune) bool {
+ return !unicode.IsSpace(r)
+ })
+ if i < 0 {
+ break
+ }
+ indent := text[:i]
+ var s []string
+ for ok && (strings.HasPrefix(text, indent) || text == "") {
+ if text != "" {
+ text = text[i:]
+ }
+ s = append(s, text)
+ text, ok = lines.next()
+ }
+ lines.back()
+ pre := strings.Join(s, "\n")
+ pre = strings.Replace(pre, "\t", " ", -1) // browsers treat tabs badly
+ pre = strings.TrimRightFunc(pre, unicode.IsSpace)
+ e = Text{Lines: []string{pre}, Pre: true}
+ case strings.HasPrefix(text, "- "):
+ var b []string
+ for ok && strings.HasPrefix(text, "- ") {
+ b = append(b, text[2:])
+ text, ok = lines.next()
+ }
+ lines.back()
+ e = List{Bullet: b}
+ case isSpeakerNote(text):
+ section.Notes = append(section.Notes, text[2:])
+ case strings.HasPrefix(text, prefix+"* "):
+ lines.back()
+ subsecs, err := parseSections(ctx, name, lines, section.Number)
+ if err != nil {
+ return nil, err
+ }
+ for _, ss := range subsecs {
+ section.Elem = append(section.Elem, ss)
+ }
+ case strings.HasPrefix(text, "."):
+ args := strings.Fields(text)
+ if args[0] == ".background" {
+ section.Classes = append(section.Classes, "background")
+ section.Styles = append(section.Styles, "background-image: url('"+args[1]+"')")
+ break
+ }
+ parser := parsers[args[0]]
+ if parser == nil {
+ return nil, fmt.Errorf("%s:%d: unknown command %q\n", name, lines.line, text)
+ }
+ t, err := parser(ctx, name, lines.line, text)
+ if err != nil {
+ return nil, err
+ }
+ e = t
+ default:
+ var l []string
+ for ok && strings.TrimSpace(text) != "" {
+ if text[0] == '.' { // Command breaks text block.
+ lines.back()
+ break
+ }
+ if strings.HasPrefix(text, `\.`) { // Backslash escapes initial period.
+ text = text[1:]
+ }
+ l = append(l, text)
+ text, ok = lines.next()
+ }
+ if len(l) > 0 {
+ e = Text{Lines: l}
+ }
+ }
+ if e != nil {
+ section.Elem = append(section.Elem, e)
+ }
+ text, ok = lines.nextNonEmpty()
+ }
+ if isHeading.MatchString(text) {
+ lines.back()
+ }
+ sections = append(sections, section)
+ }
+ return sections, nil
+}
+
+func parseHeader(doc *Doc, lines *Lines) error {
+ var ok bool
+ // First non-empty line starts header.
+ doc.Title, ok = lines.nextNonEmpty()
+ if !ok {
+ return errors.New("unexpected EOF; expected title")
+ }
+ for {
+ text, ok := lines.next()
+ if !ok {
+ return errors.New("unexpected EOF")
+ }
+ if text == "" {
+ break
+ }
+ if isSpeakerNote(text) {
+ continue
+ }
+ const tagPrefix = "Tags:"
+ if strings.HasPrefix(text, tagPrefix) {
+ tags := strings.Split(text[len(tagPrefix):], ",")
+ for i := range tags {
+ tags[i] = strings.TrimSpace(tags[i])
+ }
+ doc.Tags = append(doc.Tags, tags...)
+ } else if t, ok := parseTime(text); ok {
+ doc.Time = t
+ } else if doc.Subtitle == "" {
+ doc.Subtitle = text
+ } else {
+ return fmt.Errorf("unexpected header line: %q", text)
+ }
+ }
+ return nil
+}
+
+func parseAuthors(lines *Lines) (authors []Author, err error) {
+ // This grammar demarcates authors with blanks.
+
+ // Skip blank lines.
+ if _, ok := lines.nextNonEmpty(); !ok {
+ return nil, errors.New("unexpected EOF")
+ }
+ lines.back()
+
+ var a *Author
+ for {
+ text, ok := lines.next()
+ if !ok {
+ return nil, errors.New("unexpected EOF")
+ }
+
+ // If we find a section heading, we're done.
+ if strings.HasPrefix(text, "* ") {
+ lines.back()
+ break
+ }
+
+ if isSpeakerNote(text) {
+ continue
+ }
+
+ // If we encounter a blank we're done with this author.
+ if a != nil && len(text) == 0 {
+ authors = append(authors, *a)
+ a = nil
+ continue
+ }
+ if a == nil {
+ a = new(Author)
+ }
+
+ // Parse the line. Those that
+ // - begin with @ are twitter names,
+ // - contain slashes are links, or
+ // - contain an @ symbol are an email address.
+ // The rest is just text.
+ var el Elem
+ switch {
+ case strings.HasPrefix(text, "@"):
+ el = parseURL("http://twitter.com/" + text[1:])
+ case strings.Contains(text, ":"):
+ el = parseURL(text)
+ case strings.Contains(text, "@"):
+ el = parseURL("mailto:" + text)
+ }
+ if l, ok := el.(Link); ok {
+ l.Label = text
+ el = l
+ }
+ if el == nil {
+ el = Text{Lines: []string{text}}
+ }
+ a.Elem = append(a.Elem, el)
+ }
+ if a != nil {
+ authors = append(authors, *a)
+ }
+ return authors, nil
+}
+
+func parseURL(text string) Elem {
+ u, err := url.Parse(text)
+ if err != nil {
+ log.Printf("Parse(%q): %v", text, err)
+ return nil
+ }
+ return Link{URL: u}
+}
+
+func parseTime(text string) (t time.Time, ok bool) {
+ t, err := time.Parse("15:04 2 Jan 2006", text)
+ if err == nil {
+ return t, true
+ }
+ t, err = time.Parse("2 Jan 2006", text)
+ if err == nil {
+ // at 11am UTC it is the same date everywhere
+ t = t.Add(time.Hour * 11)
+ return t, true
+ }
+ return time.Time{}, false
+}
+
+func isSpeakerNote(s string) bool {
+ return strings.HasPrefix(s, ": ")
+}
diff --git a/vendor/golang.org/x/tools/present/style.go b/vendor/golang.org/x/tools/present/style.go
new file mode 100644
index 00000000000..e2c228e55ab
--- /dev/null
+++ b/vendor/golang.org/x/tools/present/style.go
@@ -0,0 +1,167 @@
+// Copyright 2012 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 (
+ "bytes"
+ "html"
+ "html/template"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+/*
+ Fonts are demarcated by an initial and final char bracketing a
+ space-delimited word, plus possibly some terminal punctuation.
+ The chars are
+ _ for italic
+ * for bold
+ ` (back quote) for fixed width.
+ Inner appearances of the char become spaces. For instance,
+ _this_is_italic_!
+ becomes
+ this is italic!
+*/
+
+func init() {
+ funcs["style"] = Style
+}
+
+// Style returns s with HTML entities escaped and font indicators turned into
+// HTML font tags.
+func Style(s string) template.HTML {
+ return template.HTML(font(html.EscapeString(s)))
+}
+
+// font returns s with font indicators turned into HTML font tags.
+func font(s string) string {
+ if !strings.ContainsAny(s, "[`_*") {
+ return s
+ }
+ words := split(s)
+ var b bytes.Buffer
+Word:
+ for w, word := range words {
+ if len(word) < 2 {
+ continue Word
+ }
+ if link, _ := parseInlineLink(word); link != "" {
+ words[w] = link
+ continue Word
+ }
+ const marker = "_*`"
+ // Initial punctuation is OK but must be peeled off.
+ first := strings.IndexAny(word, marker)
+ if first == -1 {
+ continue Word
+ }
+ // Opening marker must be at the beginning of the token or else preceded by punctuation.
+ if first != 0 {
+ r, _ := utf8.DecodeLastRuneInString(word[:first])
+ if !unicode.IsPunct(r) {
+ continue Word
+ }
+ }
+ open, word := word[:first], word[first:]
+ char := word[0] // ASCII is OK.
+ close := ""
+ switch char {
+ default:
+ continue Word
+ case '_':
+ open += ""
+ close = ""
+ case '*':
+ open += ""
+ close = ""
+ case '`':
+ open += ""
+ close = ""
+ }
+ // Closing marker must be at the end of the token or else followed by punctuation.
+ last := strings.LastIndex(word, word[:1])
+ if last == 0 {
+ continue Word
+ }
+ if last+1 != len(word) {
+ r, _ := utf8.DecodeRuneInString(word[last+1:])
+ if !unicode.IsPunct(r) {
+ continue Word
+ }
+ }
+ head, tail := word[:last+1], word[last+1:]
+ b.Reset()
+ b.WriteString(open)
+ var wid int
+ for i := 1; i < len(head)-1; i += wid {
+ var r rune
+ r, wid = utf8.DecodeRuneInString(head[i:])
+ if r != rune(char) {
+ // Ordinary character.
+ b.WriteRune(r)
+ continue
+ }
+ if head[i+1] != char {
+ // Inner char becomes space.
+ b.WriteRune(' ')
+ continue
+ }
+ // Doubled char becomes real char.
+ // Not worth worrying about "_x__".
+ b.WriteByte(char)
+ wid++ // Consumed two chars, both ASCII.
+ }
+ b.WriteString(close) // Write closing tag.
+ b.WriteString(tail) // Restore trailing punctuation.
+ words[w] = b.String()
+ }
+ return strings.Join(words, "")
+}
+
+// split is like strings.Fields but also returns the runs of spaces
+// and treats inline links as distinct words.
+func split(s string) []string {
+ var (
+ words = make([]string, 0, 10)
+ start = 0
+ )
+
+ // appendWord appends the string s[start:end] to the words slice.
+ // If the word contains the beginning of a link, the non-link portion
+ // of the word and the entire link are appended as separate words,
+ // and the start index is advanced to the end of the link.
+ appendWord := func(end int) {
+ if j := strings.Index(s[start:end], "[["); j > -1 {
+ if _, l := parseInlineLink(s[start+j:]); l > 0 {
+ // Append portion before link, if any.
+ if j > 0 {
+ words = append(words, s[start:start+j])
+ }
+ // Append link itself.
+ words = append(words, s[start+j:start+j+l])
+ // Advance start index to end of link.
+ start = start + j + l
+ return
+ }
+ }
+ // No link; just add the word.
+ words = append(words, s[start:end])
+ start = end
+ }
+
+ wasSpace := false
+ for i, r := range s {
+ isSpace := unicode.IsSpace(r)
+ if i > start && isSpace != wasSpace {
+ appendWord(i)
+ }
+ wasSpace = isSpace
+ }
+ for start < len(s) {
+ appendWord(len(s))
+ }
+ return words
+}
diff --git a/vendor/golang.org/x/tools/present/video.go b/vendor/golang.org/x/tools/present/video.go
new file mode 100644
index 00000000000..93d93502bf2
--- /dev/null
+++ b/vendor/golang.org/x/tools/present/video.go
@@ -0,0 +1,54 @@
+// Copyright 2016 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("video", parseVideo)
+}
+
+type Video struct {
+ URL string
+ SourceType string
+ Width int
+ Height int
+}
+
+func (v Video) TemplateName() string { return "video" }
+
+func parseVideo(ctx *Context, fileName string, lineno int, text string) (Elem, error) {
+ args := strings.Fields(text)
+ if len(args) < 3 {
+ return nil, fmt.Errorf("incorrect video invocation: %q", text)
+ }
+ vid := Video{URL: args[1], SourceType: args[2]}
+ a, err := parseArgs(fileName, lineno, args[3:])
+ if err != nil {
+ return nil, err
+ }
+ switch len(a) {
+ case 0:
+ // no size parameters
+ case 2:
+ // If a parameter is empty (underscore) or invalid
+ // leave the field set to zero. The "video" action
+ // template will then omit that vid tag attribute and
+ // the browser will calculate the value to preserve
+ // the aspect ratio.
+ if v, ok := a[0].(int); ok {
+ vid.Height = v
+ }
+ if v, ok := a[1].(int); ok {
+ vid.Width = v
+ }
+ default:
+ return nil, fmt.Errorf("incorrect video invocation: %q", text)
+ }
+ return vid, nil
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 3109da215d0..41a0659c2c3 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -1,9 +1,24 @@
-# golang.org/x/tools v0.0.0-20191030225452-7871c2d76733
+# golang.org/x/tools v0.0.0-20200109174759-ac4f524c1612
+golang.org/x/tools/benchmark/parse
+golang.org/x/tools/blog/atom
+golang.org/x/tools/container/intsets
+golang.org/x/tools/go/ast/astutil
+golang.org/x/tools/go/buildutil
+golang.org/x/tools/go/callgraph
+golang.org/x/tools/go/callgraph/cha
+golang.org/x/tools/go/callgraph/rta
+golang.org/x/tools/go/callgraph/static
golang.org/x/tools/go/gcexportdata
+golang.org/x/tools/go/internal/cgo
golang.org/x/tools/go/internal/gcimporter
golang.org/x/tools/go/internal/packagesdriver
+golang.org/x/tools/go/loader
golang.org/x/tools/go/packages
+golang.org/x/tools/go/pointer
+golang.org/x/tools/go/ssa
+golang.org/x/tools/go/ssa/ssautil
+golang.org/x/tools/go/types/typeutil
golang.org/x/tools/internal/fastwalk
golang.org/x/tools/internal/gopathwalk
golang.org/x/tools/internal/semver
-golang.org/x/tools/internal/span
+golang.org/x/tools/present