From 2c1c4c2cca5cf8f6506314fa0edc01ed84fa9509 Mon Sep 17 00:00:00 2001
From: Bobby Brennan ![test] ![test](foo bar) an (: </ 123> (hai). foo bar blockquote\nblockquote continuation bar b ~~~test~ foo\nbar\nbaz foo1\t\nfoo2 quote
` | false
+ XHTMLOutput | bool | whether to output XHTML instead of HTML | false
+
+## Benchmarks
+
+Rendering spec/spec-0.28.txt on a Intel(R) Core(TM) i5-2400 CPU @ 3.10GHz
+
+ BenchmarkRenderSpecNoHTML 100 10254720 ns/op 2998037 B/op 18225 allocs/op
+ BenchmarkRenderSpec 100 10180241 ns/op 2997307 B/op 18214 allocs/op
+ BenchmarkRenderSpecBlackFriday 200 7241749 ns/op 2834340 B/op 17101 allocs/op
+ BenchmarkRenderSpecBlackFriday2 200 7448256 ns/op 2991202 B/op 16705 allocs/op
+
+## See also
+
+https://github.com/jgm/CommonMark — the reference CommonMark implementations in C and JavaScript,
+ also contains the latest spec and an online demo.
+
+http://talk.commonmark.org — the CommonMark forum, a good place to join together the efforts of the developers.
diff --git a/vendor/gitlab.com/golang-commonmark/markdown/align.go b/vendor/gitlab.com/golang-commonmark/markdown/align.go
new file mode 100644
index 000000000..bcb636007
--- /dev/null
+++ b/vendor/gitlab.com/golang-commonmark/markdown/align.go
@@ -0,0 +1,26 @@
+// Copyright 2015 The Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package markdown
+
+type Align byte
+
+const (
+ AlignNone = iota
+ AlignLeft
+ AlignCenter
+ AlignRight
+)
+
+func (a Align) String() string {
+ switch a {
+ case AlignLeft:
+ return "left"
+ case AlignCenter:
+ return "center"
+ case AlignRight:
+ return "right"
+ }
+ return ""
+}
diff --git a/vendor/gitlab.com/golang-commonmark/markdown/autolink.go b/vendor/gitlab.com/golang-commonmark/markdown/autolink.go
new file mode 100644
index 000000000..ab51c71b2
--- /dev/null
+++ b/vendor/gitlab.com/golang-commonmark/markdown/autolink.go
@@ -0,0 +1,70 @@
+// Copyright 2015 The Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package markdown
+
+import (
+ "regexp"
+ "strings"
+)
+
+var (
+ rAutolink = regexp.MustCompile(`^<([a-zA-Z][a-zA-Z0-9+.\-]{1,31}):([^<>\x00-\x20]*)>`)
+ rEmail = regexp.MustCompile(`^<([a-zA-Z0-9.!#$%&'*+/=?^_{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)>`)
+)
+
+func ruleAutolink(s *StateInline, silent bool) bool {
+ pos := s.Pos
+ src := s.Src
+
+ if src[pos] != '<' {
+ return false
+ }
+
+ tail := src[pos:]
+
+ if strings.IndexByte(tail, '>') < 0 {
+ return false
+ }
+
+ link := rAutolink.FindString(tail)
+ if link != "" {
+ link = link[1 : len(link)-1]
+ href := normalizeLink(link)
+ if !validateLink(href) {
+ return false
+ }
+
+ if !silent {
+ s.PushOpeningToken(&LinkOpen{Href: href})
+ s.PushToken(&Text{Content: normalizeLinkText(link)})
+ s.PushClosingToken(&LinkClose{})
+ }
+
+ s.Pos += len(link) + 2
+
+ return true
+ }
+
+ email := rEmail.FindString(tail)
+ if email != "" {
+ email = email[1 : len(email)-1]
+ href := normalizeLink("mailto:" + email)
+ if !validateLink(href) {
+ return false
+ }
+
+ if !silent {
+ s.PushOpeningToken(&LinkOpen{Href: href})
+ s.PushToken(&Text{Content: normalizeLinkText(email)})
+ s.PushClosingToken(&LinkClose{})
+ }
+
+ s.Pos += len(email) + 2
+
+ return true
+ }
+
+ return false
+}
diff --git a/vendor/gitlab.com/golang-commonmark/markdown/backticks.go b/vendor/gitlab.com/golang-commonmark/markdown/backticks.go
new file mode 100644
index 000000000..07f268dcc
--- /dev/null
+++ b/vendor/gitlab.com/golang-commonmark/markdown/backticks.go
@@ -0,0 +1,60 @@
+// Copyright 2015 The Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package markdown
+
+import "strings"
+
+func ruleBackticks(s *StateInline, silent bool) bool {
+ pos := s.Pos
+ src := s.Src
+
+ if src[pos] != '`' {
+ return false
+ }
+
+ start := pos
+ pos++
+ max := s.PosMax
+
+ for pos < max && src[pos] == '`' {
+ pos++
+ }
+
+ marker := src[start:pos]
+
+ matchStart := pos
+ matchEnd := pos
+ for {
+ matchStart = strings.IndexByte(src[matchEnd:], '`')
+ if matchStart == -1 {
+ break
+ }
+ matchStart += matchEnd
+
+ matchEnd = matchStart + 1
+
+ for matchEnd < max && src[matchEnd] == '`' {
+ matchEnd++
+ }
+
+ if matchEnd-matchStart == len(marker) {
+ if !silent {
+ s.PushToken(&CodeInline{
+ Content: normalizeInlineCode(src[pos:matchStart]),
+ })
+ }
+ s.Pos = matchEnd
+ return true
+ }
+ }
+
+ if !silent {
+ s.Pending.WriteString(marker)
+ }
+
+ s.Pos += len(marker)
+
+ return true
+}
diff --git a/vendor/gitlab.com/golang-commonmark/markdown/balance_pairs.go b/vendor/gitlab.com/golang-commonmark/markdown/balance_pairs.go
new file mode 100644
index 000000000..d5830634a
--- /dev/null
+++ b/vendor/gitlab.com/golang-commonmark/markdown/balance_pairs.go
@@ -0,0 +1,43 @@
+// Copyright 2015 The Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package markdown
+
+func ruleBalancePairs(s *StateInline) {
+ delimiters := s.Delimiters
+ max := len(delimiters)
+
+ for i := 0; i < max; i++ {
+ lastDelim := delimiters[i]
+
+ if !lastDelim.Close {
+ continue
+ }
+
+ j := i - lastDelim.Jump - 1
+
+ for j >= 0 {
+ currDelim := delimiters[j]
+
+ if currDelim.Open &&
+ currDelim.Marker == lastDelim.Marker &&
+ currDelim.End < 0 &&
+ currDelim.Level == lastDelim.Level {
+ oddMatch := (currDelim.Close || lastDelim.Open) &&
+ currDelim.Length != -1 &&
+ lastDelim.Length != -1 &&
+ (currDelim.Length+lastDelim.Length)%3 == 0
+ if !oddMatch {
+ delimiters[i].Jump = i - j
+ delimiters[i].Open = false
+ delimiters[j].End = i
+ delimiters[j].Jump = 0
+ break
+ }
+ }
+
+ j -= currDelim.Jump + 1
+ }
+ }
+}
diff --git a/vendor/gitlab.com/golang-commonmark/markdown/blockquote.go b/vendor/gitlab.com/golang-commonmark/markdown/blockquote.go
new file mode 100644
index 000000000..f8ad3626b
--- /dev/null
+++ b/vendor/gitlab.com/golang-commonmark/markdown/blockquote.go
@@ -0,0 +1,233 @@
+// Copyright 2015 The Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package markdown
+
+import "unicode/utf8"
+
+var blockquoteTerminatedBy []BlockRule
+
+func ruleBlockQuote(s *StateBlock, startLine, endLine int, silent bool) bool {
+ if s.SCount[startLine]-s.BlkIndent >= 4 {
+ return false
+ }
+
+ pos := s.BMarks[startLine] + s.TShift[startLine]
+ max := s.EMarks[startLine]
+ src := s.Src
+
+ if pos >= max {
+ return false
+ }
+ if src[pos] != '>' {
+ return false
+ }
+ pos++
+
+ if silent {
+ return true
+ }
+
+ initial := s.SCount[startLine] + pos - (s.BMarks[startLine] + s.TShift[startLine])
+ offset := initial
+
+ spaceAfterMarker := false
+ adjustTab := false
+ if pos < max {
+ if src[pos] == ' ' {
+ pos++
+ initial++
+ offset++
+ spaceAfterMarker = true
+ } else if src[pos] == '\t' {
+ spaceAfterMarker = true
+ if (s.BSCount[startLine]+offset)%4 == 3 {
+ pos++
+ initial++
+ offset++
+ } else {
+ adjustTab = true
+ }
+ }
+ }
+
+ oldBMarks := []int{s.BMarks[startLine]}
+ s.BMarks[startLine] = pos
+
+ for pos < max {
+ r, size := utf8.DecodeRuneInString(src[pos:])
+ if runeIsSpace(r) {
+ if r == '\t' {
+ d := 0
+ if adjustTab {
+ d = 1
+ }
+ offset += 4 - (offset+s.BSCount[startLine]+d)%4
+ } else {
+ offset++
+ }
+ } else {
+ break
+ }
+ pos += size
+ }
+
+ oldBSCount := []int{s.BSCount[startLine]}
+ d := 0
+ if spaceAfterMarker {
+ d = 1
+ }
+ s.BSCount[startLine] = s.SCount[startLine] + 1 + d
+
+ lastLineEmpty := pos >= max
+
+ oldSCount := []int{s.SCount[startLine]}
+ s.SCount[startLine] = offset - initial
+
+ oldTShift := []int{s.TShift[startLine]}
+ s.TShift[startLine] = pos - s.BMarks[startLine]
+
+ oldParentType := s.ParentType
+ s.ParentType = ptBlockQuote
+ wasOutdented := false
+
+ oldLineMax := s.LineMax
+
+ nextLine := startLine + 1
+ for ; nextLine < endLine; nextLine++ {
+ if s.SCount[nextLine] < s.BlkIndent {
+ wasOutdented = true
+ }
+ pos = s.BMarks[nextLine] + s.TShift[nextLine]
+ max = s.EMarks[nextLine]
+
+ if pos >= max {
+ break
+ }
+
+ pos++
+ if src[pos-1] == '>' && !wasOutdented {
+ initial = s.SCount[nextLine] + pos + (s.BMarks[nextLine] + s.TShift[nextLine])
+ offset = initial
+
+ if pos >= len(src) || src[pos] != ' ' && src[pos] != '\t' {
+ spaceAfterMarker = true
+ } else if src[pos] == ' ' {
+ pos++
+ initial++
+ offset++
+ adjustTab = false
+ spaceAfterMarker = true
+ } else if src[pos] == '\t' {
+ spaceAfterMarker = true
+
+ if (s.BSCount[nextLine]+offset)%4 == 3 {
+ pos++
+ initial++
+ offset++
+ adjustTab = false
+ } else {
+ adjustTab = true
+ }
+ }
+
+ oldBMarks = append(oldBMarks, s.BMarks[nextLine])
+ s.BMarks[nextLine] = pos
+
+ for pos < max {
+ r, size := utf8.DecodeRuneInString(src[pos:])
+ if runeIsSpace(r) {
+ if r == '\t' {
+ d := 0
+ if adjustTab {
+ d = 1
+ }
+ offset += 4 - (offset+s.BSCount[startLine]+d)%4
+ } else {
+ offset++
+ }
+ } else {
+ break
+ }
+ pos += size
+ }
+
+ lastLineEmpty = pos >= max
+
+ oldBSCount = append(oldBSCount, s.BSCount[nextLine])
+ d := 0
+ if spaceAfterMarker {
+ d = 1
+ }
+ s.BSCount[nextLine] = s.SCount[nextLine] + 1 + d
+
+ oldSCount = append(oldSCount, s.SCount[nextLine])
+ s.SCount[nextLine] = offset - initial
+
+ oldTShift = append(oldTShift, s.TShift[nextLine])
+ s.TShift[nextLine] = pos - s.BMarks[nextLine]
+
+ continue
+ }
+
+ if lastLineEmpty {
+ break
+ }
+
+ terminate := false
+ for _, r := range blockquoteTerminatedBy {
+ if r(s, nextLine, endLine, true) {
+ terminate = true
+ break
+ }
+ }
+
+ if terminate {
+ s.LineMax = nextLine
+
+ if s.BlkIndent != 0 {
+ oldBMarks = append(oldBMarks, s.BMarks[nextLine])
+ oldBSCount = append(oldBSCount, s.BSCount[nextLine])
+ oldTShift = append(oldTShift, s.TShift[nextLine])
+ oldSCount = append(oldSCount, s.SCount[nextLine])
+ s.SCount[nextLine] -= s.BlkIndent
+ }
+
+ break
+ }
+
+ oldBMarks = append(oldBMarks, s.BMarks[nextLine])
+ oldBSCount = append(oldBSCount, s.BSCount[nextLine])
+ oldTShift = append(oldTShift, s.TShift[nextLine])
+ oldSCount = append(oldSCount, s.SCount[nextLine])
+
+ s.SCount[nextLine] = -1
+ }
+
+ oldIndent := s.BlkIndent
+ s.BlkIndent = 0
+
+ tok := &BlockquoteOpen{
+ Map: [2]int{startLine, 0},
+ }
+ s.PushOpeningToken(tok)
+
+ s.Md.Block.Tokenize(s, startLine, nextLine)
+
+ s.PushClosingToken(&BlockquoteClose{})
+
+ s.LineMax = oldLineMax
+ s.ParentType = oldParentType
+ tok.Map[1] = s.Line
+
+ for i := 0; i < len(oldTShift); i++ {
+ s.BMarks[startLine+i] = oldBMarks[i]
+ s.TShift[startLine+i] = oldTShift[i]
+ s.SCount[startLine+i] = oldSCount[i]
+ s.BSCount[startLine+i] = oldBSCount[i]
+ }
+ s.BlkIndent = oldIndent
+
+ return true
+}
diff --git a/vendor/gitlab.com/golang-commonmark/markdown/code.go b/vendor/gitlab.com/golang-commonmark/markdown/code.go
new file mode 100644
index 000000000..c6dc574e0
--- /dev/null
+++ b/vendor/gitlab.com/golang-commonmark/markdown/code.go
@@ -0,0 +1,37 @@
+// Copyright 2015 The Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package markdown
+
+func ruleCode(s *StateBlock, startLine, endLine int, _ bool) bool {
+ if s.SCount[startLine]-s.BlkIndent < 4 {
+ return false
+ }
+
+ nextLine := startLine + 1
+ last := nextLine
+
+ for nextLine < endLine {
+ if s.IsLineEmpty(nextLine) {
+ nextLine++
+ continue
+ }
+
+ if s.SCount[nextLine]-s.BlkIndent >= 4 {
+ nextLine++
+ last = nextLine
+ continue
+ }
+
+ break
+ }
+
+ s.Line = last
+ s.PushToken(&CodeBlock{
+ Content: s.Lines(startLine, last, 4+s.BlkIndent, true),
+ Map: [2]int{startLine, s.Line},
+ })
+
+ return true
+}
diff --git a/vendor/gitlab.com/golang-commonmark/markdown/emphasis.go b/vendor/gitlab.com/golang-commonmark/markdown/emphasis.go
new file mode 100644
index 000000000..ea9cda210
--- /dev/null
+++ b/vendor/gitlab.com/golang-commonmark/markdown/emphasis.go
@@ -0,0 +1,89 @@
+// Copyright 2015 The Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package markdown
+
+type Delimiter struct {
+ Length int
+ Jump int
+ Token int
+ Level int
+ End int
+ Open bool
+ Close bool
+ Marker byte
+}
+
+func ruleEmphasis(s *StateInline, silent bool) bool {
+ src := s.Src
+ start := s.Pos
+ marker := src[start]
+
+ if silent {
+ return false
+ }
+
+ if marker != '_' && marker != '*' {
+ return false
+ }
+
+ canOpen, canClose, length := s.scanDelims(s.Pos, marker == '*')
+ for i := 0; i < length; i++ {
+ s.PushToken(&Text{Content: string(marker)})
+
+ s.Delimiters = append(s.Delimiters, Delimiter{
+ Marker: marker,
+ Length: length,
+ Jump: i,
+ Token: len(s.Tokens) - 1,
+ Level: s.Level,
+ End: -1,
+ Open: canOpen,
+ Close: canClose,
+ })
+ }
+
+ s.Pos += length
+
+ return true
+}
+
+func ruleEmphasisPostprocess(s *StateInline) {
+ delimiters := s.Delimiters
+ max := len(delimiters)
+ for i := max - 1; i >= 0; i-- {
+ startDelim := delimiters[i]
+ if startDelim.Marker != '_' && startDelim.Marker != '*' {
+ continue
+ }
+
+ if startDelim.End == -1 {
+ continue
+ }
+
+ endDelim := delimiters[startDelim.End]
+
+ isStrong := i > 0 &&
+ delimiters[i-1].End == startDelim.End+1 &&
+ delimiters[i-1].Token == startDelim.Token-1 &&
+ delimiters[startDelim.End+1].Token == endDelim.Token+1 &&
+ delimiters[i-1].Marker == startDelim.Marker
+
+ if isStrong {
+ s.Tokens[startDelim.Token] = &StrongOpen{}
+ s.Tokens[endDelim.Token] = &StrongClose{}
+
+ if text, ok := s.Tokens[delimiters[i-1].Token].(*Text); ok {
+ text.Content = ""
+ }
+ if text, ok := s.Tokens[delimiters[startDelim.End+1].Token].(*Text); ok {
+ text.Content = ""
+ }
+ i--
+ } else {
+ s.Tokens[startDelim.Token] = &EmphasisOpen{}
+ s.Tokens[endDelim.Token] = &EmphasisClose{}
+ }
+ }
+}
diff --git a/vendor/gitlab.com/golang-commonmark/markdown/entity.go b/vendor/gitlab.com/golang-commonmark/markdown/entity.go
new file mode 100644
index 000000000..fd5fba9c0
--- /dev/null
+++ b/vendor/gitlab.com/golang-commonmark/markdown/entity.go
@@ -0,0 +1,35 @@
+// Copyright 2015 The Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package markdown
+
+import "gitlab.com/golang-commonmark/html"
+
+func ruleEntity(s *StateInline, silent bool) bool {
+ pos := s.Pos
+ src := s.Src
+
+ if src[pos] != '&' {
+ return false
+ }
+
+ max := s.PosMax
+
+ if pos+1 < max {
+ if e, n := html.ParseEntity(src[pos:]); n > 0 {
+ if !silent {
+ s.Pending.WriteString(e)
+ }
+ s.Pos += n
+ return true
+ }
+ }
+
+ if !silent {
+ s.Pending.WriteByte('&')
+ }
+ s.Pos++
+
+ return true
+}
diff --git a/vendor/gitlab.com/golang-commonmark/markdown/escape.go b/vendor/gitlab.com/golang-commonmark/markdown/escape.go
new file mode 100644
index 000000000..634b9735c
--- /dev/null
+++ b/vendor/gitlab.com/golang-commonmark/markdown/escape.go
@@ -0,0 +1,61 @@
+// Copyright 2015 The Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package markdown
+
+import "strings"
+
+func escaped(b byte) bool {
+ return strings.IndexByte("\\!\"#$%&'()*+,./:;<=>?@[]^_`{|}~-", b) != -1
+}
+
+func ruleEscape(s *StateInline, silent bool) bool {
+ pos := s.Pos
+ src := s.Src
+
+ if src[pos] != '\\' {
+ return false
+ }
+
+ pos++
+ max := s.PosMax
+
+ if pos < max {
+ b := src[pos]
+
+ if b < 0x7f && escaped(b) {
+ if !silent {
+ s.Pending.WriteByte(b)
+ }
+ s.Pos += 2
+ return true
+ }
+
+ if b == '\n' {
+ if !silent {
+ s.PushToken(&Hardbreak{})
+ }
+
+ pos++
+
+ for pos < max {
+ b := src[pos]
+ if !byteIsSpace(b) {
+ break
+ }
+ pos++
+ }
+
+ s.Pos = pos
+ return true
+ }
+ }
+
+ if !silent {
+ s.Pending.WriteByte('\\')
+ }
+ s.Pos++
+
+ return true
+}
diff --git a/vendor/gitlab.com/golang-commonmark/markdown/fence.go b/vendor/gitlab.com/golang-commonmark/markdown/fence.go
new file mode 100644
index 000000000..04924dc49
--- /dev/null
+++ b/vendor/gitlab.com/golang-commonmark/markdown/fence.go
@@ -0,0 +1,102 @@
+// Copyright 2015 The Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package markdown
+
+import "strings"
+
+func ruleFence(s *StateBlock, startLine, endLine int, silent bool) bool {
+ haveEndMarker := false
+ pos := s.BMarks[startLine] + s.TShift[startLine]
+ max := s.EMarks[startLine]
+
+ if s.SCount[startLine]-s.BlkIndent >= 4 {
+ return false
+ }
+ if pos+3 > max {
+ return false
+ }
+
+ src := s.Src
+
+ marker := src[pos]
+ if marker != '~' && marker != '`' {
+ return false
+ }
+
+ mem := pos
+ pos = s.SkipBytes(pos, marker)
+
+ len := pos - mem
+
+ if len < 3 {
+ return false
+ }
+
+ params := strings.TrimSpace(src[pos:max])
+
+ if strings.IndexByte(params, marker) >= 0 {
+ return false
+ }
+
+ if silent {
+ return true
+ }
+
+ nextLine := startLine
+
+ for {
+ nextLine++
+ if nextLine >= endLine {
+ break
+ }
+
+ mem = s.BMarks[nextLine] + s.TShift[nextLine]
+ pos = mem
+ max = s.EMarks[nextLine]
+
+ if pos < max && s.SCount[nextLine] < s.BlkIndent {
+ break
+ }
+
+ if pos >= max || src[pos] != marker {
+ continue
+ }
+
+ if s.SCount[nextLine]-s.BlkIndent >= 4 {
+ continue
+ }
+
+ pos = s.SkipBytes(pos, marker)
+
+ if pos-mem < len {
+ continue
+ }
+
+ pos = s.SkipSpaces(pos)
+
+ if pos < max {
+ continue
+ }
+
+ haveEndMarker = true
+
+ break
+ }
+
+ len = s.SCount[startLine]
+
+ s.Line = nextLine
+ if haveEndMarker {
+ s.Line++
+ }
+
+ s.PushToken(&Fence{
+ Params: params,
+ Content: s.Lines(startLine+1, nextLine, len, true),
+ Map: [2]int{startLine, s.Line},
+ })
+
+ return true
+}
diff --git a/vendor/gitlab.com/golang-commonmark/markdown/fuzz.go b/vendor/gitlab.com/golang-commonmark/markdown/fuzz.go
new file mode 100644
index 000000000..66ac7697c
--- /dev/null
+++ b/vendor/gitlab.com/golang-commonmark/markdown/fuzz.go
@@ -0,0 +1,9 @@
+//+build gofuzz
+
+package markdown
+
+func Fuzz(data []byte) int {
+ md := New(HTML(true))
+ md.Parse(data)
+ return 1
+}
diff --git a/vendor/gitlab.com/golang-commonmark/markdown/heading.go b/vendor/gitlab.com/golang-commonmark/markdown/heading.go
new file mode 100644
index 000000000..a09c4f726
--- /dev/null
+++ b/vendor/gitlab.com/golang-commonmark/markdown/heading.go
@@ -0,0 +1,59 @@
+// Copyright 2015 The Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package markdown
+
+import "strings"
+
+func ruleHeading(s *StateBlock, startLine, _ int, silent bool) bool {
+ pos := s.BMarks[startLine] + s.TShift[startLine]
+ max := s.EMarks[startLine]
+ src := s.Src
+
+ if s.SCount[startLine]-s.BlkIndent >= 4 {
+ return false
+ }
+
+ if pos >= max || src[pos] != '#' {
+ return false
+ }
+
+ level := 1
+ pos++
+ for pos < max && src[pos] == '#' && level <= 6 {
+ level++
+ pos++
+ }
+
+ if level > 6 || (pos < max && !byteIsSpace(src[pos])) {
+ return false
+ }
+
+ if silent {
+ return true
+ }
+
+ max = s.SkipSpacesBack(max, pos)
+ tmp := s.SkipBytesBack(max, '#', pos)
+ if tmp > pos && byteIsSpace(src[tmp-1]) {
+ max = tmp
+ }
+
+ s.Line = startLine + 1
+
+ s.PushOpeningToken(&HeadingOpen{
+ HLevel: level,
+ Map: [2]int{startLine, s.Line},
+ })
+
+ if pos < max {
+ s.PushToken(&Inline{
+ Content: strings.TrimSpace(src[pos:max]),
+ Map: [2]int{startLine, s.Line},
+ })
+ }
+ s.PushClosingToken(&HeadingClose{HLevel: level})
+
+ return true
+}
diff --git a/vendor/gitlab.com/golang-commonmark/markdown/helpers.go b/vendor/gitlab.com/golang-commonmark/markdown/helpers.go
new file mode 100644
index 000000000..d158dfcfb
--- /dev/null
+++ b/vendor/gitlab.com/golang-commonmark/markdown/helpers.go
@@ -0,0 +1,164 @@
+// Copyright 2015 The Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package markdown
+
+func parseLinkLabel(s *StateInline, start int, disableNested bool) int {
+ src := s.Src
+ labelEnd := -1
+ max := s.PosMax
+ oldPos := s.Pos
+
+ s.Pos = start + 1
+ level := 1
+ found := false
+
+ for s.Pos < max {
+ marker := src[s.Pos]
+
+ if marker == ']' {
+ level--
+ if level == 0 {
+ found = true
+ break
+ }
+ }
+
+ prevPos := s.Pos
+
+ s.Md.Inline.SkipToken(s)
+
+ if marker == '[' {
+ if prevPos == s.Pos-1 {
+ level++
+ } else if disableNested {
+ s.Pos = oldPos
+ return -1
+ }
+ }
+ }
+
+ if found {
+ labelEnd = s.Pos
+ }
+
+ s.Pos = oldPos
+
+ return labelEnd
+}
+
+func parseLinkDestination(s string, pos, max int) (url string, lines, endpos int, ok bool) {
+ start := pos
+ if pos < max && s[pos] == '<' {
+ pos++
+ for pos < max {
+ b := s[pos]
+ if b == '\n' || byteIsSpace(b) {
+ return
+ }
+ if b == '>' {
+ endpos = pos + 1
+ url = unescapeAll(s[start+1 : pos])
+ ok = true
+ return
+ }
+ if b == '\\' && pos+1 < max {
+ pos += 2
+ continue
+ }
+
+ pos++
+ }
+
+ return
+ }
+
+ level := 0
+ for pos < max {
+ b := s[pos]
+
+ if b == ' ' {
+ break
+ }
+
+ if b < 0x20 || b == 0x7f {
+ break
+ }
+
+ if b == '\\' && pos+1 < max {
+ pos += 2
+ continue
+ }
+
+ if b == '(' {
+ level++
+ }
+
+ if b == ')' {
+ if level == 0 {
+ break
+ }
+ level--
+ }
+
+ pos++
+ }
+
+ if start == pos {
+ return
+ }
+ if level != 0 {
+ return
+ }
+
+ url = unescapeAll(s[start:pos])
+ endpos = pos
+ ok = true
+
+ return
+}
+
+func parseLinkTitle(s string, pos, max int) (title string, nlines, endpos int, ok bool) {
+ lines := 0
+ start := pos
+
+ if pos >= max {
+ return
+ }
+
+ marker := s[pos]
+
+ if marker != '"' && marker != '\'' && marker != '(' {
+ return
+ }
+
+ pos++
+
+ if marker == '(' {
+ marker = ')'
+ }
+
+ for pos < max {
+ switch s[pos] {
+ case marker:
+ endpos = pos + 1
+ nlines = lines
+ title = unescapeAll(s[start+1 : pos])
+ ok = true
+ return
+ case '\n':
+ lines++
+ case '\\':
+ if pos+1 < max {
+ pos++
+ if s[pos] == '\n' {
+ lines++
+ }
+ }
+ }
+ pos++
+ }
+
+ return
+}
diff --git a/vendor/gitlab.com/golang-commonmark/markdown/helpers_test.go b/vendor/gitlab.com/golang-commonmark/markdown/helpers_test.go
new file mode 100644
index 000000000..f180b7c4e
--- /dev/null
+++ b/vendor/gitlab.com/golang-commonmark/markdown/helpers_test.go
@@ -0,0 +1,71 @@
+// Copyright 2015 The Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package markdown
+
+import "testing"
+
+func TestParseLinkTitle(t *testing.T) {
+ type testCase struct {
+ in string
+ title string
+ nlines, endpos int
+ ok bool
+ }
+ testCases := []testCase{
+ {"(multiline\ntitle)", "multiline\ntitle", 1, 17, true},
+ {`"\"title\""`, `"title"`, 0, 11, true},
+ {`"title"`, "title", 0, 7, true},
+ {`"(title)"`, `(title)`, 0, 9, true},
+ {`("title")`, `"title"`, 0, 9, true},
+ {"", "", 0, 0, false},
+ {`"`, "", 0, 0, false},
+ {"(", "", 0, 0, false},
+ {`(\`, "", 0, 0, false},
+ {"(\\\n", "", 0, 0, false},
+ {`"\"title\"`, "", 0, 0, false},
+ {`"title`, "", 0, 0, false},
+ {`("title"`, "", 0, 0, false},
+ {"x", "", 0, 0, false},
+ }
+ for _, tc := range testCases {
+ title, nlines, endpos, ok := parseLinkTitle(tc.in, 0, len(tc.in))
+ if title != tc.title || nlines != tc.nlines || endpos != tc.endpos || ok != tc.ok {
+ t.Errorf("parseLinkTitle(%q) = (%q, %d, %d, %v), want (%q, %d, %d, %v)", tc.in, title, nlines, endpos, ok, tc.title, tc.nlines, tc.endpos, tc.ok)
+ }
+ }
+}
+
+func TestParseLinkDestination(t *testing.T) {
+ type testCase struct {
+ in string
+ title string
+ endpos int
+ ok bool
+ }
+ testCases := []testCase{
+ {"<\\>>", ">", 4, true},
+ {"http://goo gle.com", "http://goo", 10, true},
+ {"http://google.com", "http://google.com", 17, true},
+ {"http://google.com/search?query=(1)", "http://google.com/search?query=(1)", 34, true},
+ {"http://google.com/search?query=)1(", "http://google.com/search?query=", 31, true},
+ {"http://google.com/search?query=((1))", "http://google.com/search?query=((1))", 36, true},
+ {`http://google.com/search?query=a\ b\ c`, `http://google.com/search?query=a\ b\ c`, 38, true},
+ {"http://goo\x00gle.com", "http://goo", 10, true},
+ {"", "link", 6, true},
+ {"<", "", 0, false},
+ {"<\\>", "", 0, false},
+ {"<\\", "", 0, false},
+ {"", "", 0, false},
+ {"<>", "", 2, true},
+ {"= max {
+ return false
+ }
+
+ if s.SCount[startLine]-s.BlkIndent >= 4 {
+ return false
+ }
+
+ src := s.Src
+ marker := src[pos]
+ pos++
+
+ if marker != '*' && marker != '-' && marker != '_' {
+ return false
+ }
+
+ cnt := 1
+ for pos < max {
+ ch := src[pos]
+ pos++
+ if ch != marker && !byteIsSpace(ch) {
+ return false
+ }
+ if ch == marker {
+ cnt++
+ }
+ }
+
+ if cnt < 3 {
+ return false
+ }
+
+ if silent {
+ return true
+ }
+
+ s.Line = startLine + 1
+
+ s.PushToken(&Hr{
+ Map: [2]int{startLine, s.Line},
+ })
+
+ return true
+}
diff --git a/vendor/gitlab.com/golang-commonmark/markdown/html_block.go b/vendor/gitlab.com/golang-commonmark/markdown/html_block.go
new file mode 100644
index 000000000..98a89499e
--- /dev/null
+++ b/vendor/gitlab.com/golang-commonmark/markdown/html_block.go
@@ -0,0 +1,222 @@
+// Copyright 2015 The Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package markdown
+
+import (
+ "regexp"
+ "strings"
+)
+
+var (
+ htmlBlocks = []string{
+ "address",
+ "article",
+ "aside",
+ "base",
+ "basefont",
+ "blockquote",
+ "body",
+ "caption",
+ "center",
+ "col",
+ "colgroup",
+ "dd",
+ "details",
+ "dialog",
+ "dir",
+ "div",
+ "dl",
+ "dt",
+ "fieldset",
+ "figcaption",
+ "figure",
+ "footer",
+ "form",
+ "frame",
+ "frameset",
+ "h1",
+ "h2",
+ "h3",
+ "h4",
+ "h5",
+ "h6",
+ "head",
+ "header",
+ "hr",
+ "html",
+ "iframe",
+ "legend",
+ "li",
+ "link",
+ "main",
+ "menu",
+ "menuitem",
+ "meta",
+ "nav",
+ "noframes",
+ "ol",
+ "optgroup",
+ "option",
+ "p",
+ "param",
+ "section",
+ "source",
+ "summary",
+ "table",
+ "tbody",
+ "td",
+ "tfoot",
+ "th",
+ "thead",
+ "title",
+ "tr",
+ "track",
+ "ul",
+ }
+
+ htmlBlocksSet = make(map[string]bool)
+
+ rStartCond1 = regexp.MustCompile(`(?i)^(pre|script|style)([\n\t >]|$)`)
+ rEndCond1 = regexp.MustCompile(`(?i)(pre|script|style)>`)
+ rStartCond6 = regexp.MustCompile(`(?i)^/?(` + strings.Join(htmlBlocks, "|") + `)(\s|$|>|/>)`)
+ rStartCond7 = regexp.MustCompile(`(?i)^(/[a-z][a-z0-9-]*|[a-z][a-z0-9-]*(\s+[a-z_:][a-z0-9_.:-]*\s*=\s*("[^"]*"|'[^']*'|[ "'=<>\x60]))*\s*/?)>\s*$`)
+)
+
+func init() {
+ for _, tag := range htmlBlocks {
+ htmlBlocksSet[tag] = true
+ }
+}
+
+func min(a, b int) int {
+ if a < b {
+ return a
+ }
+ return b
+}
+
+func matchTagName(s string) string {
+ if len(s) < 2 {
+ return ""
+ }
+
+ i := 0
+ if s[0] == '/' {
+ i++
+ }
+ start := i
+ max := min(15+i, len(s))
+ for i < max && isLetter(s[i]) {
+ i++
+ }
+ if i >= len(s) {
+ return ""
+ }
+
+ switch s[i] {
+ case ' ', '\n', '/', '>':
+ return strings.ToLower(s[start:i])
+ default:
+ return ""
+ }
+}
+
+func ruleHTMLBlock(s *StateBlock, startLine, endLine int, silent bool) bool {
+ if !s.Md.HTML {
+ return false
+ }
+
+ pos := s.BMarks[startLine] + s.TShift[startLine]
+ max := s.EMarks[startLine]
+
+ if pos+1 >= max {
+ return false
+ }
+
+ src := s.Src
+
+ if src[pos] != '<' {
+ return false
+ }
+
+ pos++
+ b := src[pos]
+ if !htmlSecond(b) {
+ return false
+ }
+
+ nextLine := startLine + 1
+
+ var endCond func(string) bool
+
+ if pos+2 < max && isLetter(b) && rStartCond1.MatchString(src[pos:]) {
+ endCond = func(s string) bool {
+ return rEndCond1.MatchString(s)
+ }
+ } else if strings.HasPrefix(src[pos:], "!--") {
+ endCond = func(s string) bool {
+ return strings.Contains(s, "-->")
+ }
+ } else if b == '?' {
+ endCond = func(s string) bool {
+ return strings.Contains(s, "?>")
+ }
+ } else if b == '!' && pos+1 < max && isUppercaseLetter(src[pos+1]) {
+ endCond = func(s string) bool {
+ return strings.Contains(s, ">")
+ }
+ } else if strings.HasPrefix(src[pos:], "![CDATA[") {
+ endCond = func(s string) bool {
+ return strings.Contains(s, "]]>")
+ }
+ } else if pos+2 < max && (isLetter(b) || b == '/' && isLetter(src[pos+1])) {
+ terminator := true
+ if rStartCond6.MatchString(src[pos:max]) {
+ } else if rStartCond7.MatchString(src[pos:max]) {
+ terminator = false
+ } else {
+ return false
+ }
+ if silent {
+ return terminator
+ }
+ endCond = func(s string) bool {
+ return s == ""
+ }
+ } else {
+ return false
+ }
+
+ if silent {
+ return true
+ }
+
+ if !endCond(src[pos:max]) {
+ for nextLine < endLine {
+ if s.SCount[nextLine] < s.BlkIndent {
+ break
+ }
+
+ pos := s.BMarks[nextLine] + s.TShift[nextLine]
+ max := s.EMarks[nextLine]
+ lineText := src[pos:max]
+ if endCond(lineText) {
+ if pos != max {
+ nextLine++
+ }
+ break
+ }
+ nextLine++
+ }
+ }
+
+ s.Line = nextLine
+ s.PushToken(&HTMLBlock{
+ Content: s.Lines(startLine, nextLine, s.BlkIndent, true),
+ Map: [2]int{startLine, nextLine},
+ })
+
+ return true
+}
diff --git a/vendor/gitlab.com/golang-commonmark/markdown/html_block_test.go b/vendor/gitlab.com/golang-commonmark/markdown/html_block_test.go
new file mode 100644
index 000000000..d062180aa
--- /dev/null
+++ b/vendor/gitlab.com/golang-commonmark/markdown/html_block_test.go
@@ -0,0 +1,32 @@
+// Copyright 2015 The Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package markdown
+
+import "testing"
+
+func TestMatchTagName(t *testing.T) {
+ type testCase struct {
+ in string
+ want string
+ }
+ testCases := []testCase{
+ {"", ""},
+ {"a", ""},
+ {"/a>", "a"},
+ {"a>", "a"},
+ {"A>", "a"},
+ {"a\n", "a"},
+ {"br/>", "br"},
+ {"em", ""},
+ {"waytoolongtobeatag>", ""},
+ {"waytoolongtobeatag", ""},
+ }
+ for _, tc := range testCases {
+ got := matchTagName(tc.in)
+ if got != tc.want {
+ t.Errorf("matchTagName(%q) = %q, want %q", tc.in, got, tc.want)
+ }
+ }
+}
diff --git a/vendor/gitlab.com/golang-commonmark/markdown/html_inline.go b/vendor/gitlab.com/golang-commonmark/markdown/html_inline.go
new file mode 100644
index 000000000..0de54b202
--- /dev/null
+++ b/vendor/gitlab.com/golang-commonmark/markdown/html_inline.go
@@ -0,0 +1,57 @@
+// Copyright 2015 The Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package markdown
+
+import "regexp"
+
+var (
+ attrName = `[a-zA-Z_:][a-zA-Z0-9:._-]*`
+ unquoted = "[^\"'=<>`\\x00-\\x20]+"
+ singleQuoted = `'[^']*'`
+ doubleQuoted = `"[^"]*"`
+ attrValue = `(?:` + unquoted + `|` + singleQuoted + `|` + doubleQuoted + `)`
+ attribute = `(?:\s+` + attrName + `(?:\s*=\s*` + attrValue + `)?)`
+ openTag = `<[A-Za-z][A-Za-z0-9-]*` + attribute + `*\s*/?>`
+ closeTag = `[A-Za-z][A-Za-z0-9-]*\s*>`
+ comment = `|`
+ processing = `<[?].*?[?]>`
+ declaration = `]*>`
+ cdata = ``
+ rHTMLTag = regexp.MustCompile(`^(?:` + openTag + `|` + closeTag + `|` + comment +
+ `|` + processing + `|` + declaration + `|` + cdata + `)`)
+)
+
+func htmlSecond(b byte) bool {
+ return b == '!' || b == '/' || b == '?' || isLetter(b)
+}
+
+func ruleHTMLInline(s *StateInline, silent bool) bool {
+ if !s.Md.HTML {
+ return false
+ }
+
+ pos := s.Pos
+ src := s.Src
+ if pos+2 >= s.PosMax || src[pos] != '<' {
+ return false
+ }
+
+ if !htmlSecond(src[pos+1]) {
+ return false
+ }
+
+ match := rHTMLTag.FindString(src[pos:])
+ if match == "" {
+ return false
+ }
+
+ if !silent {
+ s.PushToken(&HTMLInline{Content: match})
+ }
+
+ s.Pos += len(match)
+
+ return true
+}
diff --git a/vendor/gitlab.com/golang-commonmark/markdown/image.go b/vendor/gitlab.com/golang-commonmark/markdown/image.go
new file mode 100644
index 000000000..7f2e67d30
--- /dev/null
+++ b/vendor/gitlab.com/golang-commonmark/markdown/image.go
@@ -0,0 +1,131 @@
+// Copyright 2015 The Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package markdown
+
+func ruleImage(s *StateInline, silent bool) bool {
+ pos := s.Pos
+ max := s.PosMax
+
+ if pos+2 >= max {
+ return false
+ }
+
+ src := s.Src
+ if src[pos] != '!' || src[pos+1] != '[' {
+ return false
+ }
+
+ labelStart := pos + 2
+ labelEnd := parseLinkLabel(s, pos+1, false)
+ if labelEnd < 0 {
+ return false
+ }
+
+ var href, title, label string
+ oldPos := pos
+ pos = labelEnd + 1
+ if pos < max && src[pos] == '(' {
+ pos++
+ for pos < max {
+ b := src[pos]
+ if !byteIsSpace(b) && b != '\n' {
+ break
+ }
+ pos++
+ }
+ if pos >= max {
+ return false
+ }
+
+ start := pos
+ url, _, endpos, ok := parseLinkDestination(src, pos, s.PosMax)
+ if ok {
+ url = normalizeLink(url)
+ if validateLink(url) {
+ href = url
+ pos = endpos
+ }
+ }
+
+ start = pos
+ for pos < max {
+ b := src[pos]
+ if !byteIsSpace(b) && b != '\n' {
+ break
+ }
+ pos++
+ }
+ if pos >= max {
+ return false
+ }
+
+ title, _, endpos, ok = parseLinkTitle(src, pos, s.PosMax)
+ if pos < max && start != pos && ok {
+ pos = endpos
+ for pos < max {
+ b := src[pos]
+ if !byteIsSpace(b) && b != '\n' {
+ break
+ }
+ pos++
+ }
+ }
+
+ if pos >= max || src[pos] != ')' {
+ s.Pos = oldPos
+ return false
+ }
+
+ pos++
+
+ } else {
+ if s.Env.References == nil {
+ return false
+ }
+
+ if pos < max && src[pos] == '[' {
+ start := pos + 1
+ pos = parseLinkLabel(s, pos, false)
+ if pos >= 0 {
+ label = src[start:pos]
+ pos++
+ } else {
+ pos = labelEnd + 1
+ }
+ } else {
+ pos = labelEnd + 1
+ }
+
+ if label == "" {
+ label = src[labelStart:labelEnd]
+ }
+
+ ref, ok := s.Env.References[normalizeReference(label)]
+ if !ok {
+ s.Pos = oldPos
+ return false
+ }
+
+ href = ref["href"]
+ title = ref["title"]
+ }
+
+ if !silent {
+ content := src[labelStart:labelEnd]
+
+ tokens := s.Md.Inline.Parse(content, s.Md, s.Env)
+
+ s.PushToken(&Image{
+ Src: href,
+ Title: title,
+ Tokens: tokens,
+ })
+ }
+
+ s.Pos = pos
+ s.PosMax = max
+
+ return true
+}
diff --git a/vendor/gitlab.com/golang-commonmark/markdown/inline.go b/vendor/gitlab.com/golang-commonmark/markdown/inline.go
new file mode 100644
index 000000000..0bed18acf
--- /dev/null
+++ b/vendor/gitlab.com/golang-commonmark/markdown/inline.go
@@ -0,0 +1,13 @@
+// Copyright 2015 The Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package markdown
+
+func ruleInline(s *StateCore) {
+ for _, tok := range s.Tokens {
+ if tok, ok := tok.(*Inline); ok {
+ tok.Children = s.Md.Inline.Parse(tok.Content, s.Md, s.Env)
+ }
+ }
+}
diff --git a/vendor/gitlab.com/golang-commonmark/markdown/lheading.go b/vendor/gitlab.com/golang-commonmark/markdown/lheading.go
new file mode 100644
index 000000000..b404066da
--- /dev/null
+++ b/vendor/gitlab.com/golang-commonmark/markdown/lheading.go
@@ -0,0 +1,80 @@
+// Copyright 2015 The Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package markdown
+
+import "strings"
+
+func ruleLHeading(s *StateBlock, startLine, endLine int, silent bool) bool {
+ nextLine := startLine + 1
+
+ if s.SCount[startLine]-s.BlkIndent >= 4 {
+ return false
+ }
+
+ oldParentType := s.ParentType
+ s.ParentType = ptParagraph
+ src := s.Src
+
+ var pos int
+ var hLevel int
+outer:
+ for ; nextLine < endLine && !s.IsLineEmpty(nextLine); nextLine++ {
+ if s.SCount[nextLine]-s.BlkIndent > 3 {
+ continue
+ }
+
+ if s.SCount[nextLine] >= s.BlkIndent {
+ pos = s.BMarks[nextLine] + s.TShift[nextLine]
+ max := s.EMarks[nextLine]
+
+ if pos < max {
+ marker := src[pos]
+
+ if marker == '-' || marker == '=' {
+ pos = s.SkipBytes(pos, marker)
+ pos = s.SkipSpaces(pos)
+
+ if pos >= max {
+ hLevel = 1
+ if marker == '-' {
+ hLevel++
+ }
+ break
+ }
+ }
+ }
+ }
+
+ if s.SCount[nextLine] < 0 {
+ continue
+ }
+
+ for _, r := range paragraphTerminatedBy {
+ if r(s, nextLine, endLine, true) {
+ break outer
+ }
+ }
+ }
+
+ if hLevel == 0 {
+ return false
+ }
+
+ s.Line = nextLine + 1
+
+ s.PushOpeningToken(&HeadingOpen{
+ HLevel: hLevel,
+ Map: [2]int{startLine, s.Line},
+ })
+ s.PushToken(&Inline{
+ Content: strings.TrimSpace(s.Lines(startLine, nextLine, s.BlkIndent, false)),
+ Map: [2]int{startLine, s.Line - 1},
+ })
+ s.PushClosingToken(&HeadingClose{HLevel: hLevel})
+
+ s.ParentType = oldParentType
+
+ return true
+}
diff --git a/vendor/gitlab.com/golang-commonmark/markdown/link.go b/vendor/gitlab.com/golang-commonmark/markdown/link.go
new file mode 100644
index 000000000..8ab2c04bb
--- /dev/null
+++ b/vendor/gitlab.com/golang-commonmark/markdown/link.go
@@ -0,0 +1,132 @@
+// Copyright 2015 The Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package markdown
+
+func ruleLink(s *StateInline, silent bool) bool {
+ pos := s.Pos
+ oldPos := s.Pos
+ max := s.PosMax
+ start := s.Pos
+ parseReference := true
+ src := s.Src
+
+ if src[pos] != '[' {
+ return false
+ }
+
+ labelStart := pos + 1
+ labelEnd := parseLinkLabel(s, pos, true)
+
+ if labelEnd < 0 {
+ return false
+ }
+
+ pos = labelEnd + 1
+
+ var title, href, label string
+ if pos < max && src[pos] == '(' {
+ parseReference = false
+
+ pos++
+ for pos < max {
+ code := src[pos]
+ if !byteIsSpace(code) && code != '\n' {
+ break
+ }
+ pos++
+ }
+ if pos >= max {
+ return false
+ }
+
+ start = pos
+ url, _, endpos, ok := parseLinkDestination(src, pos, s.PosMax)
+ if ok {
+ url = normalizeLink(url)
+ if validateLink(url) {
+ pos = endpos
+ href = url
+ }
+ }
+
+ start = pos
+ for pos < max {
+ code := src[pos]
+ if !byteIsSpace(code) && code != '\n' {
+ break
+ }
+ pos++
+ }
+
+ title, _, endpos, ok = parseLinkTitle(src, pos, s.PosMax)
+ if pos < max && start != pos && ok {
+ pos = endpos
+ for pos < max {
+ code := src[pos]
+ if !byteIsSpace(code) && code != '\n' {
+ break
+ }
+ pos++
+ }
+ }
+
+ if pos >= max || src[pos] != ')' {
+ parseReference = true
+ }
+
+ pos++
+ }
+
+ if parseReference {
+ if s.Env.References == nil {
+ return false
+ }
+
+ if pos < max && src[pos] == '[' {
+ start := pos + 1
+ pos = parseLinkLabel(s, pos, false)
+ if pos >= 0 {
+ label = src[start:pos]
+ pos++
+ } else {
+ pos = labelEnd + 1
+ }
+ } else {
+ pos = labelEnd + 1
+ }
+
+ if label == "" {
+ label = src[labelStart:labelEnd]
+ }
+
+ ref, ok := s.Env.References[normalizeReference(label)]
+ if !ok {
+ s.Pos = oldPos
+ return false
+ }
+
+ href = ref["href"]
+ title = ref["title"]
+ }
+
+ if !silent {
+ s.Pos = labelStart
+ s.PosMax = labelEnd
+
+ s.PushOpeningToken(&LinkOpen{
+ Href: href,
+ Title: title,
+ })
+
+ s.Md.Inline.Tokenize(s)
+
+ s.PushClosingToken(&LinkClose{})
+ }
+
+ s.Pos = pos
+ s.PosMax = max
+
+ return true
+}
diff --git a/vendor/gitlab.com/golang-commonmark/markdown/linkify.go b/vendor/gitlab.com/golang-commonmark/markdown/linkify.go
new file mode 100644
index 000000000..5935af1be
--- /dev/null
+++ b/vendor/gitlab.com/golang-commonmark/markdown/linkify.go
@@ -0,0 +1,131 @@
+// Copyright 2015 The Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package markdown
+
+import (
+ "strings"
+
+ "gitlab.com/golang-commonmark/linkify"
+)
+
+func isLinkOpen(s string) bool { return isLetter(s[1]) }
+
+func isLinkClose(s string) bool { return s[1] == '/' }
+
+func ruleLinkify(s *StateCore) {
+ blockTokens := s.Tokens
+
+ if !s.Md.Linkify {
+ return
+ }
+
+ for _, tok := range blockTokens {
+ if tok, ok := tok.(*Inline); ok {
+ tokens := tok.Children
+
+ htmlLinkLevel := 0
+
+ for i := len(tokens) - 1; i >= 0; i-- {
+ currentTok := tokens[i]
+
+ if _, ok := currentTok.(*LinkClose); ok {
+ i--
+ for tokens[i].Level() != currentTok.Level() {
+ if _, ok := tokens[i].(*LinkOpen); ok {
+ break
+ }
+ i--
+ }
+ continue
+ }
+
+ if currentTok, ok := currentTok.(*HTMLInline); ok {
+ if isLinkOpen(currentTok.Content) && htmlLinkLevel > 0 {
+ htmlLinkLevel--
+ }
+ if isLinkClose(currentTok.Content) {
+ htmlLinkLevel++
+ }
+ }
+ if htmlLinkLevel > 0 {
+ continue
+ }
+
+ if currentTok, ok := currentTok.(*Text); ok {
+ text := currentTok.Content
+ links := linkify.Links(text)
+ if len(links) == 0 {
+ continue
+ }
+
+ var nodes []Token
+ level := currentTok.Lvl
+ lastPos := 0
+
+ for _, ln := range links {
+ urlText := text[ln.Start:ln.End]
+ url := urlText
+ if ln.Scheme == "" {
+ url = "http://" + url
+ } else if ln.Scheme == "mailto:" && !strings.HasPrefix(url, "mailto:") {
+ url = "mailto:" + url
+ }
+ url = normalizeLink(url)
+ if !validateLink(url) {
+ continue
+ }
+
+ if ln.Scheme == "" {
+ urlText = strings.TrimPrefix(normalizeLinkText("http://"+urlText), "http://")
+ } else if ln.Scheme == "mailto:" && !strings.HasPrefix(urlText, "mailto:") {
+ urlText = strings.TrimPrefix(normalizeLinkText("mailto:"+urlText), "mailto:")
+ } else {
+ urlText = normalizeLinkText(urlText)
+ }
+
+ pos := ln.Start
+
+ if pos > lastPos {
+ tok := Text{
+ Content: text[lastPos:pos],
+ Lvl: level,
+ }
+ nodes = append(nodes, &tok)
+ }
+
+ nodes = append(nodes, &LinkOpen{
+ Href: url,
+ Lvl: level,
+ })
+ nodes = append(nodes, &Text{
+ Content: urlText,
+ Lvl: level + 1,
+ })
+ nodes = append(nodes, &LinkClose{
+ Lvl: level,
+ })
+
+ lastPos = ln.End
+ }
+
+ if lastPos < len(text) {
+ tok := Text{
+ Content: text[lastPos:],
+ Lvl: level,
+ }
+ nodes = append(nodes, &tok)
+ }
+
+ children := make([]Token, len(tokens)+len(nodes)-1)
+ copy(children, tokens[:i])
+ copy(children[i:], nodes)
+ copy(children[i+len(nodes):], tokens[i+1:])
+ tok.Children = children
+ tokens = children
+ }
+ }
+ }
+ }
+}
diff --git a/vendor/gitlab.com/golang-commonmark/markdown/list.go b/vendor/gitlab.com/golang-commonmark/markdown/list.go
new file mode 100644
index 000000000..3b2e02303
--- /dev/null
+++ b/vendor/gitlab.com/golang-commonmark/markdown/list.go
@@ -0,0 +1,285 @@
+// Copyright 2015 The Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package markdown
+
+import "strconv"
+
+var listTerminatedBy []BlockRule
+
+func skipBulletListMarker(s *StateBlock, startLine int) int {
+ pos := s.BMarks[startLine] + s.TShift[startLine]
+ max := s.EMarks[startLine]
+ src := s.Src
+
+ if pos >= max {
+ return -1
+ }
+
+ marker := src[pos]
+ if marker != '*' && marker != '-' && marker != '+' {
+ return -1
+ }
+ pos++
+
+ if pos < max && !byteIsSpace(src[pos]) {
+ return -1
+ }
+
+ return pos
+}
+
+func skipOrderedListMarker(s *StateBlock, startLine int) int {
+ start := s.BMarks[startLine] + s.TShift[startLine]
+ pos := start
+ max := s.EMarks[startLine]
+
+ if pos+1 >= max {
+ return -1
+ }
+
+ src := s.Src
+ ch := src[pos]
+ if ch < '0' || ch > '9' {
+ return -1
+ }
+ pos++
+
+ for {
+ if pos >= max {
+ return -1
+ }
+
+ ch = src[pos]
+ pos++
+
+ if ch >= '0' && ch <= '9' {
+ if pos-start >= 10 {
+ return -1
+ }
+ continue
+ }
+
+ if ch == ')' || ch == '.' {
+ break
+ }
+
+ return -1
+ }
+
+ if pos < max && !byteIsSpace(src[pos]) {
+ return -1
+ }
+
+ return pos
+}
+
+func markParagraphsTight(s *StateBlock, idx int) {
+ level := s.Level + 2
+ tokens := s.Tokens
+
+ for i := idx + 2; i < len(tokens)-2; i++ {
+ if tokens[i].Level() == level {
+ if tok, ok := tokens[i].(*ParagraphOpen); ok {
+ tok.Hidden = true
+ i += 2
+ tokens[i].(*ParagraphClose).Hidden = true
+ }
+ }
+ }
+}
+
+func ruleList(s *StateBlock, startLine, endLine int, silent bool) bool {
+ isTerminatingParagraph := false
+ tight := true
+
+ if s.SCount[startLine]-s.BlkIndent >= 4 {
+ return false
+ }
+ src := s.Src
+
+ if silent && s.ParentType == ptParagraph {
+ if s.TShift[startLine] >= s.BlkIndent {
+ isTerminatingParagraph = true
+ }
+ }
+
+ var start int
+ var markerValue int
+
+ isOrdered := false
+ posAfterMarker := skipOrderedListMarker(s, startLine)
+ if posAfterMarker > 0 {
+ isOrdered = true
+ start = s.BMarks[startLine] + s.TShift[startLine]
+ markerValue, _ = strconv.Atoi(src[start : posAfterMarker-1])
+
+ if isTerminatingParagraph && markerValue != 1 {
+ return false
+ }
+ } else {
+ posAfterMarker = skipBulletListMarker(s, startLine)
+ if posAfterMarker < 0 {
+ return false
+ }
+ }
+
+ if isTerminatingParagraph {
+ if s.SkipSpaces(posAfterMarker) >= s.EMarks[startLine] {
+ return false
+ }
+ }
+
+ markerChar := src[posAfterMarker-1]
+
+ if silent {
+ return true
+ }
+
+ tokenIdx := len(s.Tokens)
+
+ var listMap *[2]int
+ if isOrdered {
+ tok := &OrderedListOpen{
+ Order: markerValue,
+ Map: [2]int{startLine, 0},
+ }
+ s.PushOpeningToken(tok)
+ listMap = &tok.Map
+ } else {
+ tok := &BulletListOpen{
+ Map: [2]int{startLine, 0},
+ }
+ s.PushOpeningToken(tok)
+ listMap = &tok.Map
+ }
+
+ nextLine := startLine
+ prevEmptyEnd := false
+
+ oldParentType := s.ParentType
+ s.ParentType = ptList
+
+ var pos int
+ var contentStart int
+
+outer:
+ for nextLine < endLine {
+ pos = posAfterMarker
+ max := s.EMarks[nextLine]
+
+ initial := s.SCount[nextLine] + posAfterMarker - (s.BMarks[startLine] + s.TShift[startLine])
+ offset := initial
+
+ loop:
+ for pos < max {
+ switch src[pos] {
+ case '\t':
+ offset += 4 - (offset+s.BSCount[nextLine])%4
+ case ' ':
+ offset++
+ default:
+ break loop
+ }
+ pos++
+ }
+
+ contentStart = pos
+
+ indentAfterMarker := 1
+ if contentStart < max {
+ if iam := offset - initial; iam <= 4 {
+ indentAfterMarker = iam
+ }
+ }
+
+ indent := initial + indentAfterMarker
+
+ tok := &ListItemOpen{
+ Map: [2]int{startLine, 0},
+ }
+ s.PushOpeningToken(tok)
+ itemMap := &tok.Map
+
+ oldIndent := s.BlkIndent
+ oldTight := s.Tight
+ oldTShift := s.TShift[startLine]
+ oldLIndent := s.SCount[startLine]
+ s.BlkIndent = indent
+ s.Tight = true
+ s.TShift[startLine] = contentStart - s.BMarks[startLine]
+ s.SCount[startLine] = offset
+
+ if contentStart >= max && s.IsLineEmpty(startLine+1) {
+ s.Line = min(s.Line+2, endLine)
+ } else {
+ s.Md.Block.Tokenize(s, startLine, endLine)
+ }
+
+ if !s.Tight || prevEmptyEnd {
+ tight = false
+ }
+
+ prevEmptyEnd = s.Line-startLine > 1 && s.IsLineEmpty(s.Line-1)
+
+ s.BlkIndent = oldIndent
+ s.TShift[startLine] = oldTShift
+ s.SCount[startLine] = oldLIndent
+ s.Tight = oldTight
+
+ s.PushClosingToken(&ListItemClose{})
+
+ startLine = s.Line
+ nextLine = startLine
+ (*itemMap)[1] = nextLine
+
+ if nextLine >= endLine {
+ break
+ }
+
+ contentStart = s.BMarks[startLine]
+
+ if s.SCount[nextLine] < s.BlkIndent {
+ break
+ }
+
+ for _, r := range listTerminatedBy {
+ if r(s, nextLine, endLine, true) {
+ break outer
+ }
+ }
+
+ if isOrdered {
+ posAfterMarker = skipOrderedListMarker(s, nextLine)
+ if posAfterMarker < 0 {
+ break
+ }
+ } else {
+ posAfterMarker = skipBulletListMarker(s, nextLine)
+ if posAfterMarker < 0 {
+ break
+ }
+ }
+
+ if markerChar != src[posAfterMarker-1] {
+ break
+ }
+ }
+
+ if isOrdered {
+ s.PushClosingToken(&OrderedListClose{})
+ } else {
+ s.PushClosingToken(&BulletListClose{})
+ }
+
+ (*listMap)[1] = nextLine
+ s.Line = nextLine
+ s.ParentType = oldParentType
+
+ if tight {
+ markParagraphsTight(s, tokenIdx)
+ }
+
+ return true
+}
diff --git a/vendor/gitlab.com/golang-commonmark/markdown/markdown.go b/vendor/gitlab.com/golang-commonmark/markdown/markdown.go
new file mode 100644
index 000000000..1e37b3aee
--- /dev/null
+++ b/vendor/gitlab.com/golang-commonmark/markdown/markdown.go
@@ -0,0 +1,112 @@
+// Copyright 2015 The Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package markdown provides CommonMark-compliant markdown parser and renderer.
+package markdown
+
+import (
+ "bytes"
+ "io"
+)
+
+type Markdown struct {
+ options
+ Block ParserBlock
+ Inline ParserInline
+ renderOptions RenderOptions
+}
+
+type RenderOptions struct {
+ LangPrefix string // CSS language class prefix for fenced blocks
+ XHTML bool // render as XHTML instead of HTML
+ Breaks bool // convert \n in paragraphs into
+ Nofollow bool // add rel="nofollow" to the links
+}
+
+type options struct {
+ HTML bool // allow raw HTML in the markup
+ Tables bool // GFM tables
+ Linkify bool // autoconvert URL-like text to links
+ Typographer bool // enable some typographic replacements
+ Quotes [4]string // double/single quotes replacement pairs
+ MaxNesting int // maximum nesting level
+}
+
+type Environment struct {
+ References map[string]map[string]string
+}
+
+type CoreRule func(*StateCore)
+
+var coreRules []CoreRule
+
+func New(opts ...option) *Markdown {
+ m := &Markdown{
+ options: options{
+ Tables: true,
+ Linkify: true,
+ Typographer: true,
+ Quotes: [4]string{"“", "”", "‘", "’"},
+ MaxNesting: 20,
+ },
+ renderOptions: RenderOptions{LangPrefix: "language-"},
+ }
+ for _, opt := range opts {
+ opt(m)
+ }
+ return m
+}
+
+func (m *Markdown) Parse(src []byte) []Token {
+ if len(src) == 0 {
+ return nil
+ }
+
+ s := &StateCore{
+ Md: m,
+ Env: &Environment{},
+ }
+ s.Tokens = m.Block.Parse(src, m, s.Env)
+
+ for _, r := range coreRules {
+ r(s)
+ }
+ return s.Tokens
+}
+
+func (m *Markdown) Render(w io.Writer, src []byte) error {
+ if len(src) == 0 {
+ return nil
+ }
+
+ return NewRenderer(w).Render(m.Parse(src), m.renderOptions)
+}
+
+func (m *Markdown) RenderTokens(w io.Writer, tokens []Token) error {
+ if len(tokens) == 0 {
+ return nil
+ }
+
+ return NewRenderer(w).Render(tokens, m.renderOptions)
+}
+
+func (m *Markdown) RenderToString(src []byte) string {
+ if len(src) == 0 {
+ return ""
+ }
+
+ var buf bytes.Buffer
+ NewRenderer(&buf).Render(m.Parse(src), m.renderOptions)
+ return buf.String()
+}
+
+func (m *Markdown) RenderTokensToString(tokens []Token) string {
+ if len(tokens) == 0 {
+ return ""
+ }
+
+ var buf bytes.Buffer
+ NewRenderer(&buf).Render(tokens, m.renderOptions)
+ return buf.String()
+}
diff --git a/vendor/gitlab.com/golang-commonmark/markdown/markdown_benchmark_test.go b/vendor/gitlab.com/golang-commonmark/markdown/markdown_benchmark_test.go
new file mode 100644
index 000000000..f0299bcc0
--- /dev/null
+++ b/vendor/gitlab.com/golang-commonmark/markdown/markdown_benchmark_test.go
@@ -0,0 +1,93 @@
+// Copyright 2015 The Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package markdown
+
+import (
+ "io/ioutil"
+ "testing"
+
+ "github.com/russross/blackfriday"
+ blackfriday2 "gopkg.in/russross/blackfriday.v2"
+)
+
+func BenchmarkRenderSpecNoHTML(b *testing.B) {
+ b.StopTimer()
+ data, err := ioutil.ReadFile("spec/spec-0.28.txt")
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ md := New(HTML(false), XHTMLOutput(true))
+ b.StartTimer()
+
+ for i := 0; i < b.N; i++ {
+ md.RenderToString(data)
+ }
+}
+
+func BenchmarkRenderSpec(b *testing.B) {
+ b.StopTimer()
+ data, err := ioutil.ReadFile("spec/spec-0.28.txt")
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ md := New(HTML(true), XHTMLOutput(true))
+ b.StartTimer()
+
+ for i := 0; i < b.N; i++ {
+ md.RenderToString(data)
+ }
+}
+
+func BenchmarkRenderSpecBlackFriday(b *testing.B) {
+ b.StopTimer()
+ data, err := ioutil.ReadFile("spec/spec-0.28.txt")
+ if err != nil {
+ panic(err)
+ }
+
+ renderer := blackfriday.HtmlRenderer(blackfriday.HTML_USE_XHTML|blackfriday.HTML_USE_SMARTYPANTS|blackfriday.HTML_SMARTYPANTS_LATEX_DASHES, "", "")
+ b.StartTimer()
+
+ for i := 0; i < b.N; i++ {
+ blackfriday.Markdown(data, renderer,
+ blackfriday.EXTENSION_NO_INTRA_EMPHASIS|
+ blackfriday.EXTENSION_TABLES|
+ blackfriday.EXTENSION_FENCED_CODE|
+ blackfriday.EXTENSION_AUTOLINK|
+ blackfriday.EXTENSION_STRIKETHROUGH,
+ )
+ }
+}
+
+func BenchmarkRenderSpecBlackFriday2(b *testing.B) {
+ b.StopTimer()
+ data, err := ioutil.ReadFile("spec/spec-0.28.txt")
+ if err != nil {
+ panic(err)
+ }
+
+ renderer := blackfriday2.NewHTMLRenderer(
+ blackfriday2.HTMLRendererParameters{
+ Flags: blackfriday2.Smartypants |
+ blackfriday2.SmartypantsDashes |
+ blackfriday2.SmartypantsLatexDashes |
+ blackfriday2.SmartypantsAngledQuotes,
+ },
+ )
+ b.StartTimer()
+
+ for i := 0; i < b.N; i++ {
+ blackfriday2.Run(data, blackfriday2.WithExtensions(
+ blackfriday2.NoIntraEmphasis|
+ blackfriday2.Tables|
+ blackfriday2.FencedCode|
+ blackfriday2.Autolink|
+ blackfriday2.Strikethrough),
+ blackfriday2.WithRenderer(renderer),
+ )
+ }
+}
diff --git a/vendor/gitlab.com/golang-commonmark/markdown/markdown_it_test.go b/vendor/gitlab.com/golang-commonmark/markdown/markdown_it_test.go
new file mode 100644
index 000000000..f00f7a66b
--- /dev/null
+++ b/vendor/gitlab.com/golang-commonmark/markdown/markdown_it_test.go
@@ -0,0 +1,587 @@
+// Copyright 2015 The Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package markdown
+
+import (
+ "fmt"
+ "testing"
+)
+
+var mditTests = []struct {
+ markdown string
+ want string
+ filename string
+}{
+ // 0
+ // Issue #246. Double escaping in ALT
+ {"![&](#)\n", "\n", "commonmark_extras.txt"},
+
+ // 1
+ // Strip markdown in ALT tags
+ {"\n![*strip* [markdown __in__ alt](#)](#)\n", "\n", "commonmark_extras.txt"},
+
+ // 2
+ // Issue #55:
+ {"\n![test]\n\n![test](foo bar)\n", "
\n", "
\n", "commonmark_extras.txt"},
+
+ // 8
+ // Underscore between punctuation chars should be able to close emphasis.
+ {"\n_(hai)_.\n", "just a funny little fence\n
\n
\n\n
\n\n
\n", "commonmark_extras.txt"},
+
+ // 10
+ // Blockquote should terminate itself after paragraph continuation
+ {"\n- list\n > blockquote\nblockquote continuation\n - next list item\n", "\n
\n", "commonmark_extras.txt"},
+
+ // 11
+ // Regression test (code block + regular paragraph)
+ {"\n> foo\n> bar\n", "\n
\n\n
\n\n
\n", "commonmark_extras.txt"},
+
+ // 12
+ // Blockquotes inside indented lists should terminate correctly
+ {"\n - a\n > b\n ```\n c\n ```\n - d\n", "
\nfoo\n
\n
\n", "commonmark_extras.txt"},
+
+ // 13
+ // Don't output empty class here:
+ {"\n``` \ntest\n```\n", "\n
\n
\nc\n
\n", "commonmark_extras.txt"},
+
+ // 14
+ // Setext header text supports lazy continuations:
+ {"\n - foo\nbar\n ===\n", "test\n
\n
\n", "commonmark_extras.txt"},
+
+ // 15
+ // But setext header underline doesn't:
+ {"\n - foo\n bar\n ===\n", "foo\nbar
\n\n
\n", "commonmark_extras.txt"},
+
+ // 16
+ // Info string in fenced code block can't contain marker used for the fence
+ {"\n~~~test~\n\n~~~test`\n", "
\n", "commonmark_extras.txt"},
+
+ // 17
+ // Tabs should be stripped from the beginning of the line
+ {"\n foo\n bar\n\tbaz\n", "
\nbar\n
\n", "commonmark_extras.txt"},
+
+ // 20
+ // Coverage. Directive can terminate paragraph.
+ {"\na\na\n
\n
`foo
\n", "commonmark_extras.txt"}, + + // 23 + // Coverage. Entities. + {"\n*&*\n\n* *\n\n*&*\n", "&
\n\n
&
\n", "commonmark_extras.txt"}, + + // 24 + // Coverage. Escape. + {"\n*\\a*\n", "\\a
\n", "commonmark_extras.txt"}, + + // 25 + // Coverage. parseLinkDestination + {"\n[foo](<\nbar>)\n\n[foo]([foo](<bar)
\n", "commonmark_extras.txt"}, + + // 26 + // Coverage. parseLinkTitle + {"\n[foo](bar \"ba)\n\n[foo](bar \"ba\\\nz\")\n", "[foo](bar "ba)
\n\n", "commonmark_extras.txt"}, + + // 27 + // Coverage. Image + {"\n![test]( x )\n", "\n", "commonmark_extras.txt"}, + + // 28 + // Coverage. Image + {"![test][foo]\n\n[bar]: 123\n", "![test][foo]
\n", "commonmark_extras.txt"}, + + // 29 + // Coverage. Image + {"![test][[[\n\n[bar]: 123\n", "![test][[[
\n", "commonmark_extras.txt"}, + + // 30 + // Coverage. Image + {"![test](\n", "![test](
\n", "commonmark_extras.txt"}, + + // 31 + // Coverage. Link + {"\n[test](\n", "[test](
\n", "commonmark_extras.txt"}, + + // 32 + // Coverage. Reference + {"\n[\ntest\\\n]: 123\nfoo\nbar\n", "foo\nbar
\n", "commonmark_extras.txt"}, + + // 33 + // Coverage. Reference + {"[\ntest\n]\n", "[\ntest\n]
\n", "commonmark_extras.txt"}, + + // 34 + // Coverage. Reference + {"> [foo]: bar\n[foo]\n", "\n\n\n", "commonmark_extras.txt"}, + + // 35 + // Coverage. Tabs in blockquotes. + {"\n>\t\ttest\n\n >\t\ttest\n\n >\t\ttest\n\n> ---\n>\t\ttest\n\n > ---\n >\t\ttest\n\n > ---\n >\t\ttest\n\n>\t\t\ttest\n\n >\t\t\ttest\n\n >\t\t\ttest\n\n> ---\n>\t\t\ttest\n\n > ---\n >\t\t\ttest\n\n > ---\n >\t\t\ttest\n", "
\n\n\ntest\n
\n\n\ntest\n
\n\n\ntest\n
\n\n
\n\ntest\n
\n\n
\n\ntest\n
\n\n
\n\ntest\n
\n\n\n\ttest\n
\n\n\n\ttest\n
\n\n\n\ttest\n
\n\n
\n\n\ttest\n
\n\n
\n\n\ttest\n
\n\n", "commonmark_extras.txt"}, + + // 36 + // Coverage. Tabs in lists. + {"\n1. \tfoo\n\n\t bar\n", "
\n\n\ttest\n
foo
\n bar\n
\n\n\nfoo\n- - - -
\n
\n\nfoo\n# not a heading
\n
\n\n", "commonmark_extras.txt"}, + + // 38 + // Should not throw exception on invalid chars in URL (`*` not allowed in path) [mailformed URI] + {"[foo](<%test>)\n", "\n", "fatal.txt"}, + + // 39 + // Should not throw exception on broken utf-8 sequence in URL [mailformed URI] + {"\n[foo](%C3)\n", "\n", "fatal.txt"}, + + // 40 + // Should not throw exception on broken utf-16 surrogates sequence in URL [mailformed URI] + {"\n[foo]()\n", "\n", "fatal.txt"}, + + // 41 + // Should not hang comments regexp + {"\nfoo \n", "foo\n
\nnot a fence
foo <!— xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ->
\nfoo <!------------------------------------------------------------------->
\n", "fatal.txt"}, + + // 42 + // Should not hang cdata regexp + {"\nfoo \n", "foo <![CDATA[ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ]>
\n", "fatal.txt"}, + + // 43 + // linkify + {"url http://www.youtube.com/watch?v=5Jt5GEr4AYg.\n", "url http://www.youtube.com/watch?v=5Jt5GEr4AYg.
\n", "linkify.txt"}, + + // 44 + // don't touch text in links + {"\n[https://example.com](https://example.com)\n", "\n", "linkify.txt"}, + + // 45 + // don't touch text in autolinks + {"\ntest google.com foo
\n", "normalize.txt"}, + + // 59 + // Should support IDN in autolinks: + {"\ntest http://xn--n3h.net/ foo\n", "test http://☃.net/ foo
\n", "normalize.txt"}, + + // 60 + {"\ntest http://☃.net/ foo\n", "test http://☃.net/ foo
\n", "normalize.txt"}, + + // 61 + {"\ntest //xn--n3h.net/ foo\n", "test //☃.net/ foo
\n", "normalize.txt"}, + + // 62 + {"\ntest xn--n3h.net foo\n", "test ☃.net foo
\n", "normalize.txt"}, + + // 63 + {"\ntest xn--n3h@xn--n3h.net foo\n", "test xn--n3h@☃.net foo
\n", "normalize.txt"}, + + // 64 + {"[__proto__]\n\n[__proto__]: blah\n", "\n", "proto.txt"}, + + // 65 + {"\n[hasOwnProperty]\n\n[hasOwnProperty]: blah\n", "\n", "proto.txt"}, + + // 66 + // Should parse nested quotes: + {"\"foo 'bar' baz\"\n\n'foo 'bar' baz'\n", "“foo ‘bar’ baz”
\n‘foo ‘bar’ baz’
\n", "smartquotes.txt"}, + + // 67 + // Should not overlap quotes: + {"\n'foo \"bar' baz\"\n", "‘foo "bar’ baz"
\n", "smartquotes.txt"}, + + // 68 + // Should match quotes on the same level: + {"\n\"foo *bar* baz\"\n", "“foo bar baz”
\n", "smartquotes.txt"}, + + // 69 + // Should handle adjacent nested quotes: + {"\n'\"double in single\"'\n\n\"'single in double'\"\n", "‘“double in single”’
\n“‘single in double’”
\n", "smartquotes.txt"}, + + // 70 + // Should not match quotes on different levels: + {"\n*\"foo* bar\"\n\n\"foo *bar\"*\n\n*\"foo* bar *baz\"*\n", ""foo bar"
\n"foo bar"
\n"foo bar baz"
\n", "smartquotes.txt"}, + + // 71 + // Smartquotes should not overlap with other tags: + {"\n*foo \"bar* *baz\" quux*\n", "foo "bar baz" quux
\n", "smartquotes.txt"}, + + // 72 + // Should try and find matching quote in this case: + {"\n\"foo \"bar 'baz\"\n", ""foo “bar 'baz”
\n", "smartquotes.txt"}, + + // 73 + // Should not touch 'inches' in quotes: + {"\n\"Monitor 21\"\" and \"Monitor\"\"\n", "“Monitor 21"” and “Monitor”"
\n", "smartquotes.txt"}, + + // 74 + // Should render an apostrophe as a rsquo: + {"\nThis isn't and can't be the best approach to implement this...\n", "This isn’t and can’t be the best approach to implement this…
\n", "smartquotes.txt"}, + + // 75 + // Apostrophe could end the word, that's why original smartypants replaces all of them as rsquo: + {"\nusers' stuff\n", "users’ stuff
\n", "smartquotes.txt"}, + + // 76 + // Quotes between punctuation chars: + {"\n\"(hai)\".\n", "“(hai)”.
\n", "smartquotes.txt"}, + + // 77 + // Quotes at the start/end of the tokens: + {"\n\"*foo* bar\"\n\n\"foo *bar*\"\n\n\"*foo bar*\"\n", "“foo bar”
\n“foo bar”
\n“foo bar”
\n", "smartquotes.txt"}, + + // 78 + // Should treat softbreak as a space: + {"\n\"this\"\nand \"that\".\n\n\"this\" and\n\"that\".\n", "“this”\nand “that”.
\n“this” and\n“that”.
\n", "smartquotes.txt"}, + + // 79 + // Should treat hardbreak as a space: + {"\n\"this\"\\\nand \"that\".\n\n\"this\" and\\\n\"that\".\n", "“this”
\nand “that”.
“this” and
\n“that”.
Strikeout
x foo bar
x foo bar
x foo
x a ~~~foobar b
x a ~~~~foobar b
~~test~~
\n**test**
~~link~~
\n~~link~~
\n", "strikethrough.txt"}, + + // 87 + // Strikeouts have the same priority as emphases with respect to backticks: + {"\n~~`code~~`\n\n`~~code`~~\n", "~~code~~
~~code
~~
foo bar baz
f o o b a r
foo ~~ bar ~~ baz
\n", "strikethrough.txt"}, + + // 90 + // Newline should be considered a whitespace: + {"\n~~test\n~~\n\n~~\ntest~~\n\n~~\ntest\n~~\n", "~~test\n~~
\n~~\ntest~~
\n~~\ntest\n~~
\n", "strikethrough.txt"}, + + // 91 + // From CommonMark test suite, replacing `**` with our marker: + {"\na~~\"foo\"~~\n", "a~~“foo”~~
\n", "strikethrough.txt"}, + + // 92 + // Simple: + {"| Heading 1 | Heading 2\n| --------- | ---------\n| Cell 1 | Cell 2\n| Cell 3 | Cell 4\n", "Heading 1 | \nHeading 2 | \n
---|---|
Cell 1 | \nCell 2 | \n
Cell 3 | \nCell 4 | \n
Header 1 | \nHeader 2 | \nHeader 3 | \nHeader 4 | \n
---|---|---|---|
Cell 1 | \nCell 2 | \nCell 3 | \nCell 4 | \n
Cell 5 | \nCell 6 | \nCell 7 | \nCell 8 | \n
Header 1 | \nHeader 2 | \nHeader 3 | \nHeader 4 | \n
---|---|---|---|
Cell 1 | \nCell 2 | \nCell 3 | \nCell 4 | \n
Cell 5 | \nCell 6 | \nCell 7 | \nCell 8 | \n
\n\n\n\n
\n\n \n\n\nfoo \nfoo \n\n \n\nbar \nbar \n
baz|baz
\n", "tables.txt"}, + + // 96 + // Minimal one-column: + {"\n| foo\n|----\n| test2\n", "foo | \n
---|
test2 | \n
- foo | \nfoo | \n
---|---|
bar | \nbar | \n
foo|foo\n—|—s\nbar|bar
\n", "tables.txt"}, + + // 99 + // Second line should contain "|" symbol: + {"\nfoo|foo\n---:---\nbar|bar\n", "foo|foo\n—:—\nbar|bar
\n", "tables.txt"}, + + // 100 + // Second line should not have empty columns in the middle: + {"\nfoo|foo\n---||---\nbar|bar\n", "foo|foo\n—||—\nbar|bar
\n", "tables.txt"}, + + // 101 + // Wrong alignment symbol position: + {"\nfoo|foo\n---|-::-\nbar|bar\n", "foo|foo\n—|-::-\nbar|bar
\n", "tables.txt"}, + + // 102 + // Title line should contain "|" symbol: + {"\nfoo\n---|---\nbar|bar\n", "foo\n—|—\nbar|bar
\n", "tables.txt"}, + + // 103 + // Allow tabs as a separator on 2nd line + {"\n|\tfoo\t|\tbar\t|\n|\t---\t|\t---\t|\n|\tbaz\t|\tquux\t|\n", "foo | \nbar | \n
---|---|
baz | \nquux | \n
paragraph
\nfoo | \nfoo | \n
---|---|
bar | \nbar | \n
foo | \nfoo | \n
---|
paragraph
\n", "tables.txt"}, + + // 106 + // Delimiter escaping: + {"\n| Heading 1 \\\\\\\\| Heading 2\n| --------- | ---------\n| Cell\\|1\\|| Cell\\|2\n\\| Cell\\\\\\|3 \\\\| Cell\\|4\n", "Heading 1 \\\\ | \nHeading 2 | \n
---|---|
Cell|1| | \nCell|2 | \n
| Cell\\|3 \\ | \nCell|4 | \n
Heading 1 | \nHeading 2 | \n
---|---|
Cell 1 | \nCell 2 | \n
Cell|3 | \nCell 4 | \n
Heading 1 | \nHeading 2 | \n
---|---|
Cell 1 | \nCell 2 | \n
`Cell 3 | \nCell 4 | \n
Heading 1 | \nHeading 2 | \n
---|---|
Cell 1 | \nCell 2 | \n
\\` | \n\\` | \n
# | \n1 | \n2 | \n
---|---|---|
x | \n\\ | \nx | \n
# | \n1 | \n2 | \n
---|---|---|
x | \n`` | \nx | \n
1 | \n2 | \n
---|---|
3 | \n4 | \n
1 | \n2 | \n3 | \n4 | \n
---|---|---|---|
5 | \n6 | \n\n | \n |
foo | \n
---|
bar | \n
Col1a | \nCol2a | \n
---|---|
Col1b | \nCol2b | \n
| Col1a | Col2a |\n
\n| ----- | ----- |\n| Col1b | Col2b |
\n", "tables.txt"}, + + // 117 + // Tables should not be indented more than 4 spaces (2nd line): + {"\n | Col1a | Col2a |\n | ----- | ----- |\n | Col1b | Col2b |\n", "| Col1a | Col2a |\n| ----- | ----- |\n| Col1b | Col2b |
\n", "tables.txt"}, + + // 118 + // Tables should not be indented more than 4 spaces (3rd line): + {"\n | Col1a | Col2a |\n | ----- | ----- |\n | Col1b | Col2b |\n", "Col1a | \nCol2a | \n
---|
| Col1b | Col2b |\n
\n", "tables.txt"},
+
+ // 119
+ // Allow tables with empty body:
+ {"\n | Col1a | Col2a |\n | ----- | ----- |\n", "Col1a | \nCol2a | \n
---|
Col1a | Col1b | Col1c\n----- | -----\nCol2a | Col2b | Col2c
\n", "tables.txt"}, + + // 121 + {"(bad)\n", "(bad)
\n", "typographer.txt"}, + + // 122 + // copyright + {"\n(c) (C)\n", "© ©
\n", "typographer.txt"}, + + // 123 + // reserved + {"\n(r) (R)\n", "® ®
\n", "typographer.txt"}, + + // 124 + // trademark + {"\n(tm) (TM)\n", "™ ™
\n", "typographer.txt"}, + + // 125 + // paragraph + {"\n(p) (P)\n", "§ §
\n", "typographer.txt"}, + + // 126 + // plus-minus + {"\n+-5\n", "±5
\n", "typographer.txt"}, + + // 127 + // ellipsis + {"\ntest.. test... test..... test?..... test!....\n", "test… test… test… test?.. test!..
\n", "typographer.txt"}, + + // 128 + // dupes + {"\n!!!!!! ???? ,,\n", "!!! ??? ,
\n", "typographer.txt"}, + + // 129 + // dashes + {"\n---markdownit --- super---\n\nmarkdownit---awesome\n\nabc ----\n\n--markdownit -- super--\n\nmarkdownit--awesome\n", "—markdownit — super—
\nmarkdownit—awesome
\nabc ----
\n–markdownit – super–
\nmarkdownit–awesome
\n", "typographer.txt"}, + + // 130 + {"[normal link](javascript)\n", "\n", "xss.txt"}, + + // 131 + // Should not allow some protocols in links and images + {"\n[xss link](javascript:alert(1))\n\n[xss link](JAVASCRIPT:alert(1))\n\n[xss link](vbscript:alert(1))\n\n[xss link](VBSCRIPT:alert(1))\n\n[xss link](file:///123)\n", "[xss link](javascript:alert(1))
\n[xss link](JAVASCRIPT:alert(1))
\n[xss link](vbscript:alert(1))
\n[xss link](VBSCRIPT:alert(1))
\n[xss link](file:///123)
\n", "xss.txt"}, + + // 132 + {"\n[xss link]("><script>alert("xss")</script>)\n\n[xss link](Javascript:alert(1))\n\n[xss link](Javascript:alert(1))\n\n[xss link](\\Javascript:alert(1))\n", "\n[xss link](Javascript:alert(1))
\n\n\n", "xss.txt"}, + + // 133 + {"\n[xss link]([xss link](<javascript:alert(1)>)
\n", "xss.txt"}, + + // 134 + {"\n[xss link](javascript:alert(1))\n", "[xss link](javascript:alert(1))
\n", "xss.txt"}, + + // 135 + // Should not allow data-uri except some whitelisted mimes + {"\n![](data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)\n", "\n", "xss.txt"}, + + // 136 + {"\n[xss link](data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K)\n", "[xss link](data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K)
\n", "xss.txt"}, + + // 137 + {"\n[normal link](/javascript:link)\n", "\n", "xss.txt"}, + + // 138 + // Image parser use the same code base as link. + {"\n![xss link](javascript:alert(1))\n", "![xss link](javascript:alert(1))
\n", "xss.txt"}, + + // 139 + // Autolinks + {"\n<javascript:alert(1)>
\n<javascript:alert(1)>
\n", "xss.txt"}, + + // 140 + // Linkifier + {"\njavascript:alert(1)\n\njavascript:alert(1)\n", "javascript:alert(1)
\njavascript:alert(1)
\n", "xss.txt"}, + + // 141 + // References + {"\n[test]: javascript:alert(1)\n", "[test]: javascript:alert(1)
\n", "xss.txt"}, + + // 142 + // Make sure we decode entities before split: + {"\n```js custom-class\ntest1\n```\n\n```jscustom-class\ntest2\n```\n", "test1\n
\ntest2\n
\n", "xss.txt"},
+}
+
+func TestMarkdownIt(t *testing.T) {
+ for i, test := range mditTests {
+ switch i {
+ case 35: // XXX opennota: investigate
+ continue
+ }
+
+ test := test
+ t.Run(fmt.Sprintf("test #%d", i), func(t *testing.T) {
+ t.Parallel()
+ md := New(HTML(true), LangPrefix(""))
+ got := md.RenderToString([]byte(test.markdown))
+ if got != test.want {
+ t.Errorf("#%d: markdown(%q)\nwant %q\n got %q", i, test.markdown, test.want, got)
+ }
+ })
+ }
+}
diff --git a/vendor/gitlab.com/golang-commonmark/markdown/markdown_test.go b/vendor/gitlab.com/golang-commonmark/markdown/markdown_test.go
new file mode 100644
index 000000000..ec2710e26
--- /dev/null
+++ b/vendor/gitlab.com/golang-commonmark/markdown/markdown_test.go
@@ -0,0 +1,93 @@
+// Copyright 2015 The Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package markdown
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "testing"
+
+ "gitlab.com/opennota/wd"
+)
+
+type example struct {
+ Filename string
+ Num int `json:"example"`
+ Markdown string
+ HTML string
+ Section string
+}
+
+func loadExamplesFromJSON(fn string) []example {
+ f, err := os.Open(fn)
+ if err != nil {
+ panic(err)
+ }
+
+ var examples []example
+ err = json.NewDecoder(f).Decode(&examples)
+ if err != nil {
+ panic(err)
+ }
+
+ return examples
+}
+
+func render(src string, options ...option) (_ string, err error) {
+ defer func() {
+ if r := recover(); r != nil {
+ err = fmt.Errorf("%v", r)
+ }
+ }()
+ md := New(options...)
+ return md.RenderToString([]byte(src)), nil
+}
+
+func TestCommonMark(t *testing.T) {
+ examples := loadExamplesFromJSON("spec/commonmark-0.28.json")
+ for _, ex := range examples {
+ ex := ex
+ t.Run(fmt.Sprintf("example #%d (%s)", ex.Num, ex.Section), func(t *testing.T) {
+ result, err := render(ex.Markdown, HTML(true), XHTMLOutput(true), Linkify(false), Typographer(false), LangPrefix("language-"))
+ if err != nil {
+ t.Errorf("#%d (%s): PANIC (%v)", ex.Num, ex.Section, err)
+ } else if result != ex.HTML {
+ d := wd.ColouredDiff(ex.HTML, result, false)
+ d = wd.NumberLines(d)
+ t.Errorf("#%d (%s):\n%s", ex.Num, ex.Section, d)
+ }
+ })
+ }
+}
+
+func TestRenderSpec(t *testing.T) {
+ data, err := ioutil.ReadFile("spec/spec-0.28.txt")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ md := New(HTML(true), XHTMLOutput(true))
+ md.RenderToString(data)
+}
+
+func TestFrenchQuoteMarks(t *testing.T) {
+ md := New(Quotes([]string{"« ", " »", "‹ ", " ›"}))
+ got := md.RenderToString([]byte(`"Son 'explication' n'est qu'un mensonge", s'indigna le député.`))
+ want := "« Son ‹ explication › n’est qu’un mensonge », s’indigna le député.
\n" + if got != want { + t.Errorf("want %q, got %q", want, got) + } +} + +func TestMultibyteCharAtTheEnd(t *testing.T) { + md := New() + got := md.RenderToString([]byte("“Test”\nTest\n“Test”")) + want := "“Test”\nTest\n“Test”
\n" + if got != want { + t.Errorf("want %q, got %q", want, got) + } +} diff --git a/vendor/gitlab.com/golang-commonmark/markdown/newline.go b/vendor/gitlab.com/golang-commonmark/markdown/newline.go new file mode 100644 index 000000000..c69fa3d9f --- /dev/null +++ b/vendor/gitlab.com/golang-commonmark/markdown/newline.go @@ -0,0 +1,46 @@ +// Copyright 2015 The Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package markdown + +func ruleNewline(s *StateInline, silent bool) bool { + pos := s.Pos + src := s.Src + + if src[pos] != '\n' { + return false + } + + pending := s.Pending.Bytes() + pmax := len(pending) - 1 + max := s.PosMax + + if !silent { + if pmax >= 0 && pending[pmax] == ' ' { + if pmax >= 1 && pending[pmax-1] == ' ' { + pmax -= 2 + for pmax >= 0 && pending[pmax] == ' ' { + pmax-- + } + s.Pending.Truncate(pmax + 1) + s.PushToken(&Hardbreak{}) + } else { + s.Pending.Truncate(pmax) + s.PushToken(&Softbreak{}) + } + } else { + s.PushToken(&Softbreak{}) + } + } + + pos++ + + for pos < max && byteIsSpace(src[pos]) { + pos++ + } + + s.Pos = pos + + return true +} diff --git a/vendor/gitlab.com/golang-commonmark/markdown/options.go b/vendor/gitlab.com/golang-commonmark/markdown/options.go new file mode 100644 index 000000000..7e5b2cf37 --- /dev/null +++ b/vendor/gitlab.com/golang-commonmark/markdown/options.go @@ -0,0 +1,77 @@ +// Copyright 2015 The Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package markdown + +type option func(m *Markdown) + +func HTML(b bool) option { + return func(m *Markdown) { + m.HTML = b + } +} + +func Linkify(b bool) option { + return func(m *Markdown) { + m.Linkify = b + } +} + +func Typographer(b bool) option { + return func(m *Markdown) { + m.Typographer = b + } +} + +func Quotes(stringOrArray interface{}) option { + if s, ok := stringOrArray.(string); ok { + return func(m *Markdown) { + for i, r := range []rune(s) { + m.Quotes[i] = string(r) + } + } + } + a := stringOrArray.([]string) + return func(m *Markdown) { + for i, s := range a { + m.Quotes[i] = s + } + } +} + +func MaxNesting(n int) option { + return func(m *Markdown) { + m.MaxNesting = n + } +} + +func XHTMLOutput(b bool) option { + return func(m *Markdown) { + m.renderOptions.XHTML = b + } +} + +func Breaks(b bool) option { + return func(m *Markdown) { + m.renderOptions.Breaks = b + } +} + +func LangPrefix(p string) option { + return func(m *Markdown) { + m.renderOptions.LangPrefix = p + } +} + +func Nofollow(b bool) option { + return func(m *Markdown) { + m.renderOptions.Nofollow = b + } +} + +func Tables(b bool) option { + return func(m *Markdown) { + m.Tables = b + } +} diff --git a/vendor/gitlab.com/golang-commonmark/markdown/options_test.go b/vendor/gitlab.com/golang-commonmark/markdown/options_test.go new file mode 100644 index 000000000..4d7192abb --- /dev/null +++ b/vendor/gitlab.com/golang-commonmark/markdown/options_test.go @@ -0,0 +1,18 @@ +// Copyright 2015 The Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package markdown + +import ( + "strings" + "testing" +) + +func TestQuote(t *testing.T) { + quotes := "‘’“”" + md := New(Quotes(quotes)) + if strings.Join(md.Quotes[:], "") != quotes { + t.Errorf("expected %q, got %q", quotes, md.Quotes) + } +} diff --git a/vendor/gitlab.com/golang-commonmark/markdown/paragraph.go b/vendor/gitlab.com/golang-commonmark/markdown/paragraph.go new file mode 100644 index 000000000..a3003ef6e --- /dev/null +++ b/vendor/gitlab.com/golang-commonmark/markdown/paragraph.go @@ -0,0 +1,51 @@ +// Copyright 2015 The Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package markdown + +import "strings" + +var paragraphTerminatedBy []BlockRule + +func ruleParagraph(s *StateBlock, startLine, _ int, _ bool) bool { + nextLine := startLine + 1 + endLine := s.LineMax + + oldParentType := s.ParentType + s.ParentType = ptParagraph + +outer: + for ; nextLine < endLine && !s.IsLineEmpty(nextLine); nextLine++ { + if s.SCount[nextLine]-s.BlkIndent > 3 { + continue + } + + if s.SCount[nextLine] < 0 { + continue + } + + for _, r := range paragraphTerminatedBy { + if r(s, nextLine, endLine, true) { + break outer + } + } + } + + content := strings.TrimSpace(s.Lines(startLine, nextLine, s.BlkIndent, false)) + + s.Line = nextLine + + s.PushOpeningToken(&ParagraphOpen{ + Map: [2]int{startLine, s.Line}, + }) + s.PushToken(&Inline{ + Content: content, + Map: [2]int{startLine, s.Line}, + }) + s.PushClosingToken(&ParagraphClose{}) + + s.ParentType = oldParentType + + return true +} diff --git a/vendor/gitlab.com/golang-commonmark/markdown/parser_block.go b/vendor/gitlab.com/golang-commonmark/markdown/parser_block.go new file mode 100644 index 000000000..8a6ee1ad4 --- /dev/null +++ b/vendor/gitlab.com/golang-commonmark/markdown/parser_block.go @@ -0,0 +1,159 @@ +// Copyright 2015 The Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package markdown + +import ( + "bytes" + "unicode/utf8" +) + +type ParserBlock struct{} + +type BlockRule func(*StateBlock, int, int, bool) bool + +var blockRules []BlockRule + +var nl = []byte{'\n'} + +func normalizeNewlines(src []byte) ([]byte, int) { + if bytes.IndexByte(src, '\r') == -1 { + return src, bytes.Count(src, nl) + } + n := 0 + buf := make([]byte, 0, len(src)) + for i := 0; i < len(src); i++ { + switch ch := src[i]; ch { + case '\n': + n++ + buf = append(buf, '\n') + case '\r': + buf = append(buf, '\n') + n++ + if i < len(src)-1 && src[i+1] == '\n' { + i++ + } + default: + buf = append(buf, ch) + } + } + return buf, n +} + +func (b ParserBlock) Parse(src []byte, md *Markdown, env *Environment) []Token { + src, n := normalizeNewlines(src) + if len(src) == 0 || src[len(src)-1] != '\n' { + n++ + } + n++ + + indentFound := false + start := 0 + indent := 0 + offset := 0 + + mem := make([]int, 0, n*5) + bMarks := mem[0:0:n] + eMarks := mem[n : n : n*2] + tShift := mem[n*2 : n*2 : n*3] + sCount := mem[n*3 : n*3 : n*4] + bsCount := mem[n*4 : n*4 : n*5] + + _, lastRuneLen := utf8.DecodeLastRune(src) + lastRunePos := len(src) - lastRuneLen + for pos, r := range string(src) { + if !indentFound { + if runeIsSpace(r) { + indent++ + if r == '\t' { + offset += 4 - offset%4 + } else { + offset++ + } + continue + } + indentFound = true + } + + if r == '\n' || pos == lastRunePos { + if r != '\n' { + pos = len(src) + } + bMarks = append(bMarks, start) + eMarks = append(eMarks, pos) + tShift = append(tShift, indent) + sCount = append(sCount, offset) + bsCount = append(bsCount, 0) + + indentFound = false + indent = 0 + offset = 0 + start = pos + 1 + } + } + + bMarks = append(bMarks, len(src)) + eMarks = append(eMarks, len(src)) + tShift = append(tShift, 0) + sCount = append(sCount, 0) + bsCount = append(bsCount, 0) + + var s StateBlock + s.BMarks = bMarks + s.EMarks = eMarks + s.TShift = tShift + s.SCount = sCount + s.BSCount = bsCount + s.LineMax = n - 1 + s.Src = string(src) + s.Md = md + s.Env = env + + b.Tokenize(&s, s.Line, s.LineMax) + + return s.Tokens +} + +func (ParserBlock) Tokenize(s *StateBlock, startLine, endLine int) { + line := startLine + hasEmptyLines := false + maxNesting := s.Md.MaxNesting + + for line < endLine { + line = s.SkipEmptyLines(line) + s.Line = line + if line >= endLine { + break + } + + if s.SCount[line] < s.BlkIndent { + break + } + + if s.Level >= maxNesting { + s.Line = endLine + break + } + + for _, r := range blockRules { + if r(s, line, endLine, false) { + break + } + } + + s.Tight = !hasEmptyLines + + if s.IsLineEmpty(s.Line - 1) { + hasEmptyLines = true + } + + line = s.Line + + if line < endLine && s.IsLineEmpty(line) { + hasEmptyLines = true + line++ + s.Line = line + } + } +} diff --git a/vendor/gitlab.com/golang-commonmark/markdown/parser_inline.go b/vendor/gitlab.com/golang-commonmark/markdown/parser_inline.go new file mode 100644 index 000000000..5e976ebd9 --- /dev/null +++ b/vendor/gitlab.com/golang-commonmark/markdown/parser_inline.go @@ -0,0 +1,106 @@ +// Copyright 2015 The Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package markdown + +import "unicode/utf8" + +type ParserInline struct { +} + +type ( + InlineRule func(*StateInline, bool) bool + PostprocessRule func(*StateInline) +) + +var ( + inlineRules []InlineRule + postprocessRules []PostprocessRule +) + +func (i ParserInline) Parse(src string, md *Markdown, env *Environment) []Token { + if src == "" { + return nil + } + + var s StateInline + s.Src = src + s.Md = md + s.Env = env + s.PosMax = len(src) + s.Tokens = s.bootstrap[:0] + + i.Tokenize(&s) + + for _, r := range postprocessRules { + r(&s) + } + + return s.Tokens +} + +func (ParserInline) Tokenize(s *StateInline) { + end := s.PosMax + src := s.Src + maxNesting := s.Md.MaxNesting + ok := false + + for s.Pos < end { + if s.Level < maxNesting { + for _, rule := range inlineRules { + ok = rule(s, false) + if ok { + break + } + } + } + + if ok { + if s.Pos >= end { + break + } + continue + } + + r, size := utf8.DecodeRuneInString(src[s.Pos:]) + s.Pending.WriteRune(r) + s.Pos += size + } + + if s.Pending.Len() > 0 { + s.PushPending() + } +} + +func (ParserInline) SkipToken(s *StateInline) { + pos := s.Pos + if s.Cache != nil { + if newPos, ok := s.Cache[pos]; ok { + s.Pos = newPos + return + } + } else { + s.Cache = make(map[int]int) + } + + ok := false + if s.Level < s.Md.MaxNesting { + for _, r := range inlineRules { + s.Level++ + ok = r(s, true) + s.Level-- + if ok { + break + } + } + } else { + s.Pos = s.PosMax + } + + if !ok { + _, size := utf8.DecodeRuneInString(s.Src[s.Pos:]) + s.Pos += size + } + s.Cache[pos] = s.Pos +} diff --git a/vendor/gitlab.com/golang-commonmark/markdown/plugins.go b/vendor/gitlab.com/golang-commonmark/markdown/plugins.go new file mode 100644 index 000000000..5ba818cda --- /dev/null +++ b/vendor/gitlab.com/golang-commonmark/markdown/plugins.go @@ -0,0 +1,158 @@ +// Copyright 2015 The Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package markdown + +import "sort" + +type registeredCoreRule struct { + id int + rule CoreRule +} + +var registeredCoreRules []registeredCoreRule + +type registeredBlockRule struct { + id int + rule BlockRule + terminates []int +} + +var registeredBlockRules []registeredBlockRule + +type registeredInlineRule struct { + id int + rule InlineRule +} + +var registeredInlineRules []registeredInlineRule + +type registeredPostprocessRule struct { + id int + rule PostprocessRule +} + +var registeredPostprocessRules []registeredPostprocessRule + +func indexInt(a []int, n int) int { + for i, m := range a { + if m == n { + return i + } + } + return -1 +} + +func RegisterCoreRule(id int, rule CoreRule) { + registeredCoreRules = append(registeredCoreRules, registeredCoreRule{ + id: id, + rule: rule, + }) + sort.Slice(registeredCoreRules, func(i, j int) bool { + return registeredCoreRules[i].id < registeredCoreRules[j].id + }) + + coreRules = coreRules[:0] + for _, r := range registeredCoreRules { + coreRules = append(coreRules, r.rule) + } +} + +func RegisterBlockRule(id int, rule BlockRule, terminates []int) { + registeredBlockRules = append(registeredBlockRules, registeredBlockRule{ + id: id, + rule: rule, + terminates: terminates, + }) + sort.Slice(registeredBlockRules, func(i, j int) bool { + return registeredBlockRules[i].id < registeredBlockRules[j].id + }) + + blockRules = blockRules[:0] + blockquoteTerminatedBy = blockquoteTerminatedBy[:0] + listTerminatedBy = listTerminatedBy[:0] + referenceTerminatedBy = referenceTerminatedBy[:0] + paragraphTerminatedBy = paragraphTerminatedBy[:0] + for _, r := range registeredBlockRules { + blockRules = append(blockRules, r.rule) + if indexInt(r.terminates, 400) != -1 { + blockquoteTerminatedBy = append(blockquoteTerminatedBy, r.rule) + } + if indexInt(r.terminates, 600) != -1 { + listTerminatedBy = append(listTerminatedBy, r.rule) + } + if indexInt(r.terminates, 700) != -1 { + referenceTerminatedBy = append(referenceTerminatedBy, r.rule) + } + if indexInt(r.terminates, 1100) != -1 { + paragraphTerminatedBy = append(paragraphTerminatedBy, r.rule) + } + } +} + +func RegisterInlineRule(id int, rule InlineRule) { + registeredInlineRules = append(registeredInlineRules, registeredInlineRule{ + id: id, + rule: rule, + }) + sort.Slice(registeredInlineRules, func(i, j int) bool { + return registeredInlineRules[i].id < registeredInlineRules[j].id + }) + + inlineRules = inlineRules[:0] + for _, r := range registeredInlineRules { + inlineRules = append(inlineRules, r.rule) + } +} + +func RegisterPostprocessRule(id int, rule PostprocessRule) { + registeredPostprocessRules = append(registeredPostprocessRules, registeredPostprocessRule{ + id: id, + rule: rule, + }) + sort.Slice(registeredPostprocessRules, func(i, j int) bool { + return registeredPostprocessRules[i].id < registeredPostprocessRules[j].id + }) + + postprocessRules = postprocessRules[:0] + for _, r := range registeredPostprocessRules { + postprocessRules = append(postprocessRules, r.rule) + } +} + +func init() { + RegisterCoreRule(100, ruleInline) + RegisterCoreRule(200, ruleLinkify) + RegisterCoreRule(300, ruleReplacements) + RegisterCoreRule(400, ruleSmartQuotes) + + RegisterBlockRule(100, ruleTable, []int{1100, 700}) + RegisterBlockRule(200, ruleCode, nil) + RegisterBlockRule(300, ruleFence, []int{1100, 700, 400, 600}) + RegisterBlockRule(400, ruleBlockQuote, []int{1100, 700, 400, 600}) + RegisterBlockRule(500, ruleHR, []int{1100, 700, 400, 600}) + RegisterBlockRule(600, ruleList, []int{1100, 700, 400}) + RegisterBlockRule(700, ruleReference, nil) + RegisterBlockRule(800, ruleHeading, []int{1100, 700, 400}) + RegisterBlockRule(900, ruleLHeading, nil) + RegisterBlockRule(1000, ruleHTMLBlock, []int{1100, 700, 400}) + RegisterBlockRule(1100, ruleParagraph, nil) + + RegisterInlineRule(100, ruleText) + RegisterInlineRule(200, ruleNewline) + RegisterInlineRule(300, ruleEscape) + RegisterInlineRule(400, ruleBackticks) + RegisterInlineRule(500, ruleStrikeThrough) + RegisterInlineRule(600, ruleEmphasis) + RegisterInlineRule(700, ruleLink) + RegisterInlineRule(800, ruleImage) + RegisterInlineRule(900, ruleAutolink) + RegisterInlineRule(1000, ruleHTMLInline) + RegisterInlineRule(1100, ruleEntity) + + RegisterPostprocessRule(100, ruleBalancePairs) + RegisterPostprocessRule(200, ruleStrikethroughPostprocess) + RegisterPostprocessRule(300, ruleEmphasisPostprocess) + RegisterPostprocessRule(400, ruleTextCollapse) +} diff --git a/vendor/gitlab.com/golang-commonmark/markdown/reference.go b/vendor/gitlab.com/golang-commonmark/markdown/reference.go new file mode 100644 index 000000000..6e97809e9 --- /dev/null +++ b/vendor/gitlab.com/golang-commonmark/markdown/reference.go @@ -0,0 +1,173 @@ +// Copyright 2015 The Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package markdown + +import "strings" + +var referenceTerminatedBy []BlockRule + +func ruleReference(s *StateBlock, startLine, _ int, silent bool) bool { + lines := 0 + pos := s.BMarks[startLine] + s.TShift[startLine] + max := s.EMarks[startLine] + nextLine := startLine + 1 + + if s.SCount[startLine]-s.BlkIndent >= 4 { + return false + } + + src := s.Src + + if src[pos] != '[' { + return false + } + + pos++ + for pos < max { + if src[pos] == ']' && src[pos-1] != '\\' { + if pos+1 == max { + return false + } + if src[pos+1] != ':' { + return false + } + break + } + pos++ + } + + endLine := s.LineMax + + oldParentType := s.ParentType + s.ParentType = ptReference +outer: + for ; nextLine < endLine && !s.IsLineEmpty(nextLine); nextLine++ { + if s.SCount[nextLine]-s.BlkIndent > 3 { + continue + } + + if s.SCount[nextLine] < 0 { + continue + } + + for _, r := range referenceTerminatedBy { + if r(s, nextLine, endLine, true) { + break outer + } + } + } + + str := strings.TrimSpace(s.Lines(startLine, nextLine, s.BlkIndent, false)) + max = len(str) + + var labelEnd int + for pos = 1; pos < max; pos++ { + b := str[pos] + if b == '[' { + return false + } else if b == ']' { + labelEnd = pos + break + } else if b == '\n' { + lines++ + } else if b == '\\' { + pos++ + if pos < max && str[pos] == '\n' { + lines++ + } + } + } + + if labelEnd <= 0 || labelEnd+1 >= max || str[labelEnd+1] != ':' { + return false + } + + for pos = labelEnd + 2; pos < max; pos++ { + b := str[pos] + if b == '\n' { + lines++ + } else if !byteIsSpace(b) { + break + } + } + + href, nlines, endpos, ok := parseLinkDestination(str, pos, max) + if !ok { + return false + } + href = normalizeLink(href) + if !validateLink(href) { + return false + } + + pos = endpos + lines += nlines + + destEndPos := pos + destEndLineNo := lines + + start := pos + for ; pos < max; pos++ { + b := str[pos] + if b == '\n' { + lines++ + } else if !byteIsSpace(b) { + break + } + } + + title, nlines, endpos, ok := parseLinkTitle(str, pos, max) + if pos < max && start != pos && ok { + pos = endpos + lines += nlines + } else { + pos = destEndPos + lines = destEndLineNo + } + + for pos < max && byteIsSpace(str[pos]) { + pos++ + } + + if pos < max && str[pos] != '\n' { + if title != "" { + title = "" + pos = destEndPos + lines = destEndLineNo + for pos < max && byteIsSpace(src[pos]) { + pos++ + } + } + } + + if pos < max && str[pos] != '\n' { + return false + } + + label := normalizeReference(str[1:labelEnd]) + if label == "" { + return false + } + + if silent { + return true + } + + if s.Env.References == nil { + s.Env.References = make(map[string]map[string]string) + } + if _, ok := s.Env.References[label]; !ok { + s.Env.References[label] = map[string]string{ + "title": title, + "href": href, + } + } + + s.ParentType = oldParentType + + s.Line = startLine + lines + 1 + + return true +} diff --git a/vendor/gitlab.com/golang-commonmark/markdown/render.go b/vendor/gitlab.com/golang-commonmark/markdown/render.go new file mode 100644 index 000000000..3955f051c --- /dev/null +++ b/vendor/gitlab.com/golang-commonmark/markdown/render.go @@ -0,0 +1,333 @@ +// Copyright 2015 The Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package markdown + +import ( + "io" + "regexp" + "strconv" + "strings" + + "gitlab.com/golang-commonmark/html" +) + +type Renderer struct { + w *monadicWriter +} + +func NewRenderer(w io.Writer) *Renderer { + return &Renderer{newMonadicWriter(w)} +} + +func (r *Renderer) Render(tokens []Token, options RenderOptions) error { + for i, tok := range tokens { + if tok, ok := tok.(*Inline); ok { + r.renderInline(tok.Children, options) + } else { + r.renderToken(tokens, i, options) + } + } + + r.w.Flush() + + return r.w.err +} + +func (r *Renderer) renderInline(tokens []Token, o RenderOptions) { + for i := range tokens { + r.renderToken(tokens, i, o) + } +} + +func (r *Renderer) renderInlineAsText(tokens []Token) { + for _, tok := range tokens { + if text, ok := tok.(*Text); ok { + html.WriteEscapedString(r.w, text.Content) + } else if img, ok := tok.(*Image); ok { + r.renderInlineAsText(img.Tokens) + } + } +} + +var rNotSpace = regexp.MustCompile(`^\S+`) + +func (r *Renderer) renderToken(tokens []Token, idx int, options RenderOptions) { + tok := tokens[idx] + + if idx > 0 && tok.Block() && !tok.Closing() { + switch t := tokens[idx-1].(type) { + case *ParagraphOpen: + if t.Hidden { + r.w.WriteByte('\n') + } + case *ParagraphClose: + if t.Hidden { + r.w.WriteByte('\n') + } + } + } + + switch tok := tok.(type) { + case *BlockquoteClose: + r.w.WriteString("") + + case *BlockquoteOpen: + r.w.WriteString("") + + case *BulletListClose: + r.w.WriteString("") + + case *BulletListOpen: + r.w.WriteString("") + + case *CodeBlock: + r.w.WriteString("
") + + case *CodeInline: + r.w.WriteString("") + html.WriteEscapedString(r.w, tok.Content) + r.w.WriteString("
") + html.WriteEscapedString(r.w, tok.Content) + r.w.WriteString("
") + + case *EmphasisClose: + r.w.WriteString("") + + case *EmphasisOpen: + r.w.WriteString("") + + case *Fence: + r.w.WriteString("") + + case *Hardbreak: + if options.XHTML { + r.w.WriteString("') + html.WriteEscapedString(r.w, tok.Content) + r.w.WriteString("
\n") + } else { + r.w.WriteString("
\n") + } + + case *HeadingClose: + r.w.WriteString("") + + case *HeadingOpen: + r.w.WriteString("') + + case *Hr: + if options.XHTML { + r.w.WriteString("
") + } else { + r.w.WriteString("
") + } + + case *HTMLBlock: + r.w.WriteString(tok.Content) + return // no newline + + case *HTMLInline: + r.w.WriteString(tok.Content) + + case *Image: + r.w.WriteString(`") + } else { + r.w.WriteByte('>') + } + + case *LinkClose: + r.w.WriteString("") + + case *LinkOpen: + r.w.WriteString(`') + + case *ListItemClose: + r.w.WriteString("") + + case *ListItemOpen: + r.w.WriteString("") + + case *OrderedListClose: + r.w.WriteString("") + + case *OrderedListOpen: + if tok.Order != 1 { + r.w.WriteString(` `) + } else { + r.w.WriteString("
") + } + + case *ParagraphClose: + if tok.Hidden { + return + } + if !tok.Tight { + r.w.WriteString("") + } else if tokens[idx+1].Closing() { + return // no newline + } + + case *ParagraphOpen: + if tok.Hidden { + return + } + if !tok.Tight { + r.w.WriteString("
") + } + + case *Softbreak: + if options.Breaks { + if options.XHTML { + r.w.WriteString("
\n") + } else { + r.w.WriteString("
\n") + } + } else { + r.w.WriteByte('\n') + } + return + + case *StrongClose: + r.w.WriteString("") + + case *StrongOpen: + r.w.WriteString("") + + case *StrikethroughClose: + r.w.WriteString("") + + case *StrikethroughOpen: + r.w.WriteString("") + + case *TableClose: + r.w.WriteString("") + + case *TableOpen: + r.w.WriteString("