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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
85 changed files with 40959 additions and 6 deletions

View File

@ -12,6 +12,7 @@ import (
"github.com/42wim/matterbridge/bridge/config" "github.com/42wim/matterbridge/bridge/config"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"gitlab.com/golang-commonmark/markdown"
) )
func DownloadFile(url string) (*[]byte, error) { func DownloadFile(url string) (*[]byte, error) {
@ -151,3 +152,8 @@ func ClipMessage(text string, length int) string {
} }
return text return text
} }
func ParseMarkdown(input string) string {
md := markdown.New(markdown.XHTMLOutput(true), markdown.Breaks(true))
return (md.RenderToString([]byte(input)))
}

View File

@ -3,6 +3,7 @@ package bmatrix
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"html"
"mime" "mime"
"regexp" "regexp"
"strings" "strings"
@ -112,8 +113,8 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
// Edit message if we have an ID // Edit message if we have an ID
// matrix has no editing support // matrix has no editing support
// Post normal message // Post normal message with HTML support (eg riot.im)
resp, err := b.mc.SendText(channel, msg.Username+msg.Text) resp, err := b.mc.SendHTML(channel, msg.Text, html.EscapeString(msg.Username)+helper.ParseMarkdown(msg.Text))
if err != nil { if err != nil {
return "", err return "", err
} }

6
go.mod
View File

@ -61,6 +61,12 @@ require (
github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 // indirect github.com/valyala/fasttemplate v0.0.0-20170224212429-dcecefd839c4 // indirect
github.com/x-cray/logrus-prefixed-formatter v0.5.2 // indirect github.com/x-cray/logrus-prefixed-formatter v0.5.2 // indirect
github.com/zfjagann/golang-ring v0.0.0-20141111230621-17637388c9f6 github.com/zfjagann/golang-ring v0.0.0-20141111230621-17637388c9f6
gitlab.com/golang-commonmark/html v0.0.0-20180917080848-cfaf75183c4a // indirect
gitlab.com/golang-commonmark/linkify v0.0.0-20180917065525-c22b7bdb1179 // indirect
gitlab.com/golang-commonmark/markdown v0.0.0-20181102083822-772775880e1f
gitlab.com/golang-commonmark/mdurl v0.0.0-20180912090424-e5bce34c34f2 // indirect
gitlab.com/golang-commonmark/puny v0.0.0-20180912090636-2cd490539afe // indirect
gitlab.com/opennota/wd v0.0.0-20180912061657-c5d65f63c638 // indirect
go.uber.org/atomic v1.3.2 // indirect go.uber.org/atomic v1.3.2 // indirect
go.uber.org/multierr v1.1.0 // indirect go.uber.org/multierr v1.1.0 // indirect
go.uber.org/zap v1.9.1 // indirect go.uber.org/zap v1.9.1 // indirect

16
go.sum
View File

@ -16,8 +16,6 @@ github.com/dgrijalva/jwt-go v0.0.0-20170508165458-6c8dedd55f8a h1:MuHMeSsXbNEeUy
github.com/dgrijalva/jwt-go v0.0.0-20170508165458-6c8dedd55f8a/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v0.0.0-20170508165458-6c8dedd55f8a/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible h1:2cauKuaELYAEARXRkq2LrJ0yDDv1rW7+wrTEdVL3uaU=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible h1:i64CCJcSqkRIkm5OSdZQjZq84/gJsk2zNwHWIRYWlKE= github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible h1:i64CCJcSqkRIkm5OSdZQjZq84/gJsk2zNwHWIRYWlKE=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM= github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
github.com/golang/protobuf v0.0.0-20170613224224-e325f446bebc h1:wdhDSKrkYy24mcfzuA3oYm58h0QkyXjwERCkzJDP5kA= github.com/golang/protobuf v0.0.0-20170613224224-e325f446bebc h1:wdhDSKrkYy24mcfzuA3oYm58h0QkyXjwERCkzJDP5kA=
@ -55,8 +53,6 @@ github.com/labstack/echo v3.3.5+incompatible h1:9PfxPUmasKzeJor9uQTaXLT6WUG/r+vS
github.com/labstack/echo v3.3.5+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= github.com/labstack/echo v3.3.5+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
github.com/labstack/gommon v0.2.1 h1:C+I4NYknueQncqKYZQ34kHsLZJVeB5KwPUhnO0nmbpU= github.com/labstack/gommon v0.2.1 h1:C+I4NYknueQncqKYZQ34kHsLZJVeB5KwPUhnO0nmbpU=
github.com/labstack/gommon v0.2.1/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4= github.com/labstack/gommon v0.2.1/go.mod h1:/tj9csK2iPSBvn+3NLM9e52usepMtrd5ilFYA+wQNJ4=
github.com/lrstanley/girc v0.0.0-20181114171214-3aee8c249519 h1:o7duXxs4nxplgWrFRJoyGrPAS+U9Sk5eQyc2mflk6/Q=
github.com/lrstanley/girc v0.0.0-20181114171214-3aee8c249519/go.mod h1:7cRs1SIBfKQ7e3Tam6GKTILSNHzR862JD0JpINaZoJk=
github.com/lrstanley/girc v0.0.0-20190102153329-c1e59a02f488 h1:dDEQN5oaa0WOzEiPDSbOugW/e2I/SWY98HYRdcwmGfY= github.com/lrstanley/girc v0.0.0-20190102153329-c1e59a02f488 h1:dDEQN5oaa0WOzEiPDSbOugW/e2I/SWY98HYRdcwmGfY=
github.com/lrstanley/girc v0.0.0-20190102153329-c1e59a02f488/go.mod h1:7cRs1SIBfKQ7e3Tam6GKTILSNHzR862JD0JpINaZoJk= github.com/lrstanley/girc v0.0.0-20190102153329-c1e59a02f488/go.mod h1:7cRs1SIBfKQ7e3Tam6GKTILSNHzR862JD0JpINaZoJk=
github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5 h1:AsEBgzv3DhuYHI/GiQh2HxvTP71HCCE9E/tzGUzGdtU= github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5 h1:AsEBgzv3DhuYHI/GiQh2HxvTP71HCCE9E/tzGUzGdtU=
@ -153,6 +149,18 @@ github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJ
github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
github.com/zfjagann/golang-ring v0.0.0-20141111230621-17637388c9f6 h1:/WULP+6asFz569UbOwg87f3iDT7T+GF5/vjLmL51Pdk= github.com/zfjagann/golang-ring v0.0.0-20141111230621-17637388c9f6 h1:/WULP+6asFz569UbOwg87f3iDT7T+GF5/vjLmL51Pdk=
github.com/zfjagann/golang-ring v0.0.0-20141111230621-17637388c9f6/go.mod h1:0MsIttMJIF/8Y7x0XjonJP7K99t3sR6bjj4m5S4JmqU= github.com/zfjagann/golang-ring v0.0.0-20141111230621-17637388c9f6/go.mod h1:0MsIttMJIF/8Y7x0XjonJP7K99t3sR6bjj4m5S4JmqU=
gitlab.com/golang-commonmark/html v0.0.0-20180917080848-cfaf75183c4a h1:Ax7kdHNICZiIeFpmevmaEWb0Ae3BUj3zCTKhZHZ+zd0=
gitlab.com/golang-commonmark/html v0.0.0-20180917080848-cfaf75183c4a/go.mod h1:JT4uoTz0tfPoyVH88GZoWDNm5NHJI2VbUW+eyPClueI=
gitlab.com/golang-commonmark/linkify v0.0.0-20180917065525-c22b7bdb1179 h1:rbON2KwBnWuFMlSHM8LELLlwroDRZw6xv0e6il6e5dk=
gitlab.com/golang-commonmark/linkify v0.0.0-20180917065525-c22b7bdb1179/go.mod h1:Gn+LZmCrhPECMD3SOKlE+BOHwhOYD9j7WT9NUtkCrC8=
gitlab.com/golang-commonmark/markdown v0.0.0-20181102083822-772775880e1f h1:jwXy/CsM4xS2aoiF2fHAlukmInWhd2TlWB+HDCyvzKc=
gitlab.com/golang-commonmark/markdown v0.0.0-20181102083822-772775880e1f/go.mod h1:SIHlEr9462fpIfTrVWf3GqQDxnA65Vm3BMMsUtuA6W0=
gitlab.com/golang-commonmark/mdurl v0.0.0-20180912090424-e5bce34c34f2 h1:wD/sPUgx2QJFPTyXZpJnLaROolfeKuruh06U4pRV0WY=
gitlab.com/golang-commonmark/mdurl v0.0.0-20180912090424-e5bce34c34f2/go.mod h1:wQk4rLkWrdOPjUAtqJRJ10hIlseLSVYWP95PLrjDF9s=
gitlab.com/golang-commonmark/puny v0.0.0-20180912090636-2cd490539afe h1:5kUPFAF52umOUPH12MuNUmyVTseJRNBftDl/KfsvX3I=
gitlab.com/golang-commonmark/puny v0.0.0-20180912090636-2cd490539afe/go.mod h1:P9LSM1KVzrIstFgUaveuwiAm8PK5VTB3yJEU8kqlbrU=
gitlab.com/opennota/wd v0.0.0-20180912061657-c5d65f63c638 h1:uPZaMiz6Sz0PZs3IZJWpU5qHKGNy///1pacZC9txiUI=
gitlab.com/opennota/wd v0.0.0-20180912061657-c5d65f63c638/go.mod h1:EGRJaqe2eO9XGmFtQCvV3Lm9NLico3UhFwUpCG/+mVU=
go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4= go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=

View File

@ -0,0 +1,16 @@
image: golang:1.11
stages:
- build
- test
build:
stage: build
script:
- go build ./...
test:
stage: test
script:
- test -z "$(gofmt -l . | tee /dev/stderr)"
- go test ./...

10
vendor/gitlab.com/golang-commonmark/html/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.

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

@ -0,0 +1,8 @@
html [![License](https://img.shields.io/badge/licence-BSD--2--Clause-blue.svg)](https://opensource.org/licenses/BSD-2-Clause) [![GoDoc](http://godoc.org/gitlab.com/golang-commonmark/html?status.svg)](http://godoc.org/gitlab.com/golang-commonmark/html) [![Pipeline status](https://gitlab.com/golang-commonmark/html/badges/master/pipeline.svg)](https://gitlab.com/golang-commonmark/html/commits/master)
====
Package html provides functions for escaping/unescaping HTML text and for parsing HTML entities.
## Install
go get -u gitlab.com/golang-commonmark/html

211
vendor/gitlab.com/golang-commonmark/html/html.go generated vendored Normal file
View File

@ -0,0 +1,211 @@
// 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 html provides functions for escaping/unescaping HTML text and for parsing HTML entities.
package html
import (
"io"
"strconv"
"strings"
"unicode/utf8"
)
const BadEntity = string(utf8.RuneError)
var htmlEscapeReplacer = strings.NewReplacer(
"&", "&",
"<", "&lt;",
">", "&gt;",
`"`, "&quot;",
)
func EscapeString(s string) string {
return htmlEscapeReplacer.Replace(s)
}
func WriteEscapedString(w io.Writer, s string) error {
_, err := htmlEscapeReplacer.WriteString(w, s)
return err
}
func isValidEntityCode(c int64) bool {
switch {
case !utf8.ValidRune(rune(c)):
return false
// never used
case c >= 0xfdd0 && c <= 0xfdef:
return false
case c&0xffff == 0xffff || c&0xffff == 0xfffe:
return false
// control codes
case c >= 0x00 && c <= 0x08:
return false
case c == 0x0b:
return false
case c >= 0x0e && c <= 0x1f:
return false
case c >= 0x7f && c <= 0x9f:
return false
}
return true
}
func letter(b byte) bool { return b >= 'a' && b <= 'z' || b >= 'A' && b <= 'Z' }
func digit(b byte) bool { return b >= '0' && b <= '9' }
func alphanum(b byte) bool { return letter(b) || digit(b) }
func hexDigit(b byte) bool {
return digit(b) || b >= 'a' && b <= 'f' || b >= 'A' && b <= 'F'
}
func ParseEntity(s string) (string, int) {
st := 0
var n int
for i := 1; i < len(s); i++ {
b := s[i]
switch st {
case 0: // initial state
switch {
case b == '#':
st = 1
case letter(b):
n = 1
st = 2
default:
return "", 0
}
case 1: // &#
switch {
case b == 'x' || b == 'X':
st = 3
case digit(b):
n = 1
st = 4
default:
return "", 0
}
case 2: // &q
switch {
case alphanum(b):
n++
if n > 31 {
return "", 0
}
case b == ';':
if e, ok := entities[s[i-n:i]]; ok {
return e, i + 1
}
return "", 0
default:
return "", 0
}
case 3: // &#x
switch {
case hexDigit(b):
n = 1
st = 5
default:
return "", 0
}
case 4: // &#0
switch {
case digit(b):
n++
if n > 8 {
return "", 0
}
case b == ';':
c, _ := strconv.ParseInt(s[i-n:i], 10, 32)
if !isValidEntityCode(c) {
return BadEntity, i + 1
}
return string(rune(c)), i + 1
default:
return "", 0
}
case 5: // &#x0
switch {
case hexDigit(b):
n++
if n > 8 {
return "", 0
}
case b == ';':
c, err := strconv.ParseInt(s[i-n:i], 16, 32)
if err != nil {
return BadEntity, i + 1
}
if !isValidEntityCode(c) {
return BadEntity, i + 1
}
return string(rune(c)), i + 1
default:
return "", 0
}
}
}
return "", 0
}
func UnescapeString(s string) string {
i := strings.IndexByte(s, '&')
if i < 0 {
return s
}
anyChanges := false
var entityStr string
var entityLen int
for i < len(s) {
if s[i] == '&' {
entityStr, entityLen = ParseEntity(s[i:])
if entityLen > 0 {
anyChanges = true
break
}
}
i++
}
if !anyChanges {
return s
}
buf := make([]byte, len(s)-entityLen+len(entityStr))
copy(buf[:i], s)
n := copy(buf[i:], entityStr)
j := i + n
i += entityLen
for i < len(s) {
b := s[i]
if b == '&' {
entityStr, entityLen = ParseEntity(s[i:])
if entityLen > 0 {
n = copy(buf[j:], entityStr)
j += n
i += entityLen
continue
}
}
buf[j] = b
j++
i++
}
return string(buf[:j])
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,19 @@
image: golang:1.11
stages:
- build
- test
before_script:
- go get golang.org/x/text/unicode/rangetable
build:
stage: build
script:
- go build ./...
test:
stage: test
script:
- test -z "$(gofmt -l . | tee /dev/stderr)"
- go test ./...

10
vendor/gitlab.com/golang-commonmark/linkify/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.

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

@ -0,0 +1,12 @@
linkify [![License](https://img.shields.io/badge/licence-BSD--2--Clause-blue.svg)](https://opensource.org/licenses/BSD-2-Clause) [![GoDoc](http://godoc.org/gitlab.com/golang-commonmark/linkify?status.svg)](http://godoc.org/gitlab.com/golang-commonmark/linkify) [![Pipeline status](https://gitlab.com/golang-commonmark/linkify/badges/master/pipeline.svg)](https://gitlab.com/golang-commonmark/linkify/commits/master)
=======
Package linkify provides a way to find what looks like links in plain text.
## Install
go get -u gitlab.com/golang-commonmark/linkify
## Use
See an [example](https://gitlab.com/golang-commonmark/linkify/blob/master/linkify_example_test.go).

51
vendor/gitlab.com/golang-commonmark/linkify/charset.go generated vendored Normal file
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 linkify
import (
"unicode"
"golang.org/x/text/unicode/rangetable"
)
var (
unreserved = [256]bool{'-': true, '.': true, '_': true, '~': true}
basicPunct = [256]bool{'.': true, ',': true, '?': true, '!': true, ';': true, ':': true}
subdelims = [256]bool{'!': true, '$': true, '&': true, '\'': true, '(': true, ')': true,
'*': true, '+': true, ',': true, ';': true, '=': true}
emailcs = [256]bool{'a': true, 'b': true, 'c': true, 'd': true, 'e': true, 'f': true, 'g': true,
'h': true, 'i': true, 'j': true, 'k': true, 'l': true, 'm': true, 'n': true, 'o': true,
'p': true, 'q': true, 'r': true, 's': true, 't': true, 'u': true, 'v': true, 'w': true,
'x': true, 'y': true, 'z': true, 'A': true, 'B': true, 'C': true, 'D': true, 'E': true,
'F': true, 'G': true, 'H': true, 'I': true, 'J': true, 'K': true, 'L': true, 'M': true,
'N': true, 'O': true, 'P': true, 'Q': true, 'R': true, 'S': true, 'T': true, 'U': true,
'V': true, 'W': true, 'X': true, 'Y': true, 'Z': true, '0': true, '1': true, '2': true,
'3': true, '4': true, '5': true, '6': true, '7': true, '8': true, '9': true, '!': true,
'#': true, '$': true, '%': true, '&': true, '\'': true, '*': true, '+': true, '/': true,
'=': true, '?': true, '^': true, '_': true, '`': true, '{': true, '|': true, '}': true,
'~': true, '-': true}
letterOrDigit = rangetable.Merge(unicode.Letter, unicode.Digit)
punctSpaceCc = rangetable.Merge(unicode.Punct, unicode.Space, unicode.Cc)
)
func isAllowedInEmail(r rune) bool {
return r < 0x7f && emailcs[r]
}
func isLetterOrDigit(r rune) bool {
return unicode.Is(letterOrDigit, r)
}
func isPunctOrSpaceOrControl(r rune) bool {
return r == '<' || r == '>' || r == '\uff5c' || unicode.Is(punctSpaceCc, r)
}
func isUnreserved(r rune) bool {
return (r < 0x7f && unreserved[r]) || unicode.Is(letterOrDigit, r)
}
func isSubDelimiter(r rune) bool {
return r < 0x7f && subdelims[r]
}

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

@ -0,0 +1,93 @@
// Copyright 2015 The Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package linkify
import (
"unicode"
"unicode/utf8"
)
func findEmailStart(s string, start int) (_ int, _ bool) {
end := start
allowDot := false
for end >= 0 {
b := s[end]
switch {
case emailcs[b]:
allowDot = true
case b == '.':
if !allowDot {
return
}
allowDot = false
default:
if end == start {
return
}
if s[end+1] == '.' {
return
}
r, _ := utf8.DecodeLastRuneInString(s[:end+1])
if r == utf8.RuneError {
return
}
if !unicode.IsSpace(r) {
return
}
return end + 1, true
}
end--
}
if end < start && s[end+1] == '.' {
return
}
return end + 1, true
}
func findEmailEnd(s string, start int) (_ int, _ bool) {
end := start
allowDot := false
loop:
for end < len(s) {
b := s[end]
switch {
case emailcs[b]:
allowDot = true
case b == '.':
if !allowDot {
return
}
allowDot = false
case b == '@':
break loop
default:
return
}
end++
}
if end >= len(s)-5 {
return
}
if end > start && s[end-1] == '.' {
return
}
var dot int
var ok bool
end, dot, ok = findHostnameEnd(s, end+1)
if !ok || dot == -1 {
return
}
if dot+5 <= len(s) && s[dot+1:dot+5] == "xn--" {
return end, true
}
if length := match(s[dot+1:]); dot+length+1 != end {
return
}
return end, true
}

18175
vendor/gitlab.com/golang-commonmark/linkify/generated.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

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

@ -0,0 +1,462 @@
// 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 linkify provides a way to find links in plain text.
package linkify
import (
"strings"
"unicode/utf8"
)
// Link represents a link found in a string with a schema and a position in the string.
type Link struct {
Scheme string
Start, End int
}
func max(a, b int) int {
if a >= b {
return a
}
return b
}
// Links returns links found in s.
func Links(s string) (links []Link) {
for i := 0; i < len(s)-2; i++ {
switch s[i] {
case '.': // IP address or domain name
if i == 0 {
continue // . at the start of a line
}
if length := match(s[i+1:]); length > 0 {
pos := i + 1 + length
switch s[pos-1] {
case '.': // IP address
if pos >= len(s) {
continue // . at the end of line
}
if !digit(s[i-1]) {
i = pos
continue // . should be preceded by a digit
}
if !digit(s[pos]) {
i = pos
continue // . should be followed by a digit
}
// find the start of the IP address
j := i - 2
m := max(0, j-3)
for j >= m && digit(s[j]) {
j--
}
if i-2-j > 2 {
i = pos + 1
continue // at most 3 digits
}
start := 0
if j >= 0 {
r, rlen := utf8.DecodeLastRuneInString(s[:j+1])
if !isPunctOrSpaceOrControl(r) {
i = pos + 1
continue
}
switch r {
case '.', ':', '/', '\\', '-', '_':
i = pos + 1
continue
}
start = j + 2 - rlen
}
length, ok := skipIPv4(s[start:])
if !ok {
i = pos + 1
continue
}
end := start + length
if end == len(s) {
links = append(links, Link{
Scheme: "",
Start: start,
End: end,
})
return
}
r, _ := utf8.DecodeRuneInString(s[end:])
if !isPunctOrSpaceOrControl(r) {
continue
}
end = skipPort(s, end)
end = skipPath(s, end)
end = skipQuery(s, end)
end = skipFragment(s, end)
end = unskipPunct(s, end)
if end < len(s) {
r, _ = utf8.DecodeRuneInString(s[end:])
if !isPunctOrSpaceOrControl(r) || r == '%' {
continue
}
}
links = append(links, Link{
Scheme: "",
Start: start,
End: end,
})
i = end
default: // domain name
r, _ := utf8.DecodeLastRuneInString(s[:i])
if isPunctOrSpaceOrControl(r) {
continue
}
if pos == len(s) {
start, ok := findHostnameStart(s, i)
if !ok {
continue
}
links = append(links, Link{
Scheme: "",
Start: start,
End: pos,
})
return
}
if s[i+1:pos] != "xn--" {
r, _ = utf8.DecodeRuneInString(s[pos:])
if isLetterOrDigit(r) {
continue // should not be followed by a letter or a digit
}
}
end, dot, ok := findHostnameEnd(s, pos)
if !ok {
continue
}
dot = max(dot, i)
if !(dot+5 <= len(s) && s[dot+1:dot+5] == "xn--") {
if length := match(s[dot+1:]); dot+length+1 != end {
continue
}
}
start, ok := findHostnameStart(s, i)
if !ok {
continue
}
end = skipPort(s, end)
end = skipPath(s, end)
end = skipQuery(s, end)
end = skipFragment(s, end)
end = unskipPunct(s, end)
if end < len(s) {
r, _ = utf8.DecodeRuneInString(s[end:])
if !isPunctOrSpaceOrControl(r) || r == '%' {
continue // should be followed by punctuation or space
}
}
links = append(links, Link{
Scheme: "",
Start: start,
End: end,
})
i = end
}
}
case '/': // schema-less link
if s[i+1] != '/' {
continue
}
if i > 0 {
if s[i-1] == ':' {
i++
continue // should not be preceded by a colon
}
r, _ := utf8.DecodeLastRuneInString(s[:i])
if !isPunctOrSpaceOrControl(r) {
i++
continue // should be preceded by punctuation or space
}
}
r, _ := utf8.DecodeRuneInString(s[i+2:])
if !isLetterOrDigit(r) {
i++
continue // should be followed by a letter or a digit
}
start := i
end, dot, ok := findHostnameEnd(s, i+2)
if !ok {
continue
}
if s[i+2:end] != "localhost" {
if dot == -1 {
continue // no dot
}
if length, ok := skipIPv4(s[i+2:]); !ok || i+2+length != end {
if length := match(s[dot+1:]); dot+length+1 != end {
continue
}
}
}
end = skipPort(s, end)
end = skipPath(s, end)
end = skipQuery(s, end)
end = skipFragment(s, end)
end = unskipPunct(s, end)
if end < len(s) {
r, _ = utf8.DecodeRuneInString(s[end:])
if !isPunctOrSpaceOrControl(r) || r == '%' {
continue // should be followed by punctuation or space
}
}
links = append(links, Link{
Scheme: "//",
Start: start,
End: end,
})
i = end
case ':': // http, https, ftp, mailto or localhost
if i < 3 { // at least ftp:
continue
}
if i >= 9 && s[i-1] == 't' && s[i-9:i] == "localhost" {
j := i - 9
if !digit(s[j+10]) {
continue
}
if j > 0 {
r, _ := utf8.DecodeLastRuneInString(s[:j])
if !isPunctOrSpaceOrControl(r) {
i++
continue // should be preceded by punctuation or space
}
}
start := j
pos := j + 9
end := skipPort(s, pos)
if end == pos {
continue // invalid port
}
end = skipPath(s, end)
end = skipQuery(s, end)
end = skipFragment(s, end)
end = unskipPunct(s, end)
if end < len(s) {
r, _ := utf8.DecodeRuneInString(s[end:])
if !isPunctOrSpaceOrControl(r) || r == '%' {
i++
continue // should be followed by punctuation or space
}
}
links = append(links, Link{
Scheme: "",
Start: start,
End: end,
})
i = end
break
}
j := i - 1
var start int
var schema string
switch byteToLower(s[j]) {
case 'o': // mailto
if j < 5 {
continue // too short for mailto
}
if len(s)-j < 8 {
continue // insufficient length after
}
if strings.ToLower(s[j-5:j+2]) != "mailto:" {
continue
}
r, _ := utf8.DecodeLastRuneInString(s[:j-5])
if isLetterOrDigit(r) {
continue // should not be preceded by a letter or a digit
}
r, _ = utf8.DecodeRuneInString(s[j+2:])
if !isAllowedInEmail(r) {
continue // should be followed by a valid e-mail character
}
start = j - 5
end, ok := findEmailEnd(s, j+2)
if !ok {
continue
}
links = append(links, Link{
Scheme: "mailto:",
Start: start,
End: end,
})
i = end
continue // continue processing
case 'p': // http or ftp
if len(s)-j < 8 {
continue // insufficient length after
}
switch byteToLower(s[j-2]) {
case 'f':
if strings.ToLower(s[j-2:j+4]) != "ftp://" {
continue
}
start = j - 2
schema = "ftp:"
case 't':
if j < 3 {
continue
}
if strings.ToLower(s[j-3:j+4]) != "http://" {
continue
}
start = j - 3
schema = "http:"
default:
continue
}
case 's': // https
if j < 4 {
continue // too short for https
}
if len(s)-j < 8 {
continue // insufficient length after
}
start = j - 4
if strings.ToLower(s[start:j+4]) != "https://" {
continue
}
schema = "https:"
default:
continue
}
// http, https or ftp
if start > 0 {
r, _ := utf8.DecodeLastRuneInString(s[:start])
if !isPunctOrSpaceOrControl(r) {
continue // should be preceded by punctuation or space
}
}
r, _ := utf8.DecodeRuneInString(s[j+4:])
if isPunctOrSpaceOrControl(r) {
continue
}
end, dot, ok := findHostnameEnd(s, j+4)
if !ok {
continue
}
if s[j+4:end] != "localhost" {
if dot == -1 {
continue // no dot
}
if length, ok := skipIPv4(s[j+4:]); !ok || j+4+length != end {
if !(dot+5 <= len(s) && s[dot+1:dot+5] == "xn--") {
if length := match(s[dot+1:]); dot+length+1 != end {
continue
}
}
}
}
end = skipPort(s, end)
end = skipPath(s, end)
end = skipQuery(s, end)
end = skipFragment(s, end)
end = unskipPunct(s, end)
if end < len(s) {
r, _ = utf8.DecodeRuneInString(s[end:])
if !isPunctOrSpaceOrControl(r) || r == '%' {
continue // should be followed by punctuation or space
}
}
links = append(links, Link{
Scheme: schema,
Start: start,
End: end,
})
i = end
case '@': // schema-less e-mail
if i == 0 {
continue // @ at the start of a line
}
if len(s)-i < 5 {
continue // insufficient length after
}
r, _ := utf8.DecodeLastRuneInString(s[:i])
if !isAllowedInEmail(r) {
continue // should be preceded by a valid e-mail character
}
r, _ = utf8.DecodeRuneInString(s[i+1:])
if !isLetterOrDigit(r) {
continue // should be followed by a letter or a digit
}
start, ok := findEmailStart(s, i-1)
if !ok {
continue
}
end, dot, ok := findHostnameEnd(s, i+1)
if !ok {
continue
}
if dot == -1 {
continue // no dot
}
if !(dot+5 <= len(s) && s[dot+1:dot+5] == "xn--") {
if length := match(s[dot+1:]); dot+length+1 != end {
continue
}
}
links = append(links, Link{
Scheme: "mailto:",
Start: start,
End: end,
})
i = end
}
}
return
}

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

@ -0,0 +1,404 @@
// 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 linkify
import "unicode/utf8"
func atoi3(s string, start int) (int, bool) {
n := 0
var i int
for i = start; i < len(s) && digit(s[i]); i++ {
n = n*10 + int(s[i]-'0')
if n > 255 {
return 0, false
}
}
if i == start {
return 0, false
}
return i, true
}
func skipIPv4(s string) (_ int, _ bool) {
j := 0
for i := 0; i < 4; i++ {
if j >= len(s) {
return
}
if i > 0 {
if s[j] != '.' {
return
}
j++
}
n, ok := atoi3(s, j)
if !ok {
return
}
j = n
}
return j, true
}
func atoi5(s string, start int) (int, bool) {
n := 0
var i int
for i = start; i < len(s) && digit(s[i]); i++ {
n = n*10 + int(s[i]-'0')
if n > 65535 {
return 0, false
}
}
if i == start || n == 0 {
return 0, false
}
return i, true
}
func skipPort(s string, start int) int {
if start >= len(s) || s[start] != ':' {
return start
}
end, ok := atoi5(s, start+1)
if !ok {
return start
}
return end
}
func skipPath(s string, start int) int {
if start >= len(s) || s[start] != '/' {
return start // skip empty path
}
var stack []rune
var notClosedIndex int
var nHyphen int
end := start + 1
loop:
for end < len(s) {
r, rlen := utf8.DecodeRuneInString(s[end:])
if r == utf8.RuneError {
nHyphen = 0
break
}
switch {
case isUnreserved(r):
if r == '-' {
nHyphen++
if nHyphen > 1 {
break loop
}
} else {
nHyphen = 0
}
case isSubDelimiter(r) || r == '[' || r == ']':
nHyphen = 0
switch r {
case '[', '(':
if len(stack) == 0 {
notClosedIndex = end
}
stack = append(stack, r)
case ']', ')':
opening := '['
if r == ')' {
opening = '('
}
if len(stack) == 0 || stack[len(stack)-1] != opening {
break loop
}
stack = stack[:len(stack)-1]
}
case r == '/' || r == ':' || r == '@':
nHyphen = 0
case r == '%':
nHyphen = 0
if end+2 >= len(s) {
break loop
}
if !(hexDigit(s[end+1]) &&
hexDigit(s[end+2])) {
break loop
}
end += 2
default:
nHyphen = 0
if r != ' ' || len(stack) == 0 {
break loop
}
}
end += rlen
}
if len(stack) > 0 {
return notClosedIndex
}
if nHyphen > 0 {
return end - nHyphen + 1
}
return end
}
func skipQuery(s string, start int) int {
if start >= len(s) || s[start] != '?' {
return start
}
var stack []rune
var notClosedIndex int
var nHyphen int
end := start + 1
loop:
for end < len(s) {
r, rlen := utf8.DecodeRuneInString(s[end:])
if r == utf8.RuneError {
nHyphen = 0
break
}
switch {
case isUnreserved(r):
if r == '-' {
nHyphen++
if nHyphen > 1 {
break loop
}
} else {
nHyphen = 0
}
case isSubDelimiter(r) || r == '[' || r == ']':
nHyphen = 0
switch r {
case '[', '(':
if len(stack) == 0 {
notClosedIndex = end
}
stack = append(stack, r)
case ']', ')':
opening := '['
if r == ')' {
opening = '('
}
if len(stack) == 0 || stack[len(stack)-1] != opening {
break loop
}
stack = stack[:len(stack)-1]
}
case r == '?' || r == '/' || r == ':' || r == '@':
nHyphen = 0
case r == '%':
nHyphen = 0
if end+2 >= len(s) {
break loop
}
if !(hexDigit(s[end+1]) &&
hexDigit(s[end+2])) {
break loop
}
end += 2
default:
nHyphen = 0
if r != ' ' || len(stack) == 0 {
break loop
}
}
end += rlen
}
if len(stack) > 0 {
return notClosedIndex
}
if nHyphen > 0 {
return end - nHyphen + 1
}
return end
}
func skipFragment(s string, start int) int {
if start >= len(s) || s[start] != '#' {
return start
}
var stack []rune
var notClosedIndex int
var nHyphen int
end := start + 1
loop:
for end < len(s) {
r, rlen := utf8.DecodeRuneInString(s[end:])
if r == utf8.RuneError {
nHyphen = 0
break
}
switch {
case isUnreserved(r):
if r == '-' {
nHyphen++
if nHyphen > 1 {
break loop
}
} else {
nHyphen = 0
}
case isSubDelimiter(r) || r == '[' || r == ']':
nHyphen = 0
switch r {
case '[', '(':
if len(stack) == 0 {
notClosedIndex = end
}
stack = append(stack, r)
case ']', ')':
opening := '['
if r == ')' {
opening = '('
}
if len(stack) == 0 || stack[len(stack)-1] != opening {
break loop
}
stack = stack[:len(stack)-1]
}
case r == '?' || r == '/' || r == ':' || r == '@':
nHyphen = 0
case r == '%':
nHyphen = 0
if end+2 >= len(s) {
break loop
}
if !(hexDigit(s[end+1]) &&
hexDigit(s[end+2])) {
break loop
}
end += 2
default:
nHyphen = 0
if r != ' ' || len(stack) == 0 {
break loop
}
}
end += rlen
}
if len(stack) > 0 {
return notClosedIndex
}
if nHyphen > 0 {
return end - nHyphen + 1
}
return end
}
func unskipPunct(s string, start int) int {
end := start - 1
if end < 0 || end >= len(s) || !basicPunct[s[end]] {
return start
}
return end
}
func findHostnameStart(s string, start int) (_ int, _ bool) {
end := start
lastDot := true
nHyphen := 0
loop:
for end > 0 {
r, rlen := utf8.DecodeLastRuneInString(s[:end])
if r == utf8.RuneError {
return
}
switch {
case r == '.':
if nHyphen > 0 {
return
}
lastDot = true
case r == '-':
if end == start {
return
}
if lastDot {
return
}
nHyphen++
if nHyphen == 3 {
return
}
case r == ':' || r == '/' || r == '\\' || r == '_':
return
case isPunctOrSpaceOrControl(r):
break loop
default:
lastDot = false
nHyphen = 0
}
end -= rlen
}
if lastDot || nHyphen > 0 {
return
}
return end, true
}
func findHostnameEnd(s string, start int) (_ int, _ int, _ bool) {
end := start
lastDot := false
lastDotPos := -1
nHyphen := 0
loop:
for end < len(s) {
r, rlen := utf8.DecodeRuneInString(s[end:])
if r == utf8.RuneError {
return
}
switch {
case r == '.':
if nHyphen > 0 {
return
}
if lastDot {
break loop
}
lastDot = true
lastDotPos = end
nHyphen = 0
case r == '-':
lastDot = false
if end == start {
return
}
if lastDot {
return
}
nHyphen++
if nHyphen == 3 {
break loop
}
case r == '\\' || r == '_':
return
case isPunctOrSpaceOrControl(r):
break loop
default:
lastDot = false
nHyphen = 0
}
end += rlen
}
if nHyphen > 0 {
end -= nHyphen
} else if lastDot {
if s[end-1] == '.' {
end--
}
lastDotPos = end - 1
for lastDotPos >= start && s[lastDotPos] != '.' {
lastDotPos--
}
if lastDotPos < start {
lastDotPos = -1
}
}
return end, lastDotPos, true
}

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

@ -0,0 +1,20 @@
// 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 linkify
func digit(b byte) bool {
return b >= '0' && b <= '9'
}
func hexDigit(b byte) bool {
return digit(b) || b >= 'a' && b <= 'f' || b >= 'A' && b <= 'F'
}
func byteToLower(b byte) byte {
if b >= 'A' && b <= 'Z' {
return b - 'A' + 'a'
}
return b
}

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
}

View File

@ -0,0 +1,16 @@
image: golang:1.11
stages:
- build
- test
build:
stage: build
script:
- go build ./...
test:
stage: test
script:
- test -z "$(gofmt -l . | tee /dev/stderr)"
- go test ./...

10
vendor/gitlab.com/golang-commonmark/mdurl/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.

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

@ -0,0 +1,8 @@
mdurl [![License](https://img.shields.io/badge/licence-BSD--2--Clause-blue.svg)](https://opensource.org/licenses/BSD-2-Clause) [![GoDoc](http://godoc.org/gitlab.com/golang-commonmark/mdurl?status.svg)](http://godoc.org/gitlab.com/golang-commonmark/mdurl) [![Pipeline status](https://gitlab.com/golang-commonmark/mdurl/badges/master/pipeline.svg)](https://gitlab.com/golang-commonmark/mdurl/commits/master)
=====
Package mdurl provides functions for parsing, decoding and encoding URLs.
## Install
go get -u gitlab.com/golang-commonmark/mdurl

80
vendor/gitlab.com/golang-commonmark/mdurl/decode.go generated vendored Normal file
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 mdurl
import (
"bytes"
"unicode/utf8"
)
func advance(s string, pos int) (byte, int) {
if pos >= len(s) {
return 0, len(s) + 1
}
if s[pos] != '%' {
return s[pos], pos + 1
}
if pos+2 < len(s) &&
hexDigit(s[pos+1]) &&
hexDigit(s[pos+2]) {
return unhex(s[pos+1])<<4 | unhex(s[pos+2]), pos + 3
}
return '%', pos + 1
}
// Decode decodes a percent-encoded URL.
// Invalid percent-encoded sequences are left as is.
// Invalid UTF-8 sequences are replaced with U+FFFD.
func Decode(rawurl string) string {
var buf bytes.Buffer
i := 0
const replacement = "\xEF\xBF\xBD"
outer:
for i < len(rawurl) {
r, rlen := utf8.DecodeRuneInString(rawurl[i:])
if r == '%' && i+2 < len(rawurl) &&
hexDigit(rawurl[i+1]) &&
hexDigit(rawurl[i+2]) {
b := unhex(rawurl[i+1])<<4 | unhex(rawurl[i+2])
if b < 0x80 {
buf.WriteByte(b)
i += 3
continue
}
var n int
if b&0xe0 == 0xc0 {
n = 1
} else if b&0xf0 == 0xe0 {
n = 2
} else if b&0xf8 == 0xf0 {
n = 3
}
if n == 0 {
buf.WriteString(replacement)
i += 3
continue
}
rb := make([]byte, n+1)
rb[0] = b
j := i + 3
for k := 0; k < n; k++ {
b, j = advance(rawurl, j)
if j > len(rawurl) || b&0xc0 != 0x80 {
buf.WriteString(replacement)
i += 3
continue outer
}
rb[k+1] = b
}
r, _ := utf8.DecodeRune(rb)
buf.WriteRune(r)
i = j
continue
}
buf.WriteRune(r)
i += rlen
}
return buf.String()
}

53
vendor/gitlab.com/golang-commonmark/mdurl/encode.go generated vendored Normal file
View File

@ -0,0 +1,53 @@
// 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 mdurl
import (
"bytes"
"strings"
"unicode/utf8"
)
// Encode percent-encodes rawurl, avoiding double encoding.
// It doesn't touch:
// - alphanumeric characters ([0-9a-zA-Z]);
// - percent-encoded characters (%[0-9a-fA-F]{2});
// - excluded characters ([;/?:@&=+$,-_.!~*'()#]).
// Invalid UTF-8 sequences are replaced with U+FFFD.
func Encode(rawurl string) string {
const hexdigit = "0123456789ABCDEF"
var buf bytes.Buffer
i := 0
for i < len(rawurl) {
r, rlen := utf8.DecodeRuneInString(rawurl[i:])
if r >= 0x80 {
for j, n := i, i+rlen; j < n; j++ {
b := rawurl[j]
buf.WriteByte('%')
buf.WriteByte(hexdigit[(b>>4)&0xf])
buf.WriteByte(hexdigit[b&0xf])
}
} else if r == '%' {
if i+2 < len(rawurl) &&
hexDigit(rawurl[i+1]) &&
hexDigit(rawurl[i+2]) {
buf.WriteByte('%')
buf.WriteByte(byteToUpper(rawurl[i+1]))
buf.WriteByte(byteToUpper(rawurl[i+2]))
i += 2
} else {
buf.WriteString("%25")
}
} else if strings.IndexByte("!#$&'()*+,-./0123456789:;=?@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~", byte(r)) == -1 {
buf.WriteByte('%')
buf.WriteByte(hexdigit[(r>>4)&0xf])
buf.WriteByte(hexdigit[r&0xf])
} else {
buf.WriteByte(byte(r))
}
i += rlen
}
return buf.String()
}

146
vendor/gitlab.com/golang-commonmark/mdurl/parse.go generated vendored Normal file
View File

@ -0,0 +1,146 @@
// 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 mdurl
import (
"errors"
"strings"
)
// ErrMissingScheme error is returned by Parse if the passed URL starts with a colon.
var ErrMissingScheme = errors.New("missing protocol scheme")
var slashedProtocol = map[string]bool{
"http": true,
"https": true,
"ftp": true,
"gopher": true,
"file": true,
}
func split(s string, c byte) (string, string, bool) {
i := strings.IndexByte(s, c)
if i < 0 {
return s, "", false
}
return s[:i], s[i+1:], true
}
func findScheme(s string) (int, error) {
if s == "" {
return 0, nil
}
b := s[0]
if b == ':' {
return 0, ErrMissingScheme
}
if !letter(b) {
return 0, nil
}
for i := 1; i < len(s); i++ {
b := s[i]
switch {
case b == ':':
return i, nil
case strings.IndexByte("+-.0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", b) != -1:
// do nothing
default:
return 0, nil
}
}
return 0, nil
}
// Parse parses rawurl into a URL structure.
func Parse(rawurl string) (*URL, error) {
n, err := findScheme(rawurl)
if err != nil {
return nil, err
}
var url URL
rest := rawurl
hostless := false
if n > 0 {
url.RawScheme = rest[:n]
url.Scheme, rest = strings.ToLower(rest[:n]), rest[n+1:]
if url.Scheme == "javascript" {
hostless = true
}
}
if !hostless && strings.HasPrefix(rest, "//") {
url.Slashes, rest = true, rest[2:]
}
if !hostless && (url.Slashes || (url.Scheme != "" && !slashedProtocol[url.Scheme])) {
hostEnd := strings.IndexAny(rest, "#/?")
atSign := -1
i := hostEnd
if i == -1 {
i = len(rest) - 1
}
for i >= 0 {
if rest[i] == '@' {
atSign = i
break
}
i--
}
if atSign != -1 {
url.Auth, rest = rest[:atSign], rest[atSign+1:]
}
hostEnd = strings.IndexAny(rest, "\t\r\n \"#%'/;<>?\\^`{|}")
if hostEnd == -1 {
hostEnd = len(rest)
}
if hostEnd > 0 && hostEnd < len(rest) && rest[hostEnd-1] == ':' {
hostEnd--
}
host := rest[:hostEnd]
if len(host) > 1 {
b := host[hostEnd-1]
if digit(b) {
for i := len(host) - 2; i >= 0; i-- {
b := host[i]
if b == ':' {
url.Host, url.Port = host[:i], host[i+1:]
break
}
if !digit(b) {
break
}
}
} else if b == ':' {
host = host[:hostEnd-1]
hostEnd--
}
}
if url.Port == "" {
url.Host = host
}
rest = rest[hostEnd:]
if ipv6 := len(url.Host) > 2 &&
url.Host[0] == '[' &&
url.Host[len(url.Host)-1] == ']'; ipv6 {
url.Host = url.Host[1 : len(url.Host)-1]
url.IPv6 = true
} else if i := strings.IndexByte(url.Host, ':'); i >= 0 {
url.Host, rest = url.Host[:i], url.Host[i:]+rest
}
}
rest, url.Fragment, url.HasFragment = split(rest, '#')
url.Path, url.RawQuery, url.HasQuery = split(rest, '?')
return &url, nil
}

101
vendor/gitlab.com/golang-commonmark/mdurl/url.go generated vendored Normal file
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 url provides functions for parsing, decoding and encoding URLs.
package mdurl
// A URL represents a parsed URL.
type URL struct {
Scheme string
RawScheme string
Slashes bool
Auth string
Host string
Port string
Path string
RawQuery string
HasQuery bool
Fragment string
HasFragment bool
IPv6 bool
}
// String reassembles the URL into a URL string.
func (u *URL) String() string {
size := len(u.Path)
if u.Scheme != "" {
size += len(u.Scheme) + 1
}
if u.Slashes {
size += 2
}
if u.Auth != "" {
size += len(u.Auth) + 1
}
if u.Host != "" {
size += len(u.Host)
if u.IPv6 {
size += 2
}
}
if u.Port != "" {
size += len(u.Port) + 1
}
if u.HasQuery {
size += len(u.RawQuery) + 1
}
if u.HasFragment {
size += len(u.Fragment) + 1
}
if size == 0 {
return ""
}
buf := make([]byte, size)
i := 0
if u.Scheme != "" {
i += copy(buf, u.Scheme)
buf[i] = ':'
i++
}
if u.Slashes {
buf[i] = '/'
i++
buf[i] = '/'
i++
}
if u.Auth != "" {
i += copy(buf[i:], u.Auth)
buf[i] = '@'
i++
}
if u.Host != "" {
if u.IPv6 {
buf[i] = '['
i++
i += copy(buf[i:], u.Host)
buf[i] = ']'
i++
} else {
i += copy(buf[i:], u.Host)
}
}
if u.Port != "" {
buf[i] = ':'
i++
i += copy(buf[i:], u.Port)
}
i += copy(buf[i:], u.Path)
if u.HasQuery {
buf[i] = '?'
i++
i += copy(buf[i:], u.RawQuery)
}
if u.HasFragment {
buf[i] = '#'
i++
i += copy(buf[i:], u.Fragment)
}
return string(buf)
}

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

@ -0,0 +1,36 @@
// 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 mdurl
func hexDigit(b byte) bool {
return digit(b) || b >= 'a' && b <= 'f' || b >= 'A' && b <= 'F'
}
func unhex(b byte) byte {
switch {
case digit(b):
return b - '0'
case b >= 'a' && b <= 'f':
return b - 'a' + 10
case b >= 'A' && b <= 'F':
return b - 'A' + 10
}
panic("unhex: not a hex digit")
}
func letter(b byte) bool {
return b >= 'a' && b <= 'z' || b >= 'A' && b <= 'Z'
}
func digit(b byte) bool {
return b >= '0' && b <= '9'
}
func byteToUpper(b byte) byte {
if b >= 'a' && b <= 'z' {
return b - 'a' + 'A'
}
return b
}

View File

@ -0,0 +1,16 @@
image: golang:1.11
stages:
- build
- test
build:
stage: build
script:
- go build ./...
test:
stage: test
script:
- test -z "$(gofmt -l . | tee /dev/stderr)"
- go test ./...

10
vendor/gitlab.com/golang-commonmark/puny/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.

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

@ -0,0 +1,8 @@
puny [![License](https://img.shields.io/badge/licence-BSD--2--Clause-blue.svg)](https://opensource.org/licenses/BSD-2-Clause) [![GoDoc](http://godoc.org/gitlab.com/golang-commonmark/puny?status.svg)](http://godoc.org/gitlab.com/golang-commonmark/puny) [![Pipeline status](https://gitlab.com/golang-commonmark/puny/badges/master/pipeline.svg)](https://gitlab.com/golang-commonmark/puny/commits/master)
====
Package puny provides functions for encoding/decoding to/from punycode.
## Install
go get -u gitlab.com/golang-commonmark/puny

287
vendor/gitlab.com/golang-commonmark/puny/puny.go generated vendored Normal file
View File

@ -0,0 +1,287 @@
// 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 puny provides functions for encoding/decoding to/from punycode.
package puny
import (
"errors"
"strings"
"unicode/utf8"
)
const (
maxInt32 int32 = 2147483647
base int32 = 36
tMin int32 = 1
baseMinusTMin = base - tMin
tMax int32 = 26
skew int32 = 38
damp int32 = 700
initialBias int32 = 72
initialN int32 = 128
)
var (
ErrOverflow = errors.New("overflow: input needs wider integers to process")
ErrNotBasic = errors.New("illegal input >= 0x80 (not a basic code point)")
ErrInvalidInput = errors.New("invalid input")
)
func adapt(delta, numPoints int32, firstTime bool) int32 {
if firstTime {
delta /= damp
} else {
delta /= 2
}
delta += delta / numPoints
k := int32(0)
for delta > baseMinusTMin*tMax/2 {
delta = delta / baseMinusTMin
k += base
}
return k + (baseMinusTMin+1)*delta/(delta+skew)
}
func basicToDigit(b byte) int32 {
switch {
case b >= '0' && b <= '9':
return int32(b - 22)
case b >= 'A' && b <= 'Z':
return int32(b - 'A')
case b >= 'a' && b <= 'z':
return int32(b - 'a')
}
return base
}
func digitToBasic(digit int32) byte {
switch {
case digit >= 0 && digit <= 25:
return byte(digit) + 'a'
case digit >= 26 && digit <= 35:
return byte(digit) - 26 + '0'
}
panic("unreachable")
}
func lastIndex(s string, c byte) int {
for i := len(s) - 1; i >= 0; i-- {
if s[i] == c {
return i
}
}
return -1
}
func ascii(s string) bool {
for _, r := range s {
if r > 0x7e {
return false
}
}
return true
}
// Decode converts a Punycode string of ASCII-only symbols to a string of Unicode symbols.
func Decode(s string) (string, error) {
basic := lastIndex(s, '-')
output := make([]rune, 0, len(s))
for i := 0; i < basic; i++ {
b := s[i]
if b >= 0x80 {
return "", ErrNotBasic
}
output = append(output, rune(b))
}
i, n, bias, pos := int32(0), initialN, initialBias, basic+1
for pos < len(s) {
oldi, w, k := i, int32(1), base
for {
digit := basicToDigit(s[pos])
pos++
if digit >= base || digit > (maxInt32-i)/w {
return "", ErrOverflow
}
i += digit * w
t := k - bias
if t < tMin {
t = tMin
} else if t > tMax {
t = tMax
}
if digit < t {
break
}
if pos == len(s) {
return "", ErrInvalidInput
}
baseMinusT := base - t
if w > maxInt32/baseMinusT {
return "", ErrOverflow
}
w *= baseMinusT
k += base
}
out := int32(len(output) + 1)
bias = adapt(i-oldi, out, oldi == 0)
if i/out > maxInt32-n {
return "", ErrOverflow
}
n += i / out
i %= out
output = append(output, 0)
copy(output[i+1:], output[i:])
output[i] = rune(n)
i++
}
return string(output), nil
}
// Encode converts a string of Unicode symbols (e.g. a domain name label) to a
// Punycode string of ASCII-only symbols.
func Encode(input string) (string, error) {
n := initialN
delta := int32(0)
bias := initialBias
var output []byte
runes := 0
for _, r := range input {
if r >= 0x80 {
runes++
continue
}
output = append(output, byte(r))
}
basicLength := len(output)
handledCPCount := basicLength
if basicLength > 0 {
output = append(output, '-')
}
for runes > 0 {
m := maxInt32
for _, r := range input {
if r >= n && r < m {
m = r
}
}
handledCPCountPlusOne := int32(handledCPCount + 1)
if m-n > (maxInt32-delta)/handledCPCountPlusOne {
return "", ErrOverflow
}
delta += (m - n) * handledCPCountPlusOne
n = m
for _, r := range input {
if r < n {
delta++
if delta < 0 {
return "", ErrOverflow
}
continue
}
if r > n {
continue
}
q := delta
for k := base; ; k += base {
t := k - bias
if t < tMin {
t = tMin
} else if t > tMax {
t = tMax
}
if q < t {
break
}
qMinusT := q - t
baseMinusT := base - t
output = append(output, digitToBasic(t+qMinusT%baseMinusT))
q = qMinusT / baseMinusT
}
output = append(output, digitToBasic(q))
bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength)
delta = 0
handledCPCount++
runes--
}
delta++
n++
}
return string(output), nil
}
func sep(r rune) bool { return r == '.' || r == '。' || r == '' || r == '。' }
func mapLabels(s string, fn func(string) string) string {
var result string
i := strings.IndexByte(s, '@')
if i != -1 {
result = s[:i+1]
s = s[i+1:]
}
var labels []string
start := 0
for i, r := range s {
if !sep(r) {
continue
}
labels = append(labels, fn(s[start:i]))
start = i + utf8.RuneLen(r)
}
labels = append(labels, fn(s[start:]))
return result + strings.Join(labels, ".")
}
// ToUnicode converts a Punycode string representing a domain name or an email address
// to Unicode. Only the Punycoded parts of the input will be converted.
func ToUnicode(s string) string {
return mapLabels(s, func(s string) string {
if !strings.HasPrefix(s, "xn--") {
return s
}
d, err := Decode(strings.ToLower(s[4:]))
if err != nil {
return s
}
return d
})
}
// ToASCII converts a Unicode string representing a domain name or an email address to
// Punycode. Only the non-ASCII parts of the domain name will be converted.
func ToASCII(s string) string {
return mapLabels(s, func(s string) string {
if ascii(s) {
return s
}
d, err := Encode(s)
if err != nil {
return s
}
return "xn--" + d
})
}

115
vendor/golang.org/x/text/unicode/rangetable/gen.go generated vendored Normal file
View File

@ -0,0 +1,115 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build ignore
package main
import (
"bytes"
"flag"
"fmt"
"io"
"log"
"reflect"
"strings"
"unicode"
"golang.org/x/text/collate"
"golang.org/x/text/internal/gen"
"golang.org/x/text/internal/ucd"
"golang.org/x/text/language"
"golang.org/x/text/unicode/rangetable"
)
var versionList = flag.String("versions", "",
"list of versions for which to generate RangeTables")
const bootstrapMessage = `No versions specified.
To bootstrap the code generation, run:
go run gen.go --versions=4.1.0,5.0.0,6.0.0,6.1.0,6.2.0,6.3.0,7.0.0
and ensure that the latest versions are included by checking:
http://www.unicode.org/Public/`
func getVersions() []string {
if *versionList == "" {
log.Fatal(bootstrapMessage)
}
c := collate.New(language.Und, collate.Numeric)
versions := strings.Split(*versionList, ",")
c.SortStrings(versions)
// Ensure that at least the current version is included.
for _, v := range versions {
if v == gen.UnicodeVersion() {
return versions
}
}
versions = append(versions, gen.UnicodeVersion())
c.SortStrings(versions)
return versions
}
func main() {
gen.Init()
versions := getVersions()
w := &bytes.Buffer{}
fmt.Fprintf(w, "//go:generate go run gen.go --versions=%s\n\n", strings.Join(versions, ","))
fmt.Fprintf(w, "import \"unicode\"\n\n")
vstr := func(s string) string { return strings.Replace(s, ".", "_", -1) }
fmt.Fprintf(w, "var assigned = map[string]*unicode.RangeTable{\n")
for _, v := range versions {
fmt.Fprintf(w, "\t%q: assigned%s,\n", v, vstr(v))
}
fmt.Fprintf(w, "}\n\n")
var size int
for _, v := range versions {
assigned := []rune{}
r := gen.Open("http://www.unicode.org/Public/", "", v+"/ucd/UnicodeData.txt")
ucd.Parse(r, func(p *ucd.Parser) {
assigned = append(assigned, p.Rune(0))
})
rt := rangetable.New(assigned...)
sz := int(reflect.TypeOf(unicode.RangeTable{}).Size())
sz += int(reflect.TypeOf(unicode.Range16{}).Size()) * len(rt.R16)
sz += int(reflect.TypeOf(unicode.Range32{}).Size()) * len(rt.R32)
fmt.Fprintf(w, "// size %d bytes (%d KiB)\n", sz, sz/1024)
fmt.Fprintf(w, "var assigned%s = ", vstr(v))
print(w, rt)
size += sz
}
fmt.Fprintf(w, "// Total size %d bytes (%d KiB)\n", size, size/1024)
gen.WriteVersionedGoFile("tables.go", "rangetable", w.Bytes())
}
func print(w io.Writer, rt *unicode.RangeTable) {
fmt.Fprintln(w, "&unicode.RangeTable{")
fmt.Fprintln(w, "\tR16: []unicode.Range16{")
for _, r := range rt.R16 {
fmt.Fprintf(w, "\t\t{%#04x, %#04x, %d},\n", r.Lo, r.Hi, r.Stride)
}
fmt.Fprintln(w, "\t},")
fmt.Fprintln(w, "\tR32: []unicode.Range32{")
for _, r := range rt.R32 {
fmt.Fprintf(w, "\t\t{%#08x, %#08x, %d},\n", r.Lo, r.Hi, r.Stride)
}
fmt.Fprintln(w, "\t},")
fmt.Fprintf(w, "\tLatinOffset: %d,\n", rt.LatinOffset)
fmt.Fprintf(w, "}\n\n")
}

260
vendor/golang.org/x/text/unicode/rangetable/merge.go generated vendored Normal file
View File

@ -0,0 +1,260 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package rangetable
import (
"unicode"
)
// atEnd is used to mark a completed iteration.
const atEnd = unicode.MaxRune + 1
// Merge returns a new RangeTable that is the union of the given tables.
// It can also be used to compact user-created RangeTables. The entries in
// R16 and R32 for any given RangeTable should be sorted and non-overlapping.
//
// A lookup in the resulting table can be several times faster than using In
// directly on the ranges. Merge is an expensive operation, however, and only
// makes sense if one intends to use the result for more than a couple of
// hundred lookups.
func Merge(ranges ...*unicode.RangeTable) *unicode.RangeTable {
rt := &unicode.RangeTable{}
if len(ranges) == 0 {
return rt
}
iter := tablesIter(make([]tableIndex, len(ranges)))
for i, t := range ranges {
iter[i] = tableIndex{t, 0, atEnd}
if len(t.R16) > 0 {
iter[i].next = rune(t.R16[0].Lo)
}
}
if r0 := iter.next16(); r0.Stride != 0 {
for {
r1 := iter.next16()
if r1.Stride == 0 {
rt.R16 = append(rt.R16, r0)
break
}
stride := r1.Lo - r0.Hi
if (r1.Lo == r1.Hi || stride == r1.Stride) && (r0.Lo == r0.Hi || stride == r0.Stride) {
// Fully merge the next range into the previous one.
r0.Hi, r0.Stride = r1.Hi, stride
continue
} else if stride == r0.Stride {
// Move the first element of r1 to r0. This may eliminate an
// entry.
r0.Hi = r1.Lo
r0.Stride = stride
r1.Lo = r1.Lo + r1.Stride
if r1.Lo > r1.Hi {
continue
}
}
rt.R16 = append(rt.R16, r0)
r0 = r1
}
}
for i, t := range ranges {
iter[i] = tableIndex{t, 0, atEnd}
if len(t.R32) > 0 {
iter[i].next = rune(t.R32[0].Lo)
}
}
if r0 := iter.next32(); r0.Stride != 0 {
for {
r1 := iter.next32()
if r1.Stride == 0 {
rt.R32 = append(rt.R32, r0)
break
}
stride := r1.Lo - r0.Hi
if (r1.Lo == r1.Hi || stride == r1.Stride) && (r0.Lo == r0.Hi || stride == r0.Stride) {
// Fully merge the next range into the previous one.
r0.Hi, r0.Stride = r1.Hi, stride
continue
} else if stride == r0.Stride {
// Move the first element of r1 to r0. This may eliminate an
// entry.
r0.Hi = r1.Lo
r1.Lo = r1.Lo + r1.Stride
if r1.Lo > r1.Hi {
continue
}
}
rt.R32 = append(rt.R32, r0)
r0 = r1
}
}
for i := 0; i < len(rt.R16) && rt.R16[i].Hi <= unicode.MaxLatin1; i++ {
rt.LatinOffset = i + 1
}
return rt
}
type tableIndex struct {
t *unicode.RangeTable
p uint32
next rune
}
type tablesIter []tableIndex
// sortIter does an insertion sort using the next field of tableIndex. Insertion
// sort is a good sorting algorithm for this case.
func sortIter(t []tableIndex) {
for i := range t {
for j := i; j > 0 && t[j-1].next > t[j].next; j-- {
t[j], t[j-1] = t[j-1], t[j]
}
}
}
// next16 finds the ranged to be added to the table. If ranges overlap between
// multiple tables it clips the result to a non-overlapping range if the
// elements are not fully subsumed. It returns a zero range if there are no more
// ranges.
func (ti tablesIter) next16() unicode.Range16 {
sortIter(ti)
t0 := ti[0]
if t0.next == atEnd {
return unicode.Range16{}
}
r0 := t0.t.R16[t0.p]
r0.Lo = uint16(t0.next)
// We restrict the Hi of the current range if it overlaps with another range.
for i := range ti {
tn := ti[i]
// Since our tableIndices are sorted by next, we can break if the there
// is no overlap. The first value of a next range can always be merged
// into the current one, so we can break in case of equality as well.
if rune(r0.Hi) <= tn.next {
break
}
rn := tn.t.R16[tn.p]
rn.Lo = uint16(tn.next)
// Limit r0.Hi based on next ranges in list, but allow it to overlap
// with ranges as long as it subsumes it.
m := (rn.Lo - r0.Lo) % r0.Stride
if m == 0 && (rn.Stride == r0.Stride || rn.Lo == rn.Hi) {
// Overlap, take the min of the two Hi values: for simplicity's sake
// we only process one range at a time.
if r0.Hi > rn.Hi {
r0.Hi = rn.Hi
}
} else {
// Not a compatible stride. Set to the last possible value before
// rn.Lo, but ensure there is at least one value.
if x := rn.Lo - m; r0.Lo <= x {
r0.Hi = x
}
break
}
}
// Update the next values for each table.
for i := range ti {
tn := &ti[i]
if rune(r0.Hi) < tn.next {
break
}
rn := tn.t.R16[tn.p]
stride := rune(rn.Stride)
tn.next += stride * (1 + ((rune(r0.Hi) - tn.next) / stride))
if rune(rn.Hi) < tn.next {
if tn.p++; int(tn.p) == len(tn.t.R16) {
tn.next = atEnd
} else {
tn.next = rune(tn.t.R16[tn.p].Lo)
}
}
}
if r0.Lo == r0.Hi {
r0.Stride = 1
}
return r0
}
// next32 finds the ranged to be added to the table. If ranges overlap between
// multiple tables it clips the result to a non-overlapping range if the
// elements are not fully subsumed. It returns a zero range if there are no more
// ranges.
func (ti tablesIter) next32() unicode.Range32 {
sortIter(ti)
t0 := ti[0]
if t0.next == atEnd {
return unicode.Range32{}
}
r0 := t0.t.R32[t0.p]
r0.Lo = uint32(t0.next)
// We restrict the Hi of the current range if it overlaps with another range.
for i := range ti {
tn := ti[i]
// Since our tableIndices are sorted by next, we can break if the there
// is no overlap. The first value of a next range can always be merged
// into the current one, so we can break in case of equality as well.
if rune(r0.Hi) <= tn.next {
break
}
rn := tn.t.R32[tn.p]
rn.Lo = uint32(tn.next)
// Limit r0.Hi based on next ranges in list, but allow it to overlap
// with ranges as long as it subsumes it.
m := (rn.Lo - r0.Lo) % r0.Stride
if m == 0 && (rn.Stride == r0.Stride || rn.Lo == rn.Hi) {
// Overlap, take the min of the two Hi values: for simplicity's sake
// we only process one range at a time.
if r0.Hi > rn.Hi {
r0.Hi = rn.Hi
}
} else {
// Not a compatible stride. Set to the last possible value before
// rn.Lo, but ensure there is at least one value.
if x := rn.Lo - m; r0.Lo <= x {
r0.Hi = x
}
break
}
}
// Update the next values for each table.
for i := range ti {
tn := &ti[i]
if rune(r0.Hi) < tn.next {
break
}
rn := tn.t.R32[tn.p]
stride := rune(rn.Stride)
tn.next += stride * (1 + ((rune(r0.Hi) - tn.next) / stride))
if rune(rn.Hi) < tn.next {
if tn.p++; int(tn.p) == len(tn.t.R32) {
tn.next = atEnd
} else {
tn.next = rune(tn.t.R32[tn.p].Lo)
}
}
}
if r0.Lo == r0.Hi {
r0.Stride = 1
}
return r0
}

View File

@ -0,0 +1,70 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package rangetable provides utilities for creating and inspecting
// unicode.RangeTables.
package rangetable
import (
"sort"
"unicode"
)
// New creates a RangeTable from the given runes, which may contain duplicates.
func New(r ...rune) *unicode.RangeTable {
if len(r) == 0 {
return &unicode.RangeTable{}
}
sort.Sort(byRune(r))
// Remove duplicates.
k := 1
for i := 1; i < len(r); i++ {
if r[k-1] != r[i] {
r[k] = r[i]
k++
}
}
var rt unicode.RangeTable
for _, r := range r[:k] {
if r <= 0xFFFF {
rt.R16 = append(rt.R16, unicode.Range16{Lo: uint16(r), Hi: uint16(r), Stride: 1})
} else {
rt.R32 = append(rt.R32, unicode.Range32{Lo: uint32(r), Hi: uint32(r), Stride: 1})
}
}
// Optimize RangeTable.
return Merge(&rt)
}
type byRune []rune
func (r byRune) Len() int { return len(r) }
func (r byRune) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
func (r byRune) Less(i, j int) bool { return r[i] < r[j] }
// Visit visits all runes in the given RangeTable in order, calling fn for each.
func Visit(rt *unicode.RangeTable, fn func(rune)) {
for _, r16 := range rt.R16 {
for r := rune(r16.Lo); r <= rune(r16.Hi); r += rune(r16.Stride) {
fn(r)
}
}
for _, r32 := range rt.R32 {
for r := rune(r32.Lo); r <= rune(r32.Hi); r += rune(r32.Stride) {
fn(r)
}
}
}
// Assigned returns a RangeTable with all assigned code points for a given
// Unicode version. This includes graphic, format, control, and private-use
// characters. It returns nil if the data for the given version is not
// available.
func Assigned(version string) *unicode.RangeTable {
return assigned[version]
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

11
vendor/modules.txt vendored
View File

@ -147,6 +147,16 @@ github.com/valyala/bytebufferpool
github.com/valyala/fasttemplate github.com/valyala/fasttemplate
# github.com/zfjagann/golang-ring v0.0.0-20141111230621-17637388c9f6 # github.com/zfjagann/golang-ring v0.0.0-20141111230621-17637388c9f6
github.com/zfjagann/golang-ring github.com/zfjagann/golang-ring
# gitlab.com/golang-commonmark/html v0.0.0-20180917080848-cfaf75183c4a
gitlab.com/golang-commonmark/html
# gitlab.com/golang-commonmark/linkify v0.0.0-20180917065525-c22b7bdb1179
gitlab.com/golang-commonmark/linkify
# gitlab.com/golang-commonmark/markdown v0.0.0-20181102083822-772775880e1f
gitlab.com/golang-commonmark/markdown
# gitlab.com/golang-commonmark/mdurl v0.0.0-20180912090424-e5bce34c34f2
gitlab.com/golang-commonmark/mdurl
# gitlab.com/golang-commonmark/puny v0.0.0-20180912090636-2cd490539afe
gitlab.com/golang-commonmark/puny
# go.uber.org/atomic v1.3.2 # go.uber.org/atomic v1.3.2
go.uber.org/atomic go.uber.org/atomic
# go.uber.org/multierr v1.1.0 # go.uber.org/multierr v1.1.0
@ -184,6 +194,7 @@ golang.org/x/text/encoding/simplifiedchinese
golang.org/x/text/encoding/traditionalchinese golang.org/x/text/encoding/traditionalchinese
golang.org/x/text/transform golang.org/x/text/transform
golang.org/x/text/unicode/norm golang.org/x/text/unicode/norm
golang.org/x/text/unicode/rangetable
golang.org/x/text/encoding/internal/identifier golang.org/x/text/encoding/internal/identifier
golang.org/x/text/encoding/internal golang.org/x/text/encoding/internal
# gopkg.in/natefinch/lumberjack.v2 v2.0.0 # gopkg.in/natefinch/lumberjack.v2 v2.0.0