From e39e7064a5984b8cb070c715be86d510ac5bc314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauris=20Buk=C5=A1is-Haberkorns?= Date: Thu, 17 Oct 2019 15:15:29 +0300 Subject: [PATCH 01/14] Rewrite markdown rendering to blackfriday v2.0 --- go.mod | 5 +- go.sum | 6 +- modules/markup/markdown/markdown.go | 208 +-- modules/markup/mdstripper/mdstripper.go | 259 +-- modules/markup/orgmode/orgmode.go | 112 +- .../blevesearch/segment/maketesttables.go | 219 +++ .../chaseadamsio/goorgeous/.gitignore | 1 - .../chaseadamsio/goorgeous/.travis.yml | 12 - .../github.com/chaseadamsio/goorgeous/LICENSE | 21 - .../chaseadamsio/goorgeous/README.org | 66 - .../chaseadamsio/goorgeous/goorgeous.go | 803 --------- .../chaseadamsio/goorgeous/gopher.gif | Bin 15232 -> 0 bytes .../chaseadamsio/goorgeous/gopher_small.gif | Bin 3270 -> 0 bytes .../chaseadamsio/goorgeous/header.go | 70 - .../go-github/v24/github/gen-accessors.go | 332 ++++ .../klauspost/compress/flate/gen.go | 265 +++ .../github.com/klauspost/cpuid/private-gen.go | 476 ++++++ vendor/github.com/lib/pq/oid/gen.go | 93 + .../russross/blackfriday/.travis.yml | 40 +- .../github.com/russross/blackfriday/README.md | 124 +- .../github.com/russross/blackfriday/block.go | 820 +++++---- vendor/github.com/russross/blackfriday/doc.go | 40 +- vendor/github.com/russross/blackfriday/esc.go | 34 + .../github.com/russross/blackfriday/html.go | 1510 ++++++++-------- .../github.com/russross/blackfriday/inline.go | 552 +++--- .../github.com/russross/blackfriday/latex.go | 334 ---- .../russross/blackfriday/markdown.go | 773 +++++---- .../github.com/russross/blackfriday/node.go | 354 ++++ .../russross/blackfriday/smartypants.go | 139 +- vendor/golang.org/x/net/html/atom/gen.go | 712 ++++++++ vendor/golang.org/x/sys/unix/mkasm_darwin.go | 61 + vendor/golang.org/x/sys/unix/mkpost.go | 122 ++ vendor/golang.org/x/sys/unix/mksyscall.go | 407 +++++ .../x/sys/unix/mksyscall_aix_ppc.go | 415 +++++ .../x/sys/unix/mksyscall_aix_ppc64.go | 614 +++++++ .../x/sys/unix/mksyscall_solaris.go | 335 ++++ .../golang.org/x/sys/unix/mksysctl_openbsd.go | 355 ++++ vendor/golang.org/x/sys/unix/mksysnum.go | 190 +++ vendor/golang.org/x/sys/unix/types_aix.go | 237 +++ vendor/golang.org/x/sys/unix/types_darwin.go | 283 +++ .../golang.org/x/sys/unix/types_dragonfly.go | 263 +++ vendor/golang.org/x/sys/unix/types_freebsd.go | 400 +++++ vendor/golang.org/x/sys/unix/types_netbsd.go | 290 ++++ vendor/golang.org/x/sys/unix/types_openbsd.go | 283 +++ vendor/golang.org/x/sys/unix/types_solaris.go | 266 +++ .../x/text/encoding/charmap/maketables.go | 556 ++++++ .../x/text/encoding/htmlindex/gen.go | 173 ++ .../text/encoding/internal/identifier/gen.go | 142 ++ .../x/text/encoding/japanese/maketables.go | 161 ++ .../x/text/encoding/korean/maketables.go | 143 ++ .../encoding/simplifiedchinese/maketables.go | 161 ++ .../encoding/traditionalchinese/maketables.go | 140 ++ .../x/text/internal/language/compact/gen.go | 64 + .../internal/language/compact/gen_index.go | 113 ++ .../internal/language/compact/gen_parents.go | 54 + .../x/text/internal/language/gen.go | 1520 +++++++++++++++++ .../x/text/internal/language/gen_common.go | 20 + vendor/golang.org/x/text/language/gen.go | 305 ++++ vendor/golang.org/x/text/unicode/bidi/gen.go | 133 ++ .../x/text/unicode/bidi/gen_ranges.go | 57 + .../x/text/unicode/bidi/gen_trieval.go | 64 + .../x/text/unicode/norm/maketables.go | 986 +++++++++++ .../golang.org/x/text/unicode/norm/triegen.go | 117 ++ vendor/golang.org/x/text/width/gen.go | 115 ++ vendor/golang.org/x/text/width/gen_common.go | 96 ++ vendor/golang.org/x/text/width/gen_trieval.go | 34 + .../x/tools/go/gcexportdata/main.go | 99 ++ .../x/tools/internal/imports/mkindex.go | 173 ++ .../x/tools/internal/imports/mkstdlib.go | 128 ++ vendor/modules.txt | 202 ++- 70 files changed, 14926 insertions(+), 3731 deletions(-) create mode 100644 vendor/github.com/blevesearch/segment/maketesttables.go delete mode 100644 vendor/github.com/chaseadamsio/goorgeous/.gitignore delete mode 100644 vendor/github.com/chaseadamsio/goorgeous/.travis.yml delete mode 100644 vendor/github.com/chaseadamsio/goorgeous/LICENSE delete mode 100644 vendor/github.com/chaseadamsio/goorgeous/README.org delete mode 100644 vendor/github.com/chaseadamsio/goorgeous/goorgeous.go delete mode 100644 vendor/github.com/chaseadamsio/goorgeous/gopher.gif delete mode 100644 vendor/github.com/chaseadamsio/goorgeous/gopher_small.gif delete mode 100644 vendor/github.com/chaseadamsio/goorgeous/header.go create mode 100644 vendor/github.com/google/go-github/v24/github/gen-accessors.go create mode 100644 vendor/github.com/klauspost/compress/flate/gen.go create mode 100644 vendor/github.com/klauspost/cpuid/private-gen.go create mode 100644 vendor/github.com/lib/pq/oid/gen.go create mode 100644 vendor/github.com/russross/blackfriday/esc.go delete mode 100644 vendor/github.com/russross/blackfriday/latex.go create mode 100644 vendor/github.com/russross/blackfriday/node.go create mode 100644 vendor/golang.org/x/net/html/atom/gen.go create mode 100644 vendor/golang.org/x/sys/unix/mkasm_darwin.go create mode 100644 vendor/golang.org/x/sys/unix/mkpost.go create mode 100644 vendor/golang.org/x/sys/unix/mksyscall.go create mode 100644 vendor/golang.org/x/sys/unix/mksyscall_aix_ppc.go create mode 100644 vendor/golang.org/x/sys/unix/mksyscall_aix_ppc64.go create mode 100644 vendor/golang.org/x/sys/unix/mksyscall_solaris.go create mode 100644 vendor/golang.org/x/sys/unix/mksysctl_openbsd.go create mode 100644 vendor/golang.org/x/sys/unix/mksysnum.go create mode 100644 vendor/golang.org/x/sys/unix/types_aix.go create mode 100644 vendor/golang.org/x/sys/unix/types_darwin.go create mode 100644 vendor/golang.org/x/sys/unix/types_dragonfly.go create mode 100644 vendor/golang.org/x/sys/unix/types_freebsd.go create mode 100644 vendor/golang.org/x/sys/unix/types_netbsd.go create mode 100644 vendor/golang.org/x/sys/unix/types_openbsd.go create mode 100644 vendor/golang.org/x/sys/unix/types_solaris.go create mode 100644 vendor/golang.org/x/text/encoding/charmap/maketables.go create mode 100644 vendor/golang.org/x/text/encoding/htmlindex/gen.go create mode 100644 vendor/golang.org/x/text/encoding/internal/identifier/gen.go create mode 100644 vendor/golang.org/x/text/encoding/japanese/maketables.go create mode 100644 vendor/golang.org/x/text/encoding/korean/maketables.go create mode 100644 vendor/golang.org/x/text/encoding/simplifiedchinese/maketables.go create mode 100644 vendor/golang.org/x/text/encoding/traditionalchinese/maketables.go create mode 100644 vendor/golang.org/x/text/internal/language/compact/gen.go create mode 100644 vendor/golang.org/x/text/internal/language/compact/gen_index.go create mode 100644 vendor/golang.org/x/text/internal/language/compact/gen_parents.go create mode 100644 vendor/golang.org/x/text/internal/language/gen.go create mode 100644 vendor/golang.org/x/text/internal/language/gen_common.go create mode 100644 vendor/golang.org/x/text/language/gen.go create mode 100644 vendor/golang.org/x/text/unicode/bidi/gen.go create mode 100644 vendor/golang.org/x/text/unicode/bidi/gen_ranges.go create mode 100644 vendor/golang.org/x/text/unicode/bidi/gen_trieval.go create mode 100644 vendor/golang.org/x/text/unicode/norm/maketables.go create mode 100644 vendor/golang.org/x/text/unicode/norm/triegen.go create mode 100644 vendor/golang.org/x/text/width/gen.go create mode 100644 vendor/golang.org/x/text/width/gen_common.go create mode 100644 vendor/golang.org/x/text/width/gen_trieval.go create mode 100644 vendor/golang.org/x/tools/go/gcexportdata/main.go create mode 100644 vendor/golang.org/x/tools/internal/imports/mkindex.go create mode 100644 vendor/golang.org/x/tools/internal/imports/mkstdlib.go diff --git a/go.mod b/go.mod index e1bbd9ac89d73..6a801982c3c63 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,6 @@ require ( github.com/blevesearch/go-porterstemmer v0.0.0-20141230013033-23a2c8e5cf1f // indirect github.com/blevesearch/segment v0.0.0-20160105220820-db70c57796cc // indirect github.com/boombuler/barcode v0.0.0-20161226211916-fe0f26ff6d26 // indirect - github.com/chaseadamsio/goorgeous v0.0.0-20170901132237-098da33fde5f github.com/couchbase/vellum v0.0.0-20190111184608-e91b68ff3efe // indirect github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d // indirect github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect @@ -70,6 +69,8 @@ require ( github.com/mattn/go-sqlite3 v1.11.0 github.com/mcuadros/go-version v0.0.0-20190308113854-92cdf37c5b75 github.com/microcosm-cc/bluemonday v0.0.0-20161012083705-f77f16ffc87a + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.1 // indirect github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae // indirect github.com/msteinert/pam v0.0.0-20151204160544-02ccfbfaf0cc github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5 @@ -80,7 +81,7 @@ require ( github.com/prometheus/client_golang v1.1.0 github.com/prometheus/procfs v0.0.4 // indirect github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001 // indirect - github.com/russross/blackfriday v0.0.0-20180428102519-11635eb403ff + github.com/russross/blackfriday v2.0.0+incompatible github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // indirect github.com/satori/go.uuid v1.2.0 github.com/sergi/go-diff v1.0.0 diff --git a/go.sum b/go.sum index 2eeaa79810c0f..8b42b992002fb 100644 --- a/go.sum +++ b/go.sum @@ -86,8 +86,6 @@ github.com/boombuler/barcode v0.0.0-20161226211916-fe0f26ff6d26/go.mod h1:paBWMc github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668 h1:U/lr3Dgy4WK+hNk4tyD+nuGjpVLPEHuJSFXMw11/HPA= github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/chaseadamsio/goorgeous v0.0.0-20170901132237-098da33fde5f h1:REH9VH5ubNR0skLaOxK7TRJeRbE2dDfvaouQo8FsRcA= -github.com/chaseadamsio/goorgeous v0.0.0-20170901132237-098da33fde5f/go.mod h1:6QaC0vFoKWYDth94dHFNgRT2YkT5FHdQp/Yx15aAAi0= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/corbym/gocrest v1.0.3 h1:gwEdq6RkTmq+09CTuM29DfKOCtZ7G7bcyxs3IZ6EVdU= github.com/corbym/gocrest v1.0.3/go.mod h1:maVFL5lbdS2PgfOQgGRWDYTeunSWQeiEgoNdTABShCs= @@ -487,8 +485,8 @@ github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001/go.mod h1:qq github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/russross/blackfriday v0.0.0-20180428102519-11635eb403ff h1:g9ZlAHmkc/h5So+OjNCkZWh+FjuKEOOOoyRkqlGA8+c= -github.com/russross/blackfriday v0.0.0-20180428102519-11635eb403ff/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday v2.0.0+incompatible h1:cBXrhZNUf9C+La9/YpS+UHpUT8YD6Td9ZMSU9APFcsk= +github.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI= github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= diff --git a/modules/markup/markdown/markdown.go b/modules/markup/markdown/markdown.go index d9fc768891f11..881344e05974d 100644 --- a/modules/markup/markdown/markdown.go +++ b/modules/markup/markdown/markdown.go @@ -7,6 +7,7 @@ package markdown import ( "bytes" + "io" "strings" "code.gitea.io/gitea/modules/markup" @@ -25,134 +26,139 @@ type Renderer struct { var byteMailto = []byte("mailto:") -// Link defines how formal links should be processed to produce corresponding HTML elements. -func (r *Renderer) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) { - // special case: this is not a link, a hash link or a mailto:, so it's a - // relative URL - if len(link) > 0 && !markup.IsLink(link) && - link[0] != '#' && !bytes.HasPrefix(link, byteMailto) { - lnk := string(link) - if r.IsWiki { - lnk = util.URLJoin("wiki", lnk) - } - mLink := util.URLJoin(r.URLPrefix, lnk) - link = []byte(mLink) - } - - if len(content) > 10 && string(content[0:9]) == "': []byte(">"), + '"': []byte("""), } -// List renders markdown bullet or digit lists to HTML -func (r *Renderer) List(out *bytes.Buffer, text func() bool, flags int) { - marker := out.Len() - if out.Len() > 0 { - out.WriteByte('\n') - } - - if flags&blackfriday.LIST_TYPE_DEFINITION != 0 { - out.WriteString("
") - } else if flags&blackfriday.LIST_TYPE_ORDERED != 0 { - out.WriteString("
    ") - } else { - out.WriteString("
      ") - } - if !text() { - out.Truncate(marker) - return +func escapeHTML(w io.Writer, s []byte) { + var start, end int + for end < len(s) { + escSeq := htmlEscaper[s[end]] + if escSeq != nil { + w.Write(s[start:end]) + w.Write(escSeq) + start = end + 1 + } + end++ } - if flags&blackfriday.LIST_TYPE_DEFINITION != 0 { - out.WriteString("
\n") - } else if flags&blackfriday.LIST_TYPE_ORDERED != 0 { - out.WriteString("\n") - } else { - out.WriteString("\n") + if start < len(s) && end <= len(s) { + w.Write(s[start:end]) } } -// ListItem defines how list items should be processed to produce corresponding HTML elements. -func (r *Renderer) ListItem(out *bytes.Buffer, text []byte, flags int) { - // Detect procedures to draw checkboxes. - prefix := "" - if bytes.HasPrefix(text, []byte("

")) { - prefix = "

" - } - switch { - case bytes.HasPrefix(text, []byte(prefix+"[ ] ")): - text = append([]byte(``), text[3+len(prefix):]...) - if prefix != "" { - text = bytes.Replace(text, []byte(prefix), []byte{}, 1) +// RenderNode is a default renderer of a single node of a syntax tree. For +// block nodes it will be called twice: first time with entering=true, second +// time with entering=false, so that it could know when it's working on an open +// tag and when on close. It writes the result to w. +// +// The return value is a way to tell the calling walker to adjust its walk +// pattern: e.g. it can terminate the traversal by returning Terminate. Or it +// can ask the walker to skip a subtree of this node by returning SkipChildren. +// The typical behavior is to return GoToNext, which asks for the usual +// traversal to the next node. +func (r *Renderer) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus { + switch node.Type { + case blackfriday.Image: + prefix := r.URLPrefix + if r.IsWiki { + prefix = util.URLJoin(prefix, "wiki", "raw") } - case bytes.HasPrefix(text, []byte(prefix+"[x] ")): - text = append([]byte(``), text[3+len(prefix):]...) - if prefix != "" { - text = bytes.Replace(text, []byte(prefix), []byte{}, 1) + prefix = strings.Replace(prefix, "/src/", "/media/", 1) + link := node.LinkData.Destination + if len(link) > 0 && !markup.IsLink(link) { + lnk := string(link) + lnk = util.URLJoin(prefix, lnk) + lnk = strings.Replace(lnk, " ", "+", -1) + link = []byte(lnk) + } + node.LinkData.Destination = link + // Render link around image only if parent is not link already + if node.Parent != nil && node.Parent.Type != blackfriday.Link { + if entering { + w.Write([]byte(``)) + return r.Renderer.RenderNode(w, node, entering) + } else { + s := r.Renderer.RenderNode(w, node, entering) + w.Write([]byte(``)) + return s + } + } + return r.Renderer.RenderNode(w, node, entering) + case blackfriday.Link: + // special case: this is not a link, a hash link or a mailto:, so it's a + // relative URL + link := node.LinkData.Destination + if len(link) > 0 && !markup.IsLink(link) && + link[0] != '#' && !bytes.HasPrefix(link, byteMailto) && + node.LinkData.Footnote == nil { + lnk := string(link) + if r.IsWiki { + lnk = util.URLJoin("wiki", lnk) + } + link = []byte(util.URLJoin(r.URLPrefix, lnk)) + } + node.LinkData.Destination = link + return r.Renderer.RenderNode(w, node, entering) + case blackfriday.Text: + isListItem := false + for n := node.Parent; n != nil; n = n.Parent { + if n.Type == blackfriday.Item { + isListItem = true + break + } + } + if isListItem { + text := node.Literal + switch { + case bytes.HasPrefix(text, []byte("[ ] ")): + w.Write([]byte(``)) + text = text[3:] + case bytes.HasPrefix(text, []byte("[x] ")): + w.Write([]byte(``)) + text = text[3:] + } + node.Literal = text } } - r.Renderer.ListItem(out, text, flags) -} - -// Image defines how images should be processed to produce corresponding HTML elements. -func (r *Renderer) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) { - prefix := r.URLPrefix - if r.IsWiki { - prefix = util.URLJoin(prefix, "wiki", "raw") - } - prefix = strings.Replace(prefix, "/src/", "/media/", 1) - if len(link) > 0 && !markup.IsLink(link) { - lnk := string(link) - lnk = util.URLJoin(prefix, lnk) - lnk = strings.Replace(lnk, " ", "+", -1) - link = []byte(lnk) - } - - // Put a link around it pointing to itself by default - out.WriteString(``) - r.Renderer.Image(out, link, title, alt) - out.WriteString("") + return r.Renderer.RenderNode(w, node, entering) } const ( blackfridayExtensions = 0 | - blackfriday.EXTENSION_NO_INTRA_EMPHASIS | - blackfriday.EXTENSION_TABLES | - blackfriday.EXTENSION_FENCED_CODE | - blackfriday.EXTENSION_STRIKETHROUGH | - blackfriday.EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK | - blackfriday.EXTENSION_DEFINITION_LISTS | - blackfriday.EXTENSION_FOOTNOTES | - blackfriday.EXTENSION_HEADER_IDS | - blackfriday.EXTENSION_AUTO_HEADER_IDS + blackfriday.NoIntraEmphasis | + blackfriday.Tables | + blackfriday.FencedCode | + blackfriday.Strikethrough | + blackfriday.NoEmptyLineBeforeBlock | + blackfriday.DefinitionLists | + blackfriday.Footnotes | + blackfriday.HeadingIDs | + blackfriday.AutoHeadingIDs blackfridayHTMLFlags = 0 | - blackfriday.HTML_SKIP_STYLE | - blackfriday.HTML_OMIT_CONTENTS | - blackfriday.HTML_USE_SMARTYPANTS + blackfriday.Smartypants ) // RenderRaw renders Markdown to HTML without handling special links. func RenderRaw(body []byte, urlPrefix string, wikiMarkdown bool) []byte { renderer := &Renderer{ - Renderer: blackfriday.HtmlRenderer(blackfridayHTMLFlags, "", ""), + Renderer: blackfriday.NewHTMLRenderer(blackfriday.HTMLRendererParameters{ + Flags: blackfridayHTMLFlags, + }), URLPrefix: urlPrefix, IsWiki: wikiMarkdown, } exts := blackfridayExtensions if setting.Markdown.EnableHardLineBreak { - exts |= blackfriday.EXTENSION_HARD_LINE_BREAK + exts |= blackfriday.HardLineBreak } - body = blackfriday.Markdown(body, renderer, exts) + body = blackfriday.Run(body, blackfriday.WithRenderer(renderer), blackfriday.WithExtensions(exts)) return markup.SanitizeBytes(body) } diff --git a/modules/markup/mdstripper/mdstripper.go b/modules/markup/mdstripper/mdstripper.go index 7a901b17a925f..b9d736d12d322 100644 --- a/modules/markup/mdstripper/mdstripper.go +++ b/modules/markup/mdstripper/mdstripper.go @@ -6,43 +6,40 @@ package mdstripper import ( "bytes" + "io" "github.com/russross/blackfriday" ) // MarkdownStripper extends blackfriday.Renderer type MarkdownStripper struct { - blackfriday.Renderer + renderer blackfriday.Renderer links []string coallesce bool + empty bool } const ( blackfridayExtensions = 0 | - blackfriday.EXTENSION_NO_INTRA_EMPHASIS | - blackfriday.EXTENSION_TABLES | - blackfriday.EXTENSION_FENCED_CODE | - blackfriday.EXTENSION_STRIKETHROUGH | - blackfriday.EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK | - blackfriday.EXTENSION_DEFINITION_LISTS | - blackfriday.EXTENSION_FOOTNOTES | - blackfriday.EXTENSION_HEADER_IDS | - blackfriday.EXTENSION_AUTO_HEADER_IDS | + blackfriday.NoIntraEmphasis | + blackfriday.Tables | + blackfriday.FencedCode | + blackfriday.Strikethrough | + blackfriday.NoEmptyLineBeforeBlock | + blackfriday.DefinitionLists | + blackfriday.Footnotes | + blackfriday.HeadingIDs | + blackfriday.AutoHeadingIDs | // Not included in modules/markup/markdown/markdown.go; // required here to process inline links - blackfriday.EXTENSION_AUTOLINK + blackfriday.Autolink ) -//revive:disable:var-naming Implementing the Rendering interface requires breaking some linting rules - // StripMarkdown parses markdown content by removing all markup and code blocks // in order to extract links and other references func StripMarkdown(rawBytes []byte) (string, []string) { - stripper := &MarkdownStripper{ - links: make([]string, 0, 10), - } - body := blackfriday.Markdown(rawBytes, stripper, blackfridayExtensions) - return string(body), stripper.GetLinks() + buf, links := StripMarkdownBytes(rawBytes) + return string(buf), links } // StripMarkdownBytes parses markdown content by removing all markup and code blocks @@ -50,205 +47,67 @@ func StripMarkdown(rawBytes []byte) (string, []string) { func StripMarkdownBytes(rawBytes []byte) ([]byte, []string) { stripper := &MarkdownStripper{ links: make([]string, 0, 10), + empty: true, } - body := blackfriday.Markdown(rawBytes, stripper, blackfridayExtensions) - return body, stripper.GetLinks() -} - -// block-level callbacks - -// BlockCode dummy function to proceed with rendering -func (r *MarkdownStripper) BlockCode(out *bytes.Buffer, text []byte, infoString string) { - // Not rendered - r.coallesce = false -} - -// BlockQuote dummy function to proceed with rendering -func (r *MarkdownStripper) BlockQuote(out *bytes.Buffer, text []byte) { - // FIXME: perhaps it's better to leave out block quote for this? - r.processString(out, text, false) -} - -// BlockHtml dummy function to proceed with rendering -func (r *MarkdownStripper) BlockHtml(out *bytes.Buffer, text []byte) { //nolint - // Not rendered - r.coallesce = false -} - -// Header dummy function to proceed with rendering -func (r *MarkdownStripper) Header(out *bytes.Buffer, text func() bool, level int, id string) { - text() - r.coallesce = false -} - -// HRule dummy function to proceed with rendering -func (r *MarkdownStripper) HRule(out *bytes.Buffer) { - // Not rendered - r.coallesce = false -} - -// List dummy function to proceed with rendering -func (r *MarkdownStripper) List(out *bytes.Buffer, text func() bool, flags int) { - text() - r.coallesce = false -} - -// ListItem dummy function to proceed with rendering -func (r *MarkdownStripper) ListItem(out *bytes.Buffer, text []byte, flags int) { - r.processString(out, text, false) -} - -// Paragraph dummy function to proceed with rendering -func (r *MarkdownStripper) Paragraph(out *bytes.Buffer, text func() bool) { - text() - r.coallesce = false -} - -// Table dummy function to proceed with rendering -func (r *MarkdownStripper) Table(out *bytes.Buffer, header []byte, body []byte, columnData []int) { - r.processString(out, header, false) - r.processString(out, body, false) -} - -// TableRow dummy function to proceed with rendering -func (r *MarkdownStripper) TableRow(out *bytes.Buffer, text []byte) { - r.processString(out, text, false) -} - -// TableHeaderCell dummy function to proceed with rendering -func (r *MarkdownStripper) TableHeaderCell(out *bytes.Buffer, text []byte, flags int) { - r.processString(out, text, false) -} - -// TableCell dummy function to proceed with rendering -func (r *MarkdownStripper) TableCell(out *bytes.Buffer, text []byte, flags int) { - r.processString(out, text, false) -} - -// Footnotes dummy function to proceed with rendering -func (r *MarkdownStripper) Footnotes(out *bytes.Buffer, text func() bool) { - text() -} - -// FootnoteItem dummy function to proceed with rendering -func (r *MarkdownStripper) FootnoteItem(out *bytes.Buffer, name, text []byte, flags int) { - r.processString(out, text, false) -} - -// TitleBlock dummy function to proceed with rendering -func (r *MarkdownStripper) TitleBlock(out *bytes.Buffer, text []byte) { - r.processString(out, text, false) -} - -// Span-level callbacks - -// AutoLink dummy function to proceed with rendering -func (r *MarkdownStripper) AutoLink(out *bytes.Buffer, link []byte, kind int) { - r.processLink(out, link, []byte{}) -} - -// CodeSpan dummy function to proceed with rendering -func (r *MarkdownStripper) CodeSpan(out *bytes.Buffer, text []byte) { - // Not rendered - r.coallesce = false -} - -// DoubleEmphasis dummy function to proceed with rendering -func (r *MarkdownStripper) DoubleEmphasis(out *bytes.Buffer, text []byte) { - r.processString(out, text, false) -} - -// Emphasis dummy function to proceed with rendering -func (r *MarkdownStripper) Emphasis(out *bytes.Buffer, text []byte) { - r.processString(out, text, false) -} -// Image dummy function to proceed with rendering -func (r *MarkdownStripper) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) { - // Not rendered - r.coallesce = false -} - -// LineBreak dummy function to proceed with rendering -func (r *MarkdownStripper) LineBreak(out *bytes.Buffer) { - // Not rendered - r.coallesce = false -} - -// Link dummy function to proceed with rendering -func (r *MarkdownStripper) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) { - r.processLink(out, link, content) -} - -// RawHtmlTag dummy function to proceed with rendering -func (r *MarkdownStripper) RawHtmlTag(out *bytes.Buffer, tag []byte) { //nolint - // Not rendered - r.coallesce = false -} - -// TripleEmphasis dummy function to proceed with rendering -func (r *MarkdownStripper) TripleEmphasis(out *bytes.Buffer, text []byte) { - r.processString(out, text, false) -} - -// StrikeThrough dummy function to proceed with rendering -func (r *MarkdownStripper) StrikeThrough(out *bytes.Buffer, text []byte) { - r.processString(out, text, false) -} - -// FootnoteRef dummy function to proceed with rendering -func (r *MarkdownStripper) FootnoteRef(out *bytes.Buffer, ref []byte, id int) { - // Not rendered - r.coallesce = false -} - -// Low-level callbacks - -// Entity dummy function to proceed with rendering -func (r *MarkdownStripper) Entity(out *bytes.Buffer, entity []byte) { - // FIXME: literal entities are not parsed; perhaps they should - r.coallesce = false -} - -// NormalText dummy function to proceed with rendering -func (r *MarkdownStripper) NormalText(out *bytes.Buffer, text []byte) { - r.processString(out, text, true) -} - -// Header and footer - -// DocumentHeader dummy function to proceed with rendering -func (r *MarkdownStripper) DocumentHeader(out *bytes.Buffer) { + parser := blackfriday.New(blackfriday.WithRenderer(stripper), blackfriday.WithExtensions(blackfridayExtensions)) + ast := parser.Parse(rawBytes) + var buf bytes.Buffer + stripper.RenderHeader(&buf, ast) + ast.Walk(func(node *blackfriday.Node, entering bool) blackfriday.WalkStatus { + return stripper.RenderNode(&buf, node, entering) + }) + stripper.RenderFooter(&buf, ast) + return buf.Bytes(), stripper.GetLinks() +} + +// RenderNode is the main rendering method. It will be called once for +// every leaf node and twice for every non-leaf node (first with +// entering=true, then with entering=false). The method should write its +// rendition of the node to the supplied writer w. +func (r *MarkdownStripper) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus { + if !entering { + return blackfriday.GoToNext + } + switch node.Type { + case blackfriday.Text: + r.processString(w, node.Literal, node.Parent == nil) + return blackfriday.GoToNext + case blackfriday.Link: + r.processLink(w, node.LinkData.Destination) + r.coallesce = false + return blackfriday.SkipChildren + } r.coallesce = false + return blackfriday.GoToNext } -// DocumentFooter dummy function to proceed with rendering -func (r *MarkdownStripper) DocumentFooter(out *bytes.Buffer) { - r.coallesce = false +// RenderHeader is a method that allows the renderer to produce some +// content preceding the main body of the output document. +func (r *MarkdownStripper) RenderHeader(w io.Writer, ast *blackfriday.Node) { } -// GetFlags returns rendering flags -func (r *MarkdownStripper) GetFlags() int { - return 0 +// RenderFooter is a symmetric counterpart of RenderHeader. +func (r *MarkdownStripper) RenderFooter(w io.Writer, ast *blackfriday.Node) { } -//revive:enable:var-naming - -func doubleSpace(out *bytes.Buffer) { - if out.Len() > 0 { - out.WriteByte('\n') +func (r *MarkdownStripper) doubleSpace(w io.Writer) { + if !r.empty { + w.Write([]byte{'\n'}) } } -func (r *MarkdownStripper) processString(out *bytes.Buffer, text []byte, coallesce bool) { +func (r *MarkdownStripper) processString(w io.Writer, text []byte, coallesce bool) { // Always break-up words if !coallesce || !r.coallesce { - doubleSpace(out) + r.doubleSpace(w) } - out.Write(text) + w.Write(text) r.coallesce = coallesce + r.empty = false } -func (r *MarkdownStripper) processLink(out *bytes.Buffer, link []byte, content []byte) { + +func (r *MarkdownStripper) processLink(w io.Writer, link []byte) { // Links are processed out of band r.links = append(r.links, string(link)) r.coallesce = false diff --git a/modules/markup/orgmode/orgmode.go b/modules/markup/orgmode/orgmode.go index f63155201ed9c..c12336ae6956e 100644 --- a/modules/markup/orgmode/orgmode.go +++ b/modules/markup/orgmode/orgmode.go @@ -4,59 +4,59 @@ package markup -import ( - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/markup" - "code.gitea.io/gitea/modules/markup/markdown" - - "github.com/chaseadamsio/goorgeous" - "github.com/russross/blackfriday" -) - -func init() { - markup.RegisterParser(Parser{}) -} - -// Parser implements markup.Parser for orgmode -type Parser struct { -} - -// Name implements markup.Parser -func (Parser) Name() string { - return "orgmode" -} - -// Extensions implements markup.Parser -func (Parser) Extensions() []string { - return []string{".org"} -} - -// Render renders orgmode rawbytes to HTML -func Render(rawBytes []byte, urlPrefix string, metas map[string]string, isWiki bool) (result []byte) { - defer func() { - if err := recover(); err != nil { - log.Error("Panic in orgmode.Render: %v Just returning the rawBytes", err) - result = rawBytes - } - }() - htmlFlags := blackfriday.HTML_USE_XHTML - htmlFlags |= blackfriday.HTML_SKIP_STYLE - htmlFlags |= blackfriday.HTML_OMIT_CONTENTS - renderer := &markdown.Renderer{ - Renderer: blackfriday.HtmlRenderer(htmlFlags, "", ""), - URLPrefix: urlPrefix, - IsWiki: isWiki, - } - result = goorgeous.Org(rawBytes, renderer) - return -} - -// RenderString reners orgmode string to HTML string -func RenderString(rawContent string, urlPrefix string, metas map[string]string, isWiki bool) string { - return string(Render([]byte(rawContent), urlPrefix, metas, isWiki)) -} - -// Render implements markup.Parser -func (Parser) Render(rawBytes []byte, urlPrefix string, metas map[string]string, isWiki bool) []byte { - return Render(rawBytes, urlPrefix, metas, isWiki) -} +// import ( +// "code.gitea.io/gitea/modules/log" +// "code.gitea.io/gitea/modules/markup" +// "code.gitea.io/gitea/modules/markup/markdown" + +// "github.com/chaseadamsio/goorgeous" +// "github.com/russross/blackfriday" +// ) + +// func init() { +// markup.RegisterParser(Parser{}) +// } + +// // Parser implements markup.Parser for orgmode +// type Parser struct { +// } + +// // Name implements markup.Parser +// func (Parser) Name() string { +// return "orgmode" +// } + +// // Extensions implements markup.Parser +// func (Parser) Extensions() []string { +// return []string{".org"} +// } + +// // Render renders orgmode rawbytes to HTML +// func Render(rawBytes []byte, urlPrefix string, metas map[string]string, isWiki bool) (result []byte) { +// defer func() { +// if err := recover(); err != nil { +// log.Error("Panic in orgmode.Render: %v Just returning the rawBytes", err) +// result = rawBytes +// } +// }() +// htmlFlags := blackfriday.HTML_USE_XHTML +// htmlFlags |= blackfriday.HTML_SKIP_STYLE +// htmlFlags |= blackfriday.HTML_OMIT_CONTENTS +// renderer := &markdown.Renderer{ +// Renderer: blackfriday.HtmlRenderer(htmlFlags, "", ""), +// URLPrefix: urlPrefix, +// IsWiki: isWiki, +// } +// result = goorgeous.Org(rawBytes, renderer) +// return +// } + +// // RenderString reners orgmode string to HTML string +// func RenderString(rawContent string, urlPrefix string, metas map[string]string, isWiki bool) string { +// return string(Render([]byte(rawContent), urlPrefix, metas, isWiki)) +// } + +// // Render implements markup.Parser +// func (Parser) Render(rawBytes []byte, urlPrefix string, metas map[string]string, isWiki bool) []byte { +// return Render(rawBytes, urlPrefix, metas, isWiki) +// } diff --git a/vendor/github.com/blevesearch/segment/maketesttables.go b/vendor/github.com/blevesearch/segment/maketesttables.go new file mode 100644 index 0000000000000..9d7df9584f4f4 --- /dev/null +++ b/vendor/github.com/blevesearch/segment/maketesttables.go @@ -0,0 +1,219 @@ +// Copyright (c) 2015 Couchbase, Inc. +// Licensed under the Apache 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://www.apache.org/licenses/LICENSE-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. + +// +build ignore + +package main + +import ( + "bufio" + "bytes" + "flag" + "fmt" + "io" + "log" + "net/http" + "os" + "os/exec" + "strconv" + "strings" + "unicode" +) + +var url = flag.String("url", + "http://www.unicode.org/Public/"+unicode.Version+"/ucd/auxiliary/", + "URL of Unicode database directory") +var verbose = flag.Bool("verbose", + false, + "write data to stdout as it is parsed") +var localFiles = flag.Bool("local", + false, + "data files have been copied to the current directory; for debugging only") + +var outputFile = flag.String("output", + "", + "output file for generated tables; default stdout") + +var output *bufio.Writer + +func main() { + flag.Parse() + setupOutput() + + graphemeTests := make([]test, 0) + graphemeComments := make([]string, 0) + graphemeTests, graphemeComments = loadUnicodeData("GraphemeBreakTest.txt", graphemeTests, graphemeComments) + wordTests := make([]test, 0) + wordComments := make([]string, 0) + wordTests, wordComments = loadUnicodeData("WordBreakTest.txt", wordTests, wordComments) + sentenceTests := make([]test, 0) + sentenceComments := make([]string, 0) + sentenceTests, sentenceComments = loadUnicodeData("SentenceBreakTest.txt", sentenceTests, sentenceComments) + + fmt.Fprintf(output, fileHeader, *url) + generateTestTables("Grapheme", graphemeTests, graphemeComments) + generateTestTables("Word", wordTests, wordComments) + generateTestTables("Sentence", sentenceTests, sentenceComments) + + flushOutput() +} + +// WordBreakProperty.txt has the form: +// 05F0..05F2 ; Hebrew_Letter # Lo [3] HEBREW LIGATURE YIDDISH DOUBLE VAV..HEBREW LIGATURE YIDDISH DOUBLE YOD +// FB1D ; Hebrew_Letter # Lo HEBREW LETTER YOD WITH HIRIQ +func openReader(file string) (input io.ReadCloser) { + if *localFiles { + f, err := os.Open(file) + if err != nil { + log.Fatal(err) + } + input = f + } else { + path := *url + file + resp, err := http.Get(path) + if err != nil { + log.Fatal(err) + } + if resp.StatusCode != 200 { + log.Fatal("bad GET status for "+file, resp.Status) + } + input = resp.Body + } + return +} + +func loadUnicodeData(filename string, tests []test, comments []string) ([]test, []string) { + f := openReader(filename) + defer f.Close() + bufioReader := bufio.NewReader(f) + line, err := bufioReader.ReadString('\n') + for err == nil { + tests, comments = parseLine(line, tests, comments) + line, err = bufioReader.ReadString('\n') + } + // if the err was EOF still need to process last value + if err == io.EOF { + tests, comments = parseLine(line, tests, comments) + } + return tests, comments +} + +const comment = "#" +const brk = "÷" +const nbrk = "×" + +type test [][]byte + +func parseLine(line string, tests []test, comments []string) ([]test, []string) { + if strings.HasPrefix(line, comment) { + return tests, comments + } + line = strings.TrimSpace(line) + if len(line) == 0 { + return tests, comments + } + commentStart := strings.Index(line, comment) + comment := strings.TrimSpace(line[commentStart+1:]) + if commentStart > 0 { + line = line[0:commentStart] + } + pieces := strings.Split(line, brk) + t := make(test, 0) + for _, piece := range pieces { + piece = strings.TrimSpace(piece) + if len(piece) > 0 { + codePoints := strings.Split(piece, nbrk) + word := "" + for _, codePoint := range codePoints { + codePoint = strings.TrimSpace(codePoint) + r, err := strconv.ParseInt(codePoint, 16, 64) + if err != nil { + log.Printf("err: %v for '%s'", err, string(r)) + return tests, comments + } + + word += string(r) + } + t = append(t, []byte(word)) + } + } + tests = append(tests, t) + comments = append(comments, comment) + return tests, comments +} + +func generateTestTables(prefix string, tests []test, comments []string) { + fmt.Fprintf(output, testHeader, prefix) + for i, t := range tests { + fmt.Fprintf(output, "\t\t{\n") + fmt.Fprintf(output, "\t\t\tinput: %#v,\n", bytes.Join(t, []byte{})) + fmt.Fprintf(output, "\t\t\toutput: %s,\n", generateTest(t)) + fmt.Fprintf(output, "\t\t\tcomment: `%s`,\n", comments[i]) + fmt.Fprintf(output, "\t\t},\n") + } + fmt.Fprintf(output, "}\n") +} + +func generateTest(t test) string { + rv := "[][]byte{" + for _, te := range t { + rv += fmt.Sprintf("%#v,", te) + } + rv += "}" + return rv +} + +const fileHeader = `// Generated by running +// maketesttables --url=%s +// DO NOT EDIT + +package segment +` + +const testHeader = `var unicode%sTests = []struct { + input []byte + output [][]byte + comment string + }{ +` + +func setupOutput() { + output = bufio.NewWriter(startGofmt()) +} + +// startGofmt connects output to a gofmt process if -output is set. +func startGofmt() io.Writer { + if *outputFile == "" { + return os.Stdout + } + stdout, err := os.Create(*outputFile) + if err != nil { + log.Fatal(err) + } + // Pipe output to gofmt. + gofmt := exec.Command("gofmt") + fd, err := gofmt.StdinPipe() + if err != nil { + log.Fatal(err) + } + gofmt.Stdout = stdout + gofmt.Stderr = os.Stderr + err = gofmt.Start() + if err != nil { + log.Fatal(err) + } + return fd +} + +func flushOutput() { + err := output.Flush() + if err != nil { + log.Fatal(err) + } +} diff --git a/vendor/github.com/chaseadamsio/goorgeous/.gitignore b/vendor/github.com/chaseadamsio/goorgeous/.gitignore deleted file mode 100644 index 496ee2ca6a2f0..0000000000000 --- a/vendor/github.com/chaseadamsio/goorgeous/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.DS_Store \ No newline at end of file diff --git a/vendor/github.com/chaseadamsio/goorgeous/.travis.yml b/vendor/github.com/chaseadamsio/goorgeous/.travis.yml deleted file mode 100644 index 31dca02817e7c..0000000000000 --- a/vendor/github.com/chaseadamsio/goorgeous/.travis.yml +++ /dev/null @@ -1,12 +0,0 @@ -language: go - -go: - - 1.7 - -before_install: - - go get golang.org/x/tools/cmd/cover - - go get github.com/mattn/goveralls - -script: - - go test -v -covermode=count -coverprofile=coverage.out - - $HOME/gopath/bin/goveralls -coverprofile=coverage.out -service=travis-ci diff --git a/vendor/github.com/chaseadamsio/goorgeous/LICENSE b/vendor/github.com/chaseadamsio/goorgeous/LICENSE deleted file mode 100644 index d7a37c6a3b3d8..0000000000000 --- a/vendor/github.com/chaseadamsio/goorgeous/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2017 Chase Adams - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/chaseadamsio/goorgeous/README.org b/vendor/github.com/chaseadamsio/goorgeous/README.org deleted file mode 100644 index 37e0f2ec730db..0000000000000 --- a/vendor/github.com/chaseadamsio/goorgeous/README.org +++ /dev/null @@ -1,66 +0,0 @@ -#+TITLE: chaseadamsio/goorgeous - -[[https://travis-ci.org/chaseadamsio/goorgeous.svg?branch=master]] -[[https://coveralls.io/repos/github/chaseadamsio/goorgeous/badge.svg?branch=master]] - -/goorgeous is a Go Org to HTML Parser./ - -[[file:gopher_small.gif]] - -*Pronounced: Go? Org? Yes!* - -#+BEGIN_QUOTE -"Org mode is for keeping notes, maintaining TODO lists, planning projects, and authoring documents with a fast and effective plain-text system." - -- [[orgmode.org]] -#+END_QUOTE - -The purpose of this package is to come as close as possible as parsing an =*.org= document into HTML, the same way one might publish [[http://orgmode.org/worg/org-tutorials/org-publish-html-tutorial.html][with org-publish-html from Emacs]]. - -* Installation - -#+BEGIN_SRC sh - go get -u github.com/chaseadamsio/goorgeous -#+END_SRC - -* Usage - -** Org Headers - -To retrieve the headers from a =[]byte=, call =OrgHeaders= and it will return a =map[string]interface{}=: - -#+BEGIN_SRC go - input := "#+title: goorgeous\n* Some Headline\n" - out := goorgeous.OrgHeaders(input) -#+END_SRC - -#+BEGIN_SRC go - map[string]interface{}{ - "title": "goorgeous" - } -#+END_SRC - -** Org Content - -After importing =github.com/chaseadamsio/goorgeous=, you can call =Org= with a =[]byte= and it will return an =html= version of the content as a =[]byte= - -#+BEGIN_SRC go - input := "#+TITLE: goorgeous\n* Some Headline\n" - out := goorgeous.Org(input) -#+END_SRC - -=out= will be: - -#+BEGIN_SRC html -

Some Headline

/n -#+END_SRC - -* Why? - -First off, I've become an unapologetic user of Emacs & ever since finding =org-mode= I use it for anything having to do with writing content, organizing my life and keeping documentation of my days/weeks/months. - -Although I like Emacs & =emacs-lisp=, I publish all of my html sites with [[https://gohugo.io][Hugo Static Site Generator]] and wanted to be able to write my content in =org-mode= in Emacs rather than markdown. - -Hugo's implementation of templating and speed are unmatched, so the only way I knew for sure I could continue to use Hugo and write in =org-mode= seamlessly was to write a golang parser for org content and submit a PR for Hugo to use it. -* Acknowledgements -I leaned heavily on russross' [[https://github.com/russross/blackfriday][blackfriday markdown renderer]] as both an example of how to write a parser (with some updates to leverage the go we know today) and reusing the blackfriday HTML Renderer so I didn't have to write my own! diff --git a/vendor/github.com/chaseadamsio/goorgeous/goorgeous.go b/vendor/github.com/chaseadamsio/goorgeous/goorgeous.go deleted file mode 100644 index f1b2671d6572b..0000000000000 --- a/vendor/github.com/chaseadamsio/goorgeous/goorgeous.go +++ /dev/null @@ -1,803 +0,0 @@ -package goorgeous - -import ( - "bufio" - "bytes" - "regexp" - - "github.com/russross/blackfriday" - "github.com/shurcooL/sanitized_anchor_name" -) - -type inlineParser func(p *parser, out *bytes.Buffer, data []byte, offset int) int - -type footnotes struct { - id string - def string -} - -type parser struct { - r blackfriday.Renderer - inlineCallback [256]inlineParser - notes []footnotes -} - -// NewParser returns a new parser with the inlineCallbacks required for org content -func NewParser(renderer blackfriday.Renderer) *parser { - p := new(parser) - p.r = renderer - - p.inlineCallback['='] = generateVerbatim - p.inlineCallback['~'] = generateCode - p.inlineCallback['/'] = generateEmphasis - p.inlineCallback['_'] = generateUnderline - p.inlineCallback['*'] = generateBold - p.inlineCallback['+'] = generateStrikethrough - p.inlineCallback['['] = generateLinkOrImg - - return p -} - -// OrgCommon is the easiest way to parse a byte slice of org content and makes assumptions -// that the caller wants to use blackfriday's HTMLRenderer with XHTML -func OrgCommon(input []byte) []byte { - renderer := blackfriday.HtmlRenderer(blackfriday.HTML_USE_XHTML, "", "") - return OrgOptions(input, renderer) -} - -// Org is a convenience name for OrgOptions -func Org(input []byte, renderer blackfriday.Renderer) []byte { - return OrgOptions(input, renderer) -} - -// OrgOptions takes an org content byte slice and a renderer to use -func OrgOptions(input []byte, renderer blackfriday.Renderer) []byte { - // in the case that we need to render something in isEmpty but there isn't a new line char - input = append(input, '\n') - var output bytes.Buffer - - p := NewParser(renderer) - - scanner := bufio.NewScanner(bytes.NewReader(input)) - // used to capture code blocks - marker := "" - syntax := "" - listType := "" - inParagraph := false - inList := false - inTable := false - inFixedWidthArea := false - var tmpBlock bytes.Buffer - - for scanner.Scan() { - data := scanner.Bytes() - - if !isEmpty(data) && isComment(data) || IsKeyword(data) { - switch { - case inList: - if tmpBlock.Len() > 0 { - p.generateList(&output, tmpBlock.Bytes(), listType) - } - inList = false - listType = "" - tmpBlock.Reset() - case inTable: - if tmpBlock.Len() > 0 { - p.generateTable(&output, tmpBlock.Bytes()) - } - inTable = false - tmpBlock.Reset() - case inParagraph: - if tmpBlock.Len() > 0 { - p.generateParagraph(&output, tmpBlock.Bytes()[:len(tmpBlock.Bytes())-1]) - } - inParagraph = false - tmpBlock.Reset() - case inFixedWidthArea: - if tmpBlock.Len() > 0 { - tmpBlock.WriteString("\n") - output.Write(tmpBlock.Bytes()) - } - inFixedWidthArea = false - tmpBlock.Reset() - } - - } - - switch { - case isEmpty(data): - switch { - case inList: - if tmpBlock.Len() > 0 { - p.generateList(&output, tmpBlock.Bytes(), listType) - } - inList = false - listType = "" - tmpBlock.Reset() - case inTable: - if tmpBlock.Len() > 0 { - p.generateTable(&output, tmpBlock.Bytes()) - } - inTable = false - tmpBlock.Reset() - case inParagraph: - if tmpBlock.Len() > 0 { - p.generateParagraph(&output, tmpBlock.Bytes()[:len(tmpBlock.Bytes())-1]) - } - inParagraph = false - tmpBlock.Reset() - case inFixedWidthArea: - if tmpBlock.Len() > 0 { - tmpBlock.WriteString("\n") - output.Write(tmpBlock.Bytes()) - } - inFixedWidthArea = false - tmpBlock.Reset() - case marker != "": - tmpBlock.WriteByte('\n') - default: - continue - } - case isPropertyDrawer(data) || marker == "PROPERTIES": - if marker == "" { - marker = "PROPERTIES" - } - if bytes.Equal(data, []byte(":END:")) { - marker = "" - } - continue - case isBlock(data) || marker != "": - matches := reBlock.FindSubmatch(data) - if len(matches) > 0 { - if string(matches[1]) == "END" { - switch marker { - case "QUOTE": - var tmpBuf bytes.Buffer - p.inline(&tmpBuf, tmpBlock.Bytes()) - p.r.BlockQuote(&output, tmpBuf.Bytes()) - case "CENTER": - var tmpBuf bytes.Buffer - output.WriteString("
\n") - p.inline(&tmpBuf, tmpBlock.Bytes()) - output.Write(tmpBuf.Bytes()) - output.WriteString("
\n") - default: - tmpBlock.WriteByte('\n') - p.r.BlockCode(&output, tmpBlock.Bytes(), syntax) - } - marker = "" - tmpBlock.Reset() - continue - } - - } - if marker != "" { - if marker != "SRC" && marker != "EXAMPLE" { - var tmpBuf bytes.Buffer - tmpBuf.Write([]byte("

\n")) - p.inline(&tmpBuf, data) - tmpBuf.WriteByte('\n') - tmpBuf.Write([]byte("

\n")) - tmpBlock.Write(tmpBuf.Bytes()) - - } else { - tmpBlock.WriteByte('\n') - tmpBlock.Write(data) - } - - } else { - marker = string(matches[2]) - syntax = string(matches[3]) - } - case isFootnoteDef(data): - matches := reFootnoteDef.FindSubmatch(data) - for i := range p.notes { - if p.notes[i].id == string(matches[1]) { - p.notes[i].def = string(matches[2]) - } - } - case isTable(data): - if inTable != true { - inTable = true - } - tmpBlock.Write(data) - tmpBlock.WriteByte('\n') - case IsKeyword(data): - continue - case isComment(data): - p.generateComment(&output, data) - case isHeadline(data): - p.generateHeadline(&output, data) - case isDefinitionList(data): - if inList != true { - listType = "dl" - inList = true - } - var work bytes.Buffer - flags := blackfriday.LIST_TYPE_DEFINITION - matches := reDefinitionList.FindSubmatch(data) - flags |= blackfriday.LIST_TYPE_TERM - p.inline(&work, matches[1]) - p.r.ListItem(&tmpBlock, work.Bytes(), flags) - work.Reset() - flags &= ^blackfriday.LIST_TYPE_TERM - p.inline(&work, matches[2]) - p.r.ListItem(&tmpBlock, work.Bytes(), flags) - case isUnorderedList(data): - if inList != true { - listType = "ul" - inList = true - } - matches := reUnorderedList.FindSubmatch(data) - var work bytes.Buffer - p.inline(&work, matches[2]) - p.r.ListItem(&tmpBlock, work.Bytes(), 0) - case isOrderedList(data): - if inList != true { - listType = "ol" - inList = true - } - matches := reOrderedList.FindSubmatch(data) - var work bytes.Buffer - tmpBlock.WriteString(" 0 { - tmpBlock.WriteString(" value=\"") - tmpBlock.Write(matches[2]) - tmpBlock.WriteString("\"") - matches[3] = matches[3][1:] - } - p.inline(&work, matches[3]) - tmpBlock.WriteString(">") - tmpBlock.Write(work.Bytes()) - tmpBlock.WriteString("\n") - case isHorizontalRule(data): - p.r.HRule(&output) - case isExampleLine(data): - if inParagraph == true { - if len(tmpBlock.Bytes()) > 0 { - p.generateParagraph(&output, tmpBlock.Bytes()[:len(tmpBlock.Bytes())-1]) - inParagraph = false - } - tmpBlock.Reset() - } - if inFixedWidthArea != true { - tmpBlock.WriteString("
\n")
-				inFixedWidthArea = true
-			}
-			matches := reExampleLine.FindSubmatch(data)
-			tmpBlock.Write(matches[1])
-			tmpBlock.WriteString("\n")
-			break
-		default:
-			if inParagraph == false {
-				inParagraph = true
-				if inFixedWidthArea == true {
-					if tmpBlock.Len() > 0 {
-						tmpBlock.WriteString("
") - output.Write(tmpBlock.Bytes()) - } - inFixedWidthArea = false - tmpBlock.Reset() - } - } - tmpBlock.Write(data) - tmpBlock.WriteByte('\n') - } - } - - if len(tmpBlock.Bytes()) > 0 { - if inParagraph == true { - p.generateParagraph(&output, tmpBlock.Bytes()[:len(tmpBlock.Bytes())-1]) - } else if inFixedWidthArea == true { - tmpBlock.WriteString("\n") - output.Write(tmpBlock.Bytes()) - } - } - - // Writing footnote def. list - if len(p.notes) > 0 { - flags := blackfriday.LIST_ITEM_BEGINNING_OF_LIST - p.r.Footnotes(&output, func() bool { - for i := range p.notes { - p.r.FootnoteItem(&output, []byte(p.notes[i].id), []byte(p.notes[i].def), flags) - } - return true - }) - } - - return output.Bytes() -} - -// Org Syntax has been broken up into 4 distinct sections based on -// the org-syntax draft (http://orgmode.org/worg/dev/org-syntax.html): -// - Headlines -// - Greater Elements -// - Elements -// - Objects - -// Headlines -func isHeadline(data []byte) bool { - if !charMatches(data[0], '*') { - return false - } - level := 0 - for level < 6 && charMatches(data[level], '*') { - level++ - } - return charMatches(data[level], ' ') -} - -func (p *parser) generateHeadline(out *bytes.Buffer, data []byte) { - level := 1 - status := "" - priority := "" - - for level < 6 && data[level] == '*' { - level++ - } - - start := skipChar(data, level, ' ') - - data = data[start:] - i := 0 - - // Check if has a status so it can be rendered as a separate span that can be hidden or - // modified with CSS classes - if hasStatus(data[i:4]) { - status = string(data[i:4]) - i += 5 // one extra character for the next whitespace - } - - // Check if the next byte is a priority marker - if data[i] == '[' && hasPriority(data[i+1]) { - priority = string(data[i+1]) - i += 4 // for "[c]" + ' ' - } - - tags, tagsFound := findTags(data, i) - - headlineID := sanitized_anchor_name.Create(string(data[i:])) - - generate := func() bool { - dataEnd := len(data) - if tagsFound > 0 { - dataEnd = tagsFound - } - - headline := bytes.TrimRight(data[i:dataEnd], " \t") - - if status != "" { - out.WriteString("" + status + "") - out.WriteByte(' ') - } - - if priority != "" { - out.WriteString("[" + priority + "]") - out.WriteByte(' ') - } - - p.inline(out, headline) - - if tagsFound > 0 { - for _, tag := range tags { - out.WriteByte(' ') - out.WriteString("" + tag + "") - out.WriteByte(' ') - } - } - return true - } - - p.r.Header(out, generate, level, headlineID) -} - -func hasStatus(data []byte) bool { - return bytes.Contains(data, []byte("TODO")) || bytes.Contains(data, []byte("DONE")) -} - -func hasPriority(char byte) bool { - return (charMatches(char, 'A') || charMatches(char, 'B') || charMatches(char, 'C')) -} - -func findTags(data []byte, start int) ([]string, int) { - tags := []string{} - tagOpener := 0 - tagMarker := tagOpener - for tIdx := start; tIdx < len(data); tIdx++ { - if tagMarker > 0 && data[tIdx] == ':' { - tags = append(tags, string(data[tagMarker+1:tIdx])) - tagMarker = tIdx - } - if data[tIdx] == ':' && tagOpener == 0 && data[tIdx-1] == ' ' { - tagMarker = tIdx - tagOpener = tIdx - } - } - return tags, tagOpener -} - -// Greater Elements -// ~~ Definition Lists -var reDefinitionList = regexp.MustCompile(`^\s*-\s+(.+?)\s+::\s+(.*)`) - -func isDefinitionList(data []byte) bool { - return reDefinitionList.Match(data) -} - -// ~~ Example lines -var reExampleLine = regexp.MustCompile(`^\s*:\s(\s*.*)|^\s*:$`) - -func isExampleLine(data []byte) bool { - return reExampleLine.Match(data) -} - -// ~~ Ordered Lists -var reOrderedList = regexp.MustCompile(`^(\s*)\d+\.\s+\[?@?(\d*)\]?(.+)`) - -func isOrderedList(data []byte) bool { - return reOrderedList.Match(data) -} - -// ~~ Unordered Lists -var reUnorderedList = regexp.MustCompile(`^(\s*)[-\+]\s+(.+)`) - -func isUnorderedList(data []byte) bool { - return reUnorderedList.Match(data) -} - -// ~~ Tables -var reTableHeaders = regexp.MustCompile(`^[|+-]*$`) - -func isTable(data []byte) bool { - return charMatches(data[0], '|') -} - -func (p *parser) generateTable(output *bytes.Buffer, data []byte) { - var table bytes.Buffer - rows := bytes.Split(bytes.Trim(data, "\n"), []byte("\n")) - hasTableHeaders := len(rows) > 1 - if len(rows) > 1 { - hasTableHeaders = reTableHeaders.Match(rows[1]) - } - tbodySet := false - - for idx, row := range rows { - var rowBuff bytes.Buffer - if hasTableHeaders && idx == 0 { - table.WriteString("") - for _, cell := range bytes.Split(row[1:len(row)-1], []byte("|")) { - p.r.TableHeaderCell(&rowBuff, bytes.Trim(cell, " \t"), 0) - } - p.r.TableRow(&table, rowBuff.Bytes()) - table.WriteString("\n") - } else if hasTableHeaders && idx == 1 { - continue - } else { - if !tbodySet { - table.WriteString("") - tbodySet = true - } - if !reTableHeaders.Match(row) { - for _, cell := range bytes.Split(row[1:len(row)-1], []byte("|")) { - var cellBuff bytes.Buffer - p.inline(&cellBuff, bytes.Trim(cell, " \t")) - p.r.TableCell(&rowBuff, cellBuff.Bytes(), 0) - } - p.r.TableRow(&table, rowBuff.Bytes()) - } - if tbodySet && idx == len(rows)-1 { - table.WriteString("\n") - tbodySet = false - } - } - } - - output.WriteString("\n\n") - output.Write(table.Bytes()) - output.WriteString("
\n") -} - -// ~~ Property Drawers - -func isPropertyDrawer(data []byte) bool { - return bytes.Equal(data, []byte(":PROPERTIES:")) -} - -// ~~ Dynamic Blocks -var reBlock = regexp.MustCompile(`^#\+(BEGIN|END)_(\w+)\s*([0-9A-Za-z_\-]*)?`) - -func isBlock(data []byte) bool { - return reBlock.Match(data) -} - -// ~~ Footnotes -var reFootnoteDef = regexp.MustCompile(`^\[fn:([\w]+)\] +(.+)`) - -func isFootnoteDef(data []byte) bool { - return reFootnoteDef.Match(data) -} - -// Elements -// ~~ Keywords -func IsKeyword(data []byte) bool { - return len(data) > 2 && charMatches(data[0], '#') && charMatches(data[1], '+') && !charMatches(data[2], ' ') -} - -// ~~ Comments -func isComment(data []byte) bool { - return charMatches(data[0], '#') && charMatches(data[1], ' ') -} - -func (p *parser) generateComment(out *bytes.Buffer, data []byte) { - var work bytes.Buffer - work.WriteString("") - work.WriteByte('\n') - out.Write(work.Bytes()) -} - -// ~~ Horizontal Rules -var reHorizontalRule = regexp.MustCompile(`^\s*?-----\s?$`) - -func isHorizontalRule(data []byte) bool { - return reHorizontalRule.Match(data) -} - -// ~~ Paragraphs -func (p *parser) generateParagraph(out *bytes.Buffer, data []byte) { - generate := func() bool { - p.inline(out, bytes.Trim(data, " ")) - return true - } - p.r.Paragraph(out, generate) -} - -func (p *parser) generateList(output *bytes.Buffer, data []byte, listType string) { - generateList := func() bool { - output.WriteByte('\n') - p.inline(output, bytes.Trim(data, " ")) - return true - } - switch listType { - case "ul": - p.r.List(output, generateList, 0) - case "ol": - p.r.List(output, generateList, blackfriday.LIST_TYPE_ORDERED) - case "dl": - p.r.List(output, generateList, blackfriday.LIST_TYPE_DEFINITION) - } -} - -// Objects - -func (p *parser) inline(out *bytes.Buffer, data []byte) { - i, end := 0, 0 - - for i < len(data) { - for end < len(data) && p.inlineCallback[data[end]] == nil { - end++ - } - - p.r.Entity(out, data[i:end]) - - if end >= len(data) { - break - } - i = end - - handler := p.inlineCallback[data[i]] - - if consumed := handler(p, out, data, i); consumed > 0 { - i += consumed - end = i - continue - } - - end = i + 1 - } -} - -func isAcceptablePreOpeningChar(dataIn, data []byte, offset int) bool { - if len(dataIn) == len(data) { - return true - } - - char := dataIn[offset-1] - return charMatches(char, ' ') || isPreChar(char) -} - -func isPreChar(char byte) bool { - return charMatches(char, '>') || charMatches(char, '(') || charMatches(char, '{') || charMatches(char, '[') -} - -func isAcceptablePostClosingChar(char byte) bool { - return charMatches(char, ' ') || isTerminatingChar(char) -} - -func isTerminatingChar(char byte) bool { - return charMatches(char, '.') || charMatches(char, ',') || charMatches(char, '?') || charMatches(char, '!') || charMatches(char, ')') || charMatches(char, '}') || charMatches(char, ']') -} - -func findLastCharInInline(data []byte, char byte) int { - timesFound := 0 - last := 0 - // Start from character after the inline indicator - for i := 1; i < len(data); i++ { - if timesFound == 1 { - break - } - if data[i] == char { - if len(data) == i+1 || (len(data) > i+1 && isAcceptablePostClosingChar(data[i+1])) { - last = i - timesFound += 1 - } - } - } - return last -} - -func generator(p *parser, out *bytes.Buffer, dataIn []byte, offset int, char byte, doInline bool, renderer func(*bytes.Buffer, []byte)) int { - data := dataIn[offset:] - c := byte(char) - start := 1 - i := start - if len(data) <= 1 { - return 0 - } - - lastCharInside := findLastCharInInline(data, c) - - // Org mode spec says a non-whitespace character must immediately follow. - // if the current char is the marker, then there's no text between, not a candidate - if isSpace(data[i]) || lastCharInside == i || !isAcceptablePreOpeningChar(dataIn, data, offset) { - return 0 - } - - if lastCharInside > 0 { - var work bytes.Buffer - if doInline { - p.inline(&work, data[start:lastCharInside]) - renderer(out, work.Bytes()) - } else { - renderer(out, data[start:lastCharInside]) - } - next := lastCharInside + 1 - return next - } - - return 0 -} - -// ~~ Text Markup -func generateVerbatim(p *parser, out *bytes.Buffer, data []byte, offset int) int { - return generator(p, out, data, offset, '=', false, p.r.CodeSpan) -} - -func generateCode(p *parser, out *bytes.Buffer, data []byte, offset int) int { - return generator(p, out, data, offset, '~', false, p.r.CodeSpan) -} - -func generateEmphasis(p *parser, out *bytes.Buffer, data []byte, offset int) int { - return generator(p, out, data, offset, '/', true, p.r.Emphasis) -} - -func generateUnderline(p *parser, out *bytes.Buffer, data []byte, offset int) int { - underline := func(out *bytes.Buffer, text []byte) { - out.WriteString("") - out.Write(text) - out.WriteString("") - } - - return generator(p, out, data, offset, '_', true, underline) -} - -func generateBold(p *parser, out *bytes.Buffer, data []byte, offset int) int { - return generator(p, out, data, offset, '*', true, p.r.DoubleEmphasis) -} - -func generateStrikethrough(p *parser, out *bytes.Buffer, data []byte, offset int) int { - return generator(p, out, data, offset, '+', true, p.r.StrikeThrough) -} - -// ~~ Images and Links (inc. Footnote) -var reLinkOrImg = regexp.MustCompile(`\[\[(.+?)\]\[?(.*?)\]?\]`) - -func generateLinkOrImg(p *parser, out *bytes.Buffer, data []byte, offset int) int { - data = data[offset+1:] - start := 1 - i := start - var hyperlink []byte - isImage := false - isFootnote := false - closedLink := false - hasContent := false - - if bytes.Equal(data[0:3], []byte("fn:")) { - isFootnote = true - } else if data[0] != '[' { - return 0 - } - - if bytes.Equal(data[1:6], []byte("file:")) { - isImage = true - } - - for i < len(data) { - currChar := data[i] - switch { - case charMatches(currChar, ']') && closedLink == false: - if isImage { - hyperlink = data[start+5 : i] - } else if isFootnote { - refid := data[start+2 : i] - if bytes.Equal(refid, bytes.Trim(refid, " ")) { - p.notes = append(p.notes, footnotes{string(refid), "DEFINITION NOT FOUND"}) - p.r.FootnoteRef(out, refid, len(p.notes)) - return i + 2 - } else { - return 0 - } - } else if bytes.Equal(data[i-4:i], []byte(".org")) { - orgStart := start - if bytes.Equal(data[orgStart:orgStart+2], []byte("./")) { - orgStart = orgStart + 1 - } - hyperlink = data[orgStart : i-4] - } else { - hyperlink = data[start:i] - } - closedLink = true - case charMatches(currChar, '['): - start = i + 1 - hasContent = true - case charMatches(currChar, ']') && closedLink == true && hasContent == true && isImage == true: - p.r.Image(out, hyperlink, data[start:i], data[start:i]) - return i + 3 - case charMatches(currChar, ']') && closedLink == true && hasContent == true: - var tmpBuf bytes.Buffer - p.inline(&tmpBuf, data[start:i]) - p.r.Link(out, hyperlink, tmpBuf.Bytes(), tmpBuf.Bytes()) - return i + 3 - case charMatches(currChar, ']') && closedLink == true && hasContent == false && isImage == true: - p.r.Image(out, hyperlink, hyperlink, hyperlink) - return i + 2 - case charMatches(currChar, ']') && closedLink == true && hasContent == false: - p.r.Link(out, hyperlink, hyperlink, hyperlink) - return i + 2 - } - i++ - } - - return 0 -} - -// Helpers -func skipChar(data []byte, start int, char byte) int { - i := start - for i < len(data) && charMatches(data[i], char) { - i++ - } - return i -} - -func isSpace(char byte) bool { - return charMatches(char, ' ') -} - -func isEmpty(data []byte) bool { - if len(data) == 0 { - return true - } - - for i := 0; i < len(data) && !charMatches(data[i], '\n'); i++ { - if !charMatches(data[i], ' ') && !charMatches(data[i], '\t') { - return false - } - } - return true -} - -func charMatches(a byte, b byte) bool { - return a == b -} diff --git a/vendor/github.com/chaseadamsio/goorgeous/gopher.gif b/vendor/github.com/chaseadamsio/goorgeous/gopher.gif deleted file mode 100644 index be7567e3cfd666ee40c56b1d808f97116c092053..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15232 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 vTwbHjFEt;&5pDgN=1cw&96@-xsG_RGKMSARK% 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 4 is value, including table link + +const ( + huffmanChunkBits = 9 + huffmanNumChunks = 1 << huffmanChunkBits + huffmanCountMask = 15 + huffmanValueShift = 4 +) + +type huffmanDecoder struct { + min int // the minimum code length + chunks [huffmanNumChunks]uint32 // chunks as described above + links [][]uint32 // overflow links + linkMask uint32 // mask the width of the link table +} + +// Initialize Huffman decoding tables from array of code lengths. +// Following this function, h is guaranteed to be initialized into a complete +// tree (i.e., neither over-subscribed nor under-subscribed). The exception is a +// degenerate case where the tree has only a single symbol with length 1. Empty +// trees are permitted. +func (h *huffmanDecoder) init(bits []int) bool { + // Sanity enables additional runtime tests during Huffman + // table construction. It's intended to be used during + // development to supplement the currently ad-hoc unit tests. + const sanity = false + + if h.min != 0 { + *h = huffmanDecoder{} + } + + // Count number of codes of each length, + // compute min and max length. + var count [maxCodeLen]int + var min, max int + for _, n := range bits { + if n == 0 { + continue + } + if min == 0 || n < min { + min = n + } + if n > max { + max = n + } + count[n]++ + } + + // Empty tree. The decompressor.huffSym function will fail later if the tree + // is used. Technically, an empty tree is only valid for the HDIST tree and + // not the HCLEN and HLIT tree. However, a stream with an empty HCLEN tree + // is guaranteed to fail since it will attempt to use the tree to decode the + // codes for the HLIT and HDIST trees. Similarly, an empty HLIT tree is + // guaranteed to fail later since the compressed data section must be + // composed of at least one symbol (the end-of-block marker). + if max == 0 { + return true + } + + code := 0 + var nextcode [maxCodeLen]int + for i := min; i <= max; i++ { + code <<= 1 + nextcode[i] = code + code += count[i] + } + + // Check that the coding is complete (i.e., that we've + // assigned all 2-to-the-max possible bit sequences). + // Exception: To be compatible with zlib, we also need to + // accept degenerate single-code codings. See also + // TestDegenerateHuffmanCoding. + if code != 1< huffmanChunkBits { + numLinks := 1 << (uint(max) - huffmanChunkBits) + h.linkMask = uint32(numLinks - 1) + + // create link tables + link := nextcode[huffmanChunkBits+1] >> 1 + h.links = make([][]uint32, huffmanNumChunks-link) + for j := uint(link); j < huffmanNumChunks; j++ { + reverse := int(reverseByte[j>>8]) | int(reverseByte[j&0xff])<<8 + reverse >>= uint(16 - huffmanChunkBits) + off := j - uint(link) + if sanity && h.chunks[reverse] != 0 { + panic("impossible: overwriting existing chunk") + } + h.chunks[reverse] = uint32(off<>8]) | int(reverseByte[code&0xff])<<8 + reverse >>= uint(16 - n) + if n <= huffmanChunkBits { + for off := reverse; off < len(h.chunks); off += 1 << uint(n) { + // We should never need to overwrite + // an existing chunk. Also, 0 is + // never a valid chunk, because the + // lower 4 "count" bits should be + // between 1 and 15. + if sanity && h.chunks[off] != 0 { + panic("impossible: overwriting existing chunk") + } + h.chunks[off] = chunk + } + } else { + j := reverse & (huffmanNumChunks - 1) + if sanity && h.chunks[j]&huffmanCountMask != huffmanChunkBits+1 { + // Longer codes should have been + // associated with a link table above. + panic("impossible: not an indirect chunk") + } + value := h.chunks[j] >> huffmanValueShift + linktab := h.links[value] + reverse >>= huffmanChunkBits + for off := reverse; off < len(linktab); off += 1 << uint(n-huffmanChunkBits) { + if sanity && linktab[off] != 0 { + panic("impossible: overwriting existing chunk") + } + linktab[off] = chunk + } + } + } + + if sanity { + // Above we've sanity checked that we never overwrote + // an existing entry. Here we additionally check that + // we filled the tables completely. + for i, chunk := range h.chunks { + if chunk == 0 { + // As an exception, in the degenerate + // single-code case, we allow odd + // chunks to be missing. + if code == 1 && i%2 == 1 { + continue + } + panic("impossible: missing chunk") + } + } + for _, linktab := range h.links { + for _, chunk := range linktab { + if chunk == 0 { + panic("impossible: missing chunk") + } + } + } + } + + return true +} + +func main() { + flag.Parse() + + var h huffmanDecoder + var bits [288]int + initReverseByte() + for i := 0; i < 144; i++ { + bits[i] = 8 + } + for i := 144; i < 256; i++ { + bits[i] = 9 + } + for i := 256; i < 280; i++ { + bits[i] = 7 + } + for i := 280; i < 288; i++ { + bits[i] = 8 + } + h.init(bits[:]) + if h.links != nil { + log.Fatal("Unexpected links table in fixed Huffman decoder") + } + + var buf bytes.Buffer + + fmt.Fprintf(&buf, `// 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.`+"\n\n") + + fmt.Fprintln(&buf, "package flate") + fmt.Fprintln(&buf) + fmt.Fprintln(&buf, "// autogenerated by go run gen.go -output fixedhuff.go, DO NOT EDIT") + fmt.Fprintln(&buf) + fmt.Fprintln(&buf, "var fixedHuffmanDecoder = huffmanDecoder{") + fmt.Fprintf(&buf, "\t%d,\n", h.min) + fmt.Fprintln(&buf, "\t[huffmanNumChunks]uint32{") + for i := 0; i < huffmanNumChunks; i++ { + if i&7 == 0 { + fmt.Fprintf(&buf, "\t\t") + } else { + fmt.Fprintf(&buf, " ") + } + fmt.Fprintf(&buf, "0x%04x,", h.chunks[i]) + if i&7 == 7 { + fmt.Fprintln(&buf) + } + } + fmt.Fprintln(&buf, "\t},") + fmt.Fprintln(&buf, "\tnil, 0,") + fmt.Fprintln(&buf, "}") + + data, err := format.Source(buf.Bytes()) + if err != nil { + log.Fatal(err) + } + err = ioutil.WriteFile(*filename, data, 0644) + if err != nil { + log.Fatal(err) + } +} + +var reverseByte [256]byte + +func initReverseByte() { + for x := 0; x < 256; x++ { + var result byte + for i := uint(0); i < 8; i++ { + result |= byte(((x >> i) & 1) << (7 - i)) + } + reverseByte[x] = result + } +} diff --git a/vendor/github.com/klauspost/cpuid/private-gen.go b/vendor/github.com/klauspost/cpuid/private-gen.go new file mode 100644 index 0000000000000..437333d292245 --- /dev/null +++ b/vendor/github.com/klauspost/cpuid/private-gen.go @@ -0,0 +1,476 @@ +// +build ignore + +package main + +import ( + "bytes" + "fmt" + "go/ast" + "go/parser" + "go/printer" + "go/token" + "io" + "io/ioutil" + "log" + "os" + "reflect" + "strings" + "unicode" + "unicode/utf8" +) + +var inFiles = []string{"cpuid.go", "cpuid_test.go"} +var copyFiles = []string{"cpuid_amd64.s", "cpuid_386.s", "detect_ref.go", "detect_intel.go"} +var fileSet = token.NewFileSet() +var reWrites = []rewrite{ + initRewrite("CPUInfo -> cpuInfo"), + initRewrite("Vendor -> vendor"), + initRewrite("Flags -> flags"), + initRewrite("Detect -> detect"), + initRewrite("CPU -> cpu"), +} +var excludeNames = map[string]bool{"string": true, "join": true, "trim": true, + // cpuid_test.go + "t": true, "println": true, "logf": true, "log": true, "fatalf": true, "fatal": true, +} + +var excludePrefixes = []string{"test", "benchmark"} + +func main() { + Package := "private" + parserMode := parser.ParseComments + exported := make(map[string]rewrite) + for _, file := range inFiles { + in, err := os.Open(file) + if err != nil { + log.Fatalf("opening input", err) + } + + src, err := ioutil.ReadAll(in) + if err != nil { + log.Fatalf("reading input", err) + } + + astfile, err := parser.ParseFile(fileSet, file, src, parserMode) + if err != nil { + log.Fatalf("parsing input", err) + } + + for _, rw := range reWrites { + astfile = rw(astfile) + } + + // Inspect the AST and print all identifiers and literals. + var startDecl token.Pos + var endDecl token.Pos + ast.Inspect(astfile, func(n ast.Node) bool { + var s string + switch x := n.(type) { + case *ast.Ident: + if x.IsExported() { + t := strings.ToLower(x.Name) + for _, pre := range excludePrefixes { + if strings.HasPrefix(t, pre) { + return true + } + } + if excludeNames[t] != true { + //if x.Pos() > startDecl && x.Pos() < endDecl { + exported[x.Name] = initRewrite(x.Name + " -> " + t) + } + } + + case *ast.GenDecl: + if x.Tok == token.CONST && x.Lparen > 0 { + startDecl = x.Lparen + endDecl = x.Rparen + // fmt.Printf("Decl:%s -> %s\n", fileSet.Position(startDecl), fileSet.Position(endDecl)) + } + } + if s != "" { + fmt.Printf("%s:\t%s\n", fileSet.Position(n.Pos()), s) + } + return true + }) + + for _, rw := range exported { + astfile = rw(astfile) + } + + var buf bytes.Buffer + + printer.Fprint(&buf, fileSet, astfile) + + // Remove package documentation and insert information + s := buf.String() + ind := strings.Index(buf.String(), "\npackage cpuid") + s = s[ind:] + s = "// Generated, DO NOT EDIT,\n" + + "// but copy it to your own project and rename the package.\n" + + "// See more at http://github.com/klauspost/cpuid\n" + + s + + outputName := Package + string(os.PathSeparator) + file + + err = ioutil.WriteFile(outputName, []byte(s), 0644) + if err != nil { + log.Fatalf("writing output: %s", err) + } + log.Println("Generated", outputName) + } + + for _, file := range copyFiles { + dst := "" + if strings.HasPrefix(file, "cpuid") { + dst = Package + string(os.PathSeparator) + file + } else { + dst = Package + string(os.PathSeparator) + "cpuid_" + file + } + err := copyFile(file, dst) + if err != nil { + log.Fatalf("copying file: %s", err) + } + log.Println("Copied", dst) + } +} + +// CopyFile copies a file from src to dst. If src and dst files exist, and are +// the same, then return success. Copy the file contents from src to dst. +func copyFile(src, dst string) (err error) { + sfi, err := os.Stat(src) + if err != nil { + return + } + if !sfi.Mode().IsRegular() { + // cannot copy non-regular files (e.g., directories, + // symlinks, devices, etc.) + return fmt.Errorf("CopyFile: non-regular source file %s (%q)", sfi.Name(), sfi.Mode().String()) + } + dfi, err := os.Stat(dst) + if err != nil { + if !os.IsNotExist(err) { + return + } + } else { + if !(dfi.Mode().IsRegular()) { + return fmt.Errorf("CopyFile: non-regular destination file %s (%q)", dfi.Name(), dfi.Mode().String()) + } + if os.SameFile(sfi, dfi) { + return + } + } + err = copyFileContents(src, dst) + return +} + +// copyFileContents copies the contents of the file named src to the file named +// by dst. The file will be created if it does not already exist. If the +// destination file exists, all it's contents will be replaced by the contents +// of the source file. +func copyFileContents(src, dst string) (err error) { + in, err := os.Open(src) + if err != nil { + return + } + defer in.Close() + out, err := os.Create(dst) + if err != nil { + return + } + defer func() { + cerr := out.Close() + if err == nil { + err = cerr + } + }() + if _, err = io.Copy(out, in); err != nil { + return + } + err = out.Sync() + return +} + +type rewrite func(*ast.File) *ast.File + +// Mostly copied from gofmt +func initRewrite(rewriteRule string) rewrite { + f := strings.Split(rewriteRule, "->") + if len(f) != 2 { + fmt.Fprintf(os.Stderr, "rewrite rule must be of the form 'pattern -> replacement'\n") + os.Exit(2) + } + pattern := parseExpr(f[0], "pattern") + replace := parseExpr(f[1], "replacement") + return func(p *ast.File) *ast.File { return rewriteFile(pattern, replace, p) } +} + +// parseExpr parses s as an expression. +// It might make sense to expand this to allow statement patterns, +// but there are problems with preserving formatting and also +// with what a wildcard for a statement looks like. +func parseExpr(s, what string) ast.Expr { + x, err := parser.ParseExpr(s) + if err != nil { + fmt.Fprintf(os.Stderr, "parsing %s %s at %s\n", what, s, err) + os.Exit(2) + } + return x +} + +// Keep this function for debugging. +/* +func dump(msg string, val reflect.Value) { + fmt.Printf("%s:\n", msg) + ast.Print(fileSet, val.Interface()) + fmt.Println() +} +*/ + +// rewriteFile applies the rewrite rule 'pattern -> replace' to an entire file. +func rewriteFile(pattern, replace ast.Expr, p *ast.File) *ast.File { + cmap := ast.NewCommentMap(fileSet, p, p.Comments) + m := make(map[string]reflect.Value) + pat := reflect.ValueOf(pattern) + repl := reflect.ValueOf(replace) + + var rewriteVal func(val reflect.Value) reflect.Value + rewriteVal = func(val reflect.Value) reflect.Value { + // don't bother if val is invalid to start with + if !val.IsValid() { + return reflect.Value{} + } + for k := range m { + delete(m, k) + } + val = apply(rewriteVal, val) + if match(m, pat, val) { + val = subst(m, repl, reflect.ValueOf(val.Interface().(ast.Node).Pos())) + } + return val + } + + r := apply(rewriteVal, reflect.ValueOf(p)).Interface().(*ast.File) + r.Comments = cmap.Filter(r).Comments() // recreate comments list + return r +} + +// set is a wrapper for x.Set(y); it protects the caller from panics if x cannot be changed to y. +func set(x, y reflect.Value) { + // don't bother if x cannot be set or y is invalid + if !x.CanSet() || !y.IsValid() { + return + } + defer func() { + if x := recover(); x != nil { + if s, ok := x.(string); ok && + (strings.Contains(s, "type mismatch") || strings.Contains(s, "not assignable")) { + // x cannot be set to y - ignore this rewrite + return + } + panic(x) + } + }() + x.Set(y) +} + +// Values/types for special cases. +var ( + objectPtrNil = reflect.ValueOf((*ast.Object)(nil)) + scopePtrNil = reflect.ValueOf((*ast.Scope)(nil)) + + identType = reflect.TypeOf((*ast.Ident)(nil)) + objectPtrType = reflect.TypeOf((*ast.Object)(nil)) + positionType = reflect.TypeOf(token.NoPos) + callExprType = reflect.TypeOf((*ast.CallExpr)(nil)) + scopePtrType = reflect.TypeOf((*ast.Scope)(nil)) +) + +// apply replaces each AST field x in val with f(x), returning val. +// To avoid extra conversions, f operates on the reflect.Value form. +func apply(f func(reflect.Value) reflect.Value, val reflect.Value) reflect.Value { + if !val.IsValid() { + return reflect.Value{} + } + + // *ast.Objects introduce cycles and are likely incorrect after + // rewrite; don't follow them but replace with nil instead + if val.Type() == objectPtrType { + return objectPtrNil + } + + // similarly for scopes: they are likely incorrect after a rewrite; + // replace them with nil + if val.Type() == scopePtrType { + return scopePtrNil + } + + switch v := reflect.Indirect(val); v.Kind() { + case reflect.Slice: + for i := 0; i < v.Len(); i++ { + e := v.Index(i) + set(e, f(e)) + } + case reflect.Struct: + for i := 0; i < v.NumField(); i++ { + e := v.Field(i) + set(e, f(e)) + } + case reflect.Interface: + e := v.Elem() + set(v, f(e)) + } + return val +} + +func isWildcard(s string) bool { + rune, size := utf8.DecodeRuneInString(s) + return size == len(s) && unicode.IsLower(rune) +} + +// match returns true if pattern matches val, +// recording wildcard submatches in m. +// If m == nil, match checks whether pattern == val. +func match(m map[string]reflect.Value, pattern, val reflect.Value) bool { + // Wildcard matches any expression. If it appears multiple + // times in the pattern, it must match the same expression + // each time. + if m != nil && pattern.IsValid() && pattern.Type() == identType { + name := pattern.Interface().(*ast.Ident).Name + if isWildcard(name) && val.IsValid() { + // wildcards only match valid (non-nil) expressions. + if _, ok := val.Interface().(ast.Expr); ok && !val.IsNil() { + if old, ok := m[name]; ok { + return match(nil, old, val) + } + m[name] = val + return true + } + } + } + + // Otherwise, pattern and val must match recursively. + if !pattern.IsValid() || !val.IsValid() { + return !pattern.IsValid() && !val.IsValid() + } + if pattern.Type() != val.Type() { + return false + } + + // Special cases. + switch pattern.Type() { + case identType: + // For identifiers, only the names need to match + // (and none of the other *ast.Object information). + // This is a common case, handle it all here instead + // of recursing down any further via reflection. + p := pattern.Interface().(*ast.Ident) + v := val.Interface().(*ast.Ident) + return p == nil && v == nil || p != nil && v != nil && p.Name == v.Name + case objectPtrType, positionType: + // object pointers and token positions always match + return true + case callExprType: + // For calls, the Ellipsis fields (token.Position) must + // match since that is how f(x) and f(x...) are different. + // Check them here but fall through for the remaining fields. + p := pattern.Interface().(*ast.CallExpr) + v := val.Interface().(*ast.CallExpr) + if p.Ellipsis.IsValid() != v.Ellipsis.IsValid() { + return false + } + } + + p := reflect.Indirect(pattern) + v := reflect.Indirect(val) + if !p.IsValid() || !v.IsValid() { + return !p.IsValid() && !v.IsValid() + } + + switch p.Kind() { + case reflect.Slice: + if p.Len() != v.Len() { + return false + } + for i := 0; i < p.Len(); i++ { + if !match(m, p.Index(i), v.Index(i)) { + return false + } + } + return true + + case reflect.Struct: + for i := 0; i < p.NumField(); i++ { + if !match(m, p.Field(i), v.Field(i)) { + return false + } + } + return true + + case reflect.Interface: + return match(m, p.Elem(), v.Elem()) + } + + // Handle token integers, etc. + return p.Interface() == v.Interface() +} + +// subst returns a copy of pattern with values from m substituted in place +// of wildcards and pos used as the position of tokens from the pattern. +// if m == nil, subst returns a copy of pattern and doesn't change the line +// number information. +func subst(m map[string]reflect.Value, pattern reflect.Value, pos reflect.Value) reflect.Value { + if !pattern.IsValid() { + return reflect.Value{} + } + + // Wildcard gets replaced with map value. + if m != nil && pattern.Type() == identType { + name := pattern.Interface().(*ast.Ident).Name + if isWildcard(name) { + if old, ok := m[name]; ok { + return subst(nil, old, reflect.Value{}) + } + } + } + + if pos.IsValid() && pattern.Type() == positionType { + // use new position only if old position was valid in the first place + if old := pattern.Interface().(token.Pos); !old.IsValid() { + return pattern + } + return pos + } + + // Otherwise copy. + switch p := pattern; p.Kind() { + case reflect.Slice: + v := reflect.MakeSlice(p.Type(), p.Len(), p.Len()) + for i := 0; i < p.Len(); i++ { + v.Index(i).Set(subst(m, p.Index(i), pos)) + } + return v + + case reflect.Struct: + v := reflect.New(p.Type()).Elem() + for i := 0; i < p.NumField(); i++ { + v.Field(i).Set(subst(m, p.Field(i), pos)) + } + return v + + case reflect.Ptr: + v := reflect.New(p.Type()).Elem() + if elem := p.Elem(); elem.IsValid() { + v.Set(subst(m, elem, pos).Addr()) + } + return v + + case reflect.Interface: + v := reflect.New(p.Type()).Elem() + if elem := p.Elem(); elem.IsValid() { + v.Set(subst(m, elem, pos)) + } + return v + } + + return pattern +} diff --git a/vendor/github.com/lib/pq/oid/gen.go b/vendor/github.com/lib/pq/oid/gen.go new file mode 100644 index 0000000000000..7c634cdc5cd63 --- /dev/null +++ b/vendor/github.com/lib/pq/oid/gen.go @@ -0,0 +1,93 @@ +// +build ignore + +// Generate the table of OID values +// Run with 'go run gen.go'. +package main + +import ( + "database/sql" + "fmt" + "log" + "os" + "os/exec" + "strings" + + _ "github.com/lib/pq" +) + +// OID represent a postgres Object Identifier Type. +type OID struct { + ID int + Type string +} + +// Name returns an upper case version of the oid type. +func (o OID) Name() string { + return strings.ToUpper(o.Type) +} + +func main() { + datname := os.Getenv("PGDATABASE") + sslmode := os.Getenv("PGSSLMODE") + + if datname == "" { + os.Setenv("PGDATABASE", "pqgotest") + } + + if sslmode == "" { + os.Setenv("PGSSLMODE", "disable") + } + + db, err := sql.Open("postgres", "") + if err != nil { + log.Fatal(err) + } + rows, err := db.Query(` + SELECT typname, oid + FROM pg_type WHERE oid < 10000 + ORDER BY oid; + `) + if err != nil { + log.Fatal(err) + } + oids := make([]*OID, 0) + for rows.Next() { + var oid OID + if err = rows.Scan(&oid.Type, &oid.ID); err != nil { + log.Fatal(err) + } + oids = append(oids, &oid) + } + if err = rows.Err(); err != nil { + log.Fatal(err) + } + cmd := exec.Command("gofmt") + cmd.Stderr = os.Stderr + w, err := cmd.StdinPipe() + if err != nil { + log.Fatal(err) + } + f, err := os.Create("types.go") + if err != nil { + log.Fatal(err) + } + cmd.Stdout = f + err = cmd.Start() + if err != nil { + log.Fatal(err) + } + fmt.Fprintln(w, "// Code generated by gen.go. DO NOT EDIT.") + fmt.Fprintln(w, "\npackage oid") + fmt.Fprintln(w, "const (") + for _, oid := range oids { + fmt.Fprintf(w, "T_%s Oid = %d\n", oid.Type, oid.ID) + } + fmt.Fprintln(w, ")") + fmt.Fprintln(w, "var TypeName = map[Oid]string{") + for _, oid := range oids { + fmt.Fprintf(w, "T_%s: \"%s\",\n", oid.Type, oid.Name()) + } + fmt.Fprintln(w, "}") + w.Close() + cmd.Wait() +} diff --git a/vendor/github.com/russross/blackfriday/.travis.yml b/vendor/github.com/russross/blackfriday/.travis.yml index a1687f17a3fec..a4eb257790bb2 100644 --- a/vendor/github.com/russross/blackfriday/.travis.yml +++ b/vendor/github.com/russross/blackfriday/.travis.yml @@ -1,30 +1,18 @@ -sudo: false +# Travis CI (http://travis-ci.org/) is a continuous integration service for +# open source projects. This file configures it to run unit tests for +# blackfriday. + language: go + go: - - 1.5.4 - - 1.6.2 - - tip -matrix: - include: - - go: 1.2.2 - script: - - go get -t -v ./... - - go test -v -race ./... - - go: 1.3.3 - script: - - go get -t -v ./... - - go test -v -race ./... - - go: 1.4.3 - script: - - go get -t -v ./... - - go test -v -race ./... - allow_failures: - - go: tip - fast_finish: true + - 1.5 + - 1.6 + - 1.7 + install: - - # Do nothing. This is needed to prevent default install action "go get -t -v ./..." from happening here (we want it to happen inside script step). + - go get -d -t -v ./... + - go build -v ./... + script: - - go get -t -v ./... - - diff -u <(echo -n) <(gofmt -d -s .) - - go tool vet . - - go test -v -race ./... + - go test -v ./... + - go test -run=^$ -bench=BenchmarkReference -benchmem diff --git a/vendor/github.com/russross/blackfriday/README.md b/vendor/github.com/russross/blackfriday/README.md index e0066b0fc1afc..2e0db3555c8a6 100644 --- a/vendor/github.com/russross/blackfriday/README.md +++ b/vendor/github.com/russross/blackfriday/README.md @@ -1,6 +1,4 @@ -Blackfriday -[![Build Status][BuildSVG]][BuildURL] -[![Godoc][GodocV2SVG]][GodocV2URL] +Blackfriday [![Build Status](https://travis-ci.org/russross/blackfriday.svg?branch=master)](https://travis-ci.org/russross/blackfriday) =========== Blackfriday is a [Markdown][1] processor implemented in [Go][2]. It @@ -18,27 +16,27 @@ It started as a translation from C of [Sundown][3]. Installation ------------ -Blackfriday is compatible with any modern Go release. With Go and git installed: +Blackfriday is compatible with any modern Go release. With Go 1.7 and git +installed: - go get -u gopkg.in/russross/blackfriday.v2 + go get gopkg.in/russross/blackfriday.v2 -will download, compile, and install the package into your `$GOPATH` directory -hierarchy. +will download, compile, and install the package into your `$GOPATH` +directory hierarchy. Alternatively, you can achieve the same if you +import it into a project: + + import "gopkg.in/russross/blackfriday.v2" + +and `go get` without parameters. Versions -------- Currently maintained and recommended version of Blackfriday is `v2`. It's being -developed on its own branch: https://github.com/russross/blackfriday/tree/v2 and the -documentation is available at -https://godoc.org/gopkg.in/russross/blackfriday.v2. - -It is `go get`-able via [gopkg.in][6] at `gopkg.in/russross/blackfriday.v2`, -but we highly recommend using package management tool like [dep][7] or -[Glide][8] and make use of semantic versioning. With package management you -should import `github.com/russross/blackfriday` and specify that you're using -version 2.0.0. +developed on its own branch: https://github.com/russross/blackfriday/v2. You +should install and import it via [gopkg.in][6] at +`gopkg.in/russross/blackfriday.v2`. Version 2 offers a number of improvements over v1: @@ -58,43 +56,9 @@ Potential drawbacks: v2. See issue [#348](https://github.com/russross/blackfriday/issues/348) for tracking. -If you are still interested in the legacy `v1`, you can import it from -`github.com/russross/blackfriday`. Documentation for the legacy v1 can be found -here: https://godoc.org/github.com/russross/blackfriday - -### Known issue with `dep` - -There is a known problem with using Blackfriday v1 _transitively_ and `dep`. -Currently `dep` prioritizes semver versions over anything else, and picks the -latest one, plus it does not apply a `[[constraint]]` specifier to transitively -pulled in packages. So if you're using something that uses Blackfriday v1, but -that something does not use `dep` yet, you will get Blackfriday v2 pulled in and -your first dependency will fail to build. - -There are couple of fixes for it, documented here: -https://github.com/golang/dep/blob/master/docs/FAQ.md#how-do-i-constrain-a-transitive-dependencys-version - -Meanwhile, `dep` team is working on a more general solution to the constraints -on transitive dependencies problem: https://github.com/golang/dep/issues/1124. - - Usage ----- -### v1 - -For basic usage, it is as simple as getting your input into a byte -slice and calling: - - output := blackfriday.MarkdownBasic(input) - -This renders it with no extensions enabled. To get a more useful -feature set, use this instead: - - output := blackfriday.MarkdownCommon(input) - -### v2 - For the most sensible markdown processing, it is as simple as getting your input into a byte slice and calling: @@ -121,7 +85,7 @@ Here's an example of simple usage of Blackfriday together with Bluemonday: ```go import ( "github.com/microcosm-cc/bluemonday" - "gopkg.in/russross/blackfriday.v2" + "github.com/russross/blackfriday" ) // ... @@ -129,21 +93,11 @@ unsafe := blackfriday.Run(input) html := bluemonday.UGCPolicy().SanitizeBytes(unsafe) ``` -### Custom options, v1 - -If you want to customize the set of options, first get a renderer -(currently only the HTML output engine), then use it to -call the more general `Markdown` function. For examples, see the -implementations of `MarkdownBasic` and `MarkdownCommon` in -`markdown.go`. - -### Custom options, v2 +### Custom options If you want to customize the set of options, use `blackfriday.WithExtensions`, `blackfriday.WithRenderer` and `blackfriday.WithRefOverride`. -### `blackfriday-tool` - You can also check out `blackfriday-tool` for a more complete example of how to use it. Download and install it using: @@ -163,22 +117,6 @@ installed in `$GOPATH/bin`. This is a statically-linked binary that can be copied to wherever you need it without worrying about dependencies and library versions. -### Sanitized anchor names - -Blackfriday includes an algorithm for creating sanitized anchor names -corresponding to a given input text. This algorithm is used to create -anchors for headings when `EXTENSION_AUTO_HEADER_IDS` is enabled. The -algorithm has a specification, so that other packages can create -compatible anchor names and links to those anchors. - -The specification is located at https://godoc.org/github.com/russross/blackfriday#hdr-Sanitized_Anchor_Names. - -[`SanitizedAnchorName`](https://godoc.org/github.com/russross/blackfriday#SanitizedAnchorName) exposes this functionality, and can be used to -create compatible links to the anchor names generated by blackfriday. -This algorithm is also implemented in a small standalone package at -[`github.com/shurcooL/sanitized_anchor_name`](https://godoc.org/github.com/shurcooL/sanitized_anchor_name). It can be useful for clients -that want a small package and don't need full functionality of blackfriday. - Features -------- @@ -246,7 +184,7 @@ implements the following extensions: and supply a language (to make syntax highlighting simple). Just mark it like this: - ``` go + ```go func getTrue() bool { return true } @@ -255,15 +193,6 @@ implements the following extensions: You can use 3 or more backticks to mark the beginning of the block, and the same number to mark the end of the block. - To preserve classes of fenced code blocks while using the bluemonday - HTML sanitizer, use the following policy: - - ``` go - p := bluemonday.UGCPolicy() - p.AllowAttrs("class").Matching(regexp.MustCompile("^language-[a-zA-Z0-9]+$")).OnElements("code") - html := p.SanitizeBytes(unsafe) - ``` - * **Definition lists**. A simple definition list is made of a single-line term followed by a colon and the definition for that term. @@ -289,10 +218,8 @@ implements the following extensions: * **Strikethrough**. Use two tildes (`~~`) to mark text that should be crossed out. -* **Hard line breaks**. With this extension enabled (it is off by - default in the `MarkdownBasic` and `MarkdownCommon` convenience - functions), newlines in the input translate into line breaks in - the output. +* **Hard line breaks**. With this extension enabled newlines in the input + translate into line breaks in the output. This extension is off by default. * **Smart quotes**. Smartypants-style punctuation substitution is supported, turning normal double- and single-quote marks into @@ -332,14 +259,14 @@ are a few of note: renders output as LaTeX. -TODO +Todo ---- * More unit testing -* Improve Unicode support. It does not understand all Unicode +* Improve unicode support. It does not understand all unicode rules (about what constitutes a letter, a punctuation symbol, etc.), so it may fail to detect word boundaries correctly in - some instances. It is safe on all UTF-8 input. + some instances. It is safe on all utf-8 input. License @@ -354,10 +281,3 @@ License [4]: https://godoc.org/gopkg.in/russross/blackfriday.v2#Parse "Parse func" [5]: https://github.com/microcosm-cc/bluemonday "Bluemonday" [6]: https://labix.org/gopkg.in "gopkg.in" - [7]: https://github.com/golang/dep/ "dep" - [8]: https://github.com/Masterminds/glide "Glide" - - [BuildSVG]: https://travis-ci.org/russross/blackfriday.svg?branch=master - [BuildURL]: https://travis-ci.org/russross/blackfriday - [GodocV2SVG]: https://godoc.org/gopkg.in/russross/blackfriday.v2?status.svg - [GodocV2URL]: https://godoc.org/gopkg.in/russross/blackfriday.v2 diff --git a/vendor/github.com/russross/blackfriday/block.go b/vendor/github.com/russross/blackfriday/block.go index 929638aa483c4..d7da33f2136ae 100644 --- a/vendor/github.com/russross/blackfriday/block.go +++ b/vendor/github.com/russross/blackfriday/block.go @@ -15,18 +15,26 @@ package blackfriday import ( "bytes" - "strings" - "unicode" + "html" + "regexp" + + "github.com/shurcooL/sanitized_anchor_name" +) + +const ( + charEntity = "&(?:#x[a-f0-9]{1,8}|#[0-9]{1,8}|[a-z][a-z0-9]{1,31});" + escapable = "[!\"#$%&'()*+,./:;<=>?@[\\\\\\]^_`{|}~-]" +) + +var ( + reBackslashOrAmp = regexp.MustCompile("[\\&]") + reEntityOrEscapedChar = regexp.MustCompile("(?i)\\\\" + escapable + "|" + charEntity) ) // Parse block-level data. // Note: this function and many that it calls assume that // the input buffer ends with a newline. -func (p *parser) block(out *bytes.Buffer, data []byte) { - if len(data) == 0 || data[len(data)-1] != '\n' { - panic("block input is missing terminating newline") - } - +func (p *Markdown) block(data []byte) { // this is called recursively: enforce a maximum depth if p.nesting >= p.maxNesting { return @@ -35,14 +43,14 @@ func (p *parser) block(out *bytes.Buffer, data []byte) { // parse out one block-level construct at a time for len(data) > 0 { - // prefixed header: + // prefixed heading: // - // # Header 1 - // ## Header 2 + // # Heading 1 + // ## Heading 2 // ... - // ###### Header 6 - if p.isPrefixHeader(data) { - data = data[p.prefixHeader(out, data):] + // ###### Heading 6 + if p.isPrefixHeading(data) { + data = data[p.prefixHeading(data):] continue } @@ -52,7 +60,7 @@ func (p *parser) block(out *bytes.Buffer, data []byte) { // ... // if data[0] == '<' { - if i := p.html(out, data, true); i > 0 { + if i := p.html(data, true); i > 0 { data = data[i:] continue } @@ -63,9 +71,9 @@ func (p *parser) block(out *bytes.Buffer, data []byte) { // % stuff // % more stuff // % even more stuff - if p.flags&EXTENSION_TITLEBLOCK != 0 { + if p.extensions&Titleblock != 0 { if data[0] == '%' { - if i := p.titleBlock(out, data, true); i > 0 { + if i := p.titleBlock(data, true); i > 0 { data = data[i:] continue } @@ -87,13 +95,13 @@ func (p *parser) block(out *bytes.Buffer, data []byte) { // return b // } if p.codePrefix(data) > 0 { - data = data[p.code(out, data):] + data = data[p.code(data):] continue } // fenced code block: // - // ``` go info string here + // ``` go // func fact(n int) int { // if n <= 1 { // return n @@ -101,8 +109,8 @@ func (p *parser) block(out *bytes.Buffer, data []byte) { // return n * fact(n-1) // } // ``` - if p.flags&EXTENSION_FENCED_CODE != 0 { - if i := p.fencedCodeBlock(out, data, true); i > 0 { + if p.extensions&FencedCode != 0 { + if i := p.fencedCodeBlock(data, true); i > 0 { data = data[i:] continue } @@ -116,9 +124,9 @@ func (p *parser) block(out *bytes.Buffer, data []byte) { // or // ______ if p.isHRule(data) { - p.r.HRule(out) + p.addBlock(HorizontalRule, nil) var i int - for i = 0; data[i] != '\n'; i++ { + for i = 0; i < len(data) && data[i] != '\n'; i++ { } data = data[i:] continue @@ -129,7 +137,7 @@ func (p *parser) block(out *bytes.Buffer, data []byte) { // > A big quote I found somewhere // > on the web if p.quotePrefix(data) > 0 { - data = data[p.quote(out, data):] + data = data[p.quote(data):] continue } @@ -139,8 +147,8 @@ func (p *parser) block(out *bytes.Buffer, data []byte) { // ------|-----|--------- // Bob | 31 | 555-1234 // Alice | 27 | 555-4321 - if p.flags&EXTENSION_TABLES != 0 { - if i := p.table(out, data); i > 0 { + if p.extensions&Tables != 0 { + if i := p.table(data); i > 0 { data = data[i:] continue } @@ -153,7 +161,7 @@ func (p *parser) block(out *bytes.Buffer, data []byte) { // // also works with + or - if p.uliPrefix(data) > 0 { - data = data[p.list(out, data, 0):] + data = data[p.list(data, 0):] continue } @@ -162,7 +170,7 @@ func (p *parser) block(out *bytes.Buffer, data []byte) { // 1. Item 1 // 2. Item 2 if p.oliPrefix(data) > 0 { - data = data[p.list(out, data, LIST_TYPE_ORDERED):] + data = data[p.list(data, ListTypeOrdered):] continue } @@ -174,55 +182,62 @@ func (p *parser) block(out *bytes.Buffer, data []byte) { // // Term 2 // : Definition c - if p.flags&EXTENSION_DEFINITION_LISTS != 0 { + if p.extensions&DefinitionLists != 0 { if p.dliPrefix(data) > 0 { - data = data[p.list(out, data, LIST_TYPE_DEFINITION):] + data = data[p.list(data, ListTypeDefinition):] continue } } // anything else must look like a normal paragraph - // note: this finds underlined headers, too - data = data[p.paragraph(out, data):] + // note: this finds underlined headings, too + data = data[p.paragraph(data):] } p.nesting-- } -func (p *parser) isPrefixHeader(data []byte) bool { +func (p *Markdown) addBlock(typ NodeType, content []byte) *Node { + p.closeUnmatchedBlocks() + container := p.addChild(typ, 0) + container.content = content + return container +} + +func (p *Markdown) isPrefixHeading(data []byte) bool { if data[0] != '#' { return false } - if p.flags&EXTENSION_SPACE_HEADERS != 0 { + if p.extensions&SpaceHeadings != 0 { level := 0 - for level < 6 && data[level] == '#' { + for level < 6 && level < len(data) && data[level] == '#' { level++ } - if data[level] != ' ' { + if level == len(data) || data[level] != ' ' { return false } } return true } -func (p *parser) prefixHeader(out *bytes.Buffer, data []byte) int { +func (p *Markdown) prefixHeading(data []byte) int { level := 0 - for level < 6 && data[level] == '#' { + for level < 6 && level < len(data) && data[level] == '#' { level++ } i := skipChar(data, level, ' ') end := skipUntilChar(data, i, '\n') skip := end id := "" - if p.flags&EXTENSION_HEADER_IDS != 0 { + if p.extensions&HeadingIDs != 0 { j, k := 0, 0 - // find start/end of header id + // find start/end of heading id for j = i; j < end-1 && (data[j] != '{' || data[j+1] != '#'); j++ { } for k = j + 1; k < end && data[k] != '}'; k++ { } - // extract header id iff found + // extract heading id iff found if j < end && k < end { id = string(data[j+2 : k]) end = j @@ -242,45 +257,41 @@ func (p *parser) prefixHeader(out *bytes.Buffer, data []byte) int { end-- } if end > i { - if id == "" && p.flags&EXTENSION_AUTO_HEADER_IDS != 0 { - id = SanitizedAnchorName(string(data[i:end])) + if id == "" && p.extensions&AutoHeadingIDs != 0 { + id = sanitized_anchor_name.Create(string(data[i:end])) } - work := func() bool { - p.inline(out, data[i:end]) - return true - } - p.r.Header(out, work, level, id) + block := p.addBlock(Heading, data[i:end]) + block.HeadingID = id + block.Level = level } return skip } -func (p *parser) isUnderlinedHeader(data []byte) int { - // test of level 1 header +func (p *Markdown) isUnderlinedHeading(data []byte) int { + // test of level 1 heading if data[0] == '=' { i := skipChar(data, 1, '=') i = skipChar(data, i, ' ') - if data[i] == '\n' { + if i < len(data) && data[i] == '\n' { return 1 - } else { - return 0 } + return 0 } - // test of level 2 header + // test of level 2 heading if data[0] == '-' { i := skipChar(data, 1, '-') i = skipChar(data, i, ' ') - if data[i] == '\n' { + if i < len(data) && data[i] == '\n' { return 2 - } else { - return 0 } + return 0 } return 0 } -func (p *parser) titleBlock(out *bytes.Buffer, data []byte, doRender bool) int { +func (p *Markdown) titleBlock(data []byte, doRender bool) int { if data[0] != '%' { return 0 } @@ -294,12 +305,17 @@ func (p *parser) titleBlock(out *bytes.Buffer, data []byte, doRender bool) int { } data = bytes.Join(splitData[0:i], []byte("\n")) - p.r.TitleBlock(out, data) - - return len(data) + consumed := len(data) + data = bytes.TrimPrefix(data, []byte("% ")) + data = bytes.Replace(data, []byte("\n% "), []byte("\n"), -1) + block := p.addBlock(Heading, data) + block.Level = 1 + block.IsTitleblock = true + + return consumed } -func (p *parser) html(out *bytes.Buffer, data []byte, doRender bool) int { +func (p *Markdown) html(data []byte, doRender bool) int { var i, j int // identify the opening tag @@ -311,17 +327,12 @@ func (p *parser) html(out *bytes.Buffer, data []byte, doRender bool) int { // handle special cases if !tagfound { // check for an HTML comment - if size := p.htmlComment(out, data, doRender); size > 0 { + if size := p.htmlComment(data, doRender); size > 0 { return size } // check for an
tag - if size := p.htmlHr(out, data, doRender); size > 0 { - return size - } - - // check for HTML CDATA - if size := p.htmlCDATA(out, data, doRender); size > 0 { + if size := p.htmlHr(data, doRender); size > 0 { return size } @@ -396,60 +407,42 @@ func (p *parser) html(out *bytes.Buffer, data []byte, doRender bool) int { for end > 0 && data[end-1] == '\n' { end-- } - p.r.BlockHtml(out, data[:end]) + finalizeHTMLBlock(p.addBlock(HTMLBlock, data[:end])) } return i } -func (p *parser) renderHTMLBlock(out *bytes.Buffer, data []byte, start int, doRender bool) int { - // html block needs to end with a blank line - if i := p.isEmpty(data[start:]); i > 0 { - size := start + i +func finalizeHTMLBlock(block *Node) { + block.Literal = block.content + block.content = nil +} + +// HTML comment, lax form +func (p *Markdown) htmlComment(data []byte, doRender bool) int { + i := p.inlineHTMLComment(data) + // needs to end with a blank line + if j := p.isEmpty(data[i:]); j > 0 { + size := i + j if doRender { // trim trailing newlines end := size for end > 0 && data[end-1] == '\n' { end-- } - p.r.BlockHtml(out, data[:end]) + block := p.addBlock(HTMLBlock, data[:end]) + finalizeHTMLBlock(block) } return size } return 0 } -// HTML comment, lax form -func (p *parser) htmlComment(out *bytes.Buffer, data []byte, doRender bool) int { - i := p.inlineHTMLComment(out, data) - return p.renderHTMLBlock(out, data, i, doRender) -} - -// HTML CDATA section -func (p *parser) htmlCDATA(out *bytes.Buffer, data []byte, doRender bool) int { - const cdataTag = "') { - i++ - } - i++ - // no end-of-comment marker - if i >= len(data) { +// HR, which is the only self-closing block tag considered +func (p *Markdown) htmlHr(data []byte, doRender bool) int { + if len(data) < 4 { return 0 } - return p.renderHTMLBlock(out, data, i, doRender) -} - -// HR, which is the only self-closing block tag considered -func (p *parser) htmlHr(out *bytes.Buffer, data []byte, doRender bool) int { if data[0] != '<' || (data[1] != 'h' && data[1] != 'H') || (data[2] != 'r' && data[2] != 'R') { return 0 } @@ -457,22 +450,31 @@ func (p *parser) htmlHr(out *bytes.Buffer, data []byte, doRender bool) int { // not an
tag after all; at least not a valid one return 0 } - i := 3 - for data[i] != '>' && data[i] != '\n' { + for i < len(data) && data[i] != '>' && data[i] != '\n' { i++ } - - if data[i] == '>' { - return p.renderHTMLBlock(out, data, i+1, doRender) + if i < len(data) && data[i] == '>' { + i++ + if j := p.isEmpty(data[i:]); j > 0 { + size := i + j + if doRender { + // trim newlines + end := size + for end > 0 && data[end-1] == '\n' { + end-- + } + finalizeHTMLBlock(p.addBlock(HTMLBlock, data[:end])) + } + return size + } } - return 0 } -func (p *parser) htmlFindTag(data []byte) (string, bool) { +func (p *Markdown) htmlFindTag(data []byte) (string, bool) { i := 0 - for isalnum(data[i]) { + for i < len(data) && isalnum(data[i]) { i++ } key := string(data[:i]) @@ -482,9 +484,11 @@ func (p *parser) htmlFindTag(data []byte) (string, bool) { return "", false } -func (p *parser) htmlFindEnd(tag string, data []byte) int { +func (p *Markdown) htmlFindEnd(tag string, data []byte) int { // assume data[0] == '<' && data[1] == '/' already tested - + if tag == "hr" { + return 2 + } // check if tag is a match closetag := []byte("") if !bytes.HasPrefix(data, closetag) { @@ -504,7 +508,7 @@ func (p *parser) htmlFindEnd(tag string, data []byte) int { return i } - if p.flags&EXTENSION_LAX_HTML_BLOCKS != 0 { + if p.extensions&LaxHTMLBlocks != 0 { return i } if skip = p.isEmpty(data[i:]); skip == 0 { @@ -515,7 +519,7 @@ func (p *parser) htmlFindEnd(tag string, data []byte) int { return i + skip } -func (*parser) isEmpty(data []byte) int { +func (*Markdown) isEmpty(data []byte) int { // it is okay to call isEmpty on an empty buffer if len(data) == 0 { return 0 @@ -527,10 +531,13 @@ func (*parser) isEmpty(data []byte) int { return 0 } } - return i + 1 + if i < len(data) && data[i] == '\n' { + i++ + } + return i } -func (*parser) isHRule(data []byte) bool { +func (*Markdown) isHRule(data []byte) bool { i := 0 // skip up to three spaces @@ -546,7 +553,7 @@ func (*parser) isHRule(data []byte) bool { // the whole line must be the char or whitespace n := 0 - for data[i] != '\n' { + for i < len(data) && data[i] != '\n' { switch { case data[i] == c: n++ @@ -562,8 +569,7 @@ func (*parser) isHRule(data []byte) bool { // isFenceLine checks if there's a fence line (e.g., ``` or ``` go) at the beginning of data, // and returns the end index if so, or 0 otherwise. It also returns the marker found. // If syntax is not nil, it gets set to the syntax specified in the fence line. -// A final newline is mandatory to recognize the fence line, unless newlineOptional is true. -func isFenceLine(data []byte, info *string, oldmarker string, newlineOptional bool) (end int, marker string) { +func isFenceLine(data []byte, syntax *string, oldmarker string) (end int, marker string) { i, size := 0, 0 // skip up to three spaces @@ -599,26 +605,26 @@ func isFenceLine(data []byte, info *string, oldmarker string, newlineOptional bo } // TODO(shurcooL): It's probably a good idea to simplify the 2 code paths here - // into one, always get the info string, and discard it if the caller doesn't care. - if info != nil { - infoLength := 0 + // into one, always get the syntax, and discard it if the caller doesn't care. + if syntax != nil { + syn := 0 i = skipChar(data, i, ' ') if i >= len(data) { - if newlineOptional && i == len(data) { + if i == len(data) { return i, marker } return 0, "" } - infoStart := i + syntaxStart := i if data[i] == '{' { i++ - infoStart++ + syntaxStart++ for i < len(data) && data[i] != '}' && data[i] != '\n' { - infoLength++ + syn++ i++ } @@ -628,55 +634,55 @@ func isFenceLine(data []byte, info *string, oldmarker string, newlineOptional bo // strip all whitespace at the beginning and the end // of the {} block - for infoLength > 0 && isspace(data[infoStart]) { - infoStart++ - infoLength-- + for syn > 0 && isspace(data[syntaxStart]) { + syntaxStart++ + syn-- } - for infoLength > 0 && isspace(data[infoStart+infoLength-1]) { - infoLength-- + for syn > 0 && isspace(data[syntaxStart+syn-1]) { + syn-- } i++ } else { - for i < len(data) && !isverticalspace(data[i]) { - infoLength++ + for i < len(data) && !isspace(data[i]) { + syn++ i++ } } - *info = strings.TrimSpace(string(data[infoStart : infoStart+infoLength])) + *syntax = string(data[syntaxStart : syntaxStart+syn]) } i = skipChar(data, i, ' ') if i >= len(data) || data[i] != '\n' { - if newlineOptional && i == len(data) { + if i == len(data) { return i, marker } return 0, "" } - return i + 1, marker // Take newline into account. } // fencedCodeBlock returns the end index if data contains a fenced code block at the beginning, // or 0 otherwise. It writes to out if doRender is true, otherwise it has no side effects. // If doRender is true, a final newline is mandatory to recognize the fenced code block. -func (p *parser) fencedCodeBlock(out *bytes.Buffer, data []byte, doRender bool) int { - var infoString string - beg, marker := isFenceLine(data, &infoString, "", false) +func (p *Markdown) fencedCodeBlock(data []byte, doRender bool) int { + var syntax string + beg, marker := isFenceLine(data, &syntax, "") if beg == 0 || beg >= len(data) { return 0 } var work bytes.Buffer + work.Write([]byte(syntax)) + work.WriteByte('\n') for { // safe to assume beg < len(data) // check for the end of the code block - newlineOptional := !doRender - fenceEnd, _ := isFenceLine(data[beg:], nil, marker, newlineOptional) + fenceEnd, _ := isFenceLine(data[beg:], nil, marker) if fenceEnd != 0 { beg += fenceEnd break @@ -698,24 +704,55 @@ func (p *parser) fencedCodeBlock(out *bytes.Buffer, data []byte, doRender bool) } if doRender { - p.r.BlockCode(out, work.Bytes(), infoString) + block := p.addBlock(CodeBlock, work.Bytes()) // TODO: get rid of temp buffer + block.IsFenced = true + finalizeCodeBlock(block) } return beg } -func (p *parser) table(out *bytes.Buffer, data []byte) int { - var header bytes.Buffer - i, columns := p.tableHeader(&header, data) +func unescapeChar(str []byte) []byte { + if str[0] == '\\' { + return []byte{str[1]} + } + return []byte(html.UnescapeString(string(str))) +} + +func unescapeString(str []byte) []byte { + if reBackslashOrAmp.Match(str) { + return reEntityOrEscapedChar.ReplaceAllFunc(str, unescapeChar) + } + return str +} + +func finalizeCodeBlock(block *Node) { + if block.IsFenced { + newlinePos := bytes.IndexByte(block.content, '\n') + firstLine := block.content[:newlinePos] + rest := block.content[newlinePos+1:] + block.Info = unescapeString(bytes.Trim(firstLine, "\n")) + block.Literal = rest + } else { + block.Literal = block.content + } + block.content = nil +} + +func (p *Markdown) table(data []byte) int { + table := p.addBlock(Table, nil) + i, columns := p.tableHeader(data) if i == 0 { + p.tip = table.Parent + table.Unlink() return 0 } - var body bytes.Buffer + p.addBlock(TableBody, nil) for i < len(data) { pipes, rowStart := 0, i - for ; data[i] != '\n'; i++ { + for ; i < len(data) && data[i] != '\n'; i++ { if data[i] == '|' { pipes++ } @@ -727,12 +764,12 @@ func (p *parser) table(out *bytes.Buffer, data []byte) int { } // include the newline in data sent to tableRow - i++ - p.tableRow(&body, data[rowStart:i], columns, false) + if i < len(data) && data[i] == '\n' { + i++ + } + p.tableRow(data[rowStart:i], columns, false) } - p.r.Table(out, header.Bytes(), body.Bytes(), columns) - return i } @@ -745,10 +782,10 @@ func isBackslashEscaped(data []byte, i int) bool { return backslashes&1 == 1 } -func (p *parser) tableHeader(out *bytes.Buffer, data []byte) (size int, columns []int) { +func (p *Markdown) tableHeader(data []byte) (size int, columns []CellAlignFlags) { i := 0 colCount := 1 - for i = 0; data[i] != '\n'; i++ { + for i = 0; i < len(data) && data[i] != '\n'; i++ { if data[i] == '|' && !isBackslashEscaped(data, i) { colCount++ } @@ -760,7 +797,11 @@ func (p *parser) tableHeader(out *bytes.Buffer, data []byte) (size int, columns } // include the newline in the data sent to tableRow - header := data[:i+1] + j := i + if j < len(data) && data[j] == '\n' { + j++ + } + header := data[:j] // column count ignores pipes at beginning or end of line if data[0] == '|' { @@ -770,7 +811,7 @@ func (p *parser) tableHeader(out *bytes.Buffer, data []byte) (size int, columns colCount-- } - columns = make([]int, colCount) + columns = make([]CellAlignFlags, colCount) // move on to the header underline i++ @@ -786,27 +827,29 @@ func (p *parser) tableHeader(out *bytes.Buffer, data []byte) (size int, columns // each column header is of form: / *:?-+:? *|/ with # dashes + # colons >= 3 // and trailing | optional on last column col := 0 - for data[i] != '\n' { + for i < len(data) && data[i] != '\n' { dashes := 0 if data[i] == ':' { i++ - columns[col] |= TABLE_ALIGNMENT_LEFT + columns[col] |= TableAlignmentLeft dashes++ } - for data[i] == '-' { + for i < len(data) && data[i] == '-' { i++ dashes++ } - if data[i] == ':' { + if i < len(data) && data[i] == ':' { i++ - columns[col] |= TABLE_ALIGNMENT_RIGHT + columns[col] |= TableAlignmentRight dashes++ } - for data[i] == ' ' { + for i < len(data) && data[i] == ' ' { i++ } - + if i == len(data) { + return + } // end of column test is messy switch { case dashes < 3: @@ -817,12 +860,12 @@ func (p *parser) tableHeader(out *bytes.Buffer, data []byte) (size int, columns // marker found, now skip past trailing whitespace col++ i++ - for data[i] == ' ' { + for i < len(data) && data[i] == ' ' { i++ } // trailing junk found after last column - if col >= colCount && data[i] != '\n' { + if col >= colCount && i < len(data) && data[i] != '\n' { return } @@ -843,27 +886,31 @@ func (p *parser) tableHeader(out *bytes.Buffer, data []byte) (size int, columns return } - p.tableRow(out, header, columns, true) - size = i + 1 + p.addBlock(TableHead, nil) + p.tableRow(header, columns, true) + size = i + if size < len(data) && data[size] == '\n' { + size++ + } return } -func (p *parser) tableRow(out *bytes.Buffer, data []byte, columns []int, header bool) { +func (p *Markdown) tableRow(data []byte, columns []CellAlignFlags, header bool) { + p.addBlock(TableRow, nil) i, col := 0, 0 - var rowWork bytes.Buffer if data[i] == '|' && !isBackslashEscaped(data, i) { i++ } for col = 0; col < len(columns) && i < len(data); col++ { - for data[i] == ' ' { + for i < len(data) && data[i] == ' ' { i++ } cellStart := i - for (data[i] != '|' || isBackslashEscaped(data, i)) && data[i] != '\n' { + for i < len(data) && (data[i] != '|' || isBackslashEscaped(data, i)) && data[i] != '\n' { i++ } @@ -872,42 +919,33 @@ func (p *parser) tableRow(out *bytes.Buffer, data []byte, columns []int, header // skip the end-of-cell marker, possibly taking us past end of buffer i++ - for cellEnd > cellStart && data[cellEnd-1] == ' ' { + for cellEnd > cellStart && cellEnd-1 < len(data) && data[cellEnd-1] == ' ' { cellEnd-- } - var cellWork bytes.Buffer - p.inline(&cellWork, data[cellStart:cellEnd]) - - if header { - p.r.TableHeaderCell(&rowWork, cellWork.Bytes(), columns[col]) - } else { - p.r.TableCell(&rowWork, cellWork.Bytes(), columns[col]) - } + cell := p.addBlock(TableCell, data[cellStart:cellEnd]) + cell.IsHeader = header + cell.Align = columns[col] } // pad it out with empty columns to get the right number for ; col < len(columns); col++ { - if header { - p.r.TableHeaderCell(&rowWork, nil, columns[col]) - } else { - p.r.TableCell(&rowWork, nil, columns[col]) - } + cell := p.addBlock(TableCell, nil) + cell.IsHeader = header + cell.Align = columns[col] } // silently ignore rows with too many cells - - p.r.TableRow(out, rowWork.Bytes()) } // returns blockquote prefix length -func (p *parser) quotePrefix(data []byte) int { +func (p *Markdown) quotePrefix(data []byte) int { i := 0 - for i < 3 && data[i] == ' ' { + for i < 3 && i < len(data) && data[i] == ' ' { i++ } - if data[i] == '>' { - if data[i+1] == ' ' { + if i < len(data) && data[i] == '>' { + if i+1 < len(data) && data[i+1] == ' ' { return i + 2 } return i + 1 @@ -917,7 +955,7 @@ func (p *parser) quotePrefix(data []byte) int { // blockquote ends with at least one blank line // followed by something without a blockquote prefix -func (p *parser) terminateBlockquote(data []byte, beg, end int) bool { +func (p *Markdown) terminateBlockquote(data []byte, beg, end int) bool { if p.isEmpty(data[beg:]) <= 0 { return false } @@ -928,7 +966,8 @@ func (p *parser) terminateBlockquote(data []byte, beg, end int) bool { } // parse a blockquote fragment -func (p *parser) quote(out *bytes.Buffer, data []byte) int { +func (p *Markdown) quote(data []byte) int { + block := p.addBlock(BlockQuote, nil) var raw bytes.Buffer beg, end := 0, 0 for beg < len(data) { @@ -936,9 +975,9 @@ func (p *parser) quote(out *bytes.Buffer, data []byte) int { // Step over whole lines, collecting them. While doing that, check for // fenced code and if one's found, incorporate it altogether, // irregardless of any contents inside it - for data[end] != '\n' { - if p.flags&EXTENSION_FENCED_CODE != 0 { - if i := p.fencedCodeBlock(out, data[end:], false); i > 0 { + for end < len(data) && data[end] != '\n' { + if p.extensions&FencedCode != 0 { + if i := p.fencedCodeBlock(data[end:], false); i > 0 { // -1 to compensate for the extra end++ after the loop: end += i - 1 break @@ -946,44 +985,47 @@ func (p *parser) quote(out *bytes.Buffer, data []byte) int { } end++ } - end++ - + if end < len(data) && data[end] == '\n' { + end++ + } if pre := p.quotePrefix(data[beg:]); pre > 0 { // skip the prefix beg += pre } else if p.terminateBlockquote(data, beg, end) { break } - // this line is part of the blockquote raw.Write(data[beg:end]) beg = end } - - var cooked bytes.Buffer - p.block(&cooked, raw.Bytes()) - p.r.BlockQuote(out, cooked.Bytes()) + p.block(raw.Bytes()) + p.finalize(block) return end } // returns prefix length for block code -func (p *parser) codePrefix(data []byte) int { - if data[0] == ' ' && data[1] == ' ' && data[2] == ' ' && data[3] == ' ' { +func (p *Markdown) codePrefix(data []byte) int { + if len(data) >= 1 && data[0] == '\t' { + return 1 + } + if len(data) >= 4 && data[0] == ' ' && data[1] == ' ' && data[2] == ' ' && data[3] == ' ' { return 4 } return 0 } -func (p *parser) code(out *bytes.Buffer, data []byte) int { +func (p *Markdown) code(data []byte) int { var work bytes.Buffer i := 0 for i < len(data) { beg := i - for data[i] != '\n' { + for i < len(data) && data[i] != '\n' { + i++ + } + if i < len(data) && data[i] == '\n' { i++ } - i++ blankline := p.isEmpty(data[beg:i]) > 0 if pre := p.codePrefix(data[beg:i]); pre > 0 { @@ -994,7 +1036,7 @@ func (p *parser) code(out *bytes.Buffer, data []byte) int { break } - // verbatim copy to the working buffeu + // verbatim copy to the working buffer if blankline { work.WriteByte('\n') } else { @@ -1014,122 +1056,183 @@ func (p *parser) code(out *bytes.Buffer, data []byte) int { work.WriteByte('\n') - p.r.BlockCode(out, work.Bytes(), "") + block := p.addBlock(CodeBlock, work.Bytes()) // TODO: get rid of temp buffer + block.IsFenced = false + finalizeCodeBlock(block) return i } // returns unordered list item prefix -func (p *parser) uliPrefix(data []byte) int { +func (p *Markdown) uliPrefix(data []byte) int { i := 0 - // start with up to 3 spaces - for i < 3 && data[i] == ' ' { + for i < len(data) && i < 3 && data[i] == ' ' { i++ } - - // need a *, +, or - followed by a space + if i >= len(data)-1 { + return 0 + } + // need one of {'*', '+', '-'} followed by a space or a tab if (data[i] != '*' && data[i] != '+' && data[i] != '-') || - data[i+1] != ' ' { + (data[i+1] != ' ' && data[i+1] != '\t') { return 0 } return i + 2 } // returns ordered list item prefix -func (p *parser) oliPrefix(data []byte) int { +func (p *Markdown) oliPrefix(data []byte) int { i := 0 // start with up to 3 spaces - for i < 3 && data[i] == ' ' { + for i < 3 && i < len(data) && data[i] == ' ' { i++ } // count the digits start := i - for data[i] >= '0' && data[i] <= '9' { + for i < len(data) && data[i] >= '0' && data[i] <= '9' { i++ } + if start == i || i >= len(data)-1 { + return 0 + } - // we need >= 1 digits followed by a dot and a space - if start == i || data[i] != '.' || data[i+1] != ' ' { + // we need >= 1 digits followed by a dot and a space or a tab + if data[i] != '.' || !(data[i+1] == ' ' || data[i+1] == '\t') { return 0 } return i + 2 } // returns definition list item prefix -func (p *parser) dliPrefix(data []byte) int { +func (p *Markdown) dliPrefix(data []byte) int { + if len(data) < 2 { + return 0 + } i := 0 - - // need a : followed by a spaces - if data[i] != ':' || data[i+1] != ' ' { + // need a ':' followed by a space or a tab + if data[i] != ':' || !(data[i+1] == ' ' || data[i+1] == '\t') { return 0 } - for data[i] == ' ' { + for i < len(data) && data[i] == ' ' { i++ } return i + 2 } // parse ordered or unordered list block -func (p *parser) list(out *bytes.Buffer, data []byte, flags int) int { +func (p *Markdown) list(data []byte, flags ListType) int { i := 0 - flags |= LIST_ITEM_BEGINNING_OF_LIST - work := func() bool { - for i < len(data) { - skip := p.listItem(out, data[i:], &flags) - i += skip + flags |= ListItemBeginningOfList + block := p.addBlock(List, nil) + block.ListFlags = flags + block.Tight = true - if skip == 0 || flags&LIST_ITEM_END_OF_LIST != 0 { - break - } - flags &= ^LIST_ITEM_BEGINNING_OF_LIST + for i < len(data) { + skip := p.listItem(data[i:], &flags) + if flags&ListItemContainsBlock != 0 { + block.ListData.Tight = false } - return true + i += skip + if skip == 0 || flags&ListItemEndOfList != 0 { + break + } + flags &= ^ListItemBeginningOfList } - p.r.List(out, work, flags) + above := block.Parent + finalizeList(block) + p.tip = above return i } +// Returns true if block ends with a blank line, descending if needed +// into lists and sublists. +func endsWithBlankLine(block *Node) bool { + // TODO: figure this out. Always false now. + for block != nil { + //if block.lastLineBlank { + //return true + //} + t := block.Type + if t == List || t == Item { + block = block.LastChild + } else { + break + } + } + return false +} + +func finalizeList(block *Node) { + block.open = false + item := block.FirstChild + for item != nil { + // check for non-final list item ending with blank line: + if endsWithBlankLine(item) && item.Next != nil { + block.ListData.Tight = false + break + } + // recurse into children of list item, to see if there are spaces + // between any of them: + subItem := item.FirstChild + for subItem != nil { + if endsWithBlankLine(subItem) && (item.Next != nil || subItem.Next != nil) { + block.ListData.Tight = false + break + } + subItem = subItem.Next + } + item = item.Next + } +} + // Parse a single list item. // Assumes initial prefix is already removed if this is a sublist. -func (p *parser) listItem(out *bytes.Buffer, data []byte, flags *int) int { +func (p *Markdown) listItem(data []byte, flags *ListType) int { // keep track of the indentation of the first line itemIndent := 0 - for itemIndent < 3 && data[itemIndent] == ' ' { - itemIndent++ + if data[0] == '\t' { + itemIndent += 4 + } else { + for itemIndent < 3 && data[itemIndent] == ' ' { + itemIndent++ + } } + var bulletChar byte = '*' i := p.uliPrefix(data) if i == 0 { i = p.oliPrefix(data) + } else { + bulletChar = data[i-2] } if i == 0 { i = p.dliPrefix(data) // reset definition term flag if i > 0 { - *flags &= ^LIST_TYPE_TERM + *flags &= ^ListTypeTerm } } if i == 0 { - // if in defnition list, set term flag and continue - if *flags&LIST_TYPE_DEFINITION != 0 { - *flags |= LIST_TYPE_TERM + // if in definition list, set term flag and continue + if *flags&ListTypeDefinition != 0 { + *flags |= ListTypeTerm } else { return 0 } } // skip leading whitespace on first line - for data[i] == ' ' { + for i < len(data) && data[i] == ' ' { i++ } // find the end of the line line := i - for i > 0 && data[i-1] != '\n' { + for i > 0 && i < len(data) && data[i-1] != '\n' { i++ } @@ -1149,7 +1252,7 @@ gatherlines: i++ // find the end of this line - for data[i-1] != '\n' { + for i < len(data) && data[i-1] != '\n' { i++ } @@ -1157,18 +1260,24 @@ gatherlines: // and move on to the next line if p.isEmpty(data[line:i]) > 0 { containsBlankLine = true - raw.Write(data[line:i]) line = i continue } // calculate the indentation indent := 0 - for indent < 4 && line+indent < i && data[line+indent] == ' ' { - indent++ + indentIndex := 0 + if data[line] == '\t' { + indentIndex++ + indent += 4 + } else { + for indent < 4 && line+indent < i && data[line+indent] == ' ' { + indent++ + indentIndex++ + } } - chunk := data[line+indent : i] + chunk := data[line+indentIndex : i] // evaluate how this line fits in switch { @@ -1178,15 +1287,7 @@ gatherlines: p.dliPrefix(chunk) > 0: if containsBlankLine { - // end the list if the type changed after a blank line - if indent <= itemIndent && - ((*flags&LIST_TYPE_ORDERED != 0 && p.uliPrefix(chunk) > 0) || - (*flags&LIST_TYPE_ORDERED == 0 && p.oliPrefix(chunk) > 0)) { - - *flags |= LIST_ITEM_END_OF_LIST - break gatherlines - } - *flags |= LIST_ITEM_CONTAINS_BLOCK + *flags |= ListItemContainsBlock } // to be a nested list, it must be indented more @@ -1200,93 +1301,89 @@ gatherlines: sublist = raw.Len() } - // is this a nested prefix header? - case p.isPrefixHeader(chunk): - // if the header is not indented, it is not nested in the list + // is this a nested prefix heading? + case p.isPrefixHeading(chunk): + // if the heading is not indented, it is not nested in the list // and thus ends the list if containsBlankLine && indent < 4 { - *flags |= LIST_ITEM_END_OF_LIST + *flags |= ListItemEndOfList break gatherlines } - *flags |= LIST_ITEM_CONTAINS_BLOCK + *flags |= ListItemContainsBlock // anything following an empty line is only part // of this item if it is indented 4 spaces // (regardless of the indentation of the beginning of the item) case containsBlankLine && indent < 4: - if *flags&LIST_TYPE_DEFINITION != 0 && i < len(data)-1 { + if *flags&ListTypeDefinition != 0 && i < len(data)-1 { // is the next item still a part of this list? next := i - for data[next] != '\n' { + for next < len(data) && data[next] != '\n' { next++ } for next < len(data)-1 && data[next] == '\n' { next++ } if i < len(data)-1 && data[i] != ':' && data[next] != ':' { - *flags |= LIST_ITEM_END_OF_LIST + *flags |= ListItemEndOfList } } else { - *flags |= LIST_ITEM_END_OF_LIST + *flags |= ListItemEndOfList } break gatherlines // a blank line means this should be parsed as a block case containsBlankLine: - *flags |= LIST_ITEM_CONTAINS_BLOCK + raw.WriteByte('\n') + *flags |= ListItemContainsBlock } - containsBlankLine = false + // if this line was preceded by one or more blanks, + // re-introduce the blank into the buffer + if containsBlankLine { + containsBlankLine = false + raw.WriteByte('\n') + } // add the line into the working buffer without prefix - raw.Write(data[line+indent : i]) + raw.Write(data[line+indentIndex : i]) line = i } - // If reached end of data, the Renderer.ListItem call we're going to make below - // is definitely the last in the list. - if line >= len(data) { - *flags |= LIST_ITEM_END_OF_LIST - } - rawBytes := raw.Bytes() + block := p.addBlock(Item, nil) + block.ListFlags = *flags + block.Tight = false + block.BulletChar = bulletChar + block.Delimiter = '.' // Only '.' is possible in Markdown, but ')' will also be possible in CommonMark + // render the contents of the list item - var cooked bytes.Buffer - if *flags&LIST_ITEM_CONTAINS_BLOCK != 0 && *flags&LIST_TYPE_TERM == 0 { + if *flags&ListItemContainsBlock != 0 && *flags&ListTypeTerm == 0 { // intermediate render of block item, except for definition term if sublist > 0 { - p.block(&cooked, rawBytes[:sublist]) - p.block(&cooked, rawBytes[sublist:]) + p.block(rawBytes[:sublist]) + p.block(rawBytes[sublist:]) } else { - p.block(&cooked, rawBytes) + p.block(rawBytes) } } else { // intermediate render of inline item if sublist > 0 { - p.inline(&cooked, rawBytes[:sublist]) - p.block(&cooked, rawBytes[sublist:]) + child := p.addChild(Paragraph, 0) + child.content = rawBytes[:sublist] + p.block(rawBytes[sublist:]) } else { - p.inline(&cooked, rawBytes) + child := p.addChild(Paragraph, 0) + child.content = rawBytes } } - - // render the actual list item - cookedBytes := cooked.Bytes() - parsedEnd := len(cookedBytes) - - // strip trailing newlines - for parsedEnd > 0 && cookedBytes[parsedEnd-1] == '\n' { - parsedEnd-- - } - p.r.ListItem(out, cookedBytes[:parsedEnd], *flags) - return line } // render a single paragraph that has already been parsed out -func (p *parser) renderParagraph(out *bytes.Buffer, data []byte) { +func (p *Markdown) renderParagraph(data []byte) { if len(data) == 0 { return } @@ -1297,27 +1394,29 @@ func (p *parser) renderParagraph(out *bytes.Buffer, data []byte) { beg++ } + end := len(data) // trim trailing newline - end := len(data) - 1 + if data[len(data)-1] == '\n' { + end-- + } // trim trailing spaces for end > beg && data[end-1] == ' ' { end-- } - work := func() bool { - p.inline(out, data[beg:end]) - return true - } - p.r.Paragraph(out, work) + p.addBlock(Paragraph, data[beg:end]) } -func (p *parser) paragraph(out *bytes.Buffer, data []byte) int { +func (p *Markdown) paragraph(data []byte) int { // prev: index of 1st char of previous line // line: index of 1st char of current line // i: index of cursor/end of current line var prev, line, i int - + tabSize := TabSizeDefault + if p.extensions&TabSizeEight != 0 { + tabSize = TabSizeDouble + } // keep going until we find something to mark the end of the paragraph for i < len(data) { // mark the beginning of the current line @@ -1325,24 +1424,32 @@ func (p *parser) paragraph(out *bytes.Buffer, data []byte) int { current := data[i:] line = i + // did we find a reference or a footnote? If so, end a paragraph + // preceding it and report that we have consumed up to the end of that + // reference: + if refEnd := isReference(p, current, tabSize); refEnd > 0 { + p.renderParagraph(data[:i]) + return i + refEnd + } + // did we find a blank line marking the end of the paragraph? if n := p.isEmpty(current); n > 0 { // did this blank line followed by a definition list item? - if p.flags&EXTENSION_DEFINITION_LISTS != 0 { + if p.extensions&DefinitionLists != 0 { if i < len(data)-1 && data[i+1] == ':' { - return p.list(out, data[prev:], LIST_TYPE_DEFINITION) + return p.list(data[prev:], ListTypeDefinition) } } - p.renderParagraph(out, data[:i]) + p.renderParagraph(data[:i]) return i + n } - // an underline under some text marks a header, so our paragraph ended on prev line + // an underline under some text marks a heading, so our paragraph ended on prev line if i > 0 { - if level := p.isUnderlinedHeader(current); level > 0 { + if level := p.isUnderlinedHeading(current); level > 0 { // render the paragraph - p.renderParagraph(out, data[:prev]) + p.renderParagraph(data[:prev]) // ignore leading and trailing whitespace eol := i - 1 @@ -1353,24 +1460,17 @@ func (p *parser) paragraph(out *bytes.Buffer, data []byte) int { eol-- } - // render the header - // this ugly double closure avoids forcing variables onto the heap - work := func(o *bytes.Buffer, pp *parser, d []byte) func() bool { - return func() bool { - pp.inline(o, d) - return true - } - }(out, p, data[prev:eol]) - id := "" - if p.flags&EXTENSION_AUTO_HEADER_IDS != 0 { - id = SanitizedAnchorName(string(data[prev:eol])) + if p.extensions&AutoHeadingIDs != 0 { + id = sanitized_anchor_name.Create(string(data[prev:eol])) } - p.r.Header(out, work, level, id) + block := p.addBlock(Heading, data[prev:eol]) + block.Level = level + block.HeadingID = id // find the end of the underline - for data[i] != '\n' { + for i < len(data) && data[i] != '\n' { i++ } return i @@ -1378,74 +1478,72 @@ func (p *parser) paragraph(out *bytes.Buffer, data []byte) int { } // if the next line starts a block of HTML, then the paragraph ends here - if p.flags&EXTENSION_LAX_HTML_BLOCKS != 0 { - if data[i] == '<' && p.html(out, current, false) > 0 { + if p.extensions&LaxHTMLBlocks != 0 { + if data[i] == '<' && p.html(current, false) > 0 { // rewind to before the HTML block - p.renderParagraph(out, data[:i]) + p.renderParagraph(data[:i]) return i } } - // if there's a prefixed header or a horizontal rule after this, paragraph is over - if p.isPrefixHeader(current) || p.isHRule(current) { - p.renderParagraph(out, data[:i]) + // if there's a prefixed heading or a horizontal rule after this, paragraph is over + if p.isPrefixHeading(current) || p.isHRule(current) { + p.renderParagraph(data[:i]) return i } // if there's a fenced code block, paragraph is over - if p.flags&EXTENSION_FENCED_CODE != 0 { - if p.fencedCodeBlock(out, current, false) > 0 { - p.renderParagraph(out, data[:i]) + if p.extensions&FencedCode != 0 { + if p.fencedCodeBlock(current, false) > 0 { + p.renderParagraph(data[:i]) return i } } // if there's a definition list item, prev line is a definition term - if p.flags&EXTENSION_DEFINITION_LISTS != 0 { + if p.extensions&DefinitionLists != 0 { if p.dliPrefix(current) != 0 { - return p.list(out, data[prev:], LIST_TYPE_DEFINITION) + ret := p.list(data[prev:], ListTypeDefinition) + return ret } } // if there's a list after this, paragraph is over - if p.flags&EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK != 0 { + if p.extensions&NoEmptyLineBeforeBlock != 0 { if p.uliPrefix(current) != 0 || p.oliPrefix(current) != 0 || p.quotePrefix(current) != 0 || p.codePrefix(current) != 0 { - p.renderParagraph(out, data[:i]) + p.renderParagraph(data[:i]) return i } } // otherwise, scan to the beginning of the next line - for data[i] != '\n' { - i++ + nl := bytes.IndexByte(data[i:], '\n') + if nl >= 0 { + i += nl + 1 + } else { + i += len(data[i:]) } - i++ } - p.renderParagraph(out, data[:i]) + p.renderParagraph(data[:i]) return i } -// SanitizedAnchorName returns a sanitized anchor name for the given text. -// -// It implements the algorithm specified in the package comment. -func SanitizedAnchorName(text string) string { - var anchorName []rune - futureDash := false - for _, r := range text { - switch { - case unicode.IsLetter(r) || unicode.IsNumber(r): - if futureDash && len(anchorName) > 0 { - anchorName = append(anchorName, '-') - } - futureDash = false - anchorName = append(anchorName, unicode.ToLower(r)) - default: - futureDash = true - } +func skipChar(data []byte, start int, char byte) int { + i := start + for i < len(data) && data[i] == char { + i++ + } + return i +} + +func skipUntilChar(text []byte, start int, char byte) int { + i := start + for i < len(text) && text[i] != char { + i++ } - return string(anchorName) + return i } diff --git a/vendor/github.com/russross/blackfriday/doc.go b/vendor/github.com/russross/blackfriday/doc.go index 9656c42a1916e..5b3fa9876ac8c 100644 --- a/vendor/github.com/russross/blackfriday/doc.go +++ b/vendor/github.com/russross/blackfriday/doc.go @@ -1,32 +1,18 @@ -// Package blackfriday is a Markdown processor. +// Package blackfriday is a markdown processor. // -// It translates plain text with simple formatting rules into HTML or LaTeX. +// It translates plain text with simple formatting rules into an AST, which can +// then be further processed to HTML (provided by Blackfriday itself) or other +// formats (provided by the community). // -// Sanitized Anchor Names +// The simplest way to invoke Blackfriday is to call the Run function. It will +// take a text input and produce a text output in HTML (or other format). // -// Blackfriday includes an algorithm for creating sanitized anchor names -// corresponding to a given input text. This algorithm is used to create -// anchors for headings when EXTENSION_AUTO_HEADER_IDS is enabled. The -// algorithm is specified below, so that other packages can create -// compatible anchor names and links to those anchors. +// A slightly more sophisticated way to use Blackfriday is to create a Markdown +// processor and to call Parse, which returns a syntax tree for the input +// document. You can leverage Blackfriday's parsing for content extraction from +// markdown documents. You can assign a custom renderer and set various options +// to the Markdown processor. // -// The algorithm iterates over the input text, interpreted as UTF-8, -// one Unicode code point (rune) at a time. All runes that are letters (category L) -// or numbers (category N) are considered valid characters. They are mapped to -// lower case, and included in the output. All other runes are considered -// invalid characters. Invalid characters that preceed the first valid character, -// as well as invalid character that follow the last valid character -// are dropped completely. All other sequences of invalid characters -// between two valid characters are replaced with a single dash character '-'. -// -// SanitizedAnchorName exposes this functionality, and can be used to -// create compatible links to the anchor names generated by blackfriday. -// This algorithm is also implemented in a small standalone package at -// github.com/shurcooL/sanitized_anchor_name. It can be useful for clients -// that want a small package and don't need full functionality of blackfriday. +// If you're interested in calling Blackfriday from command line, see +// https://github.com/russross/blackfriday-tool. package blackfriday - -// NOTE: Keep Sanitized Anchor Name algorithm in sync with package -// github.com/shurcooL/sanitized_anchor_name. -// Otherwise, users of sanitized_anchor_name will get anchor names -// that are incompatible with those generated by blackfriday. diff --git a/vendor/github.com/russross/blackfriday/esc.go b/vendor/github.com/russross/blackfriday/esc.go new file mode 100644 index 0000000000000..6385f27cb6a47 --- /dev/null +++ b/vendor/github.com/russross/blackfriday/esc.go @@ -0,0 +1,34 @@ +package blackfriday + +import ( + "html" + "io" +) + +var htmlEscaper = [256][]byte{ + '&': []byte("&"), + '<': []byte("<"), + '>': []byte(">"), + '"': []byte("""), +} + +func escapeHTML(w io.Writer, s []byte) { + var start, end int + for end < len(s) { + escSeq := htmlEscaper[s[end]] + if escSeq != nil { + w.Write(s[start:end]) + w.Write(escSeq) + start = end + 1 + } + end++ + } + if start < len(s) && end <= len(s) { + w.Write(s[start:end]) + } +} + +func escLink(w io.Writer, text []byte) { + unesc := html.UnescapeString(string(text)) + escapeHTML(w, []byte(unesc)) +} diff --git a/vendor/github.com/russross/blackfriday/html.go b/vendor/github.com/russross/blackfriday/html.go index e0a6c69c96d71..25fb185e9db63 100644 --- a/vendor/github.com/russross/blackfriday/html.go +++ b/vendor/github.com/russross/blackfriday/html.go @@ -18,46 +18,62 @@ package blackfriday import ( "bytes" "fmt" + "io" "regexp" - "strconv" "strings" ) -// Html renderer configuration options. +// HTMLFlags control optional behavior of HTML renderer. +type HTMLFlags int + +// HTML renderer configuration options. const ( - HTML_SKIP_HTML = 1 << iota // skip preformatted HTML blocks - HTML_SKIP_STYLE // skip embedded