4
0
mirror of https://github.com/cwinfo/matterbridge.git synced 2025-06-26 21:19:22 +00:00

Add support for markdown to HTML conversion (matrix). Closes #663 (#670)

This uses our own gomatrix lib with the SendHTML function which
adds HTML to formatted_body in matrix.
golang-commonmark is used to convert markdown into valid HTML.
This commit is contained in:
Wim
2019-01-06 22:25:19 +01:00
committed by GitHub
parent 048158ad6d
commit 04567c765e
85 changed files with 40959 additions and 6 deletions

View File

@ -0,0 +1,25 @@
image: golang:1.11
stages:
- build
- test
before_script:
- go get github.com/russross/blackfriday
- go get gitlab.com/golang-commonmark/html
- go get gitlab.com/golang-commonmark/linkify
- go get gitlab.com/golang-commonmark/mdurl
- go get gitlab.com/golang-commonmark/puny
- go get gitlab.com/opennota/wd
- go get gopkg.in/russross/blackfriday.v2
build:
stage: build
script:
- go build ./...
test:
stage: test
script:
- test -z "$(gofmt -l . | tee /dev/stderr)"
- go test -cover ./...

1
vendor/gitlab.com/golang-commonmark/markdown/AUTHORS generated vendored Normal file
View File

@ -0,0 +1 @@
opennota@gmail.com

10
vendor/gitlab.com/golang-commonmark/markdown/LICENSE generated vendored Normal file
View File

@ -0,0 +1,10 @@
Copyright (c) 2015, The Authors
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

64
vendor/gitlab.com/golang-commonmark/markdown/README.md generated vendored Normal file
View File

@ -0,0 +1,64 @@
markdown [![GoDoc](http://godoc.org/gitlab.com/golang-commonmark/markdown?status.svg)](http://godoc.org/gitlab.com/golang-commonmark/markdown) [![License](https://img.shields.io/badge/licence-BSD--2--Clause-blue.svg)](https://opensource.org/licenses/BSD-2-Clause) [![Pipeline status](https://gitlab.com/golang-commonmark/markdown/badges/master/pipeline.svg)](https://gitlab.com/golang-commonmark/markdown/commits/master) [![Coverage report](https://gitlab.com/golang-commonmark/markdown/badges/master/coverage.svg)](https://gitlab.com/golang-commonmark/markdown/commits/master)
========
Package golang-commonmark/markdown provides a CommonMark-compliant markdown parser and renderer, written in Go.
## Installation
go get -u gitlab.com/golang-commonmark/markdown
You can also go get [mdtool](https://gitlab.com/golang-commonmark/mdtool), an example command-line tool:
go get -u gitlab.com/golang-commonmark/mdtool
## Standards support
Currently supported CommonMark spec: [v0.28](http://spec.commonmark.org/0.28/).
## Extensions
Besides the features required by CommonMark, golang-commonmark/markdown supports:
* Tables (GFM)
* Strikethrough (GFM)
* Autoconverting plain-text URLs to links
* Typographic replacements (smart quotes and other)
## Usage
``` go
md := markdown.New(markdown.XHTMLOutput(true))
fmt.Println(md.RenderToString([]byte("Header\n===\nText")))
```
Check out [the source of mdtool](https://gitlab.com/golang-commonmark/mdtool/blob/master/main.go) for a more complete example.
The following options are currently supported:
Name | Type | Description | Default
--------------- | --------- | ----------------------------------------------------------- | ---------
HTML | bool | whether to enable raw HTML | false
Tables | bool | whether to enable GFM tables | true
Linkify | bool | whether to autoconvert plain-text URLs to links | true
Typographer | bool | whether to enable typographic replacements | true
Quotes | string / []string | double + single quote replacement pairs for the typographer | “”‘’
MaxNesting | int | maximum nesting level | 20
LangPrefix | string | CSS language prefix for fenced blocks | language-
Breaks | bool | whether to convert newlines inside paragraphs into `<br>` | 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.

26
vendor/gitlab.com/golang-commonmark/markdown/align.go generated vendored Normal file
View File

@ -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 ""
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}
}
}

View File

@ -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
}

37
vendor/gitlab.com/golang-commonmark/markdown/code.go generated vendored Normal file
View File

@ -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
}

View File

@ -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{}
}
}
}

35
vendor/gitlab.com/golang-commonmark/markdown/entity.go generated vendored Normal file
View File

@ -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
}

61
vendor/gitlab.com/golang-commonmark/markdown/escape.go generated vendored Normal file
View File

@ -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
}

102
vendor/gitlab.com/golang-commonmark/markdown/fence.go generated vendored Normal file
View File

@ -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
}

9
vendor/gitlab.com/golang-commonmark/markdown/fuzz.go generated vendored Normal file
View File

@ -0,0 +1,9 @@
//+build gofuzz
package markdown
func Fuzz(data []byte) int {
md := New(HTML(true))
md.Parse(data)
return 1
}

View File

@ -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
}

164
vendor/gitlab.com/golang-commonmark/markdown/helpers.go generated vendored Normal file
View File

@ -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
}

54
vendor/gitlab.com/golang-commonmark/markdown/hr.go generated vendored Normal file
View File

@ -0,0 +1,54 @@
// 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 ruleHR(s *StateBlock, startLine, endLine int, silent bool) bool {
pos := s.BMarks[startLine] + s.TShift[startLine]
max := s.EMarks[startLine]
if pos >= 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
}

View File

@ -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
}

View File

@ -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 = `<![A-Z]+\s+[^>]*>`
cdata = `<!\[CDATA\[[\s\S]*?\]\]>`
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
}

131
vendor/gitlab.com/golang-commonmark/markdown/image.go generated vendored Normal file
View File

@ -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
}

13
vendor/gitlab.com/golang-commonmark/markdown/inline.go generated vendored Normal file
View File

@ -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)
}
}
}

View File

@ -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
}

132
vendor/gitlab.com/golang-commonmark/markdown/link.go generated vendored Normal file
View File

@ -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
}

131
vendor/gitlab.com/golang-commonmark/markdown/linkify.go generated vendored Normal file
View File

@ -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
}
}
}
}
}

285
vendor/gitlab.com/golang-commonmark/markdown/list.go generated vendored Normal file
View File

@ -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
}

View File

@ -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 <br>
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()
}

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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
}
}
}

View File

@ -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
}

158
vendor/gitlab.com/golang-commonmark/markdown/plugins.go generated vendored Normal file
View File

@ -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)
}

View File

@ -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
}

333
vendor/gitlab.com/golang-commonmark/markdown/render.go generated vendored Normal file
View File

@ -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("</blockquote>")
case *BlockquoteOpen:
r.w.WriteString("<blockquote>")
case *BulletListClose:
r.w.WriteString("</ul>")
case *BulletListOpen:
r.w.WriteString("<ul>")
case *CodeBlock:
r.w.WriteString("<pre><code>")
html.WriteEscapedString(r.w, tok.Content)
r.w.WriteString("</code></pre>")
case *CodeInline:
r.w.WriteString("<code>")
html.WriteEscapedString(r.w, tok.Content)
r.w.WriteString("</code>")
case *EmphasisClose:
r.w.WriteString("</em>")
case *EmphasisOpen:
r.w.WriteString("<em>")
case *Fence:
r.w.WriteString("<pre><code")
if tok.Params != "" {
langName := strings.SplitN(unescapeAll(tok.Params), " ", 2)[0]
langName = rNotSpace.FindString(langName)
if langName != "" {
r.w.WriteString(` class="`)
r.w.WriteString(options.LangPrefix)
html.WriteEscapedString(r.w, langName)
r.w.WriteByte('"')
}
}
r.w.WriteByte('>')
html.WriteEscapedString(r.w, tok.Content)
r.w.WriteString("</code></pre>")
case *Hardbreak:
if options.XHTML {
r.w.WriteString("<br />\n")
} else {
r.w.WriteString("<br>\n")
}
case *HeadingClose:
r.w.WriteString("</h")
r.w.WriteByte("0123456789"[tok.HLevel])
r.w.WriteString(">")
case *HeadingOpen:
r.w.WriteString("<h")
r.w.WriteByte("0123456789"[tok.HLevel])
r.w.WriteByte('>')
case *Hr:
if options.XHTML {
r.w.WriteString("<hr />")
} else {
r.w.WriteString("<hr>")
}
case *HTMLBlock:
r.w.WriteString(tok.Content)
return // no newline
case *HTMLInline:
r.w.WriteString(tok.Content)
case *Image:
r.w.WriteString(`<img src="`)
html.WriteEscapedString(r.w, tok.Src)
r.w.WriteString(`" alt="`)
r.renderInlineAsText(tok.Tokens)
r.w.WriteByte('"')
if tok.Title != "" {
r.w.WriteString(` title="`)
html.WriteEscapedString(r.w, tok.Title)
r.w.WriteByte('"')
}
if options.XHTML {
r.w.WriteString(" />")
} else {
r.w.WriteByte('>')
}
case *LinkClose:
r.w.WriteString("</a>")
case *LinkOpen:
r.w.WriteString(`<a href="`)
html.WriteEscapedString(r.w, tok.Href)
r.w.WriteByte('"')
if tok.Title != "" {
r.w.WriteString(` title="`)
html.WriteEscapedString(r.w, (tok.Title))
r.w.WriteByte('"')
}
if tok.Target != "" {
r.w.WriteString(` target="`)
html.WriteEscapedString(r.w, tok.Target)
r.w.WriteByte('"')
}
if options.Nofollow {
r.w.WriteString(` rel="nofollow"`)
}
r.w.WriteByte('>')
case *ListItemClose:
r.w.WriteString("</li>")
case *ListItemOpen:
r.w.WriteString("<li>")
case *OrderedListClose:
r.w.WriteString("</ol>")
case *OrderedListOpen:
if tok.Order != 1 {
r.w.WriteString(`<ol start="`)
r.w.WriteString(strconv.Itoa(tok.Order))
r.w.WriteString(`">`)
} else {
r.w.WriteString("<ol>")
}
case *ParagraphClose:
if tok.Hidden {
return
}
if !tok.Tight {
r.w.WriteString("</p>")
} else if tokens[idx+1].Closing() {
return // no newline
}
case *ParagraphOpen:
if tok.Hidden {
return
}
if !tok.Tight {
r.w.WriteString("<p>")
}
case *Softbreak:
if options.Breaks {
if options.XHTML {
r.w.WriteString("<br />\n")
} else {
r.w.WriteString("<br>\n")
}
} else {
r.w.WriteByte('\n')
}
return
case *StrongClose:
r.w.WriteString("</strong>")
case *StrongOpen:
r.w.WriteString("<strong>")
case *StrikethroughClose:
r.w.WriteString("</s>")
case *StrikethroughOpen:
r.w.WriteString("<s>")
case *TableClose:
r.w.WriteString("</table>")
case *TableOpen:
r.w.WriteString("<table>")
case *TbodyClose:
r.w.WriteString("</tbody>")
case *TbodyOpen:
r.w.WriteString("<tbody>")
case *TdClose:
r.w.WriteString("</td>")
case *TdOpen:
if tok.Align != AlignNone {
r.w.WriteString(`<td style="text-align:`)
r.w.WriteString(tok.Align.String())
r.w.WriteString(`">`)
} else {
r.w.WriteString("<td>")
}
case *Text:
html.WriteEscapedString(r.w, tok.Content)
case *TheadClose:
r.w.WriteString("</thead>")
case *TheadOpen:
r.w.WriteString("<thead>")
case *ThClose:
r.w.WriteString("</th>")
case *ThOpen:
if align := tok.Align; align != AlignNone {
r.w.WriteString(`<th style="text-align:`)
r.w.WriteString(align.String())
r.w.WriteString(`">`)
} else {
r.w.WriteString("<th>")
}
case *TrClose:
r.w.WriteString("</tr>")
case *TrOpen:
r.w.WriteString("<tr>")
default:
panic("unknown token type")
}
needLf := false
if tok.Block() {
needLf = true
if tok.Opening() {
nextTok := tokens[idx+1]
blockquote := false
switch nextTok := nextTok.(type) {
case *Inline:
needLf = false
case *ParagraphOpen:
if nextTok.Tight || nextTok.Hidden {
needLf = false
}
case *ParagraphClose:
if nextTok.Tight || nextTok.Hidden {
needLf = false
}
case *BlockquoteClose:
blockquote = true
}
if !blockquote && needLf && nextTok.Closing() && nextTok.Tag() == tok.Tag() {
needLf = false
}
}
}
if needLf {
r.w.WriteByte('\n')
}
}

View File

@ -0,0 +1,229 @@
// 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 exclquest(b byte) bool {
return b == '!' || b == '?'
}
func byteToLower(b byte) byte {
if b >= 'A' && b <= 'Z' {
return b - 'A' + 'a'
}
return b
}
var replChar = [256]bool{
'(': true,
'!': true,
'+': true,
',': true,
'-': true,
'.': true,
'?': true,
}
func performReplacements(s string) string {
var ss []string
start := 0
for i := 0; i < len(s); i++ {
b := s[i]
if replChar[b] {
outer:
switch b {
case '(':
if i+2 >= len(s) {
break
}
b2 := s[i+1]
b2 = byteToLower(b2)
switch b2 {
case 'c', 'r', 'p':
if s[i+2] != ')' {
break outer
}
switch b2 {
case 'c':
if start < i {
ss = append(ss, s[start:i])
}
ss = append(ss, "©")
case 'r':
if start < i {
ss = append(ss, s[start:i])
}
ss = append(ss, "®")
case 'p':
if start < i {
ss = append(ss, s[start:i])
}
ss = append(ss, "§")
}
i += 2
start = i + 1
continue
case 't':
if i+3 >= len(s) {
break outer
}
if s[i+3] != ')' || byteToLower(s[i+2]) != 'm' {
break outer
}
if start < i {
ss = append(ss, s[start:i])
}
ss = append(ss, "™")
i += 3
start = i + 1
continue
default:
break outer
}
case '+':
if i+1 >= len(s) || s[i+1] != '-' {
break
}
if start < i {
ss = append(ss, s[start:i])
}
ss = append(ss, "±")
i++
start = i + 1
continue
case '.':
if i+1 >= len(s) || s[i+1] != '.' {
break
}
j := i + 2
for j < len(s) && s[j] == '.' {
j++
}
if start < i {
ss = append(ss, s[start:i])
}
if i == 0 || !(s[i-1] == '?' || s[i-1] == '!') {
ss = append(ss, "…")
} else {
ss = append(ss, "..")
}
i = j - 1
start = i + 1
continue
case '?', '!':
if i+3 >= len(s) {
break
}
if !(exclquest(s[i+1]) && exclquest(s[i+2]) && exclquest(s[i+3])) {
break
}
if start < i {
ss = append(ss, s[start:i])
}
ss = append(ss, s[i:i+3])
j := i + 3
for j < len(s) && exclquest(s[j]) {
j++
}
i = j - 1
start = i + 1
continue
case ',':
if i+1 >= len(s) || s[i+1] != ',' {
break
}
if start < i {
ss = append(ss, s[start:i])
}
ss = append(ss, ",")
j := i + 2
for j < len(s) && s[j] == ',' {
j++
}
i = j - 1
start = i + 1
continue
case '-':
if i+1 >= len(s) || s[i+1] != '-' {
break
}
if i+2 >= len(s) || s[i+2] != '-' {
if start < i {
ss = append(ss, s[start:i])
}
ss = append(ss, "")
i++
start = i + 1
continue
}
if i+3 >= len(s) || s[i+3] != '-' {
if start < i {
ss = append(ss, s[start:i])
}
ss = append(ss, "—")
i += 2
start = i + 1
continue
}
j := i + 3
for j < len(s) && s[j] == '-' {
j++
}
if start < i {
ss = append(ss, s[start:i])
}
ss = append(ss, s[i:j])
i = j - 1
start = i + 1
continue
}
}
}
if ss == nil {
return s
}
if start < len(s) {
ss = append(ss, s[start:])
}
return strings.Join(ss, "")
}
func ruleReplacements(s *StateCore) {
if !s.Md.Typographer {
return
}
insideLink := false
for _, tok := range s.Tokens {
if tok, ok := tok.(*Inline); ok {
for _, itok := range tok.Children {
switch itok := itok.(type) {
case *LinkOpen:
insideLink = true
case *LinkClose:
insideLink = false
case *Text:
if !insideLink {
itok.Content = performReplacements(itok.Content)
}
}
}
}
}
}

View File

@ -0,0 +1,256 @@
// 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"
"unicode"
"unicode/utf8"
)
func nextQuoteIndex(s []rune, from int) int {
for i := from; i < len(s); i++ {
r := s[i]
if r == '\'' || r == '"' {
return i
}
}
return -1
}
func firstRune(s string) rune {
for _, r := range s {
return r
}
return utf8.RuneError
}
func replaceQuotes(tokens []Token, s *StateCore) {
type stackItem struct {
token int
text []rune
pos int
single bool
level int
}
var stack []stackItem
var changed map[int][]rune
for i, tok := range tokens {
thisLevel := tok.Level()
j := len(stack) - 1
for j >= 0 {
if stack[j].level <= thisLevel {
break
}
j--
}
stack = stack[:j+1]
tok, ok := tok.(*Text)
if !ok || !strings.ContainsAny(tok.Content, `"'`) {
continue
}
text := []rune(tok.Content)
pos := 0
max := len(text)
loop:
for pos < max {
index := nextQuoteIndex(text, pos)
if index < 0 {
break
}
canOpen := true
canClose := true
pos = index + 1
isSingle := text[index] == '\''
lastChar := ' '
if index-1 > 0 {
lastChar = text[index-1]
} else {
loop1:
for j := i - 1; j >= 0; j-- {
switch tok := tokens[j].(type) {
case *Softbreak:
break loop1
case *Hardbreak:
break loop1
case *Text:
lastChar, _ = utf8.DecodeLastRuneInString(tok.Content)
break loop1
default:
continue
}
}
}
nextChar := ' '
if pos < max {
nextChar = text[pos]
} else {
loop2:
for j := i + 1; j < len(tokens); j++ {
switch tok := tokens[j].(type) {
case *Softbreak:
break loop2
case *Hardbreak:
break loop2
case *Text:
nextChar, _ = utf8.DecodeRuneInString(tok.Content)
break loop2
default:
continue
}
}
}
isLastPunct := isMdAsciiPunct(lastChar) || unicode.IsPunct(lastChar)
isNextPunct := isMdAsciiPunct(nextChar) || unicode.IsPunct(nextChar)
isLastWhiteSpace := unicode.IsSpace(lastChar)
isNextWhiteSpace := unicode.IsSpace(nextChar)
if isNextWhiteSpace {
canOpen = false
} else if isNextPunct {
if !(isLastWhiteSpace || isLastPunct) {
canOpen = false
}
}
if isLastWhiteSpace {
canClose = false
} else if isLastPunct {
if !(isNextWhiteSpace || isNextPunct) {
canClose = false
}
}
if nextChar == '"' && text[index] == '"' {
if lastChar >= '0' && lastChar <= '9' {
canClose = false
canOpen = false
}
}
if canOpen && canClose {
canOpen = false
canClose = isNextPunct
}
if !canOpen && !canClose {
if isSingle {
text[index] = ''
if changed == nil {
changed = make(map[int][]rune)
}
changed[i] = text
}
continue
}
if canClose {
for j := len(stack) - 1; j >= 0; j-- {
item := stack[j]
if item.level < thisLevel {
break
}
if item.single == isSingle && item.level == thisLevel {
if changed == nil {
changed = make(map[int][]rune)
}
var q1, q2 string
if isSingle {
q1 = s.Md.options.Quotes[2]
q2 = s.Md.options.Quotes[3]
} else {
q1 = s.Md.options.Quotes[0]
q2 = s.Md.options.Quotes[1]
}
if utf8.RuneCountInString(q1) == 1 && utf8.RuneCountInString(q2) == 1 {
item.text[item.pos] = firstRune(q1)
text[index] = firstRune(q2)
} else if tok == tokens[item.token] {
newText := make([]rune, 0, len(text)-2+len(q1)+len(q2))
newText = append(newText, text[:item.pos]...)
newText = append(newText, []rune(q1)...)
newText = append(newText, text[item.pos+1:index]...)
newText = append(newText, []rune(q2)...)
newText = append(newText, text[index+1:]...)
text = newText
item.text = newText
} else {
newText := make([]rune, 0, len(item.text)-1+len(q1))
newText = append(newText, item.text[:item.pos]...)
newText = append(newText, []rune(q1)...)
newText = append(newText, item.text[item.pos+1:]...)
item.text = newText
newText = make([]rune, 0, len(text)-1+len(q2))
newText = append(newText, text[:index]...)
newText = append(newText, []rune(q2)...)
newText = append(newText, text[index+1:]...)
text = newText
}
max = len(text)
if changed == nil {
changed = make(map[int][]rune)
}
changed[i] = text
changed[item.token] = item.text
stack = stack[:j]
continue loop
}
}
}
if canOpen {
stack = append(stack, stackItem{
token: i,
text: text,
pos: index,
single: isSingle,
level: thisLevel,
})
} else if canClose && isSingle {
text[index] = ''
if changed == nil {
changed = make(map[int][]rune)
}
changed[i] = text
}
}
}
if changed != nil {
for i, text := range changed {
tokens[i].(*Text).Content = string(text)
}
}
}
func ruleSmartQuotes(s *StateCore) {
if !s.Md.Typographer {
return
}
tokens := s.Tokens
for i := len(tokens) - 1; i >= 0; i-- {
tok := tokens[i]
if tok, ok := tok.(*Inline); ok {
replaceQuotes(tok.Children, s)
}
}
}

View File

@ -0,0 +1,141 @@
// 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"
const (
ptRoot = iota
ptList
ptBlockQuote
ptParagraph
ptReference
)
type StateBlock struct {
StateCore
BMarks []int // offsets of the line beginnings
EMarks []int // offsets of the line endings
TShift []int // indents for each line
SCount []int
BSCount []int
BlkIndent int // required block content indent (in a list etc.)
Line int // line index in the source string
LineMax int // number of lines
Tight bool // loose or tight mode for lists
ParentType byte // parent block type
Level int
}
func (s *StateBlock) IsLineEmpty(n int) bool {
return s.BMarks[n]+s.TShift[n] >= s.EMarks[n]
}
func (s *StateBlock) SkipEmptyLines(from int) int {
for from < s.LineMax && s.IsLineEmpty(from) {
from++
}
return from
}
func (s *StateBlock) SkipSpaces(pos int) int {
src := s.Src
for pos < len(src) && byteIsSpace(src[pos]) {
pos++
}
return pos
}
func (s *StateBlock) SkipBytes(pos int, b byte) int {
src := s.Src
for pos < len(src) && src[pos] == b {
pos++
}
return pos
}
func (s *StateBlock) SkipBytesBack(pos int, b byte, min int) int {
for pos > min {
pos--
if s.Src[pos] != b {
return pos + 1
}
}
return pos
}
func (s *StateBlock) SkipSpacesBack(pos int, min int) int {
for pos > min {
pos--
if !byteIsSpace(s.Src[pos]) {
return pos + 1
}
}
return pos
}
func (s *StateBlock) Lines(begin, end, indent int, keepLastLf bool) string {
if begin == end {
return ""
}
src := s.Src
queue := make([]string, end-begin)
for i, line := 0, begin; line < end; i, line = i+1, line+1 {
lineIndent := 0
lineStart := s.BMarks[line]
first := lineStart
last := s.EMarks[line]
if (line+1 < end || keepLastLf) && last < len(src) {
last++
}
for first < last && lineIndent < indent {
ch := src[first]
if byteIsSpace(ch) {
if ch == '\t' {
lineIndent += 4 - (lineIndent+s.BSCount[line])%4
} else {
lineIndent++
}
} else if first-lineStart < s.TShift[line] {
lineIndent++
} else {
break
}
first++
}
if lineIndent > indent {
queue[i] = strings.Repeat(" ", lineIndent-indent) + src[first:last]
} else {
queue[i] = src[first:last]
}
}
return strings.Join(queue, "")
}
func (s *StateBlock) PushToken(tok Token) {
tok.SetLevel(s.Level)
s.Tokens = append(s.Tokens, tok)
}
func (s *StateBlock) PushOpeningToken(tok Token) {
tok.SetLevel(s.Level)
s.Level++
s.Tokens = append(s.Tokens, tok)
}
func (s *StateBlock) PushClosingToken(tok Token) {
s.Level--
tok.SetLevel(s.Level)
s.Tokens = append(s.Tokens, tok)
}

View File

@ -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
type StateCore struct {
Src string
Tokens []Token
bootstrap [3]Token
Md *Markdown
Env *Environment
}

View File

@ -0,0 +1,116 @@
// 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"
"unicode/utf8"
)
type StateInline struct {
StateCore
Pos int
PosMax int
Level int
Pending bytes.Buffer
PendingLevel int
Delimiters []Delimiter
Cache map[int]int
}
func (s *StateInline) PushToken(tok Token) {
if s.Pending.Len() > 0 {
s.PushPending()
}
tok.SetLevel(s.Level)
s.PendingLevel = s.Level
s.Tokens = append(s.Tokens, tok)
}
func (s *StateInline) PushOpeningToken(tok Token) {
if s.Pending.Len() > 0 {
s.PushPending()
}
tok.SetLevel(s.Level)
s.Level++
s.PendingLevel = s.Level
s.Tokens = append(s.Tokens, tok)
}
func (s *StateInline) PushClosingToken(tok Token) {
if s.Pending.Len() > 0 {
s.PushPending()
}
s.Level--
tok.SetLevel(s.Level)
s.PendingLevel = s.Level
s.Tokens = append(s.Tokens, tok)
}
func (s *StateInline) PushPending() {
s.Tokens = append(s.Tokens, &Text{
Content: s.Pending.String(),
Lvl: s.PendingLevel,
})
s.Pending.Reset()
}
func (s *StateInline) scanDelims(start int, canSplitWord bool) (canOpen bool, canClose bool, length int) {
pos := start
max := s.PosMax
src := s.Src
marker := src[start]
leftFlanking, rightFlanking := true, true
lastChar := ' '
if start > 0 {
lastChar, _ = utf8.DecodeLastRuneInString(src[:start])
}
for pos < max && src[pos] == marker {
pos++
}
length = pos - start
nextChar := ' '
if pos < max {
nextChar, _ = utf8.DecodeRuneInString(src[pos:])
}
isLastPunct := isMdAsciiPunct(lastChar) || unicode.IsPunct(lastChar)
isNextPunct := isMdAsciiPunct(nextChar) || unicode.IsPunct(nextChar)
isLastWhiteSpace := unicode.IsSpace(lastChar)
isNextWhiteSpace := unicode.IsSpace(nextChar)
if isNextWhiteSpace {
leftFlanking = false
} else if isNextPunct {
if !(isLastWhiteSpace || isLastPunct) {
leftFlanking = false
}
}
if isLastWhiteSpace {
rightFlanking = false
} else if isLastPunct {
if !(isNextWhiteSpace || isNextPunct) {
rightFlanking = false
}
}
if !canSplitWord {
canOpen = leftFlanking && (!rightFlanking || isLastPunct)
canClose = rightFlanking && (!leftFlanking || isNextPunct)
} else {
canOpen = leftFlanking
canClose = rightFlanking
}
return
}

View File

@ -0,0 +1,101 @@
// 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 ruleStrikeThrough(s *StateInline, silent bool) bool {
src := s.Src
start := s.Pos
marker := src[start]
if silent {
return false
}
if src[start] != '~' {
return false
}
canOpen, canClose, length := s.scanDelims(start, true)
origLength := length
ch := string(marker)
if length < 2 {
return false
}
if length%2 != 0 {
s.PushToken(&Text{
Content: ch,
})
length--
}
for i := 0; i < length; i += 2 {
s.PushToken(&Text{
Content: ch + ch,
})
s.Delimiters = append(s.Delimiters, Delimiter{
Marker: marker,
Length: -1,
Jump: i,
Token: len(s.Tokens) - 1,
Level: s.Level,
End: -1,
Open: canOpen,
Close: canClose,
})
}
s.Pos += origLength
return true
}
func ruleStrikethroughPostprocess(s *StateInline) {
var loneMarkers []int
delimiters := s.Delimiters
max := len(delimiters)
for i := 0; i < max; i++ {
startDelim := delimiters[i]
if startDelim.Marker != '~' {
continue
}
if startDelim.End == -1 {
continue
}
endDelim := delimiters[startDelim.End]
s.Tokens[startDelim.Token] = &StrikethroughOpen{}
s.Tokens[endDelim.Token] = &StrikethroughClose{}
if text, ok := s.Tokens[endDelim.Token-1].(*Text); ok && text.Content == "~" {
loneMarkers = append(loneMarkers, endDelim.Token-1)
}
}
for len(loneMarkers) > 0 {
i := loneMarkers[len(loneMarkers)-1]
loneMarkers = loneMarkers[:len(loneMarkers)-1]
j := i + 1
for j < len(s.Tokens) {
if _, ok := s.Tokens[j].(*StrikethroughClose); !ok {
break
}
j++
}
j--
if i != j {
s.Tokens[i], s.Tokens[j] = s.Tokens[j], s.Tokens[i]
}
}
}

229
vendor/gitlab.com/golang-commonmark/markdown/table.go generated vendored Normal file
View File

@ -0,0 +1,229 @@
// 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"
)
func getLine(s *StateBlock, line int) string {
pos := s.BMarks[line] + s.BlkIndent
max := s.EMarks[line]
if pos >= max {
return ""
}
return s.Src[pos:max]
}
func escapedSplit(s string) (result []string) {
pos := 0
escapes := 0
lastPos := 0
backTicked := false
lastBackTick := 0
for pos < len(s) {
ch := s[pos]
if ch == '`' {
if backTicked {
backTicked = false
lastBackTick = pos
} else if escapes%2 == 0 {
backTicked = true
lastBackTick = pos
}
} else if ch == '|' && (escapes%2 == 0) && !backTicked {
result = append(result, s[lastPos:pos])
lastPos = pos + 1
}
if ch == '\\' {
escapes++
} else {
escapes = 0
}
pos++
if pos == len(s) && backTicked {
backTicked = false
pos = lastBackTick + 1
}
}
return append(result, s[lastPos:])
}
var rColumn = regexp.MustCompile("^:?-+:?$")
func ruleTable(s *StateBlock, startLine, endLine int, silent bool) bool {
if !s.Md.Tables {
return false
}
if startLine+2 > endLine {
return false
}
nextLine := startLine + 1
if s.SCount[nextLine] < s.BlkIndent {
return false
}
if s.SCount[nextLine]-s.BlkIndent >= 4 {
return false
}
pos := s.BMarks[nextLine] + s.TShift[nextLine]
if pos >= s.EMarks[nextLine] {
return false
}
src := s.Src
ch := src[pos]
pos++
if ch != '|' && ch != '-' && ch != ':' {
return false
}
for pos < s.EMarks[nextLine] {
ch = src[pos]
if ch != '|' && ch != '-' && ch != ':' && !byteIsSpace(ch) {
return false
}
pos++
}
//
lineText := getLine(s, startLine+1)
columns := strings.Split(lineText, "|")
var aligns []Align
for i := 0; i < len(columns); i++ {
t := strings.TrimSpace(columns[i])
if t == "" {
if i == 0 || i == len(columns)-1 {
continue
}
return false
}
if !rColumn.MatchString(t) {
return false
}
if t[len(t)-1] == ':' {
if t[0] == ':' {
aligns = append(aligns, AlignCenter)
} else {
aligns = append(aligns, AlignRight)
}
} else if t[0] == ':' {
aligns = append(aligns, AlignLeft)
} else {
aligns = append(aligns, AlignNone)
}
}
lineText = strings.TrimSpace(getLine(s, startLine))
if strings.IndexByte(lineText, '|') == -1 {
return false
}
if s.SCount[startLine]-s.BlkIndent >= 4 {
return false
}
columns = escapedSplit(strings.TrimSuffix(strings.TrimPrefix(lineText, "|"), "|"))
columnCount := len(columns)
if columnCount > len(aligns) {
return false
}
if silent {
return true
}
tableTok := &TableOpen{
Map: [2]int{startLine, 0},
}
s.PushOpeningToken(tableTok)
s.PushOpeningToken(&TheadOpen{
Map: [2]int{startLine, startLine + 1},
})
s.PushOpeningToken(&TrOpen{
Map: [2]int{startLine, startLine + 1},
})
for i := 0; i < len(columns); i++ {
s.PushOpeningToken(&ThOpen{
Align: aligns[i],
Map: [2]int{startLine, startLine + 1},
})
s.PushToken(&Inline{
Content: strings.TrimSpace(columns[i]),
Map: [2]int{startLine, startLine + 1},
})
s.PushClosingToken(&ThClose{})
}
s.PushClosingToken(&TrClose{})
s.PushClosingToken(&TheadClose{})
tbodyTok := &TbodyOpen{
Map: [2]int{startLine + 2, 0},
}
s.PushOpeningToken(tbodyTok)
for nextLine = startLine + 2; nextLine < endLine; nextLine++ {
if s.SCount[nextLine] < s.BlkIndent {
break
}
lineText = strings.TrimSpace(getLine(s, nextLine))
if strings.IndexByte(lineText, '|') == -1 {
break
}
if s.SCount[nextLine]-s.BlkIndent >= 4 {
break
}
columns = escapedSplit(strings.TrimPrefix(strings.TrimSuffix(lineText, "|"), "|"))
if len(columns) < len(aligns) {
columns = append(columns, make([]string, len(aligns)-len(columns))...)
} else if len(columns) > len(aligns) {
columns = columns[:len(aligns)]
}
s.PushOpeningToken(&TrOpen{})
for i := 0; i < columnCount; i++ {
tdOpen := TdOpen{}
if i < len(aligns) {
tdOpen.Align = aligns[i]
}
s.PushOpeningToken(&tdOpen)
inline := Inline{}
if i < len(columns) {
inline.Content = strings.TrimSpace(columns[i])
}
s.PushToken(&inline)
s.PushClosingToken(&TdClose{})
}
s.PushClosingToken(&TrClose{})
}
s.PushClosingToken(&TbodyClose{})
s.PushClosingToken(&TableClose{})
tableTok.Map[1] = nextLine
tbodyTok.Map[1] = nextLine
s.Line = nextLine
return true
}

View File

@ -0,0 +1,108 @@
// 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 isHeaderLine(s string) bool {
if s == "" {
return false
}
st := 0
n := 0
for i := 0; i < len(s); i++ {
b := s[i]
switch st {
case 0: // initial state
switch b {
case '|':
st = 1
case ':':
st = 2
case '-':
st = 3
n++
case ' ':
break
default:
return false
}
case 1: // |
switch b {
case ' ':
break
case ':':
st = 2
case '-':
st = 3
n++
default:
return false
}
case 2: // |:
switch b {
case ' ':
break
case '-':
st = 3
n++
default:
return false
}
case 3: // |:-
switch b {
case '-':
break
case ':':
st = 4
case '|':
st = 5
case ' ':
st = 6
default:
return false
}
case 4: // |:---:
switch b {
case ' ':
break
case '|':
st = 5
default:
return false
}
case 5: // |:---:|
switch b {
case ' ':
break
case ':':
st = 2
case '-':
st = 3
n++
default:
return false
}
case 6: // |:--- SPACE
switch b {
case ' ':
break
case ':':
st = 4
case '|':
st = 5
default:
return false
}
}
}
return n >= 1
}

52
vendor/gitlab.com/golang-commonmark/markdown/text.go generated vendored Normal file
View File

@ -0,0 +1,52 @@
// 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
var terminatorCharTable = [256]bool{
'\n': true,
'!': true,
'#': true,
'$': true,
'%': true,
'&': true,
'*': true,
'+': true,
'-': true,
':': true,
'<': true,
'=': true,
'>': true,
'@': true,
'[': true,
'\\': true,
']': true,
'^': true,
'_': true,
'`': true,
'{': true,
'}': true,
'~': true,
}
func ruleText(s *StateInline, silent bool) bool {
pos := s.Pos
max := s.PosMax
src := s.Src
for pos < max && !terminatorCharTable[src[pos]] {
pos++
}
if pos == s.Pos {
return false
}
if !silent {
s.Pending.WriteString(src[s.Pos:pos])
}
s.Pos = pos
return true
}

View File

@ -0,0 +1,42 @@
// 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 ruleTextCollapse(s *StateInline) {
level := 0
tokens := s.Tokens
max := len(tokens)
curr := 0
last := 0
for ; curr < max; curr++ {
tok := tokens[curr]
if tok.Opening() {
level++
} else if tok.Closing() {
level--
}
tok.SetLevel(level)
if text, ok := tok.(*Text); ok && curr+1 < max {
if text2, ok := tokens[curr+1].(*Text); ok {
text2.Content = text.Content + text2.Content
continue
}
}
if curr != last {
tokens[last] = tokens[curr]
}
last++
}
if curr != last {
tokens = tokens[:last]
}
s.Tokens = tokens
}

753
vendor/gitlab.com/golang-commonmark/markdown/token.go generated vendored Normal file
View File

@ -0,0 +1,753 @@
// 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 Token interface {
Tag() string
Opening() bool
Closing() bool
Block() bool
Level() int
SetLevel(lvl int)
}
type BlockquoteOpen struct {
Map [2]int
Lvl int
}
type BlockquoteClose struct {
Lvl int
}
type BulletListOpen struct {
Map [2]int
Lvl int
}
type BulletListClose struct {
Lvl int
}
type OrderedListOpen struct {
Order int
Map [2]int
Lvl int
}
type OrderedListClose struct {
Lvl int
}
type ListItemOpen struct {
Map [2]int
Lvl int
}
type ListItemClose struct {
Lvl int
}
type CodeBlock struct {
Content string
Map [2]int
Lvl int
}
type CodeInline struct {
Content string
Lvl int
}
type EmphasisOpen struct {
Lvl int
}
type EmphasisClose struct {
Lvl int
}
type StrongOpen struct {
Lvl int
}
type StrongClose struct {
Lvl int
}
type StrikethroughOpen struct {
Lvl int
}
type StrikethroughClose struct {
Lvl int
}
type Fence struct {
Params string
Content string
Map [2]int
Lvl int
}
type Softbreak struct {
Lvl int
}
type Hardbreak struct {
Lvl int
}
type HeadingOpen struct {
HLevel int
Map [2]int
Lvl int
}
type HeadingClose struct {
HLevel int
Lvl int
}
type HTMLBlock struct {
Content string
Map [2]int
Lvl int
}
type HTMLInline struct {
Content string
Lvl int
}
type Hr struct {
Map [2]int
Lvl int
}
type Image struct {
Src string
Title string
Tokens []Token
Lvl int
}
type Inline struct {
Content string
Map [2]int
Children []Token
Lvl int
}
type LinkOpen struct {
Href string
Title string
Target string
Lvl int
}
type LinkClose struct {
Lvl int
}
type ParagraphOpen struct {
Tight bool
Hidden bool
Map [2]int
Lvl int
}
type ParagraphClose struct {
Tight bool
Hidden bool
Lvl int
}
type TableOpen struct {
Map [2]int
Lvl int
}
type TableClose struct {
Lvl int
}
type TheadOpen struct {
Map [2]int
Lvl int
}
type TheadClose struct {
Lvl int
}
type TrOpen struct {
Map [2]int
Lvl int
}
type TrClose struct {
Lvl int
}
type ThOpen struct {
Align Align
Map [2]int
Lvl int
}
type ThClose struct {
Lvl int
}
type TbodyOpen struct {
Map [2]int
Lvl int
}
type TbodyClose struct {
Lvl int
}
type TdOpen struct {
Align Align
Map [2]int
Lvl int
}
type TdClose struct {
Lvl int
}
type Text struct {
Content string
Lvl int
}
var htags = []string{
"",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
}
func (t *BlockquoteOpen) Level() int { return t.Lvl }
func (t *BlockquoteClose) Level() int { return t.Lvl }
func (t *BulletListOpen) Level() int { return t.Lvl }
func (t *BulletListClose) Level() int { return t.Lvl }
func (t *OrderedListOpen) Level() int { return t.Lvl }
func (t *OrderedListClose) Level() int { return t.Lvl }
func (t *ListItemOpen) Level() int { return t.Lvl }
func (t *ListItemClose) Level() int { return t.Lvl }
func (t *CodeBlock) Level() int { return t.Lvl }
func (t *CodeInline) Level() int { return t.Lvl }
func (t *EmphasisOpen) Level() int { return t.Lvl }
func (t *EmphasisClose) Level() int { return t.Lvl }
func (t *StrongOpen) Level() int { return t.Lvl }
func (t *StrongClose) Level() int { return t.Lvl }
func (t *StrikethroughOpen) Level() int { return t.Lvl }
func (t *StrikethroughClose) Level() int { return t.Lvl }
func (t *Fence) Level() int { return t.Lvl }
func (t *Softbreak) Level() int { return t.Lvl }
func (t *Hardbreak) Level() int { return t.Lvl }
func (t *HeadingOpen) Level() int { return t.Lvl }
func (t *HeadingClose) Level() int { return t.Lvl }
func (t *HTMLBlock) Level() int { return t.Lvl }
func (t *HTMLInline) Level() int { return t.Lvl }
func (t *Hr) Level() int { return t.Lvl }
func (t *Image) Level() int { return t.Lvl }
func (t *Inline) Level() int { return t.Lvl }
func (t *LinkOpen) Level() int { return t.Lvl }
func (t *LinkClose) Level() int { return t.Lvl }
func (t *ParagraphOpen) Level() int { return t.Lvl }
func (t *ParagraphClose) Level() int { return t.Lvl }
func (t *TableOpen) Level() int { return t.Lvl }
func (t *TableClose) Level() int { return t.Lvl }
func (t *TheadOpen) Level() int { return t.Lvl }
func (t *TheadClose) Level() int { return t.Lvl }
func (t *TrOpen) Level() int { return t.Lvl }
func (t *TrClose) Level() int { return t.Lvl }
func (t *ThOpen) Level() int { return t.Lvl }
func (t *ThClose) Level() int { return t.Lvl }
func (t *TbodyOpen) Level() int { return t.Lvl }
func (t *TbodyClose) Level() int { return t.Lvl }
func (t *TdOpen) Level() int { return t.Lvl }
func (t *TdClose) Level() int { return t.Lvl }
func (t *Text) Level() int { return t.Lvl }
func (t *BlockquoteOpen) SetLevel(lvl int) { t.Lvl = lvl }
func (t *BlockquoteClose) SetLevel(lvl int) { t.Lvl = lvl }
func (t *BulletListOpen) SetLevel(lvl int) { t.Lvl = lvl }
func (t *BulletListClose) SetLevel(lvl int) { t.Lvl = lvl }
func (t *OrderedListOpen) SetLevel(lvl int) { t.Lvl = lvl }
func (t *OrderedListClose) SetLevel(lvl int) { t.Lvl = lvl }
func (t *ListItemOpen) SetLevel(lvl int) { t.Lvl = lvl }
func (t *ListItemClose) SetLevel(lvl int) { t.Lvl = lvl }
func (t *CodeBlock) SetLevel(lvl int) { t.Lvl = lvl }
func (t *CodeInline) SetLevel(lvl int) { t.Lvl = lvl }
func (t *EmphasisOpen) SetLevel(lvl int) { t.Lvl = lvl }
func (t *EmphasisClose) SetLevel(lvl int) { t.Lvl = lvl }
func (t *StrongOpen) SetLevel(lvl int) { t.Lvl = lvl }
func (t *StrongClose) SetLevel(lvl int) { t.Lvl = lvl }
func (t *StrikethroughOpen) SetLevel(lvl int) { t.Lvl = lvl }
func (t *StrikethroughClose) SetLevel(lvl int) { t.Lvl = lvl }
func (t *Fence) SetLevel(lvl int) { t.Lvl = lvl }
func (t *Softbreak) SetLevel(lvl int) { t.Lvl = lvl }
func (t *Hardbreak) SetLevel(lvl int) { t.Lvl = lvl }
func (t *HeadingOpen) SetLevel(lvl int) { t.Lvl = lvl }
func (t *HeadingClose) SetLevel(lvl int) { t.Lvl = lvl }
func (t *HTMLBlock) SetLevel(lvl int) { t.Lvl = lvl }
func (t *HTMLInline) SetLevel(lvl int) { t.Lvl = lvl }
func (t *Hr) SetLevel(lvl int) { t.Lvl = lvl }
func (t *Image) SetLevel(lvl int) { t.Lvl = lvl }
func (t *Inline) SetLevel(lvl int) { t.Lvl = lvl }
func (t *LinkOpen) SetLevel(lvl int) { t.Lvl = lvl }
func (t *LinkClose) SetLevel(lvl int) { t.Lvl = lvl }
func (t *ParagraphOpen) SetLevel(lvl int) { t.Lvl = lvl }
func (t *ParagraphClose) SetLevel(lvl int) { t.Lvl = lvl }
func (t *TableOpen) SetLevel(lvl int) { t.Lvl = lvl }
func (t *TableClose) SetLevel(lvl int) { t.Lvl = lvl }
func (t *TheadOpen) SetLevel(lvl int) { t.Lvl = lvl }
func (t *TheadClose) SetLevel(lvl int) { t.Lvl = lvl }
func (t *TrOpen) SetLevel(lvl int) { t.Lvl = lvl }
func (t *TrClose) SetLevel(lvl int) { t.Lvl = lvl }
func (t *ThOpen) SetLevel(lvl int) { t.Lvl = lvl }
func (t *ThClose) SetLevel(lvl int) { t.Lvl = lvl }
func (t *TbodyOpen) SetLevel(lvl int) { t.Lvl = lvl }
func (t *TbodyClose) SetLevel(lvl int) { t.Lvl = lvl }
func (t *TdOpen) SetLevel(lvl int) { t.Lvl = lvl }
func (t *TdClose) SetLevel(lvl int) { t.Lvl = lvl }
func (t *Text) SetLevel(lvl int) { t.Lvl = lvl }
func (t *BlockquoteOpen) Opening() bool { return true }
func (t *BlockquoteClose) Opening() bool { return false }
func (t *BulletListOpen) Opening() bool { return true }
func (t *BulletListClose) Opening() bool { return false }
func (t *OrderedListOpen) Opening() bool { return true }
func (t *OrderedListClose) Opening() bool { return false }
func (t *ListItemOpen) Opening() bool { return true }
func (t *ListItemClose) Opening() bool { return false }
func (t *CodeBlock) Opening() bool { return false }
func (t *CodeInline) Opening() bool { return false }
func (t *EmphasisOpen) Opening() bool { return true }
func (t *EmphasisClose) Opening() bool { return false }
func (t *StrongOpen) Opening() bool { return true }
func (t *StrongClose) Opening() bool { return false }
func (t *StrikethroughOpen) Opening() bool { return true }
func (t *StrikethroughClose) Opening() bool { return false }
func (t *Fence) Opening() bool { return false }
func (t *Softbreak) Opening() bool { return false }
func (t *Hardbreak) Opening() bool { return false }
func (t *HeadingOpen) Opening() bool { return true }
func (t *HeadingClose) Opening() bool { return false }
func (t *HTMLBlock) Opening() bool { return false }
func (t *HTMLInline) Opening() bool { return false }
func (t *Hr) Opening() bool { return false }
func (t *Image) Opening() bool { return false }
func (t *Inline) Opening() bool { return false }
func (t *LinkOpen) Opening() bool { return true }
func (t *LinkClose) Opening() bool { return false }
func (t *ParagraphOpen) Opening() bool { return true }
func (t *ParagraphClose) Opening() bool { return false }
func (t *TableOpen) Opening() bool { return true }
func (t *TableClose) Opening() bool { return false }
func (t *TheadOpen) Opening() bool { return true }
func (t *TheadClose) Opening() bool { return false }
func (t *TrOpen) Opening() bool { return true }
func (t *TrClose) Opening() bool { return false }
func (t *ThOpen) Opening() bool { return true }
func (t *ThClose) Opening() bool { return false }
func (t *TbodyOpen) Opening() bool { return true }
func (t *TbodyClose) Opening() bool { return false }
func (t *TdOpen) Opening() bool { return true }
func (t *TdClose) Opening() bool { return false }
func (t *Text) Opening() bool { return false }
func (t *BlockquoteOpen) Closing() bool { return false }
func (t *BlockquoteClose) Closing() bool { return true }
func (t *BulletListOpen) Closing() bool { return false }
func (t *BulletListClose) Closing() bool { return true }
func (t *OrderedListOpen) Closing() bool { return false }
func (t *OrderedListClose) Closing() bool { return true }
func (t *ListItemOpen) Closing() bool { return false }
func (t *ListItemClose) Closing() bool { return true }
func (t *CodeBlock) Closing() bool { return false }
func (t *CodeInline) Closing() bool { return false }
func (t *EmphasisOpen) Closing() bool { return false }
func (t *EmphasisClose) Closing() bool { return true }
func (t *StrongOpen) Closing() bool { return false }
func (t *StrongClose) Closing() bool { return true }
func (t *StrikethroughOpen) Closing() bool { return false }
func (t *StrikethroughClose) Closing() bool { return true }
func (t *Fence) Closing() bool { return false }
func (t *Softbreak) Closing() bool { return false }
func (t *Hardbreak) Closing() bool { return false }
func (t *HeadingOpen) Closing() bool { return false }
func (t *HeadingClose) Closing() bool { return true }
func (t *HTMLBlock) Closing() bool { return false }
func (t *HTMLInline) Closing() bool { return false }
func (t *Hr) Closing() bool { return false }
func (t *Image) Closing() bool { return false }
func (t *Inline) Closing() bool { return false }
func (t *LinkOpen) Closing() bool { return false }
func (t *LinkClose) Closing() bool { return true }
func (t *ParagraphOpen) Closing() bool { return false }
func (t *ParagraphClose) Closing() bool { return true }
func (t *TableOpen) Closing() bool { return false }
func (t *TableClose) Closing() bool { return true }
func (t *TheadOpen) Closing() bool { return false }
func (t *TheadClose) Closing() bool { return true }
func (t *TrOpen) Closing() bool { return false }
func (t *TrClose) Closing() bool { return true }
func (t *ThOpen) Closing() bool { return false }
func (t *ThClose) Closing() bool { return true }
func (t *TbodyOpen) Closing() bool { return false }
func (t *TbodyClose) Closing() bool { return true }
func (t *TdOpen) Closing() bool { return false }
func (t *TdClose) Closing() bool { return true }
func (t *Text) Closing() bool { return false }
func (t *BlockquoteOpen) Block() bool { return true }
func (t *BlockquoteClose) Block() bool { return true }
func (t *BulletListOpen) Block() bool { return true }
func (t *BulletListClose) Block() bool { return true }
func (t *OrderedListOpen) Block() bool { return true }
func (t *OrderedListClose) Block() bool { return true }
func (t *ListItemOpen) Block() bool { return true }
func (t *ListItemClose) Block() bool { return true }
func (t *CodeBlock) Block() bool { return true }
func (t *CodeInline) Block() bool { return false }
func (t *EmphasisOpen) Block() bool { return false }
func (t *EmphasisClose) Block() bool { return false }
func (t *StrongOpen) Block() bool { return false }
func (t *StrongClose) Block() bool { return false }
func (t *StrikethroughOpen) Block() bool { return false }
func (t *StrikethroughClose) Block() bool { return false }
func (t *Fence) Block() bool { return true }
func (t *Softbreak) Block() bool { return false }
func (t *Hardbreak) Block() bool { return false }
func (t *HeadingOpen) Block() bool { return true }
func (t *HeadingClose) Block() bool { return true }
func (t *HTMLBlock) Block() bool { return true }
func (t *HTMLInline) Block() bool { return false }
func (t *Hr) Block() bool { return true }
func (t *Image) Block() bool { return false }
func (t *Inline) Block() bool { return false }
func (t *LinkOpen) Block() bool { return false }
func (t *LinkClose) Block() bool { return false }
func (t *ParagraphOpen) Block() bool { return true }
func (t *ParagraphClose) Block() bool { return true }
func (t *TableOpen) Block() bool { return true }
func (t *TableClose) Block() bool { return true }
func (t *TheadOpen) Block() bool { return true }
func (t *TheadClose) Block() bool { return true }
func (t *TrOpen) Block() bool { return true }
func (t *TrClose) Block() bool { return true }
func (t *ThOpen) Block() bool { return true }
func (t *ThClose) Block() bool { return true }
func (t *TbodyOpen) Block() bool { return true }
func (t *TbodyClose) Block() bool { return true }
func (t *TdOpen) Block() bool { return true }
func (t *TdClose) Block() bool { return true }
func (t *Text) Block() bool { return false }
func (t *BlockquoteOpen) Tag() string { return "blockquote" }
func (t *BlockquoteClose) Tag() string { return "blockquote" }
func (t *BulletListOpen) Tag() string { return "ul" }
func (t *BulletListClose) Tag() string { return "ul" }
func (t *OrderedListOpen) Tag() string { return "ol" }
func (t *OrderedListClose) Tag() string { return "ol" }
func (t *ListItemOpen) Tag() string { return "li" }
func (t *ListItemClose) Tag() string { return "li" }
func (t *CodeBlock) Tag() string { return "code" }
func (t *CodeInline) Tag() string { return "code" }
func (t *EmphasisOpen) Tag() string { return "em" }
func (t *EmphasisClose) Tag() string { return "em" }
func (t *StrongOpen) Tag() string { return "strong" }
func (t *StrongClose) Tag() string { return "strong" }
func (t *StrikethroughOpen) Tag() string { return "s" }
func (t *StrikethroughClose) Tag() string { return "s" }
func (t *Fence) Tag() string { return "code" }
func (t *Softbreak) Tag() string { return "br" }
func (t *Hardbreak) Tag() string { return "br" }
func (t *HeadingOpen) Tag() string { return htags[t.HLevel] }
func (t *HeadingClose) Tag() string { return htags[t.HLevel] }
func (t *HTMLBlock) Tag() string { return "" }
func (t *HTMLInline) Tag() string { return "" }
func (t *Hr) Tag() string { return "hr" }
func (t *Image) Tag() string { return "img" }
func (t *Inline) Tag() string { return "" }
func (t *LinkOpen) Tag() string { return "a" }
func (t *LinkClose) Tag() string { return "a" }
func (t *ParagraphOpen) Tag() string { return "p" }
func (t *ParagraphClose) Tag() string { return "p" }
func (t *TableOpen) Tag() string { return "table" }
func (t *TableClose) Tag() string { return "table" }
func (t *TheadOpen) Tag() string { return "thead" }
func (t *TheadClose) Tag() string { return "thead" }
func (t *TrOpen) Tag() string { return "tr" }
func (t *TrClose) Tag() string { return "tr" }
func (t *ThOpen) Tag() string { return "th" }
func (t *ThClose) Tag() string { return "th" }
func (t *TbodyOpen) Tag() string { return "tbody" }
func (t *TbodyClose) Tag() string { return "tbody" }
func (t *TdOpen) Tag() string { return "td" }
func (t *TdClose) Tag() string { return "td" }
func (t *Text) Tag() string { return "" }

View File

@ -0,0 +1,185 @@
// 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
var urlSchemas = []string{
"aaa",
"aaas",
"about",
"acap",
"adiumxtra",
"afp",
"afs",
"aim",
"apt",
"attachment",
"aw",
"beshare",
"bitcoin",
"bolo",
"callto",
"cap",
"chrome",
"chrome-extension",
"cid",
"coap",
"com-eventbrite-attendee",
"content",
"crid",
"cvs",
"data",
"dav",
"dict",
"dlna-playcontainer",
"dlna-playsingle",
"dns",
"doi",
"dtn",
"dvb",
"ed2k",
"facetime",
"feed",
"file",
"finger",
"fish",
"ftp",
"geo",
"gg",
"git",
"gizmoproject",
"go",
"gopher",
"gtalk",
"h323",
"hcp",
"http",
"https",
"iax",
"icap",
"icon",
"im",
"imap",
"info",
"ipn",
"ipp",
"irc",
"irc6",
"ircs",
"iris",
"iris.beep",
"iris.lwz",
"iris.xpc",
"iris.xpcs",
"itms",
"jar",
"javascript",
"jms",
"keyparc",
"lastfm",
"ldap",
"ldaps",
"magnet",
"mailto",
"maps",
"market",
"message",
"mid",
"mms",
"ms-help",
"msnim",
"msrp",
"msrps",
"mtqp",
"mumble",
"mupdate",
"mvn",
"news",
"nfs",
"ni",
"nih",
"nntp",
"notes",
"oid",
"opaquelocktoken",
"palm",
"paparazzi",
"platform",
"pop",
"pres",
"proxy",
"psyc",
"query",
"res",
"resource",
"rmi",
"rsync",
"rtmp",
"rtsp",
"secondlife",
"service",
"session",
"sftp",
"sgn",
"shttp",
"sieve",
"sip",
"sips",
"skype",
"smb",
"sms",
"snmp",
"soap.beep",
"soap.beeps",
"soldat",
"spotify",
"ssh",
"steam",
"svn",
"tag",
"teamspeak",
"tel",
"telnet",
"tftp",
"things",
"thismessage",
"tip",
"tn3270",
"tv",
"udp",
"unreal",
"urn",
"ut2004",
"vemmi",
"ventrilo",
"view-source",
"webcal",
"ws",
"wss",
"wtai",
"wyciwyg",
"xcon",
"xcon-userid",
"xfire",
"xmlrpc.beep",
"xmlrpc.beeps",
"xmpp",
"xri",
"ymsgr",
"z39.50r",
"z39.50s",
}
var urlSchemasSet = make(map[string]bool)
func init() {
for _, s := range urlSchemas {
urlSchemasSet[s] = true
}
}
func matchSchema(s string) bool {
_, ok := urlSchemasSet[s]
return ok
}

264
vendor/gitlab.com/golang-commonmark/markdown/util.go generated vendored Normal file
View File

@ -0,0 +1,264 @@
// 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"
"regexp"
"strings"
"unicode"
"gitlab.com/golang-commonmark/html"
"gitlab.com/golang-commonmark/mdurl"
"gitlab.com/golang-commonmark/puny"
)
func runeIsSpace(r rune) bool {
return r == ' ' || r == '\t'
}
func byteIsSpace(b byte) bool {
return b == ' ' || b == '\t'
}
func isLetter(b byte) bool {
return b >= 'a' && b <= 'z' || b >= 'A' && b <= 'Z'
}
func isUppercaseLetter(b byte) bool {
return b >= 'A' && b <= 'Z'
}
func mdpunct(b byte) bool {
return strings.IndexByte("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", b) != -1
}
func isMdAsciiPunct(r rune) bool {
if r > 0x7e {
return false
}
return mdpunct(byte(r))
}
func recodeHostnameFor(proto string) bool {
switch proto {
case "http", "https", "mailto":
return true
}
return false
}
func normalizeLink(url string) string {
parsed, err := mdurl.Parse(url)
if err != nil {
return ""
}
if parsed.Host != "" && (parsed.Scheme == "" || recodeHostnameFor(parsed.Scheme)) {
parsed.Host = puny.ToASCII(parsed.Host)
}
parsed.Scheme = parsed.RawScheme
return mdurl.Encode(parsed.String())
}
func normalizeLinkText(url string) string {
parsed, err := mdurl.Parse(url)
if err != nil {
return ""
}
if parsed.Host != "" && (parsed.Scheme == "" || recodeHostnameFor(parsed.Scheme)) {
parsed.Host = puny.ToUnicode(parsed.Host)
}
parsed.Scheme = parsed.RawScheme
return mdurl.Decode(parsed.String())
}
var badProtos = []string{"file", "javascript", "vbscript"}
var rGoodData = regexp.MustCompile(`^data:image/(gif|png|jpeg|webp);`)
func removeSpecial(s string) string {
i := 0
for i < len(s) && !(s[i] <= 0x20 || s[i] == 0x7f) {
i++
}
if i >= len(s) {
return s
}
buf := make([]byte, len(s))
j := 0
for i := 0; i < len(s); i++ {
if !(s[i] <= 0x20 || s[i] == 0x7f) {
buf[j] = s[i]
j++
}
}
return string(buf[:j])
}
func validateLink(url string) bool {
str := strings.TrimSpace(url)
str = strings.ToLower(str)
if strings.IndexByte(str, ':') >= 0 {
proto := strings.SplitN(str, ":", 2)[0]
proto = removeSpecial(proto)
for _, p := range badProtos {
if proto == p {
return false
}
}
if proto == "data" && !rGoodData.MatchString(str) {
return false
}
}
return true
}
func unescapeAll(s string) string {
anyChanges := false
i := 0
for i < len(s)-1 {
b := s[i]
if b == '\\' {
if mdpunct(s[i+1]) {
anyChanges = true
break
}
} else if b == '&' {
if e, n := html.ParseEntity(s[i:]); n > 0 && e != html.BadEntity {
anyChanges = true
break
}
}
i++
}
if !anyChanges {
return s
}
buf := make([]byte, len(s))
copy(buf[:i], s)
j := i
for i < len(s) {
b := s[i]
if b == '\\' {
if i+1 < len(s) {
b = s[i+1]
if mdpunct(b) {
buf[j] = b
j++
} else {
buf[j] = '\\'
j++
buf[j] = b
j++
}
i += 2
continue
}
} else if b == '&' {
if e, n := html.ParseEntity(s[i:]); n > 0 && e != html.BadEntity {
if len(e) > n && len(buf) == len(s) {
newBuf := make([]byte, cap(buf)*2)
copy(newBuf[:j], buf)
buf = newBuf
}
j += copy(buf[j:], e)
i += n
continue
}
}
buf[j] = b
j++
i++
}
return string(buf[:j])
}
func normalizeInlineCode(s string) string {
if s == "" {
return ""
}
byteFuckery := false
i := 0
for i < len(s)-1 {
b := s[i]
if b == '\n' {
byteFuckery = true
break
}
if b == ' ' {
i++
b = s[i]
if b == ' ' || b == '\n' {
i--
byteFuckery = true
break
}
}
i++
}
if !byteFuckery {
return strings.TrimSpace(s)
}
buf := make([]byte, len(s))
copy(buf[:i], s)
buf[i] = ' '
i++
j := i
lastSpace := true
for i < len(s) {
b := s[i]
switch b {
case ' ', '\n':
if lastSpace {
break
}
buf[j] = ' '
lastSpace = true
j++
default:
buf[j] = b
lastSpace = false
j++
}
i++
}
return string(bytes.TrimSpace(buf[:j]))
}
func normalizeReference(s string) string {
var buf bytes.Buffer
lastSpace := false
for _, r := range s {
if unicode.IsSpace(r) {
if !lastSpace {
buf.WriteByte(' ')
lastSpace = true
}
continue
}
buf.WriteRune(unicode.ToLower(r))
lastSpace = false
}
return string(bytes.TrimSpace(buf.Bytes()))
}

69
vendor/gitlab.com/golang-commonmark/markdown/writer.go generated vendored Normal file
View File

@ -0,0 +1,69 @@
// 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 (
"bufio"
"io"
)
type writer interface {
Write([]byte) (int, error)
WriteByte(byte) error
WriteString(string) (int, error)
Flush() error
}
type monadicWriter struct {
writer
err error
}
func newMonadicWriter(w io.Writer) *monadicWriter {
if w, ok := w.(writer); ok {
return &monadicWriter{writer: w}
}
return &monadicWriter{writer: bufio.NewWriter(w)}
}
func (w *monadicWriter) Write(p []byte) (n int, err error) {
if w.err != nil {
return
}
n, err = w.writer.Write(p)
w.err = err
return
}
func (w *monadicWriter) WriteByte(b byte) (err error) {
if w.err != nil {
return
}
err = w.writer.WriteByte(b)
w.err = err
return
}
func (w *monadicWriter) WriteString(s string) (n int, err error) {
if w.err != nil {
return
}
n, err = w.writer.WriteString(s)
w.err = err
return
}
func (w *monadicWriter) Flush() (err error) {
if w.err != nil {
return
}
err = w.writer.Flush()
w.err = err
return
}