Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
214fe502cd | ||
![]() |
aae45a8179 | ||
![]() |
075ca9ca47 | ||
![]() |
d4253d7a55 | ||
![]() |
0917dc8766 | ||
![]() |
aba86855b5 | ||
![]() |
ed5386c213 | ||
![]() |
455e75e92f | ||
![]() |
c394de0c88 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,4 @@
|
||||
# Exclude matterbridge binary
|
||||
matterbridge
|
||||
|
||||
# Exclude configuration file
|
||||
matterbridge.toml
|
||||
|
@@ -174,6 +174,7 @@ linters:
|
||||
- lll
|
||||
- maligned
|
||||
- prealloc
|
||||
- wsl
|
||||
|
||||
|
||||
# rules to deal with reported isues
|
||||
|
10
.travis.yml
10
.travis.yml
@@ -20,22 +20,22 @@ jobs:
|
||||
- stage: lint
|
||||
# Run linting in one Go environment only.
|
||||
script: ./ci/lint.sh
|
||||
go: 1.12.x
|
||||
go: 1.13.x
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
- GOLANGCI_VERSION="v1.17.1"
|
||||
- GOLANGCI_VERSION="v1.21.0"
|
||||
- stage: test
|
||||
# Run tests in a combination of Go environments.
|
||||
script: ./ci/test.sh
|
||||
go: 1.11.x
|
||||
go: 1.12.x
|
||||
env:
|
||||
- GO111MODULE=off
|
||||
- script: ./ci/test.sh
|
||||
go: 1.11.x
|
||||
go: 1.12.x
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
- script: ./ci/test.sh
|
||||
go: 1.12.x
|
||||
go: 1.13.x
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
- REPORT_COVERAGE=1
|
||||
|
12
README.md
12
README.md
@@ -102,6 +102,7 @@ And more...
|
||||
- [Reddit](https://github.com/bonehurtingjuice/mattereddit)
|
||||
- [Facebook messenger](https://github.com/VictorNine/fbridge)
|
||||
- [Discourse](https://github.com/DeclanHoare/matterbabble)
|
||||
- [Counter-Strike, half-life and more](https://forums.alliedmods.net/showthread.php?t=319430)
|
||||
|
||||
### API
|
||||
|
||||
@@ -115,6 +116,7 @@ Used by the projects below. Feel free to make a PR to add your project to this l
|
||||
- [Mattereddit](https://github.com/bonehurtingjuice/mattereddit) (Reddit chat support)
|
||||
- [fbridge](https://github.com/VictorNine/fbridge) (Facebook messenger support)
|
||||
- [matterbabble](https://github.com/DeclanHoare/matterbabble) (Discourse support)
|
||||
- [MatterAMXX](https://forums.alliedmods.net/showthread.php?t=319430) (Counter-Strike, half-life and more via AMXX mod)
|
||||
|
||||
## Chat with us
|
||||
|
||||
@@ -140,7 +142,7 @@ See https://github.com/42wim/matterbridge/wiki
|
||||
|
||||
### Binaries
|
||||
|
||||
- Latest stable release [v1.16.1](https://github.com/42wim/matterbridge/releases/latest)
|
||||
- Latest stable release [v1.16.2](https://github.com/42wim/matterbridge/releases/latest)
|
||||
- Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/)
|
||||
|
||||
To install or upgrade just download the latest [binary](https://github.com/42wim/matterbridge/releases/latest) and follow the instructions on the [howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) for a step by step walkthrough for creating your configuration.
|
||||
@@ -154,19 +156,17 @@ To install or upgrade just download the latest [binary](https://github.com/42wim
|
||||
Most people just want to use binaries, you can find those [here](https://github.com/42wim/matterbridge/releases/latest)
|
||||
|
||||
If you really want to build from source, follow these instructions:
|
||||
Go 1.9+ is required. Make sure you have [Go](https://golang.org/doc/install) properly installed, including setting up your [GOPATH](https://golang.org/doc/code.html#GOPATH).
|
||||
Go 1.12+ is required. Make sure you have [Go](https://golang.org/doc/install) properly installed.
|
||||
|
||||
After Go is setup, download matterbridge to your \$GOPATH directory.
|
||||
|
||||
```
|
||||
cd $GOPATH
|
||||
go get github.com/42wim/matterbridge
|
||||
```
|
||||
|
||||
You should now have matterbridge binary in the bin directory:
|
||||
You should now have matterbridge binary in the ~/go/bin directory:
|
||||
|
||||
```
|
||||
$ ls bin/
|
||||
$ ls ~/go/bin/
|
||||
matterbridge
|
||||
```
|
||||
|
||||
|
@@ -14,8 +14,9 @@ import (
|
||||
"golang.org/x/image/webp"
|
||||
|
||||
"github.com/42wim/matterbridge/bridge/config"
|
||||
"github.com/gomarkdown/markdown"
|
||||
"github.com/gomarkdown/markdown/parser"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gitlab.com/golang-commonmark/markdown"
|
||||
)
|
||||
|
||||
// DownloadFile downloads the given non-authenticated URL.
|
||||
@@ -176,9 +177,12 @@ func ClipMessage(text string, length int) string {
|
||||
return text
|
||||
}
|
||||
|
||||
// ParseMarkdown takes in an input string as markdown and parses it to html
|
||||
func ParseMarkdown(input string) string {
|
||||
md := markdown.New(markdown.XHTMLOutput(true), markdown.Breaks(true))
|
||||
res := md.RenderToString([]byte(input))
|
||||
extensions := parser.HardLineBreak
|
||||
markdownParser := parser.NewWithExtensions(extensions)
|
||||
parsedMarkdown := markdown.ToHTML([]byte(input), markdownParser, nil)
|
||||
res := string(parsedMarkdown)
|
||||
res = strings.TrimPrefix(res, "<p>")
|
||||
res = strings.TrimSuffix(res, "</p>\n")
|
||||
return res
|
||||
|
@@ -1,6 +1,9 @@
|
||||
package bkeybase
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"github.com/42wim/matterbridge/bridge"
|
||||
@@ -66,17 +69,37 @@ func (b *Bkeybase) Send(msg config.Message) (string, error) {
|
||||
// Delete message if we have an ID
|
||||
// Delete message not supported by keybase go library yet
|
||||
|
||||
// Upload a file if it exists
|
||||
// kbchat lib does not support attachments yet
|
||||
|
||||
// Edit message if we have an ID
|
||||
// kbchat lib does not support message editing yet
|
||||
|
||||
if len(msg.Extra["file"]) > 0 {
|
||||
// Upload a file
|
||||
dir, err := ioutil.TempDir("", "matterbridge")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
for _, f := range msg.Extra["file"] {
|
||||
fname := f.(config.FileInfo).Name
|
||||
fdata := *f.(config.FileInfo).Data
|
||||
fcaption := f.(config.FileInfo).Comment
|
||||
fpath := filepath.Join(dir, fname)
|
||||
|
||||
if err = ioutil.WriteFile(fpath, fdata, 0600); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
_, _ = b.kbc.SendAttachmentByTeam(b.team, fpath, fcaption, &b.channel)
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Send regular message
|
||||
resp, err := b.kbc.SendMessageByTeamName(b.team, msg.Username+msg.Text, &b.channel)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return strconv.Itoa(resp.Result.MsgID), err
|
||||
}
|
||||
|
19
changelog.md
19
changelog.md
@@ -1,4 +1,21 @@
|
||||
# dev
|
||||
# v1.16.2
|
||||
|
||||
## New features
|
||||
|
||||
- keybase: Add support for receiving attachments (keybase) (#923)
|
||||
|
||||
## Enhancements
|
||||
|
||||
- general: Switch to new emoji library kyokomi/emoji (#948)
|
||||
- general: Update markdown parsing library to github.com/gomarkdown/markdown (#944)
|
||||
- ssh-chat: Update shazow/ssh-chat dependency (#947)
|
||||
|
||||
## Bugfix
|
||||
|
||||
- slack: Fix issues with the slack block kit API #937 (#943).
|
||||
|
||||
This release couldn't exist without the following contributors:
|
||||
@42wim, @bmpickford, @goncalor
|
||||
|
||||
# v1.16.1
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
set -u -e -x -o pipefail
|
||||
|
||||
go version | grep go1.12 || exit
|
||||
go version | grep go1.13 || exit
|
||||
|
||||
VERSION=$(git describe --tags)
|
||||
mkdir ci/binaries
|
||||
|
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/d5/tengo/script"
|
||||
"github.com/d5/tengo/stdlib"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
"github.com/peterhellberg/emojilib"
|
||||
"github.com/matterbridge/emoji"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -372,7 +372,7 @@ func (gw *Gateway) modifyMessage(msg *config.Message) {
|
||||
}
|
||||
|
||||
// replace :emoji: to unicode
|
||||
msg.Text = emojilib.Replace(msg.Text)
|
||||
msg.Text = emoji.Sprint(msg.Text)
|
||||
|
||||
br := gw.Bridges[msg.Account]
|
||||
// loop to replace messages
|
||||
|
16
go.mod
16
go.mod
@@ -12,6 +12,7 @@ require (
|
||||
github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec
|
||||
github.com/fsnotify/fsnotify v1.4.7
|
||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible
|
||||
github.com/gomarkdown/markdown v0.0.0-20190912180731-281270bc6d83
|
||||
github.com/google/gops v0.3.6
|
||||
github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 // indirect
|
||||
github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f // indirect
|
||||
@@ -25,6 +26,7 @@ require (
|
||||
github.com/labstack/echo/v4 v4.1.10
|
||||
github.com/lrstanley/girc v0.0.0-20190801035559-4fc93959e1a7
|
||||
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d
|
||||
github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible
|
||||
github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91
|
||||
github.com/matterbridge/gomatrix v0.0.0-20191026211822-6fc7accd00ca
|
||||
github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18
|
||||
@@ -36,16 +38,15 @@ require (
|
||||
github.com/nelsonken/gomf v0.0.0-20180504123937-a9dd2f9deae9
|
||||
github.com/nicksnyder/go-i18n v1.4.0 // indirect
|
||||
github.com/nlopes/slack v0.6.0
|
||||
//github.com/nlopes/slack v0.6.0
|
||||
github.com/onsi/ginkgo v1.6.0 // indirect
|
||||
github.com/onsi/gomega v1.4.1 // indirect
|
||||
github.com/paulrosania/go-charset v0.0.0-20190326053356-55c9d7a5834c
|
||||
github.com/pborman/uuid v0.0.0-20160216163710-c55201b03606 // indirect
|
||||
github.com/peterhellberg/emojilib v0.0.0-20190124112554-c18758d55320
|
||||
github.com/rs/xid v1.2.1
|
||||
github.com/russross/blackfriday v1.5.2
|
||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca
|
||||
github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||
github.com/shazow/ssh-chat v1.8.2
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9 // indirect
|
||||
github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a // indirect
|
||||
@@ -54,22 +55,17 @@ require (
|
||||
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
|
||||
github.com/x-cray/logrus-prefixed-formatter v0.5.2 // indirect
|
||||
github.com/zfjagann/golang-ring v0.0.0-20190304061218-d34796e0a6c2
|
||||
gitlab.com/golang-commonmark/html v0.0.0-20180917080848-cfaf75183c4a // indirect
|
||||
gitlab.com/golang-commonmark/linkify v0.0.0-20191026162114-a0c2df6c8f82 // 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
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586 // indirect
|
||||
golang.org/x/image v0.0.0-20190902063713-cb417be4ba39
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 // indirect
|
||||
golang.org/x/text v0.3.2 // indirect
|
||||
gopkg.in/fsnotify.v1 v1.4.7 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||
gopkg.in/russross/blackfriday.v2 v2.0.0 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
)
|
||||
|
||||
replace github.com/bwmarrin/discordgo v0.19.0 => github.com/matterbridge/discordgo v0.0.0-20191026232317-01823f4ebba4
|
||||
|
||||
replace github.com/nlopes/slack v0.6.0 => github.com/matterbridge/slack v0.1.1-0.20191113220225-25f80ef0a0d1
|
||||
|
||||
go 1.13
|
||||
|
36
go.sum
36
go.sum
@@ -21,6 +21,7 @@ github.com/StackExchange/wmi v0.0.0-20170410192909-ea383cf3ba6e h1:IHXQQIpxASe3m
|
||||
github.com/StackExchange/wmi v0.0.0-20170410192909-ea383cf3ba6e/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alexcesaro/log v0.0.0-20150915221235-61e686294e58 h1:MkpmYfld/S8kXqTYI68DfL8/hHXjHogL120Dy00TIxc=
|
||||
github.com/alexcesaro/log v0.0.0-20150915221235-61e686294e58/go.mod h1:YNfsMyWSs+h+PaYkxGeMVmVCX75Zj/pqdjbu12ciCYE=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
@@ -63,6 +64,8 @@ github.com/golang/protobuf v1.3.0 h1:kbxbvI4Un1LUWKxufD+BiE6AEExYYgkQLQmLFqA1LFk
|
||||
github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
|
||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/gomarkdown/markdown v0.0.0-20190912180731-281270bc6d83 h1:w5VNUHB0SP2tr1+boQJWKvnyn3P61UFErZ2e2ih6x0A=
|
||||
github.com/gomarkdown/markdown v0.0.0-20190912180731-281270bc6d83/go.mod h1:aii0r/K0ZnHv7G0KF7xy1v0A7s2Ljrb5byB7MO5p6TU=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/gops v0.3.6 h1:6akvbMlpZrEYOuoebn2kR+ZJekbZqJ28fJXTs84+8to=
|
||||
@@ -88,7 +91,7 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T
|
||||
github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/jessevdk/go-flags v1.3.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7 h1:K//n/AqR5HjG3qxbrBCL4vJPW0MVFSs9CPK1OOJdRME=
|
||||
github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0=
|
||||
@@ -123,6 +126,8 @@ github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d h1
|
||||
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d/go.mod h1:c6MxwqHD+0HvtAJjsHMIdPCiAwGiQwPRPTp69ACMg8A=
|
||||
github.com/matterbridge/discordgo v0.0.0-20191026232317-01823f4ebba4 h1:RvH3lC4bEp+bieca+Yh5xPU8tLJgnk7ridiSwMFHrrw=
|
||||
github.com/matterbridge/discordgo v0.0.0-20191026232317-01823f4ebba4/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q=
|
||||
github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible h1:oaOqwbg5HxHRxvAbd84ks0Okwoc1ISyUZ87EiVJFhGI=
|
||||
github.com/matterbridge/emoji v2.1.1-0.20191117213217-af507f6b02db+incompatible/go.mod h1:igE6rUAn3jai2wCdsjFHfhUoekjrFthoEjFObKKwSb4=
|
||||
github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91 h1:KzDEcy8eDbTx881giW8a6llsAck3e2bJvMyKvh1IK+k=
|
||||
github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91/go.mod h1:ECDRehsR9TYTKCAsRS8/wLeOk6UUqDydw47ln7wG41Q=
|
||||
github.com/matterbridge/gomatrix v0.0.0-20191026211822-6fc7accd00ca h1:3ypqEpFpt6vg5Sv2xxA8/v4WiSOnWMXW7DqxTxpM4XI=
|
||||
@@ -131,6 +136,8 @@ github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18 h1:fLhwXtW
|
||||
github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18/go.mod h1:yAjnZ34DuDyPHMPHHjOsTk/FefW4JJjoMMCGt/8uuQA=
|
||||
github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61 h1:R/MgM/eUyRBQx2FiH6JVmXck8PaAuKfe2M1tWIzW7nE=
|
||||
github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61/go.mod h1:iXGEotOvwI1R1SjLxRc+BF5rUORTMtE0iMZBT2lxqAU=
|
||||
github.com/matterbridge/slack v0.1.1-0.20191113220225-25f80ef0a0d1 h1:wXVBLvvMFMfqnDE6aVyxScanzRP1WIErz7+fY/9dhmY=
|
||||
github.com/matterbridge/slack v0.1.1-0.20191113220225-25f80ef0a0d1/go.mod h1:2uCJim0Ct2z1Uj+XQq47KCLLC1b/9UTYaZOvDtbZfK4=
|
||||
github.com/mattermost/mattermost-server v5.5.0+incompatible h1:0wcLGgYtd+YImtLDPf2AOfpBHxbU4suATx+6XKw1XbU=
|
||||
github.com/mattermost/mattermost-server v5.5.0+incompatible/go.mod h1:5L6MjAec+XXQwMIt791Ganu45GKsSiM+I0tLR9wUj8Y=
|
||||
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
|
||||
@@ -157,8 +164,6 @@ github.com/nelsonken/gomf v0.0.0-20180504123937-a9dd2f9deae9 h1:mp6tU1r0xLostUGL
|
||||
github.com/nelsonken/gomf v0.0.0-20180504123937-a9dd2f9deae9/go.mod h1:A5SRAcpTemjGgIuBq6Kic2yHcoeUFWUinOAlMP/i9xo=
|
||||
github.com/nicksnyder/go-i18n v1.4.0 h1:AgLl+Yq7kg5OYlzCgu9cKTZOyI4tD/NgukKqLqC8E+I=
|
||||
github.com/nicksnyder/go-i18n v1.4.0/go.mod h1:HrK7VCrbOvQoUAQ7Vpy7i87N7JZZZ7R2xBGjv0j365Q=
|
||||
github.com/nlopes/slack v0.6.0 h1:jt0jxVQGhssx1Ib7naAOZEZcGdtIhTzkP0nopK0AsRA=
|
||||
github.com/nlopes/slack v0.6.0/go.mod h1:JzQ9m3PMAqcpeCam7UaHSuBuupz7CmpjehYMayT6YOk=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
@@ -170,8 +175,6 @@ github.com/pborman/uuid v0.0.0-20160216163710-c55201b03606 h1:/CPgDYrfeK2LMK6xcU
|
||||
github.com/pborman/uuid v0.0.0-20160216163710-c55201b03606/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
|
||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/peterhellberg/emojilib v0.0.0-20190124112554-c18758d55320 h1:YxcQy/DV+48NGv1lxx1vsWBzs6W1f1ogubkuCozxpX0=
|
||||
github.com/peterhellberg/emojilib v0.0.0-20190124112554-c18758d55320/go.mod h1:G7LufuPajuIvdt9OitkNt2qh0mmvD4bfRgRM7bhDIOA=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@@ -195,14 +198,12 @@ github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxT
|
||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
|
||||
github.com/shazow/rateio v0.0.0-20150116013248-e8e00881e5c1 h1:Lx3BlDGFElJt4u/zKc9A3BuGYbQAGlEFyPuUA3jeMD0=
|
||||
github.com/shazow/rateio v0.0.0-20150116013248-e8e00881e5c1/go.mod h1:vt2jWY/3Qw1bIzle5thrJWucsLuuX9iUNnp20CqCciI=
|
||||
github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296 h1:8RLq547MSVc6vhOuCl4Ca0TsAQknj6NX6ZLSZ3+xmio=
|
||||
github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296/go.mod h1:1GLXsL4esywkpNId3v4QWuMf3THtWGitWvtQ/L3aSA4=
|
||||
github.com/shazow/ssh-chat v1.8.2 h1:MMso9eWfCnPBelRsusYxKcRBUwHIPEQkR9WrO89II38=
|
||||
github.com/shazow/ssh-chat v1.8.2/go.mod h1:cXTZK/D1zujEwB0y8DIT1GX8rIKjyLDYeWd+jitPX84=
|
||||
github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7 h1:80VN+vGkqM773Br/uNNTSheo3KatTgV8IpjIKjvVLng=
|
||||
github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 h1:udFKJ0aHUL60LboW/A+DfgoHVedieIzIXE8uylPue0U=
|
||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
@@ -245,18 +246,6 @@ github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6Ut
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/zfjagann/golang-ring v0.0.0-20190304061218-d34796e0a6c2 h1:UQwvu7FjUEdVYofx0U6bsc5odNE7wa5TSA0fl559GcA=
|
||||
github.com/zfjagann/golang-ring v0.0.0-20190304061218-d34796e0a6c2/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-20191026162114-a0c2df6c8f82 h1:oYrL81N608MLZhma3ruL8qTM4xcpYECGut8KSxRY59g=
|
||||
gitlab.com/golang-commonmark/linkify v0.0.0-20191026162114-a0c2df6c8f82/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.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
@@ -264,11 +253,13 @@ go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/dl v0.0.0-20190829154251-82a15e2f2ead/go.mod h1:IUMfjQLJQd4UTqG1Z90tenwKoCX93Gn3MAQJMOSBsDQ=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586 h1:7KByu05hhLed2MO29w7p1XfZvZ13m8mub3shuVftRs0=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
@@ -297,6 +288,7 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
|
||||
@@ -324,8 +316,6 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/russross/blackfriday.v2 v2.0.0 h1:+FlnIV8DSQnT7NZ43hcVKcdJdzZoeCmJj4Ql8gq5keA=
|
||||
gopkg.in/russross/blackfriday.v2 v2.0.0/go.mod h1:6sSBNz/GtOm/pJTuh5UmBK2ZHfmnxGbl2NZg1UliSOI=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
|
@@ -15,7 +15,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
version = "1.16.1"
|
||||
version = "1.16.2"
|
||||
githash string
|
||||
|
||||
flagConfig = flag.String("conf", "matterbridge.toml", "config file")
|
||||
|
13
vendor/github.com/gomarkdown/markdown/.gitignore
generated
vendored
Normal file
13
vendor/github.com/gomarkdown/markdown/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
*.out
|
||||
*.swp
|
||||
*.8
|
||||
*.6
|
||||
_obj
|
||||
_test*
|
||||
markdown
|
||||
tags
|
||||
fuzz-workdir/
|
||||
markdown-fuzz.zip
|
||||
coverage.txt
|
||||
testdata/*_got.md
|
||||
testdata/*_ast.txt
|
7
vendor/github.com/gomarkdown/markdown/.gitpod
generated
vendored
Normal file
7
vendor/github.com/gomarkdown/markdown/.gitpod
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
checkoutLocation: "src/github.com/gomarkdown/markdown"
|
||||
workspaceLocation: "."
|
||||
tasks:
|
||||
- command: >
|
||||
cd /workspace/src/github.com/gomarkdown/markdown &&
|
||||
go get -v ./... &&
|
||||
go test -c
|
17
vendor/github.com/gomarkdown/markdown/.travis.yml
generated
vendored
Normal file
17
vendor/github.com/gomarkdown/markdown/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
dist: bionic
|
||||
language: go
|
||||
|
||||
go:
|
||||
- "1.12.x"
|
||||
|
||||
install:
|
||||
- go build -v ./...
|
||||
|
||||
script:
|
||||
- go test -v ./...
|
||||
- go test -run=^$ -bench=BenchmarkReference -benchmem
|
||||
- ./s/test_with_codecoverage.sh
|
||||
- ./s/ci_fuzzit.sh
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
31
vendor/github.com/gomarkdown/markdown/LICENSE.txt
generated
vendored
Normal file
31
vendor/github.com/gomarkdown/markdown/LICENSE.txt
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
Markdown is distributed under the Simplified BSD License:
|
||||
|
||||
Copyright © 2011 Russ Ross
|
||||
Copyright © 2018 Krzysztof Kowalczyk
|
||||
Copyright © 2018 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.
|
325
vendor/github.com/gomarkdown/markdown/README.md
generated
vendored
Normal file
325
vendor/github.com/gomarkdown/markdown/README.md
generated
vendored
Normal file
@@ -0,0 +1,325 @@
|
||||
# Markdown Parser and HTML Renderer for Go
|
||||
|
||||
[](https://godoc.org/github.com/gomarkdown/markdown) [](https://codecov.io/gh/gomarkdown/markdown)
|
||||
|
||||
Package `github.com/gomarkdown/markdown` is a very fast Go library for parsing [Markdown](https://daringfireball.net/projects/markdown/) documents and rendering them to HTML.
|
||||
|
||||
It's fast and supports common extensions.
|
||||
|
||||
## Installation
|
||||
|
||||
go get -u github.com/gomarkdown/markdown
|
||||
|
||||
API Docs:
|
||||
|
||||
- https://godoc.org/github.com/gomarkdown/markdown : top level package
|
||||
- https://godoc.org/github.com/gomarkdown/markdown/ast : defines abstract syntax tree of parsed markdown document
|
||||
- https://godoc.org/github.com/gomarkdown/markdown/parser : parser
|
||||
- https://godoc.org/github.com/gomarkdown/markdown/html : html renderer
|
||||
|
||||
## Usage
|
||||
|
||||
To convert markdown text to HTML using reasonable defaults:
|
||||
|
||||
```go
|
||||
md := []byte("## markdown document")
|
||||
output := markdown.ToHTML(md, nil, nil)
|
||||
```
|
||||
|
||||
## Customizing markdown parser
|
||||
|
||||
Markdown format is loosely specified and there are multiple extensions invented after original specification was created.
|
||||
|
||||
The parser supports several [extensions](https://godoc.org/github.com/gomarkdown/markdown/parser#Extensions).
|
||||
|
||||
Default parser uses most common `parser.CommonExtensions` but you can easily use parser with custom extension:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/gomarkdown/markdown"
|
||||
"github.com/gomarkdown/markdown/parser"
|
||||
)
|
||||
|
||||
extensions := parser.CommonExtensions | parser.AutoHeadingIDs
|
||||
parser := parser.NewWithExtensions(extensions)
|
||||
|
||||
md := []byte("markdown text")
|
||||
html := markdown.ToHTML(md, parser, nil)
|
||||
```
|
||||
|
||||
## Customizing HTML renderer
|
||||
|
||||
Similarly, HTML renderer can be configured with different [options](https://godoc.org/github.com/gomarkdown/markdown/html#RendererOptions)
|
||||
|
||||
Here's how to use a custom renderer:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/gomarkdown/markdown"
|
||||
"github.com/gomarkdown/markdown/html"
|
||||
)
|
||||
|
||||
htmlFlags := html.CommonFlags | html.HrefTargetBlank
|
||||
opts := html.RendererOptions{Flags: htmlFlags}
|
||||
renderer := html.NewRenderer(opts)
|
||||
|
||||
md := []byte("markdown text")
|
||||
html := markdown.ToHTML(md, nil, renderer)
|
||||
```
|
||||
|
||||
HTML renderer also supports reusing most of the logic and overriding rendering of only specifc nodes.
|
||||
|
||||
You can provide [RenderNodeFunc](https://godoc.org/github.com/gomarkdown/markdown/html#RenderNodeFunc) in [RendererOptions](https://godoc.org/github.com/gomarkdown/markdown/html#RendererOptions).
|
||||
|
||||
The function is called for each node in AST, you can implement custom rendering logic and tell HTML renderer to skip rendering this node.
|
||||
|
||||
Here's the simplest example that drops all code blocks from the output:
|
||||
|
||||
````go
|
||||
import (
|
||||
"github.com/gomarkdown/markdown"
|
||||
"github.com/gomarkdown/markdown/ast"
|
||||
"github.com/gomarkdown/markdown/html"
|
||||
)
|
||||
|
||||
// return (ast.GoToNext, true) to tell html renderer to skip rendering this node
|
||||
// (because you've rendered it)
|
||||
func renderHookDropCodeBlock(w io.Writer, node ast.Node, entering bool) (ast.WalkStatus, bool) {
|
||||
// skip all nodes that are not CodeBlock nodes
|
||||
if _, ok := node.(*ast.CodeBlock); !ok {
|
||||
return ast.GoToNext, false
|
||||
}
|
||||
// custom rendering logic for ast.CodeBlock. By doing nothing it won't be
|
||||
// present in the output
|
||||
return ast.GoToNext, true
|
||||
}
|
||||
|
||||
opts := html.RendererOptions{
|
||||
Flags: html.CommonFlags,
|
||||
RenderNodeHook: renderHookDropCodeBlock,
|
||||
}
|
||||
renderer := html.NewRenderer(opts)
|
||||
md := "test\n```\nthis code block will be dropped from output\n```\ntext"
|
||||
html := markdown.ToHTML([]byte(s), nil, renderer)
|
||||
````
|
||||
|
||||
## Sanitize untrusted content
|
||||
|
||||
We don't protect against malicious content. When dealing with user-provided
|
||||
markdown, run renderer HTML through HTML sanitizer such as [Bluemonday](https://github.com/microcosm-cc/bluemonday).
|
||||
|
||||
Here's an example of simple usage with Bluemonday:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/microcosm-cc/bluemonday"
|
||||
"github.com/gomarkdown/markdown"
|
||||
)
|
||||
|
||||
// ...
|
||||
maybeUnsafeHTML := markdown.ToHTML(md, nil, nil)
|
||||
html := bluemonday.UGCPolicy().SanitizeBytes(maybeUnsafeHTML)
|
||||
```
|
||||
|
||||
## mdtohtml command-line tool
|
||||
|
||||
https://github.com/gomarkdown/mdtohtml is a command-line markdown to html
|
||||
converter built using this library.
|
||||
|
||||
You can also use it as an example of how to use the library.
|
||||
|
||||
You can install it with:
|
||||
|
||||
go get -u github.com/gomarkdown/mdtohtml
|
||||
|
||||
To run: `mdtohtml input-file [output-file]`
|
||||
|
||||
## Features
|
||||
|
||||
- **Compatibility**. The Markdown v1.0.3 test suite passes with
|
||||
the `--tidy` option. Without `--tidy`, the differences are
|
||||
mostly in whitespace and entity escaping, where this package is
|
||||
more consistent and cleaner.
|
||||
|
||||
- **Common extensions**, including table support, fenced code
|
||||
blocks, autolinks, strikethroughs, non-strict emphasis, etc.
|
||||
|
||||
- **Safety**. Markdown is paranoid when parsing, making it safe
|
||||
to feed untrusted user input without fear of bad things
|
||||
happening. The test suite stress tests this and there are no
|
||||
known inputs that make it crash. If you find one, please let me
|
||||
know and send me the input that does it.
|
||||
|
||||
NOTE: "safety" in this context means _runtime safety only_. In order to
|
||||
protect yourself against JavaScript injection in untrusted content, see
|
||||
[this example](https://github.com/gomarkdown/markdown#sanitize-untrusted-content).
|
||||
|
||||
- **Fast**. It is fast enough to render on-demand in
|
||||
most web applications without having to cache the output.
|
||||
|
||||
- **Thread safety**. You can run multiple parsers in different
|
||||
goroutines without ill effect. There is no dependence on global
|
||||
shared state.
|
||||
|
||||
- **Minimal dependencies**. Only depends on standard library packages in Go.
|
||||
|
||||
- **Standards compliant**. Output successfully validates using the
|
||||
W3C validation tool for HTML 4.01 and XHTML 1.0 Transitional.
|
||||
|
||||
## Extensions
|
||||
|
||||
In addition to the standard markdown syntax, this package
|
||||
implements the following extensions:
|
||||
|
||||
- **Intra-word emphasis supression**. The `_` character is
|
||||
commonly used inside words when discussing code, so having
|
||||
markdown interpret it as an emphasis command is usually the
|
||||
wrong thing. We let you treat all emphasis markers as
|
||||
normal characters when they occur inside a word.
|
||||
|
||||
- **Tables**. Tables can be created by drawing them in the input
|
||||
using a simple syntax:
|
||||
|
||||
```
|
||||
Name | Age
|
||||
--------|------
|
||||
Bob | 27
|
||||
Alice | 23
|
||||
```
|
||||
|
||||
Table footers are supported as well and can be added with equal signs (`=`):
|
||||
|
||||
```
|
||||
Name | Age
|
||||
--------|------
|
||||
Bob | 27
|
||||
Alice | 23
|
||||
========|======
|
||||
Total | 50
|
||||
```
|
||||
|
||||
- **Fenced code blocks**. In addition to the normal 4-space
|
||||
indentation to mark code blocks, you can explicitly mark them
|
||||
and supply a language (to make syntax highlighting simple). Just
|
||||
mark it like this:
|
||||
|
||||
```go
|
||||
func getTrue() bool {
|
||||
return true
|
||||
}
|
||||
```
|
||||
|
||||
You can use 3 or more backticks to mark the beginning of the
|
||||
block, and the same number to mark the end of the block.
|
||||
|
||||
- **Definition lists**. A simple definition list is made of a single-line
|
||||
term followed by a colon and the definition for that term.
|
||||
|
||||
Cat
|
||||
: Fluffy animal everyone likes
|
||||
|
||||
Internet
|
||||
: Vector of transmission for pictures of cats
|
||||
|
||||
Terms must be separated from the previous definition by a blank line.
|
||||
|
||||
- **Footnotes**. A marker in the text that will become a superscript number;
|
||||
a footnote definition that will be placed in a list of footnotes at the
|
||||
end of the document. A footnote looks like this:
|
||||
|
||||
This is a footnote.[^1]
|
||||
|
||||
[^1]: the footnote text.
|
||||
|
||||
- **Autolinking**. We can find URLs that have not been
|
||||
explicitly marked as links and turn them into links.
|
||||
|
||||
- **Strikethrough**. Use two tildes (`~~`) to mark text that
|
||||
should be crossed out.
|
||||
|
||||
- **Hard line breaks**. With this extension enabled newlines in the input
|
||||
translate into line breaks in the output. This extension is off by default.
|
||||
|
||||
- **Non blocking space**. With this extension enabled spaces preceeded by an backslash n the input
|
||||
translate non-blocking spaces in the output. This extension is off by default.
|
||||
|
||||
- **Smart quotes**. Smartypants-style punctuation substitution is
|
||||
supported, turning normal double- and single-quote marks into
|
||||
curly quotes, etc.
|
||||
|
||||
- **LaTeX-style dash parsing** is an additional option, where `--`
|
||||
is translated into `–`, and `---` is translated into
|
||||
`—`. This differs from most smartypants processors, which
|
||||
turn a single hyphen into an ndash and a double hyphen into an
|
||||
mdash.
|
||||
|
||||
- **Smart fractions**, where anything that looks like a fraction
|
||||
is translated into suitable HTML (instead of just a few special
|
||||
cases like most smartypant processors). For example, `4/5`
|
||||
becomes `<sup>4</sup>⁄<sub>5</sub>`, which renders as
|
||||
<sup>4</sup>⁄<sub>5</sub>.
|
||||
|
||||
- **MathJaX Support** is an additional feature which is supported by
|
||||
many markdown editor. It translate inline math equation quoted by `$`
|
||||
and display math block quoted by `$$` into MathJax compatible format.
|
||||
hyphen `_` won't break LaTeX render within a math element any more.
|
||||
|
||||
```
|
||||
$$
|
||||
\left[ \begin{array}{a} a^l_1 \\ ⋮ \\ a^l_{d_l} \end{array}\right]
|
||||
= \sigma(
|
||||
\left[ \begin{matrix}
|
||||
w^l_{1,1} & ⋯ & w^l_{1,d_{l-1}} \\
|
||||
⋮ & ⋱ & ⋮ \\
|
||||
w^l_{d_l,1} & ⋯ & w^l_{d_l,d_{l-1}} \\
|
||||
\end{matrix}\right] ·
|
||||
\left[ \begin{array}{x} a^{l-1}_1 \\ ⋮ \\ ⋮ \\ a^{l-1}_{d_{l-1}} \end{array}\right] +
|
||||
\left[ \begin{array}{b} b^l_1 \\ ⋮ \\ b^l_{d_l} \end{array}\right])
|
||||
$$
|
||||
```
|
||||
|
||||
- **Ordered list start number**. With this extension enabled an ordered list will start with the
|
||||
the number that was used to start it.
|
||||
|
||||
- **Super and subscript**. With this extension enabled sequences between ^ will indicate
|
||||
superscript and ~ will become a subscript. For example: H~2~O is a liquid, 2^10^ is 1024.
|
||||
|
||||
- **Block level attributes**, allow setting attributes (ID, classes and key/value pairs) on block
|
||||
level elements. The attribute must be enclosed with braces and be put on a line before the
|
||||
element.
|
||||
|
||||
```
|
||||
{#id3 .myclass fontsize="tiny"}
|
||||
# Header 1
|
||||
```
|
||||
|
||||
Will convert into `<h1 id="id3" class="myclass" fontsize="tiny">Header 1</h1>`.
|
||||
|
||||
- **Mmark support**, see <https://mmark.nl/syntax> for all new syntax elements this adds.
|
||||
|
||||
## Todo
|
||||
|
||||
- port https://github.com/russross/blackfriday/issues/348
|
||||
- port [LaTeX output](https://github.com/Ambrevar/Blackfriday-LaTeX):
|
||||
renders output as LaTeX.
|
||||
- port https://github.com/shurcooL/github_flavored_markdown to markdown
|
||||
- port [markdownfmt](https://github.com/shurcooL/markdownfmt): like gofmt,
|
||||
but for markdown.
|
||||
- More unit testing
|
||||
- Improve unicode support. It does not understand all unicode
|
||||
rules (about what constitutes a letter, a punctuation symbol,
|
||||
etc.), so it may fail to detect word boundaries correctly in
|
||||
some instances. It is safe on all utf-8 input.
|
||||
|
||||
## History
|
||||
|
||||
markdown is a fork of v2 of https://github.com/russross/blackfriday that is:
|
||||
|
||||
- actively maintained (sadly in Feb 2018 blackfriday was inactive for 5 months with many bugs and pull requests accumulated)
|
||||
- refactored API (split into ast/parser/html sub-packages)
|
||||
|
||||
Blackfriday itself was based on C implementation [sundown](https://github.com/vmg/sundown) which in turn was based on [libsoldout](http://fossil.instinctive.eu/libsoldout/home).
|
||||
|
||||
## License
|
||||
|
||||
[Simplified BSD License](LICENSE.txt)
|
10
vendor/github.com/gomarkdown/markdown/ast/attribute.go
generated
vendored
Normal file
10
vendor/github.com/gomarkdown/markdown/ast/attribute.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
package ast
|
||||
|
||||
// An attribute can be attached to block elements. They are specified as
|
||||
// {#id .classs key="value"} where quotes for values are mandatory, multiple
|
||||
// key/value pairs are separated by whitespace.
|
||||
type Attribute struct {
|
||||
ID []byte
|
||||
Classes [][]byte
|
||||
Attrs map[string][]byte
|
||||
}
|
4
vendor/github.com/gomarkdown/markdown/ast/doc.go
generated
vendored
Normal file
4
vendor/github.com/gomarkdown/markdown/ast/doc.go
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/*
|
||||
Package ast defines tree representation of a parsed markdown document.
|
||||
*/
|
||||
package ast
|
559
vendor/github.com/gomarkdown/markdown/ast/node.go
generated
vendored
Normal file
559
vendor/github.com/gomarkdown/markdown/ast/node.go
generated
vendored
Normal file
@@ -0,0 +1,559 @@
|
||||
package ast
|
||||
|
||||
// ListType contains bitwise or'ed flags for list and list item objects.
|
||||
type ListType int
|
||||
|
||||
// These are the possible flag values for the ListItem renderer.
|
||||
// Multiple flag values may be ORed together.
|
||||
// These are mostly of interest if you are writing a new output format.
|
||||
const (
|
||||
ListTypeOrdered ListType = 1 << iota
|
||||
ListTypeDefinition
|
||||
ListTypeTerm
|
||||
|
||||
ListItemContainsBlock
|
||||
ListItemBeginningOfList // TODO: figure out if this is of any use now
|
||||
ListItemEndOfList
|
||||
)
|
||||
|
||||
// CellAlignFlags holds a type of alignment in a table cell.
|
||||
type CellAlignFlags int
|
||||
|
||||
// These are the possible flag values for the table cell renderer.
|
||||
// Only a single one of these values will be used; they are not ORed together.
|
||||
// These are mostly of interest if you are writing a new output format.
|
||||
const (
|
||||
TableAlignmentLeft CellAlignFlags = 1 << iota
|
||||
TableAlignmentRight
|
||||
TableAlignmentCenter = (TableAlignmentLeft | TableAlignmentRight)
|
||||
)
|
||||
|
||||
func (a CellAlignFlags) String() string {
|
||||
switch a {
|
||||
case TableAlignmentLeft:
|
||||
return "left"
|
||||
case TableAlignmentRight:
|
||||
return "right"
|
||||
case TableAlignmentCenter:
|
||||
return "center"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// DocumentMatters holds the type of a {front,main,back}matter in the document
|
||||
type DocumentMatters int
|
||||
|
||||
// These are all possible Document divisions.
|
||||
const (
|
||||
DocumentMatterNone DocumentMatters = iota
|
||||
DocumentMatterFront
|
||||
DocumentMatterMain
|
||||
DocumentMatterBack
|
||||
)
|
||||
|
||||
// CitationTypes holds the type of a citation, informative, normative or suppressed
|
||||
type CitationTypes int
|
||||
|
||||
const (
|
||||
CitationTypeNone CitationTypes = iota
|
||||
CitationTypeSuppressed
|
||||
CitationTypeInformative
|
||||
CitationTypeNormative
|
||||
)
|
||||
|
||||
// Node defines an ast node
|
||||
type Node interface {
|
||||
AsContainer() *Container
|
||||
AsLeaf() *Leaf
|
||||
GetParent() Node
|
||||
SetParent(newParent Node)
|
||||
GetChildren() []Node
|
||||
SetChildren(newChildren []Node)
|
||||
}
|
||||
|
||||
// Container is a type of node that can contain children
|
||||
type Container struct {
|
||||
Parent Node
|
||||
Children []Node
|
||||
|
||||
Literal []byte // Text contents of the leaf nodes
|
||||
Content []byte // Markdown content of the block nodes
|
||||
|
||||
*Attribute // Block level attribute
|
||||
}
|
||||
|
||||
// AsContainer returns itself as *Container
|
||||
func (c *Container) AsContainer() *Container {
|
||||
return c
|
||||
}
|
||||
|
||||
// AsLeaf returns nil
|
||||
func (c *Container) AsLeaf() *Leaf {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetParent returns parent node
|
||||
func (c *Container) GetParent() Node {
|
||||
return c.Parent
|
||||
}
|
||||
|
||||
// SetParent sets the parent node
|
||||
func (c *Container) SetParent(newParent Node) {
|
||||
c.Parent = newParent
|
||||
}
|
||||
|
||||
// GetChildren returns children nodes
|
||||
func (c *Container) GetChildren() []Node {
|
||||
return c.Children
|
||||
}
|
||||
|
||||
// SetChildren sets children node
|
||||
func (c *Container) SetChildren(newChildren []Node) {
|
||||
c.Children = newChildren
|
||||
}
|
||||
|
||||
// Leaf is a type of node that cannot have children
|
||||
type Leaf struct {
|
||||
Parent Node
|
||||
|
||||
Literal []byte // Text contents of the leaf nodes
|
||||
Content []byte // Markdown content of the block nodes
|
||||
|
||||
*Attribute // Block level attribute
|
||||
}
|
||||
|
||||
// AsContainer returns nil
|
||||
func (l *Leaf) AsContainer() *Container {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AsLeaf returns itself as *Leaf
|
||||
func (l *Leaf) AsLeaf() *Leaf {
|
||||
return l
|
||||
}
|
||||
|
||||
// GetParent returns parent node
|
||||
func (l *Leaf) GetParent() Node {
|
||||
return l.Parent
|
||||
}
|
||||
|
||||
// SetParent sets the parent nodd
|
||||
func (l *Leaf) SetParent(newParent Node) {
|
||||
l.Parent = newParent
|
||||
}
|
||||
|
||||
// GetChildren returns nil because Leaf cannot have children
|
||||
func (l *Leaf) GetChildren() []Node {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetChildren will panic becuase Leaf cannot have children
|
||||
func (l *Leaf) SetChildren(newChildren []Node) {
|
||||
panic("leaf node cannot have children")
|
||||
}
|
||||
|
||||
// Document represents markdown document node, a root of ast
|
||||
type Document struct {
|
||||
Container
|
||||
}
|
||||
|
||||
// DocumentMatter represents markdown node that signals a document
|
||||
// division: frontmatter, mainmatter or backmatter.
|
||||
type DocumentMatter struct {
|
||||
Container
|
||||
|
||||
Matter DocumentMatters
|
||||
}
|
||||
|
||||
// BlockQuote represents markdown block quote node
|
||||
type BlockQuote struct {
|
||||
Container
|
||||
}
|
||||
|
||||
// Aside represents an markdown aside node.
|
||||
type Aside struct {
|
||||
Container
|
||||
}
|
||||
|
||||
// List represents markdown list node
|
||||
type List struct {
|
||||
Container
|
||||
|
||||
ListFlags ListType
|
||||
Tight bool // Skip <p>s around list item data if true
|
||||
BulletChar byte // '*', '+' or '-' in bullet lists
|
||||
Delimiter byte // '.' or ')' after the number in ordered lists
|
||||
Start int // for ordered lists this indicates the starting number if > 0
|
||||
RefLink []byte // If not nil, turns this list item into a footnote item and triggers different rendering
|
||||
IsFootnotesList bool // This is a list of footnotes
|
||||
}
|
||||
|
||||
// ListItem represents markdown list item node
|
||||
type ListItem struct {
|
||||
Container
|
||||
|
||||
ListFlags ListType
|
||||
Tight bool // Skip <p>s around list item data if true
|
||||
BulletChar byte // '*', '+' or '-' in bullet lists
|
||||
Delimiter byte // '.' or ')' after the number in ordered lists
|
||||
RefLink []byte // If not nil, turns this list item into a footnote item and triggers different rendering
|
||||
IsFootnotesList bool // This is a list of footnotes
|
||||
}
|
||||
|
||||
// Paragraph represents markdown paragraph node
|
||||
type Paragraph struct {
|
||||
Container
|
||||
}
|
||||
|
||||
// Math represents markdown MathAjax inline node
|
||||
type Math struct {
|
||||
Leaf
|
||||
}
|
||||
|
||||
// MathBlock represents markdown MathAjax block node
|
||||
type MathBlock struct {
|
||||
Container
|
||||
}
|
||||
|
||||
// Heading represents markdown heading node
|
||||
type Heading struct {
|
||||
Container
|
||||
|
||||
Level int // This holds the heading level number
|
||||
HeadingID string // This might hold heading ID, if present
|
||||
IsTitleblock bool // Specifies whether it's a title block
|
||||
IsSpecial bool // We are a special heading (starts with .#)
|
||||
}
|
||||
|
||||
// HorizontalRule represents markdown horizontal rule node
|
||||
type HorizontalRule struct {
|
||||
Leaf
|
||||
}
|
||||
|
||||
// Emph represents markdown emphasis node
|
||||
type Emph struct {
|
||||
Container
|
||||
}
|
||||
|
||||
// Strong represents markdown strong node
|
||||
type Strong struct {
|
||||
Container
|
||||
}
|
||||
|
||||
// Del represents markdown del node
|
||||
type Del struct {
|
||||
Container
|
||||
}
|
||||
|
||||
// Link represents markdown link node
|
||||
type Link struct {
|
||||
Container
|
||||
|
||||
Destination []byte // Destination is what goes into a href
|
||||
Title []byte // Title is the tooltip thing that goes in a title attribute
|
||||
NoteID int // NoteID contains a serial number of a footnote, zero if it's not a footnote
|
||||
Footnote Node // If it's a footnote, this is a direct link to the footnote Node. Otherwise nil.
|
||||
DeferredID []byte // If a deferred link this holds the original ID.
|
||||
}
|
||||
|
||||
// CrossReference is a reference node.
|
||||
type CrossReference struct {
|
||||
Container
|
||||
|
||||
Destination []byte // Destination is where the reference points to
|
||||
}
|
||||
|
||||
// Citation is a citation node.
|
||||
type Citation struct {
|
||||
Leaf
|
||||
|
||||
Destination [][]byte // Destination is where the citation points to. Multiple ones are allowed.
|
||||
Type []CitationTypes // 1:1 mapping of destination and citation type
|
||||
Suffix [][]byte // Potential citation suffix, i.e. [@!RFC1035, p. 144]
|
||||
}
|
||||
|
||||
// Image represents markdown image node
|
||||
type Image struct {
|
||||
Container
|
||||
|
||||
Destination []byte // Destination is what goes into a href
|
||||
Title []byte // Title is the tooltip thing that goes in a title attribute
|
||||
}
|
||||
|
||||
// Text represents markdown text node
|
||||
type Text struct {
|
||||
Leaf
|
||||
}
|
||||
|
||||
// HTMLBlock represents markdown html node
|
||||
type HTMLBlock struct {
|
||||
Leaf
|
||||
}
|
||||
|
||||
// CodeBlock represents markdown code block node
|
||||
type CodeBlock struct {
|
||||
Leaf
|
||||
|
||||
IsFenced bool // Specifies whether it's a fenced code block or an indented one
|
||||
Info []byte // This holds the info string
|
||||
FenceChar byte
|
||||
FenceLength int
|
||||
FenceOffset int
|
||||
}
|
||||
|
||||
// Softbreak represents markdown softbreak node
|
||||
// Note: not used currently
|
||||
type Softbreak struct {
|
||||
Leaf
|
||||
}
|
||||
|
||||
// Hardbreak represents markdown hard break node
|
||||
type Hardbreak struct {
|
||||
Leaf
|
||||
}
|
||||
|
||||
// NonBlockingSpace represents markdown non-blocking space node
|
||||
type NonBlockingSpace struct {
|
||||
Leaf
|
||||
}
|
||||
|
||||
// Code represents markdown code node
|
||||
type Code struct {
|
||||
Leaf
|
||||
}
|
||||
|
||||
// HTMLSpan represents markdown html span node
|
||||
type HTMLSpan struct {
|
||||
Leaf
|
||||
}
|
||||
|
||||
// Table represents markdown table node
|
||||
type Table struct {
|
||||
Container
|
||||
}
|
||||
|
||||
// TableCell represents markdown table cell node
|
||||
type TableCell struct {
|
||||
Container
|
||||
|
||||
IsHeader bool // This tells if it's under the header row
|
||||
Align CellAlignFlags // This holds the value for align attribute
|
||||
}
|
||||
|
||||
// TableHeader represents markdown table head node
|
||||
type TableHeader struct {
|
||||
Container
|
||||
}
|
||||
|
||||
// TableBody represents markdown table body node
|
||||
type TableBody struct {
|
||||
Container
|
||||
}
|
||||
|
||||
// TableRow represents markdown table row node
|
||||
type TableRow struct {
|
||||
Container
|
||||
}
|
||||
|
||||
// TableFooter represents markdown table foot node
|
||||
type TableFooter struct {
|
||||
Container
|
||||
}
|
||||
|
||||
// Caption represents a figure, code or quote caption
|
||||
type Caption struct {
|
||||
Container
|
||||
}
|
||||
|
||||
// CaptionFigure is a node (blockquote or codeblock) that has a caption
|
||||
type CaptionFigure struct {
|
||||
Container
|
||||
|
||||
HeadingID string // This might hold heading ID, if present
|
||||
}
|
||||
|
||||
// Callout is a node that can exist both in text (where it is an actual node) and in a code block.
|
||||
type Callout struct {
|
||||
Leaf
|
||||
|
||||
ID []byte // number of this callout
|
||||
}
|
||||
|
||||
// Index is a node that contains an Index item and an optional, subitem.
|
||||
type Index struct {
|
||||
Leaf
|
||||
|
||||
Primary bool
|
||||
Item []byte
|
||||
Subitem []byte
|
||||
ID string // ID of the index
|
||||
}
|
||||
|
||||
// Subscript is a subscript node
|
||||
type Subscript struct {
|
||||
Leaf
|
||||
}
|
||||
|
||||
// Subscript is a superscript node
|
||||
type Superscript struct {
|
||||
Leaf
|
||||
}
|
||||
|
||||
// Footnotes is a node that contains all footnotes
|
||||
type Footnotes struct {
|
||||
Container
|
||||
}
|
||||
|
||||
func removeNodeFromArray(a []Node, node Node) []Node {
|
||||
n := len(a)
|
||||
for i := 0; i < n; i++ {
|
||||
if a[i] == node {
|
||||
return append(a[:i], a[i+1:]...)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AppendChild appends child to children of parent
|
||||
// It panics if either node is nil.
|
||||
func AppendChild(parent Node, child Node) {
|
||||
RemoveFromTree(child)
|
||||
child.SetParent(parent)
|
||||
newChildren := append(parent.GetChildren(), child)
|
||||
parent.SetChildren(newChildren)
|
||||
}
|
||||
|
||||
// RemoveFromTree removes this node from tree
|
||||
func RemoveFromTree(n Node) {
|
||||
if n.GetParent() == nil {
|
||||
return
|
||||
}
|
||||
// important: don't clear n.Children if n has no parent
|
||||
// we're called from AppendChild and that might happen on a node
|
||||
// that accumulated Children but hasn't been inserted into the tree
|
||||
n.SetChildren(nil)
|
||||
p := n.GetParent()
|
||||
newChildren := removeNodeFromArray(p.GetChildren(), n)
|
||||
if newChildren != nil {
|
||||
p.SetChildren(newChildren)
|
||||
}
|
||||
}
|
||||
|
||||
// GetLastChild returns last child of node n
|
||||
// It's implemented as stand-alone function to keep Node interface small
|
||||
func GetLastChild(n Node) Node {
|
||||
a := n.GetChildren()
|
||||
if len(a) > 0 {
|
||||
return a[len(a)-1]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFirstChild returns first child of node n
|
||||
// It's implemented as stand-alone function to keep Node interface small
|
||||
func GetFirstChild(n Node) Node {
|
||||
a := n.GetChildren()
|
||||
if len(a) > 0 {
|
||||
return a[0]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetNextNode returns next sibling of node n (node after n)
|
||||
// We can't make it part of Container or Leaf because we loose Node identity
|
||||
func GetNextNode(n Node) Node {
|
||||
parent := n.GetParent()
|
||||
if parent == nil {
|
||||
return nil
|
||||
}
|
||||
a := parent.GetChildren()
|
||||
len := len(a) - 1
|
||||
for i := 0; i < len; i++ {
|
||||
if a[i] == n {
|
||||
return a[i+1]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetPrevNode returns previous sibling of node n (node before n)
|
||||
// We can't make it part of Container or Leaf because we loose Node identity
|
||||
func GetPrevNode(n Node) Node {
|
||||
parent := n.GetParent()
|
||||
if parent == nil {
|
||||
return nil
|
||||
}
|
||||
a := parent.GetChildren()
|
||||
len := len(a)
|
||||
for i := 1; i < len; i++ {
|
||||
if a[i] == n {
|
||||
return a[i-1]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WalkStatus allows NodeVisitor to have some control over the tree traversal.
|
||||
// It is returned from NodeVisitor and different values allow Node.Walk to
|
||||
// decide which node to go to next.
|
||||
type WalkStatus int
|
||||
|
||||
const (
|
||||
// GoToNext is the default traversal of every node.
|
||||
GoToNext WalkStatus = iota
|
||||
// SkipChildren tells walker to skip all children of current node.
|
||||
SkipChildren
|
||||
// Terminate tells walker to terminate the traversal.
|
||||
Terminate
|
||||
)
|
||||
|
||||
// NodeVisitor is a callback to be called when traversing the syntax tree.
|
||||
// Called twice for every node: once with entering=true when the branch is
|
||||
// first visited, then with entering=false after all the children are done.
|
||||
type NodeVisitor interface {
|
||||
Visit(node Node, entering bool) WalkStatus
|
||||
}
|
||||
|
||||
// NodeVisitorFunc casts a function to match NodeVisitor interface
|
||||
type NodeVisitorFunc func(node Node, entering bool) WalkStatus
|
||||
|
||||
// Walk traverses tree recursively
|
||||
func Walk(n Node, visitor NodeVisitor) WalkStatus {
|
||||
isContainer := n.AsContainer() != nil
|
||||
status := visitor.Visit(n, true) // entering
|
||||
if status == Terminate {
|
||||
// even if terminating, close container node
|
||||
if isContainer {
|
||||
visitor.Visit(n, false)
|
||||
}
|
||||
return status
|
||||
}
|
||||
if isContainer && status != SkipChildren {
|
||||
children := n.GetChildren()
|
||||
for _, n := range children {
|
||||
status = Walk(n, visitor)
|
||||
if status == Terminate {
|
||||
return status
|
||||
}
|
||||
}
|
||||
}
|
||||
if isContainer {
|
||||
status = visitor.Visit(n, false) // exiting
|
||||
if status == Terminate {
|
||||
return status
|
||||
}
|
||||
}
|
||||
return GoToNext
|
||||
}
|
||||
|
||||
// Visit calls visitor function
|
||||
func (f NodeVisitorFunc) Visit(node Node, entering bool) WalkStatus {
|
||||
return f(node, entering)
|
||||
}
|
||||
|
||||
// WalkFunc is like Walk but accepts just a callback function
|
||||
func WalkFunc(n Node, f NodeVisitorFunc) {
|
||||
visitor := NodeVisitorFunc(f)
|
||||
Walk(n, visitor)
|
||||
}
|
165
vendor/github.com/gomarkdown/markdown/ast/print.go
generated
vendored
Normal file
165
vendor/github.com/gomarkdown/markdown/ast/print.go
generated
vendored
Normal file
@@ -0,0 +1,165 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Print is for debugging. It prints a string representation of parsed
|
||||
// markdown doc (result of parser.Parse()) to dst.
|
||||
//
|
||||
// To make output readable, it shortens text output.
|
||||
func Print(dst io.Writer, doc Node) {
|
||||
PrintWithPrefix(dst, doc, " ")
|
||||
}
|
||||
|
||||
// PrintWithPrefix is like Print but allows customizing prefix used for
|
||||
// indentation. By default it's 2 spaces. You can change it to e.g. tab
|
||||
// by passing "\t"
|
||||
func PrintWithPrefix(w io.Writer, doc Node, prefix string) {
|
||||
// for more compact output, don't print outer Document
|
||||
if _, ok := doc.(*Document); ok {
|
||||
for _, c := range doc.GetChildren() {
|
||||
printRecur(w, c, prefix, 0)
|
||||
}
|
||||
} else {
|
||||
printRecur(w, doc, prefix, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// ToString is like Dump but returns result as a string
|
||||
func ToString(doc Node) string {
|
||||
var buf bytes.Buffer
|
||||
Print(&buf, doc)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func contentToString(d1 []byte, d2 []byte) string {
|
||||
if d1 != nil {
|
||||
return string(d1)
|
||||
}
|
||||
if d2 != nil {
|
||||
return string(d2)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getContent(node Node) string {
|
||||
if c := node.AsContainer(); c != nil {
|
||||
return contentToString(c.Literal, c.Content)
|
||||
}
|
||||
leaf := node.AsLeaf()
|
||||
return contentToString(leaf.Literal, leaf.Content)
|
||||
}
|
||||
|
||||
func shortenString(s string, maxLen int) string {
|
||||
// for cleaner, one-line ouput, replace some white-space chars
|
||||
// with their escaped version
|
||||
s = strings.Replace(s, "\n", `\n`, -1)
|
||||
s = strings.Replace(s, "\r", `\r`, -1)
|
||||
s = strings.Replace(s, "\t", `\t`, -1)
|
||||
if maxLen < 0 {
|
||||
return s
|
||||
}
|
||||
if len(s) < maxLen {
|
||||
return s
|
||||
}
|
||||
// add "..." to indicate truncation
|
||||
return s[:maxLen-3] + "..."
|
||||
}
|
||||
|
||||
// get a short name of the type of v which excludes package name
|
||||
// and strips "()" from the end
|
||||
func getNodeType(node Node) string {
|
||||
s := fmt.Sprintf("%T", node)
|
||||
s = strings.TrimSuffix(s, "()")
|
||||
if idx := strings.Index(s, "."); idx != -1 {
|
||||
return s[idx+1:]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func printDefault(w io.Writer, indent string, typeName string, content string) {
|
||||
content = strings.TrimSpace(content)
|
||||
if len(content) > 0 {
|
||||
fmt.Fprintf(w, "%s%s '%s'\n", indent, typeName, content)
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s%s\n", indent, typeName)
|
||||
}
|
||||
}
|
||||
|
||||
func getListFlags(f ListType) string {
|
||||
var s string
|
||||
if f&ListTypeOrdered != 0 {
|
||||
s += "ordered "
|
||||
}
|
||||
if f&ListTypeDefinition != 0 {
|
||||
s += "definition "
|
||||
}
|
||||
if f&ListTypeTerm != 0 {
|
||||
s += "term "
|
||||
}
|
||||
if f&ListItemContainsBlock != 0 {
|
||||
s += "has_block "
|
||||
}
|
||||
if f&ListItemBeginningOfList != 0 {
|
||||
s += "start "
|
||||
}
|
||||
if f&ListItemEndOfList != 0 {
|
||||
s += "end "
|
||||
}
|
||||
s = strings.TrimSpace(s)
|
||||
return s
|
||||
}
|
||||
|
||||
func printRecur(w io.Writer, node Node, prefix string, depth int) {
|
||||
if node == nil {
|
||||
return
|
||||
}
|
||||
indent := strings.Repeat(prefix, depth)
|
||||
|
||||
content := shortenString(getContent(node), 40)
|
||||
typeName := getNodeType(node)
|
||||
switch v := node.(type) {
|
||||
case *Link:
|
||||
content := "url=" + string(v.Destination)
|
||||
printDefault(w, indent, typeName, content)
|
||||
case *Image:
|
||||
content := "url=" + string(v.Destination)
|
||||
printDefault(w, indent, typeName, content)
|
||||
case *List:
|
||||
if v.Start > 1 {
|
||||
content += fmt.Sprintf("start=%d ", v.Start)
|
||||
}
|
||||
if v.Tight {
|
||||
content += "tight "
|
||||
}
|
||||
if v.IsFootnotesList {
|
||||
content += "footnotes "
|
||||
}
|
||||
flags := getListFlags(v.ListFlags)
|
||||
if len(flags) > 0 {
|
||||
content += "flags=" + flags + " "
|
||||
}
|
||||
printDefault(w, indent, typeName, content)
|
||||
case *ListItem:
|
||||
if v.Tight {
|
||||
content += "tight "
|
||||
}
|
||||
if v.IsFootnotesList {
|
||||
content += "footnotes "
|
||||
}
|
||||
flags := getListFlags(v.ListFlags)
|
||||
if len(flags) > 0 {
|
||||
content += "flags=" + flags + " "
|
||||
}
|
||||
printDefault(w, indent, typeName, content)
|
||||
default:
|
||||
printDefault(w, indent, typeName, content)
|
||||
}
|
||||
for _, child := range node.GetChildren() {
|
||||
printRecur(w, child, prefix, depth+1)
|
||||
}
|
||||
}
|
27
vendor/github.com/gomarkdown/markdown/changes-from-blackfriday.md
generated
vendored
Normal file
27
vendor/github.com/gomarkdown/markdown/changes-from-blackfriday.md
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
## Changes from blackfriday
|
||||
|
||||
This library is derived from blackfriday library. Here's a list of changes.
|
||||
|
||||
**Redesigned API**
|
||||
|
||||
- split into 3 separate packages: ast, parser and html (for html renderer). This makes the API more manageable. It also separates e.g. parser option from renderer options
|
||||
- changed how AST node is represented from union-like representation (manually keeping track of the type of the node) to using interface{} (which is a Go way to combine an arbitrary value with its type)
|
||||
|
||||
**Allow re-using most of html renderer logic**
|
||||
|
||||
You can implement your own renderer by implementing `Renderer` interface.
|
||||
|
||||
Implementing a full renderer is a lot of work and often you just want to tweak html rendering of few node typs.
|
||||
|
||||
I've added a way to hook `Renderer.Render` function in html renderer with a custom function that can take over rendering of specific nodes.
|
||||
|
||||
I use it myself to do syntax-highlighting of code snippets.
|
||||
|
||||
**Speed up go test**
|
||||
|
||||
Running `go test` was really slow (17 secs) because it did a poor man's version of fuzzing by feeding the parser all subsets of test strings in order to find panics
|
||||
due to incorrect parsing logic.
|
||||
|
||||
I've moved that logic to `cmd/crashtest`, so that it can be run on CI but not slow down regular development.
|
||||
|
||||
Now `go test` is blazing fast.
|
8
vendor/github.com/gomarkdown/markdown/codecov.yml
generated
vendored
Normal file
8
vendor/github.com/gomarkdown/markdown/codecov.yml
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
# basic
|
||||
target: 60%
|
||||
threshold: 2%
|
||||
base: auto
|
35
vendor/github.com/gomarkdown/markdown/doc.go
generated
vendored
Normal file
35
vendor/github.com/gomarkdown/markdown/doc.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Package markdown implements markdown parser and HTML renderer.
|
||||
|
||||
It parses markdown into AST format which can be serialized to HTML
|
||||
(using html.Renderer) or possibly other formats (using alternate renderers).
|
||||
|
||||
Convert markdown to HTML
|
||||
|
||||
The simplest way to convert markdown document to HTML
|
||||
|
||||
md := []byte("## markdown document")
|
||||
html := markdown.ToHTML(md, nil, nil)
|
||||
|
||||
Customizing parsing and HTML rendering
|
||||
|
||||
You can customize parser and HTML renderer:
|
||||
|
||||
import (
|
||||
"github.com/gomarkdown/markdown/parser"
|
||||
"github.com/gomarkdown/markdown/renderer"
|
||||
"github.com/gomarkdown/markdown"
|
||||
)
|
||||
extensions := parser.CommonExtensions | parser.AutoHeadingIDs
|
||||
p := parser.NewWithExensions(extensions)
|
||||
|
||||
htmlFlags := html.CommonFlags | html.HrefTargetBlank
|
||||
opts := html.RendererOptions{Flags: htmlFlags}
|
||||
renderer := html.NewRenderer(opts)
|
||||
|
||||
md := []byte("markdown text")
|
||||
html := markdown.ToHTML(md, p, renderer)
|
||||
|
||||
For a cmd-line tool see https://github.com/gomarkdown/mdtohtml
|
||||
*/
|
||||
package markdown
|
9
vendor/github.com/gomarkdown/markdown/fuzz.go
generated
vendored
Normal file
9
vendor/github.com/gomarkdown/markdown/fuzz.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// +build gofuzz
|
||||
|
||||
package markdown
|
||||
|
||||
// Fuzz is to be used by https://github.com/dvyukov/go-fuzz
|
||||
func Fuzz(data []byte) int {
|
||||
Parse(data, nil)
|
||||
return 0
|
||||
}
|
5
vendor/github.com/gomarkdown/markdown/go.mod
generated
vendored
Normal file
5
vendor/github.com/gomarkdown/markdown/go.mod
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
module github.com/gomarkdown/markdown
|
||||
|
||||
go 1.12
|
||||
|
||||
require golang.org/dl v0.0.0-20190829154251-82a15e2f2ead // indirect
|
42
vendor/github.com/gomarkdown/markdown/html/callouts.go
generated
vendored
Normal file
42
vendor/github.com/gomarkdown/markdown/html/callouts.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
package html
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
|
||||
"github.com/gomarkdown/markdown/ast"
|
||||
"github.com/gomarkdown/markdown/parser"
|
||||
)
|
||||
|
||||
// EscapeHTMLCallouts writes html-escaped d to w. It escapes &, <, > and " characters, *but*
|
||||
// expands callouts <<N>> with the callout HTML, i.e. by calling r.callout() with a newly created
|
||||
// ast.Callout node.
|
||||
func (r *Renderer) EscapeHTMLCallouts(w io.Writer, d []byte) {
|
||||
ld := len(d)
|
||||
Parse:
|
||||
for i := 0; i < ld; i++ {
|
||||
for _, comment := range r.opts.Comments {
|
||||
if !bytes.HasPrefix(d[i:], comment) {
|
||||
break
|
||||
}
|
||||
|
||||
lc := len(comment)
|
||||
if i+lc < ld {
|
||||
if id, consumed := parser.IsCallout(d[i+lc:]); consumed > 0 {
|
||||
// We have seen a callout
|
||||
callout := &ast.Callout{ID: id}
|
||||
r.callout(w, callout)
|
||||
i += consumed + lc - 1
|
||||
continue Parse
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
escSeq := Escaper[d[i]]
|
||||
if escSeq != nil {
|
||||
w.Write(escSeq)
|
||||
} else {
|
||||
w.Write([]byte{d[i]})
|
||||
}
|
||||
}
|
||||
}
|
43
vendor/github.com/gomarkdown/markdown/html/doc.go
generated
vendored
Normal file
43
vendor/github.com/gomarkdown/markdown/html/doc.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
Package html implements HTML renderer of parsed markdown document.
|
||||
|
||||
Configuring and customizing a renderer
|
||||
|
||||
A renderer can be configured with multiple options:
|
||||
|
||||
import "github.com/gomarkdown/markdown/html"
|
||||
|
||||
flags := html.CommonFlags | html.CompletePage | html.HrefTargetBlank
|
||||
opts := html.RenderOptions{
|
||||
TItle: "A custom title",
|
||||
Flags: flags,
|
||||
}
|
||||
renderer := html.NewRenderer(opts)
|
||||
|
||||
You can also re-use most of the logic and customize rendering of selected nodes
|
||||
by providing node render hook.
|
||||
This is most useful for rendering nodes that allow for design choices, like
|
||||
links or code blocks.
|
||||
|
||||
import (
|
||||
"github.com/gomarkdown/markdown/html"
|
||||
"github.com/gomarkdown/markdown/ast"
|
||||
)
|
||||
|
||||
// a very dummy render hook that will output "code_replacements" instead of
|
||||
// <code>${content}</code> emitted by html.Renderer
|
||||
func renderHookCodeBlock(w io.Writer, node *ast.Node, entering bool) (ast.WalkStatus, bool) {
|
||||
_, ok := node.Data.(*ast.CodeBlockData)
|
||||
if !ok {
|
||||
return ast.GoToNext, false
|
||||
}
|
||||
io.WriteString(w, "code_replacement")
|
||||
return ast.GoToNext, true
|
||||
}
|
||||
|
||||
opts := html.RendererOptions{
|
||||
RenderNodeHook: renderHookCodeBlock,
|
||||
}
|
||||
renderer := html.NewRenderer(opts)
|
||||
*/
|
||||
package html
|
50
vendor/github.com/gomarkdown/markdown/html/esc.go
generated
vendored
Normal file
50
vendor/github.com/gomarkdown/markdown/html/esc.go
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
package html
|
||||
|
||||
import (
|
||||
"html"
|
||||
"io"
|
||||
)
|
||||
|
||||
var Escaper = [256][]byte{
|
||||
'&': []byte("&"),
|
||||
'<': []byte("<"),
|
||||
'>': []byte(">"),
|
||||
'"': []byte("""),
|
||||
}
|
||||
|
||||
// EscapeHTML writes html-escaped d to w. It escapes &, <, > and " characters.
|
||||
func EscapeHTML(w io.Writer, d []byte) {
|
||||
var start, end int
|
||||
n := len(d)
|
||||
for end < n {
|
||||
escSeq := Escaper[d[end]]
|
||||
if escSeq != nil {
|
||||
w.Write(d[start:end])
|
||||
w.Write(escSeq)
|
||||
start = end + 1
|
||||
}
|
||||
end++
|
||||
}
|
||||
if start < n && end <= n {
|
||||
w.Write(d[start:end])
|
||||
}
|
||||
}
|
||||
|
||||
func escLink(w io.Writer, text []byte) {
|
||||
unesc := html.UnescapeString(string(text))
|
||||
EscapeHTML(w, []byte(unesc))
|
||||
}
|
||||
|
||||
// Escape writes the text to w, but skips the escape character.
|
||||
func Escape(w io.Writer, text []byte) {
|
||||
esc := false
|
||||
for i := 0; i < len(text); i++ {
|
||||
if text[i] == '\\' {
|
||||
esc = !esc
|
||||
}
|
||||
if esc && text[i] == '\\' {
|
||||
continue
|
||||
}
|
||||
w.Write([]byte{text[i]})
|
||||
}
|
||||
}
|
1318
vendor/github.com/gomarkdown/markdown/html/renderer.go
generated
vendored
Normal file
1318
vendor/github.com/gomarkdown/markdown/html/renderer.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
444
vendor/github.com/gomarkdown/markdown/html/smartypants.go
generated
vendored
Normal file
444
vendor/github.com/gomarkdown/markdown/html/smartypants.go
generated
vendored
Normal file
@@ -0,0 +1,444 @@
|
||||
package html
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
// SmartyPants rendering
|
||||
|
||||
// SPRenderer is a struct containing state of a Smartypants renderer.
|
||||
type SPRenderer struct {
|
||||
inSingleQuote bool
|
||||
inDoubleQuote bool
|
||||
callbacks [256]smartCallback
|
||||
}
|
||||
|
||||
func wordBoundary(c byte) bool {
|
||||
return c == 0 || isSpace(c) || isPunctuation(c)
|
||||
}
|
||||
|
||||
func tolower(c byte) byte {
|
||||
if c >= 'A' && c <= 'Z' {
|
||||
return c - 'A' + 'a'
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func isdigit(c byte) bool {
|
||||
return c >= '0' && c <= '9'
|
||||
}
|
||||
|
||||
func smartQuoteHelper(out *bytes.Buffer, previousChar byte, nextChar byte, quote byte, isOpen *bool, addNBSP bool) bool {
|
||||
// edge of the buffer is likely to be a tag that we don't get to see,
|
||||
// so we treat it like text sometimes
|
||||
|
||||
// enumerate all sixteen possibilities for (previousChar, nextChar)
|
||||
// each can be one of {0, space, punct, other}
|
||||
switch {
|
||||
case previousChar == 0 && nextChar == 0:
|
||||
// context is not any help here, so toggle
|
||||
*isOpen = !*isOpen
|
||||
case isSpace(previousChar) && nextChar == 0:
|
||||
// [ "] might be [ "<code>foo...]
|
||||
*isOpen = true
|
||||
case isPunctuation(previousChar) && nextChar == 0:
|
||||
// [!"] hmm... could be [Run!"] or [("<code>...]
|
||||
*isOpen = false
|
||||
case /* isnormal(previousChar) && */ nextChar == 0:
|
||||
// [a"] is probably a close
|
||||
*isOpen = false
|
||||
case previousChar == 0 && isSpace(nextChar):
|
||||
// [" ] might be [...foo</code>" ]
|
||||
*isOpen = false
|
||||
case isSpace(previousChar) && isSpace(nextChar):
|
||||
// [ " ] context is not any help here, so toggle
|
||||
*isOpen = !*isOpen
|
||||
case isPunctuation(previousChar) && isSpace(nextChar):
|
||||
// [!" ] is probably a close
|
||||
*isOpen = false
|
||||
case /* isnormal(previousChar) && */ isSpace(nextChar):
|
||||
// [a" ] this is one of the easy cases
|
||||
*isOpen = false
|
||||
case previousChar == 0 && isPunctuation(nextChar):
|
||||
// ["!] hmm... could be ["$1.95] or [</code>"!...]
|
||||
*isOpen = false
|
||||
case isSpace(previousChar) && isPunctuation(nextChar):
|
||||
// [ "!] looks more like [ "$1.95]
|
||||
*isOpen = true
|
||||
case isPunctuation(previousChar) && isPunctuation(nextChar):
|
||||
// [!"!] context is not any help here, so toggle
|
||||
*isOpen = !*isOpen
|
||||
case /* isnormal(previousChar) && */ isPunctuation(nextChar):
|
||||
// [a"!] is probably a close
|
||||
*isOpen = false
|
||||
case previousChar == 0 /* && isnormal(nextChar) */ :
|
||||
// ["a] is probably an open
|
||||
*isOpen = true
|
||||
case isSpace(previousChar) /* && isnormal(nextChar) */ :
|
||||
// [ "a] this is one of the easy cases
|
||||
*isOpen = true
|
||||
case isPunctuation(previousChar) /* && isnormal(nextChar) */ :
|
||||
// [!"a] is probably an open
|
||||
*isOpen = true
|
||||
default:
|
||||
// [a'b] maybe a contraction?
|
||||
*isOpen = false
|
||||
}
|
||||
|
||||
// Note that with the limited lookahead, this non-breaking
|
||||
// space will also be appended to single double quotes.
|
||||
if addNBSP && !*isOpen {
|
||||
out.WriteString(" ")
|
||||
}
|
||||
|
||||
out.WriteByte('&')
|
||||
if *isOpen {
|
||||
out.WriteByte('l')
|
||||
} else {
|
||||
out.WriteByte('r')
|
||||
}
|
||||
out.WriteByte(quote)
|
||||
out.WriteString("quo;")
|
||||
|
||||
if addNBSP && *isOpen {
|
||||
out.WriteString(" ")
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *SPRenderer) smartSingleQuote(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
if len(text) >= 2 {
|
||||
t1 := tolower(text[1])
|
||||
|
||||
if t1 == '\'' {
|
||||
nextChar := byte(0)
|
||||
if len(text) >= 3 {
|
||||
nextChar = text[2]
|
||||
}
|
||||
if smartQuoteHelper(out, previousChar, nextChar, 'd', &r.inDoubleQuote, false) {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
if (t1 == 's' || t1 == 't' || t1 == 'm' || t1 == 'd') && (len(text) < 3 || wordBoundary(text[2])) {
|
||||
out.WriteString("’")
|
||||
return 0
|
||||
}
|
||||
|
||||
if len(text) >= 3 {
|
||||
t2 := tolower(text[2])
|
||||
|
||||
if ((t1 == 'r' && t2 == 'e') || (t1 == 'l' && t2 == 'l') || (t1 == 'v' && t2 == 'e')) &&
|
||||
(len(text) < 4 || wordBoundary(text[3])) {
|
||||
out.WriteString("’")
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nextChar := byte(0)
|
||||
if len(text) > 1 {
|
||||
nextChar = text[1]
|
||||
}
|
||||
if smartQuoteHelper(out, previousChar, nextChar, 's', &r.inSingleQuote, false) {
|
||||
return 0
|
||||
}
|
||||
|
||||
out.WriteByte(text[0])
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *SPRenderer) smartParens(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
if len(text) >= 3 {
|
||||
t1 := tolower(text[1])
|
||||
t2 := tolower(text[2])
|
||||
|
||||
if t1 == 'c' && t2 == ')' {
|
||||
out.WriteString("©")
|
||||
return 2
|
||||
}
|
||||
|
||||
if t1 == 'r' && t2 == ')' {
|
||||
out.WriteString("®")
|
||||
return 2
|
||||
}
|
||||
|
||||
if len(text) >= 4 && t1 == 't' && t2 == 'm' && text[3] == ')' {
|
||||
out.WriteString("™")
|
||||
return 3
|
||||
}
|
||||
}
|
||||
|
||||
out.WriteByte(text[0])
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *SPRenderer) smartDash(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
if len(text) >= 2 {
|
||||
if text[1] == '-' {
|
||||
out.WriteString("—")
|
||||
return 1
|
||||
}
|
||||
|
||||
if wordBoundary(previousChar) && wordBoundary(text[1]) {
|
||||
out.WriteString("–")
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
out.WriteByte(text[0])
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *SPRenderer) smartDashLatex(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
if len(text) >= 3 && text[1] == '-' && text[2] == '-' {
|
||||
out.WriteString("—")
|
||||
return 2
|
||||
}
|
||||
if len(text) >= 2 && text[1] == '-' {
|
||||
out.WriteString("–")
|
||||
return 1
|
||||
}
|
||||
|
||||
out.WriteByte(text[0])
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *SPRenderer) smartAmpVariant(out *bytes.Buffer, previousChar byte, text []byte, quote byte, addNBSP bool) int {
|
||||
if bytes.HasPrefix(text, []byte(""")) {
|
||||
nextChar := byte(0)
|
||||
if len(text) >= 7 {
|
||||
nextChar = text[6]
|
||||
}
|
||||
if smartQuoteHelper(out, previousChar, nextChar, quote, &r.inDoubleQuote, addNBSP) {
|
||||
return 5
|
||||
}
|
||||
}
|
||||
|
||||
if bytes.HasPrefix(text, []byte("�")) {
|
||||
return 3
|
||||
}
|
||||
|
||||
out.WriteByte('&')
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *SPRenderer) smartAmp(angledQuotes, addNBSP bool) func(*bytes.Buffer, byte, []byte) int {
|
||||
var quote byte = 'd'
|
||||
if angledQuotes {
|
||||
quote = 'a'
|
||||
}
|
||||
|
||||
return func(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
return r.smartAmpVariant(out, previousChar, text, quote, addNBSP)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *SPRenderer) smartPeriod(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
if len(text) >= 3 && text[1] == '.' && text[2] == '.' {
|
||||
out.WriteString("…")
|
||||
return 2
|
||||
}
|
||||
|
||||
if len(text) >= 5 && text[1] == ' ' && text[2] == '.' && text[3] == ' ' && text[4] == '.' {
|
||||
out.WriteString("…")
|
||||
return 4
|
||||
}
|
||||
|
||||
out.WriteByte(text[0])
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *SPRenderer) smartBacktick(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
if len(text) >= 2 && text[1] == '`' {
|
||||
nextChar := byte(0)
|
||||
if len(text) >= 3 {
|
||||
nextChar = text[2]
|
||||
}
|
||||
if smartQuoteHelper(out, previousChar, nextChar, 'd', &r.inDoubleQuote, false) {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
out.WriteByte(text[0])
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *SPRenderer) smartNumberGeneric(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
if wordBoundary(previousChar) && previousChar != '/' && len(text) >= 3 {
|
||||
// is it of the form digits/digits(word boundary)?, i.e., \d+/\d+\b
|
||||
// note: check for regular slash (/) or fraction slash (⁄, 0x2044, or 0xe2 81 84 in utf-8)
|
||||
// and avoid changing dates like 1/23/2005 into fractions.
|
||||
numEnd := 0
|
||||
for len(text) > numEnd && isdigit(text[numEnd]) {
|
||||
numEnd++
|
||||
}
|
||||
if numEnd == 0 {
|
||||
out.WriteByte(text[0])
|
||||
return 0
|
||||
}
|
||||
denStart := numEnd + 1
|
||||
if len(text) > numEnd+3 && text[numEnd] == 0xe2 && text[numEnd+1] == 0x81 && text[numEnd+2] == 0x84 {
|
||||
denStart = numEnd + 3
|
||||
} else if len(text) < numEnd+2 || text[numEnd] != '/' {
|
||||
out.WriteByte(text[0])
|
||||
return 0
|
||||
}
|
||||
denEnd := denStart
|
||||
for len(text) > denEnd && isdigit(text[denEnd]) {
|
||||
denEnd++
|
||||
}
|
||||
if denEnd == denStart {
|
||||
out.WriteByte(text[0])
|
||||
return 0
|
||||
}
|
||||
if len(text) == denEnd || wordBoundary(text[denEnd]) && text[denEnd] != '/' {
|
||||
out.WriteString("<sup>")
|
||||
out.Write(text[:numEnd])
|
||||
out.WriteString("</sup>⁄<sub>")
|
||||
out.Write(text[denStart:denEnd])
|
||||
out.WriteString("</sub>")
|
||||
return denEnd - 1
|
||||
}
|
||||
}
|
||||
|
||||
out.WriteByte(text[0])
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *SPRenderer) smartNumber(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
if wordBoundary(previousChar) && previousChar != '/' && len(text) >= 3 {
|
||||
if text[0] == '1' && text[1] == '/' && text[2] == '2' {
|
||||
if len(text) < 4 || wordBoundary(text[3]) && text[3] != '/' {
|
||||
out.WriteString("½")
|
||||
return 2
|
||||
}
|
||||
}
|
||||
|
||||
if text[0] == '1' && text[1] == '/' && text[2] == '4' {
|
||||
if len(text) < 4 || wordBoundary(text[3]) && text[3] != '/' || (len(text) >= 5 && tolower(text[3]) == 't' && tolower(text[4]) == 'h') {
|
||||
out.WriteString("¼")
|
||||
return 2
|
||||
}
|
||||
}
|
||||
|
||||
if text[0] == '3' && text[1] == '/' && text[2] == '4' {
|
||||
if len(text) < 4 || wordBoundary(text[3]) && text[3] != '/' || (len(text) >= 6 && tolower(text[3]) == 't' && tolower(text[4]) == 'h' && tolower(text[5]) == 's') {
|
||||
out.WriteString("¾")
|
||||
return 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out.WriteByte(text[0])
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *SPRenderer) smartDoubleQuoteVariant(out *bytes.Buffer, previousChar byte, text []byte, quote byte) int {
|
||||
nextChar := byte(0)
|
||||
if len(text) > 1 {
|
||||
nextChar = text[1]
|
||||
}
|
||||
if !smartQuoteHelper(out, previousChar, nextChar, quote, &r.inDoubleQuote, false) {
|
||||
out.WriteString(""")
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *SPRenderer) smartDoubleQuote(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
return r.smartDoubleQuoteVariant(out, previousChar, text, 'd')
|
||||
}
|
||||
|
||||
func (r *SPRenderer) smartAngledDoubleQuote(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
return r.smartDoubleQuoteVariant(out, previousChar, text, 'a')
|
||||
}
|
||||
|
||||
func (r *SPRenderer) smartLeftAngle(out *bytes.Buffer, previousChar byte, text []byte) int {
|
||||
i := 0
|
||||
|
||||
for i < len(text) && text[i] != '>' {
|
||||
i++
|
||||
}
|
||||
|
||||
out.Write(text[:i+1])
|
||||
return i
|
||||
}
|
||||
|
||||
type smartCallback func(out *bytes.Buffer, previousChar byte, text []byte) int
|
||||
|
||||
// NewSmartypantsRenderer constructs a Smartypants renderer object.
|
||||
func NewSmartypantsRenderer(flags Flags) *SPRenderer {
|
||||
var (
|
||||
r SPRenderer
|
||||
|
||||
smartAmpAngled = r.smartAmp(true, false)
|
||||
smartAmpAngledNBSP = r.smartAmp(true, true)
|
||||
smartAmpRegular = r.smartAmp(false, false)
|
||||
smartAmpRegularNBSP = r.smartAmp(false, true)
|
||||
|
||||
addNBSP = flags&SmartypantsQuotesNBSP != 0
|
||||
)
|
||||
|
||||
if flags&SmartypantsAngledQuotes == 0 {
|
||||
r.callbacks['"'] = r.smartDoubleQuote
|
||||
if !addNBSP {
|
||||
r.callbacks['&'] = smartAmpRegular
|
||||
} else {
|
||||
r.callbacks['&'] = smartAmpRegularNBSP
|
||||
}
|
||||
} else {
|
||||
r.callbacks['"'] = r.smartAngledDoubleQuote
|
||||
if !addNBSP {
|
||||
r.callbacks['&'] = smartAmpAngled
|
||||
} else {
|
||||
r.callbacks['&'] = smartAmpAngledNBSP
|
||||
}
|
||||
}
|
||||
r.callbacks['\''] = r.smartSingleQuote
|
||||
r.callbacks['('] = r.smartParens
|
||||
if flags&SmartypantsDashes != 0 {
|
||||
if flags&SmartypantsLatexDashes == 0 {
|
||||
r.callbacks['-'] = r.smartDash
|
||||
} else {
|
||||
r.callbacks['-'] = r.smartDashLatex
|
||||
}
|
||||
}
|
||||
r.callbacks['.'] = r.smartPeriod
|
||||
if flags&SmartypantsFractions == 0 {
|
||||
r.callbacks['1'] = r.smartNumber
|
||||
r.callbacks['3'] = r.smartNumber
|
||||
} else {
|
||||
for ch := '1'; ch <= '9'; ch++ {
|
||||
r.callbacks[ch] = r.smartNumberGeneric
|
||||
}
|
||||
}
|
||||
r.callbacks['<'] = r.smartLeftAngle
|
||||
r.callbacks['`'] = r.smartBacktick
|
||||
return &r
|
||||
}
|
||||
|
||||
// Process is the entry point of the Smartypants renderer.
|
||||
func (r *SPRenderer) Process(w io.Writer, text []byte) {
|
||||
mark := 0
|
||||
for i := 0; i < len(text); i++ {
|
||||
if action := r.callbacks[text[i]]; action != nil {
|
||||
if i > mark {
|
||||
w.Write(text[mark:i])
|
||||
}
|
||||
previousChar := byte(0)
|
||||
if i > 0 {
|
||||
previousChar = text[i-1]
|
||||
}
|
||||
var tmp bytes.Buffer
|
||||
i += action(&tmp, previousChar, text[i:])
|
||||
w.Write(tmp.Bytes())
|
||||
mark = i + 1
|
||||
}
|
||||
}
|
||||
if mark < len(text) {
|
||||
w.Write(text[mark:])
|
||||
}
|
||||
}
|
85
vendor/github.com/gomarkdown/markdown/markdown.go
generated
vendored
Normal file
85
vendor/github.com/gomarkdown/markdown/markdown.go
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
package markdown
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
|
||||
"github.com/gomarkdown/markdown/ast"
|
||||
"github.com/gomarkdown/markdown/html"
|
||||
"github.com/gomarkdown/markdown/parser"
|
||||
)
|
||||
|
||||
// Renderer is an interface for implementing custom renderers.
|
||||
type Renderer interface {
|
||||
// RenderNode renders markdown node to w.
|
||||
// It's called once for a leaf node.
|
||||
// It's called twice for non-leaf nodes:
|
||||
// * first with entering=true
|
||||
// * then with entering=false
|
||||
//
|
||||
// Return value is a way to tell the calling walker to adjust its walk
|
||||
// pattern: e.g. it can terminate the traversal by returning Terminate. Or it
|
||||
// can ask the walker to skip a subtree of this node by returning SkipChildren.
|
||||
// The typical behavior is to return GoToNext, which asks for the usual
|
||||
// traversal to the next node.
|
||||
RenderNode(w io.Writer, node ast.Node, entering bool) ast.WalkStatus
|
||||
|
||||
// RenderHeader is a method that allows the renderer to produce some
|
||||
// content preceding the main body of the output document. The header is
|
||||
// understood in the broad sense here. For example, the default HTML
|
||||
// renderer will write not only the HTML document preamble, but also the
|
||||
// table of contents if it was requested.
|
||||
//
|
||||
// The method will be passed an entire document tree, in case a particular
|
||||
// implementation needs to inspect it to produce output.
|
||||
//
|
||||
// The output should be written to the supplied writer w. If your
|
||||
// implementation has no header to write, supply an empty implementation.
|
||||
RenderHeader(w io.Writer, ast ast.Node)
|
||||
|
||||
// RenderFooter is a symmetric counterpart of RenderHeader.
|
||||
RenderFooter(w io.Writer, ast ast.Node)
|
||||
}
|
||||
|
||||
// Parse parsers a markdown document using provided parser. If parser is nil,
|
||||
// we use parser configured with parser.CommonExtensions.
|
||||
//
|
||||
// It returns AST (abstract syntax tree) that can be converted to another
|
||||
// format using Render function.
|
||||
func Parse(markdown []byte, p *parser.Parser) ast.Node {
|
||||
if p == nil {
|
||||
p = parser.New()
|
||||
}
|
||||
return p.Parse(markdown)
|
||||
}
|
||||
|
||||
// Render uses renderer to convert parsed markdown document into a different format.
|
||||
//
|
||||
// To convert to HTML, pass html.Renderer
|
||||
func Render(doc ast.Node, renderer Renderer) []byte {
|
||||
var buf bytes.Buffer
|
||||
renderer.RenderHeader(&buf, doc)
|
||||
ast.WalkFunc(doc, func(node ast.Node, entering bool) ast.WalkStatus {
|
||||
return renderer.RenderNode(&buf, node, entering)
|
||||
})
|
||||
renderer.RenderFooter(&buf, doc)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// ToHTML converts markdownDoc to HTML.
|
||||
//
|
||||
// You can optionally pass a parser and renderer. This allows to customize
|
||||
// a parser, use a customized html render or use completely custom renderer.
|
||||
//
|
||||
// If you pass nil for both, we use parser configured with parser.CommonExtensions
|
||||
// and html.Renderer configured with html.CommonFlags.
|
||||
func ToHTML(markdown []byte, p *parser.Parser, renderer Renderer) []byte {
|
||||
doc := Parse(markdown, p)
|
||||
if renderer == nil {
|
||||
opts := html.RendererOptions{
|
||||
Flags: html.CommonFlags,
|
||||
}
|
||||
renderer = html.NewRenderer(opts)
|
||||
}
|
||||
return Render(doc, renderer)
|
||||
}
|
73
vendor/github.com/gomarkdown/markdown/parser/aside.go
generated
vendored
Normal file
73
vendor/github.com/gomarkdown/markdown/parser/aside.go
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/gomarkdown/markdown/ast"
|
||||
)
|
||||
|
||||
// returns aisde prefix length
|
||||
func (p *Parser) asidePrefix(data []byte) int {
|
||||
i := 0
|
||||
n := len(data)
|
||||
for i < 3 && i < n && data[i] == ' ' {
|
||||
i++
|
||||
}
|
||||
if i+1 < n && data[i] == 'A' && data[i+1] == '>' {
|
||||
if i+2 < n && data[i+2] == ' ' {
|
||||
return i + 3
|
||||
}
|
||||
return i + 2
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// aside ends with at least one blank line
|
||||
// followed by something without a aside prefix
|
||||
func (p *Parser) terminateAside(data []byte, beg, end int) bool {
|
||||
if p.isEmpty(data[beg:]) <= 0 {
|
||||
return false
|
||||
}
|
||||
if end >= len(data) {
|
||||
return true
|
||||
}
|
||||
return p.asidePrefix(data[end:]) == 0 && p.isEmpty(data[end:]) == 0
|
||||
}
|
||||
|
||||
// parse a aside fragment
|
||||
func (p *Parser) aside(data []byte) int {
|
||||
var raw bytes.Buffer
|
||||
beg, end := 0, 0
|
||||
// identical to quote
|
||||
for beg < len(data) {
|
||||
end = beg
|
||||
// Step over whole lines, collecting them. While doing that, check for
|
||||
// fenced code and if one's found, incorporate it altogether,
|
||||
// irregardless of any contents inside it
|
||||
for end < len(data) && data[end] != '\n' {
|
||||
if p.extensions&FencedCode != 0 {
|
||||
if i := p.fencedCodeBlock(data[end:], false); i > 0 {
|
||||
// -1 to compensate for the extra end++ after the loop:
|
||||
end += i - 1
|
||||
break
|
||||
}
|
||||
}
|
||||
end++
|
||||
}
|
||||
end = skipCharN(data, end, '\n', 1)
|
||||
if pre := p.asidePrefix(data[beg:]); pre > 0 {
|
||||
// skip the prefix
|
||||
beg += pre
|
||||
} else if p.terminateAside(data, beg, end) {
|
||||
break
|
||||
}
|
||||
// this line is part of the aside
|
||||
raw.Write(data[beg:end])
|
||||
beg = end
|
||||
}
|
||||
|
||||
block := p.addBlock(&ast.Aside{})
|
||||
p.block(raw.Bytes())
|
||||
p.finalize(block)
|
||||
return end
|
||||
}
|
116
vendor/github.com/gomarkdown/markdown/parser/attribute.go
generated
vendored
Normal file
116
vendor/github.com/gomarkdown/markdown/parser/attribute.go
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/gomarkdown/markdown/ast"
|
||||
)
|
||||
|
||||
// attribute parses a (potential) block attribute and adds it to p.
|
||||
func (p *Parser) attribute(data []byte) []byte {
|
||||
if len(data) < 3 {
|
||||
return data
|
||||
}
|
||||
i := 0
|
||||
if data[i] != '{' {
|
||||
return data
|
||||
}
|
||||
i++
|
||||
|
||||
// last character must be a } otherwise it's not an attribute
|
||||
end := skipUntilChar(data, i, '\n')
|
||||
if data[end-1] != '}' {
|
||||
return data
|
||||
}
|
||||
|
||||
i = skipSpace(data, i)
|
||||
b := &ast.Attribute{Attrs: make(map[string][]byte)}
|
||||
|
||||
esc := false
|
||||
quote := false
|
||||
trail := 0
|
||||
Loop:
|
||||
for ; i < len(data); i++ {
|
||||
switch data[i] {
|
||||
case ' ', '\t', '\f', '\v':
|
||||
if quote {
|
||||
continue
|
||||
}
|
||||
chunk := data[trail+1 : i]
|
||||
if len(chunk) == 0 {
|
||||
trail = i
|
||||
continue
|
||||
}
|
||||
switch {
|
||||
case chunk[0] == '.':
|
||||
b.Classes = append(b.Classes, chunk[1:])
|
||||
case chunk[0] == '#':
|
||||
b.ID = chunk[1:]
|
||||
default:
|
||||
k, v := keyValue(chunk)
|
||||
if k != nil && v != nil {
|
||||
b.Attrs[string(k)] = v
|
||||
} else {
|
||||
// this is illegal in an attribute
|
||||
return data
|
||||
}
|
||||
}
|
||||
trail = i
|
||||
case '"':
|
||||
if esc {
|
||||
esc = !esc
|
||||
continue
|
||||
}
|
||||
quote = !quote
|
||||
case '\\':
|
||||
esc = !esc
|
||||
case '}':
|
||||
if esc {
|
||||
esc = !esc
|
||||
continue
|
||||
}
|
||||
chunk := data[trail+1 : i]
|
||||
if len(chunk) == 0 {
|
||||
return data
|
||||
}
|
||||
switch {
|
||||
case chunk[0] == '.':
|
||||
b.Classes = append(b.Classes, chunk[1:])
|
||||
case chunk[0] == '#':
|
||||
b.ID = chunk[1:]
|
||||
default:
|
||||
k, v := keyValue(chunk)
|
||||
if k != nil && v != nil {
|
||||
b.Attrs[string(k)] = v
|
||||
} else {
|
||||
return data
|
||||
}
|
||||
}
|
||||
i++
|
||||
break Loop
|
||||
default:
|
||||
esc = false
|
||||
}
|
||||
}
|
||||
|
||||
p.attr = b
|
||||
return data[i:]
|
||||
}
|
||||
|
||||
// key="value" quotes are mandatory.
|
||||
func keyValue(data []byte) ([]byte, []byte) {
|
||||
chunk := bytes.SplitN(data, []byte{'='}, 2)
|
||||
if len(chunk) != 2 {
|
||||
return nil, nil
|
||||
}
|
||||
key := chunk[0]
|
||||
value := chunk[1]
|
||||
|
||||
if len(value) < 3 || len(key) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
if value[0] != '"' || value[len(value)-1] != '"' {
|
||||
return key, nil
|
||||
}
|
||||
return key, value[1 : len(value)-1]
|
||||
}
|
1978
vendor/github.com/gomarkdown/markdown/parser/block.go
generated
vendored
Normal file
1978
vendor/github.com/gomarkdown/markdown/parser/block.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
29
vendor/github.com/gomarkdown/markdown/parser/callout.go
generated
vendored
Normal file
29
vendor/github.com/gomarkdown/markdown/parser/callout.go
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// IsCallout detects a callout in the following format: <<N>> Where N is a integer > 0.
|
||||
func IsCallout(data []byte) (id []byte, consumed int) {
|
||||
if !bytes.HasPrefix(data, []byte("<<")) {
|
||||
return nil, 0
|
||||
}
|
||||
start := 2
|
||||
end := bytes.Index(data[start:], []byte(">>"))
|
||||
if end < 0 {
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
b := data[start : start+end]
|
||||
b = bytes.TrimSpace(b)
|
||||
i, err := strconv.Atoi(string(b))
|
||||
if err != nil {
|
||||
return nil, 0
|
||||
}
|
||||
if i <= 0 {
|
||||
return nil, 0
|
||||
}
|
||||
return b, start + end + 2 // 2 for >>
|
||||
}
|
70
vendor/github.com/gomarkdown/markdown/parser/caption.go
generated
vendored
Normal file
70
vendor/github.com/gomarkdown/markdown/parser/caption.go
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
// caption checks for a caption, it returns the caption data and a potential "headingID".
|
||||
func (p *Parser) caption(data, caption []byte) ([]byte, string, int) {
|
||||
if !bytes.HasPrefix(data, caption) {
|
||||
return nil, "", 0
|
||||
}
|
||||
j := len(caption)
|
||||
data = data[j:]
|
||||
end := p.linesUntilEmpty(data)
|
||||
|
||||
data = data[:end]
|
||||
|
||||
id, start := captionID(data)
|
||||
if id != "" {
|
||||
return data[:start], id, end + j
|
||||
}
|
||||
|
||||
return data, "", end + j
|
||||
}
|
||||
|
||||
// linesUntilEmpty scans lines up to the first empty line.
|
||||
func (p *Parser) linesUntilEmpty(data []byte) int {
|
||||
line, i := 0, 0
|
||||
|
||||
for line < len(data) {
|
||||
i++
|
||||
|
||||
// find the end of this line
|
||||
for i < len(data) && data[i-1] != '\n' {
|
||||
i++
|
||||
}
|
||||
|
||||
if p.isEmpty(data[line:i]) == 0 {
|
||||
line = i
|
||||
continue
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
// captionID checks if the caption *ends* in {#....}. If so the text after {# is taken to be
|
||||
// the ID/anchor of the entire figure block.
|
||||
func captionID(data []byte) (string, int) {
|
||||
end := len(data)
|
||||
|
||||
j, k := 0, 0
|
||||
// find start/end of heading id
|
||||
for j = 0; j < end-1 && (data[j] != '{' || data[j+1] != '#'); j++ {
|
||||
}
|
||||
for k = j + 1; k < end && data[k] != '}'; k++ {
|
||||
}
|
||||
// remains must be whitespace.
|
||||
for l := k + 1; l < end; l++ {
|
||||
if !isSpace(data[l]) {
|
||||
return "", 0
|
||||
}
|
||||
}
|
||||
|
||||
if j > 0 && k > 0 && j+2 < k {
|
||||
return string(data[j+2 : k]), j
|
||||
}
|
||||
return "", 0
|
||||
}
|
86
vendor/github.com/gomarkdown/markdown/parser/citation.go
generated
vendored
Normal file
86
vendor/github.com/gomarkdown/markdown/parser/citation.go
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/gomarkdown/markdown/ast"
|
||||
)
|
||||
|
||||
// citation parses a citation. In its most simple form [@ref], we allow multiple
|
||||
// being separated by semicolons and a sub reference inside ala pandoc: [@ref, p. 23].
|
||||
// Each citation can have a modifier: !, ? or - wich mean:
|
||||
//
|
||||
// ! - normative
|
||||
// ? - formative
|
||||
// - - suppressed
|
||||
//
|
||||
// The suffix starts after a comma, we strip any whitespace before and after. If the output
|
||||
// allows for it, this can be rendered.
|
||||
func citation(p *Parser, data []byte, offset int) (int, ast.Node) {
|
||||
// look for the matching closing bracket
|
||||
i := offset + 1
|
||||
for level := 1; level > 0 && i < len(data); i++ {
|
||||
switch {
|
||||
case data[i] == '\n':
|
||||
// no newlines allowed.
|
||||
return 0, nil
|
||||
|
||||
case data[i-1] == '\\':
|
||||
continue
|
||||
|
||||
case data[i] == '[':
|
||||
level++
|
||||
|
||||
case data[i] == ']':
|
||||
level--
|
||||
if level <= 0 {
|
||||
i-- // compensate for extra i++ in for loop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if i >= len(data) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
node := &ast.Citation{}
|
||||
|
||||
citations := bytes.Split(data[1:i], []byte(";"))
|
||||
for _, citation := range citations {
|
||||
var suffix []byte
|
||||
citation = bytes.TrimSpace(citation)
|
||||
j := 0
|
||||
if citation[j] != '@' {
|
||||
// not a citation, drop out entirely.
|
||||
return 0, nil
|
||||
}
|
||||
if c := bytes.Index(citation, []byte(",")); c > 0 {
|
||||
part := citation[:c]
|
||||
suff := citation[c+1:]
|
||||
part = bytes.TrimSpace(part)
|
||||
suff = bytes.TrimSpace(suff)
|
||||
|
||||
citation = part
|
||||
suffix = suff
|
||||
}
|
||||
|
||||
citeType := ast.CitationTypeInformative
|
||||
j = 1
|
||||
switch citation[j] {
|
||||
case '!':
|
||||
citeType = ast.CitationTypeNormative
|
||||
j++
|
||||
case '?':
|
||||
citeType = ast.CitationTypeInformative
|
||||
j++
|
||||
case '-':
|
||||
citeType = ast.CitationTypeSuppressed
|
||||
j++
|
||||
}
|
||||
node.Destination = append(node.Destination, citation[j:])
|
||||
node.Type = append(node.Type, citeType)
|
||||
node.Suffix = append(node.Suffix, suffix)
|
||||
}
|
||||
|
||||
return i + 1, node
|
||||
}
|
20
vendor/github.com/gomarkdown/markdown/parser/esc.go
generated
vendored
Normal file
20
vendor/github.com/gomarkdown/markdown/parser/esc.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
package parser
|
||||
|
||||
// isEscape returns true if byte i is prefixed by an odd number of backslahses.
|
||||
func isEscape(data []byte, i int) bool {
|
||||
if i == 0 {
|
||||
return false
|
||||
}
|
||||
if i == 1 {
|
||||
return data[0] == '\\'
|
||||
}
|
||||
j := i - 1
|
||||
for ; j >= 0; j-- {
|
||||
if data[j] != '\\' {
|
||||
break
|
||||
}
|
||||
}
|
||||
j++
|
||||
// odd number of backslahes means escape
|
||||
return (i-j)%2 != 0
|
||||
}
|
119
vendor/github.com/gomarkdown/markdown/parser/figures.go
generated
vendored
Normal file
119
vendor/github.com/gomarkdown/markdown/parser/figures.go
generated
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/gomarkdown/markdown/ast"
|
||||
)
|
||||
|
||||
// sFigureLine checks if there's a figure line (e.g., !--- ) at the beginning of data,
|
||||
// and returns the end index if so, or 0 otherwise.
|
||||
func sFigureLine(data []byte, oldmarker string) (end int, marker string) {
|
||||
i, size := 0, 0
|
||||
|
||||
n := len(data)
|
||||
// skip up to three spaces
|
||||
for i < n && i < 3 && data[i] == ' ' {
|
||||
i++
|
||||
}
|
||||
|
||||
// check for the marker characters: !
|
||||
if i+1 >= n {
|
||||
return 0, ""
|
||||
}
|
||||
if data[i] != '!' || data[i+1] != '-' {
|
||||
return 0, ""
|
||||
}
|
||||
i++
|
||||
|
||||
c := data[i] // i.e. the -
|
||||
|
||||
// the whole line must be the same char or whitespace
|
||||
for i < n && data[i] == c {
|
||||
size++
|
||||
i++
|
||||
}
|
||||
|
||||
// the marker char must occur at least 3 times
|
||||
if size < 3 {
|
||||
return 0, ""
|
||||
}
|
||||
marker = string(data[i-size : i])
|
||||
|
||||
// if this is the end marker, it must match the beginning marker
|
||||
if oldmarker != "" && marker != oldmarker {
|
||||
return 0, ""
|
||||
}
|
||||
|
||||
// there is no syntax modifier although it might be an idea to re-use this space for something?
|
||||
|
||||
i = skipChar(data, i, ' ')
|
||||
if i >= n || data[i] != '\n' {
|
||||
if i == n {
|
||||
return i, marker
|
||||
}
|
||||
return 0, ""
|
||||
}
|
||||
return i + 1, marker // Take newline into account.
|
||||
}
|
||||
|
||||
// figureBlock returns the end index if data contains a figure block at the beginning,
|
||||
// or 0 otherwise. It writes to out if doRender is true, otherwise it has no side effects.
|
||||
// If doRender is true, a final newline is mandatory to recognize the figure block.
|
||||
func (p *Parser) figureBlock(data []byte, doRender bool) int {
|
||||
beg, marker := sFigureLine(data, "")
|
||||
if beg == 0 || beg >= len(data) {
|
||||
return 0
|
||||
}
|
||||
|
||||
var raw bytes.Buffer
|
||||
|
||||
for {
|
||||
// safe to assume beg < len(data)
|
||||
|
||||
// check for the end of the code block
|
||||
figEnd, _ := sFigureLine(data[beg:], marker)
|
||||
if figEnd != 0 {
|
||||
beg += figEnd
|
||||
break
|
||||
}
|
||||
|
||||
// copy the current line
|
||||
end := skipUntilChar(data, beg, '\n') + 1
|
||||
|
||||
// did we reach the end of the buffer without a closing marker?
|
||||
if end >= len(data) {
|
||||
return 0
|
||||
}
|
||||
|
||||
// verbatim copy to the working buffer
|
||||
if doRender {
|
||||
raw.Write(data[beg:end])
|
||||
}
|
||||
beg = end
|
||||
}
|
||||
|
||||
if !doRender {
|
||||
return beg
|
||||
}
|
||||
|
||||
figure := &ast.CaptionFigure{}
|
||||
p.addBlock(figure)
|
||||
p.block(raw.Bytes())
|
||||
|
||||
defer p.finalize(figure)
|
||||
|
||||
if captionContent, id, consumed := p.caption(data[beg:], []byte("Figure: ")); consumed > 0 {
|
||||
caption := &ast.Caption{}
|
||||
p.Inline(caption, captionContent)
|
||||
|
||||
figure.HeadingID = id
|
||||
|
||||
p.addChild(caption)
|
||||
|
||||
beg += consumed
|
||||
}
|
||||
|
||||
p.finalize(figure)
|
||||
return beg
|
||||
}
|
129
vendor/github.com/gomarkdown/markdown/parser/include.go
generated
vendored
Normal file
129
vendor/github.com/gomarkdown/markdown/parser/include.go
generated
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"path"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// isInclude parses {{...}}[...], that contains a path between the {{, the [...] syntax contains
|
||||
// an address to select which lines to include. It is treated as an opaque string and just given
|
||||
// to readInclude.
|
||||
func (p *Parser) isInclude(data []byte) (filename string, address []byte, consumed int) {
|
||||
i := skipCharN(data, 0, ' ', 3) // start with up to 3 spaces
|
||||
if len(data[i:]) < 3 {
|
||||
return "", nil, 0
|
||||
}
|
||||
if data[i] != '{' || data[i+1] != '{' {
|
||||
return "", nil, 0
|
||||
}
|
||||
start := i + 2
|
||||
|
||||
// find the end delimiter
|
||||
i = skipUntilChar(data, i, '}')
|
||||
if i+1 >= len(data) {
|
||||
return "", nil, 0
|
||||
}
|
||||
end := i
|
||||
i++
|
||||
if data[i] != '}' {
|
||||
return "", nil, 0
|
||||
}
|
||||
filename = string(data[start:end])
|
||||
|
||||
if i+1 < len(data) && data[i+1] == '[' { // potential address specification
|
||||
start := i + 2
|
||||
|
||||
end = skipUntilChar(data, start, ']')
|
||||
if end >= len(data) {
|
||||
return "", nil, 0
|
||||
}
|
||||
address = data[start:end]
|
||||
return filename, address, end + 1
|
||||
}
|
||||
|
||||
return filename, address, i + 1
|
||||
}
|
||||
|
||||
func (p *Parser) readInclude(from, file string, address []byte) []byte {
|
||||
if p.Opts.ReadIncludeFn != nil {
|
||||
return p.Opts.ReadIncludeFn(from, file, address)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// isCodeInclude parses <{{...}} which is similar to isInclude the returned bytes are, however wrapped in a code block.
|
||||
func (p *Parser) isCodeInclude(data []byte) (filename string, address []byte, consumed int) {
|
||||
i := skipCharN(data, 0, ' ', 3) // start with up to 3 spaces
|
||||
if len(data[i:]) < 3 {
|
||||
return "", nil, 0
|
||||
}
|
||||
if data[i] != '<' {
|
||||
return "", nil, 0
|
||||
}
|
||||
start := i
|
||||
|
||||
filename, address, consumed = p.isInclude(data[i+1:])
|
||||
if consumed == 0 {
|
||||
return "", nil, 0
|
||||
}
|
||||
return filename, address, start + consumed + 1
|
||||
}
|
||||
|
||||
// readCodeInclude acts like include except the returned bytes are wrapped in a fenced code block.
|
||||
func (p *Parser) readCodeInclude(from, file string, address []byte) []byte {
|
||||
data := p.readInclude(from, file, address)
|
||||
if data == nil {
|
||||
return nil
|
||||
}
|
||||
ext := path.Ext(file)
|
||||
buf := &bytes.Buffer{}
|
||||
buf.Write([]byte("```"))
|
||||
if ext != "" { // starts with a dot
|
||||
buf.WriteString(" " + ext[1:] + "\n")
|
||||
} else {
|
||||
buf.WriteByte('\n')
|
||||
}
|
||||
buf.Write(data)
|
||||
buf.WriteString("```\n")
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// incStack hold the current stack of chained includes. Each value is the containing
|
||||
// path of the file being parsed.
|
||||
type incStack struct {
|
||||
stack []string
|
||||
}
|
||||
|
||||
func newIncStack() *incStack {
|
||||
return &incStack{stack: []string{}}
|
||||
}
|
||||
|
||||
// Push updates i with new.
|
||||
func (i *incStack) Push(new string) {
|
||||
if path.IsAbs(new) {
|
||||
i.stack = append(i.stack, path.Dir(new))
|
||||
return
|
||||
}
|
||||
last := ""
|
||||
if len(i.stack) > 0 {
|
||||
last = i.stack[len(i.stack)-1]
|
||||
}
|
||||
i.stack = append(i.stack, path.Dir(filepath.Join(last, new)))
|
||||
}
|
||||
|
||||
// Pop pops the last value.
|
||||
func (i *incStack) Pop() {
|
||||
if len(i.stack) == 0 {
|
||||
return
|
||||
}
|
||||
i.stack = i.stack[:len(i.stack)-1]
|
||||
}
|
||||
|
||||
func (i *incStack) Last() string {
|
||||
if len(i.stack) == 0 {
|
||||
return ""
|
||||
}
|
||||
return i.stack[len(i.stack)-1]
|
||||
}
|
1284
vendor/github.com/gomarkdown/markdown/parser/inline.go
generated
vendored
Normal file
1284
vendor/github.com/gomarkdown/markdown/parser/inline.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
36
vendor/github.com/gomarkdown/markdown/parser/matter.go
generated
vendored
Normal file
36
vendor/github.com/gomarkdown/markdown/parser/matter.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/gomarkdown/markdown/ast"
|
||||
)
|
||||
|
||||
func (p *Parser) documentMatter(data []byte) int {
|
||||
if data[0] != '{' {
|
||||
return 0
|
||||
}
|
||||
|
||||
consumed := 0
|
||||
matter := ast.DocumentMatterNone
|
||||
if bytes.HasPrefix(data, []byte("{frontmatter}")) {
|
||||
consumed = len("{frontmatter}")
|
||||
matter = ast.DocumentMatterFront
|
||||
}
|
||||
if bytes.HasPrefix(data, []byte("{mainmatter}")) {
|
||||
consumed = len("{mainmatter}")
|
||||
matter = ast.DocumentMatterMain
|
||||
}
|
||||
if bytes.HasPrefix(data, []byte("{backmatter}")) {
|
||||
consumed = len("{backmatter}")
|
||||
matter = ast.DocumentMatterBack
|
||||
}
|
||||
if consumed == 0 {
|
||||
return 0
|
||||
}
|
||||
node := &ast.DocumentMatter{Matter: matter}
|
||||
p.addBlock(node)
|
||||
p.finalize(node)
|
||||
|
||||
return consumed
|
||||
}
|
32
vendor/github.com/gomarkdown/markdown/parser/options.go
generated
vendored
Normal file
32
vendor/github.com/gomarkdown/markdown/parser/options.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"github.com/gomarkdown/markdown/ast"
|
||||
)
|
||||
|
||||
// Flags control optional behavior of parser.
|
||||
type Flags int
|
||||
|
||||
// Options is a collection of supplementary parameters tweaking the behavior of various parts of the parser.
|
||||
type Options struct {
|
||||
ParserHook BlockFunc
|
||||
ReadIncludeFn ReadIncludeFunc
|
||||
|
||||
Flags Flags // Flags allow customizing parser's behavior
|
||||
}
|
||||
|
||||
// Parser renderer configuration options.
|
||||
const (
|
||||
FlagsNone Flags = 0
|
||||
SkipFootnoteList Flags = 1 << iota // Skip adding the footnote list (regardless if they are parsed)
|
||||
)
|
||||
|
||||
// BlockFunc allows to registration of a parser function. If successful it
|
||||
// returns an ast.Node, a buffer that should be parsed as a block and the the number of bytes consumed.
|
||||
type BlockFunc func(data []byte) (ast.Node, []byte, int)
|
||||
|
||||
// ReadIncludeFunc should read the file under path and returns the read bytes,
|
||||
// from will be set to the name of the current file being parsed. Initially
|
||||
// this will be empty. address is the optional address specifier of which lines
|
||||
// of the file to return. If this function is not set no data will be read.
|
||||
type ReadIncludeFunc func(from, path string, address []byte) []byte
|
812
vendor/github.com/gomarkdown/markdown/parser/parser.go
generated
vendored
Normal file
812
vendor/github.com/gomarkdown/markdown/parser/parser.go
generated
vendored
Normal file
@@ -0,0 +1,812 @@
|
||||
/*
|
||||
Package parser implements parser for markdown text that generates AST (abstract syntax tree).
|
||||
*/
|
||||
package parser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/gomarkdown/markdown/ast"
|
||||
)
|
||||
|
||||
// Extensions is a bitmask of enabled parser extensions.
|
||||
type Extensions int
|
||||
|
||||
// Bit flags representing markdown parsing extensions.
|
||||
// Use | (or) to specify multiple extensions.
|
||||
const (
|
||||
NoExtensions Extensions = 0
|
||||
NoIntraEmphasis Extensions = 1 << iota // Ignore emphasis markers inside words
|
||||
Tables // Parse tables
|
||||
FencedCode // Parse fenced code blocks
|
||||
Autolink // Detect embedded URLs that are not explicitly marked
|
||||
Strikethrough // Strikethrough text using ~~test~~
|
||||
LaxHTMLBlocks // Loosen up HTML block parsing rules
|
||||
SpaceHeadings // Be strict about prefix heading rules
|
||||
HardLineBreak // Translate newlines into line breaks
|
||||
NonBlockingSpace // Translate backspace spaces into line non-blocking spaces
|
||||
TabSizeEight // Expand tabs to eight spaces instead of four
|
||||
Footnotes // Pandoc-style footnotes
|
||||
NoEmptyLineBeforeBlock // No need to insert an empty line to start a (code, quote, ordered list, unordered list) block
|
||||
HeadingIDs // specify heading IDs with {#id}
|
||||
Titleblock // Titleblock ala pandoc
|
||||
AutoHeadingIDs // Create the heading ID from the text
|
||||
BackslashLineBreak // Translate trailing backslashes into line breaks
|
||||
DefinitionLists // Parse definition lists
|
||||
MathJax // Parse MathJax
|
||||
OrderedListStart // Keep track of the first number used when starting an ordered list.
|
||||
Attributes // Block Attributes
|
||||
SuperSubscript // Super- and subscript support: 2^10^, H~2~O.
|
||||
EmptyLinesBreakList // 2 empty lines break out of list
|
||||
Includes // Support including other files.
|
||||
Mmark // Support Mmark syntax, see https://mmark.nl/syntax
|
||||
|
||||
CommonExtensions Extensions = NoIntraEmphasis | Tables | FencedCode |
|
||||
Autolink | Strikethrough | SpaceHeadings | HeadingIDs |
|
||||
BackslashLineBreak | DefinitionLists | MathJax
|
||||
)
|
||||
|
||||
// The size of a tab stop.
|
||||
const (
|
||||
tabSizeDefault = 4
|
||||
tabSizeDouble = 8
|
||||
)
|
||||
|
||||
// for each character that triggers a response when parsing inline data.
|
||||
type inlineParser func(p *Parser, data []byte, offset int) (int, ast.Node)
|
||||
|
||||
// ReferenceOverrideFunc is expected to be called with a reference string and
|
||||
// return either a valid Reference type that the reference string maps to or
|
||||
// nil. If overridden is false, the default reference logic will be executed.
|
||||
// See the documentation in Options for more details on use-case.
|
||||
type ReferenceOverrideFunc func(reference string) (ref *Reference, overridden bool)
|
||||
|
||||
// Parser is a type that holds extensions and the runtime state used by
|
||||
// Parse, and the renderer. You can not use it directly, construct it with New.
|
||||
type Parser struct {
|
||||
|
||||
// ReferenceOverride is an optional function callback that is called every
|
||||
// time a reference is resolved. It can be set before starting parsing.
|
||||
//
|
||||
// In Markdown, the link reference syntax can be made to resolve a link to
|
||||
// a reference instead of an inline URL, in one of the following ways:
|
||||
//
|
||||
// * [link text][refid]
|
||||
// * [refid][]
|
||||
//
|
||||
// Usually, the refid is defined at the bottom of the Markdown document. If
|
||||
// this override function is provided, the refid is passed to the override
|
||||
// function first, before consulting the defined refids at the bottom. If
|
||||
// the override function indicates an override did not occur, the refids at
|
||||
// the bottom will be used to fill in the link details.
|
||||
ReferenceOverride ReferenceOverrideFunc
|
||||
|
||||
Opts Options
|
||||
|
||||
// after parsing, this is AST root of parsed markdown text
|
||||
Doc ast.Node
|
||||
|
||||
extensions Extensions
|
||||
|
||||
refs map[string]*reference
|
||||
refsRecord map[string]struct{}
|
||||
inlineCallback [256]inlineParser
|
||||
nesting int
|
||||
maxNesting int
|
||||
insideLink bool
|
||||
indexCnt int // incremented after every index
|
||||
|
||||
// Footnotes need to be ordered as well as available to quickly check for
|
||||
// presence. If a ref is also a footnote, it's stored both in refs and here
|
||||
// in notes. Slice is nil if footnotes not enabled.
|
||||
notes []*reference
|
||||
|
||||
tip ast.Node // = doc
|
||||
oldTip ast.Node
|
||||
lastMatchedContainer ast.Node // = doc
|
||||
allClosed bool
|
||||
|
||||
// Attributes are attached to block level elements.
|
||||
attr *ast.Attribute
|
||||
|
||||
includeStack *incStack
|
||||
}
|
||||
|
||||
// New creates a markdown parser with CommonExtensions.
|
||||
//
|
||||
// You can then call `doc := p.Parse(markdown)` to parse markdown document
|
||||
// and `markdown.Render(doc, renderer)` to convert it to another format with
|
||||
// a renderer.
|
||||
func New() *Parser {
|
||||
return NewWithExtensions(CommonExtensions)
|
||||
}
|
||||
|
||||
// NewWithExtensions creates a markdown parser with given extensions.
|
||||
func NewWithExtensions(extension Extensions) *Parser {
|
||||
p := Parser{
|
||||
refs: make(map[string]*reference),
|
||||
refsRecord: make(map[string]struct{}),
|
||||
maxNesting: 16,
|
||||
insideLink: false,
|
||||
Doc: &ast.Document{},
|
||||
extensions: extension,
|
||||
allClosed: true,
|
||||
includeStack: newIncStack(),
|
||||
}
|
||||
p.tip = p.Doc
|
||||
p.oldTip = p.Doc
|
||||
p.lastMatchedContainer = p.Doc
|
||||
|
||||
p.inlineCallback[' '] = maybeLineBreak
|
||||
p.inlineCallback['*'] = emphasis
|
||||
p.inlineCallback['_'] = emphasis
|
||||
if p.extensions&Strikethrough != 0 {
|
||||
p.inlineCallback['~'] = emphasis
|
||||
}
|
||||
p.inlineCallback['`'] = codeSpan
|
||||
p.inlineCallback['\n'] = lineBreak
|
||||
p.inlineCallback['['] = link
|
||||
p.inlineCallback['<'] = leftAngle
|
||||
p.inlineCallback['\\'] = escape
|
||||
p.inlineCallback['&'] = entity
|
||||
p.inlineCallback['!'] = maybeImage
|
||||
if p.extensions&Mmark != 0 {
|
||||
p.inlineCallback['('] = maybeShortRefOrIndex
|
||||
}
|
||||
p.inlineCallback['^'] = maybeInlineFootnoteOrSuper
|
||||
if p.extensions&Autolink != 0 {
|
||||
p.inlineCallback['h'] = maybeAutoLink
|
||||
p.inlineCallback['m'] = maybeAutoLink
|
||||
p.inlineCallback['f'] = maybeAutoLink
|
||||
p.inlineCallback['H'] = maybeAutoLink
|
||||
p.inlineCallback['M'] = maybeAutoLink
|
||||
p.inlineCallback['F'] = maybeAutoLink
|
||||
}
|
||||
if p.extensions&MathJax != 0 {
|
||||
p.inlineCallback['$'] = math
|
||||
}
|
||||
|
||||
return &p
|
||||
}
|
||||
|
||||
func (p *Parser) getRef(refid string) (ref *reference, found bool) {
|
||||
if p.ReferenceOverride != nil {
|
||||
r, overridden := p.ReferenceOverride(refid)
|
||||
if overridden {
|
||||
if r == nil {
|
||||
return nil, false
|
||||
}
|
||||
return &reference{
|
||||
link: []byte(r.Link),
|
||||
title: []byte(r.Title),
|
||||
noteID: 0,
|
||||
hasBlock: false,
|
||||
text: []byte(r.Text)}, true
|
||||
}
|
||||
}
|
||||
// refs are case insensitive
|
||||
ref, found = p.refs[strings.ToLower(refid)]
|
||||
return ref, found
|
||||
}
|
||||
|
||||
func (p *Parser) isFootnote(ref *reference) bool {
|
||||
_, ok := p.refsRecord[string(ref.link)]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (p *Parser) finalize(block ast.Node) {
|
||||
p.tip = block.GetParent()
|
||||
}
|
||||
|
||||
func (p *Parser) addChild(node ast.Node) ast.Node {
|
||||
for !canNodeContain(p.tip, node) {
|
||||
p.finalize(p.tip)
|
||||
}
|
||||
ast.AppendChild(p.tip, node)
|
||||
p.tip = node
|
||||
return node
|
||||
}
|
||||
|
||||
func canNodeContain(n ast.Node, v ast.Node) bool {
|
||||
switch n.(type) {
|
||||
case *ast.List:
|
||||
return isListItem(v)
|
||||
case *ast.Document, *ast.BlockQuote, *ast.Aside, *ast.ListItem, *ast.CaptionFigure:
|
||||
return !isListItem(v)
|
||||
case *ast.Table:
|
||||
switch v.(type) {
|
||||
case *ast.TableHeader, *ast.TableBody, *ast.TableFooter:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
case *ast.TableHeader, *ast.TableBody, *ast.TableFooter:
|
||||
_, ok := v.(*ast.TableRow)
|
||||
return ok
|
||||
case *ast.TableRow:
|
||||
_, ok := v.(*ast.TableCell)
|
||||
return ok
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *Parser) closeUnmatchedBlocks() {
|
||||
if p.allClosed {
|
||||
return
|
||||
}
|
||||
for p.oldTip != p.lastMatchedContainer {
|
||||
parent := p.oldTip.GetParent()
|
||||
p.finalize(p.oldTip)
|
||||
p.oldTip = parent
|
||||
}
|
||||
p.allClosed = true
|
||||
}
|
||||
|
||||
// Reference represents the details of a link.
|
||||
// See the documentation in Options for more details on use-case.
|
||||
type Reference struct {
|
||||
// Link is usually the URL the reference points to.
|
||||
Link string
|
||||
// Title is the alternate text describing the link in more detail.
|
||||
Title string
|
||||
// Text is the optional text to override the ref with if the syntax used was
|
||||
// [refid][]
|
||||
Text string
|
||||
}
|
||||
|
||||
// Parse generates AST (abstract syntax tree) representing markdown document.
|
||||
//
|
||||
// The result is a root of the tree whose underlying type is *ast.Document
|
||||
//
|
||||
// You can then convert AST to html using html.Renderer, to some other format
|
||||
// using a custom renderer or transform the tree.
|
||||
func (p *Parser) Parse(input []byte) ast.Node {
|
||||
p.block(input)
|
||||
// Walk the tree and finish up some of unfinished blocks
|
||||
for p.tip != nil {
|
||||
p.finalize(p.tip)
|
||||
}
|
||||
// Walk the tree again and process inline markdown in each block
|
||||
ast.WalkFunc(p.Doc, func(node ast.Node, entering bool) ast.WalkStatus {
|
||||
switch node.(type) {
|
||||
case *ast.Paragraph, *ast.Heading, *ast.TableCell:
|
||||
p.Inline(node, node.AsContainer().Content)
|
||||
node.AsContainer().Content = nil
|
||||
}
|
||||
return ast.GoToNext
|
||||
})
|
||||
|
||||
if p.Opts.Flags&SkipFootnoteList == 0 {
|
||||
p.parseRefsToAST()
|
||||
}
|
||||
return p.Doc
|
||||
}
|
||||
|
||||
func (p *Parser) parseRefsToAST() {
|
||||
if p.extensions&Footnotes == 0 || len(p.notes) == 0 {
|
||||
return
|
||||
}
|
||||
p.tip = p.Doc
|
||||
list := &ast.List{
|
||||
IsFootnotesList: true,
|
||||
ListFlags: ast.ListTypeOrdered,
|
||||
}
|
||||
p.addBlock(&ast.Footnotes{})
|
||||
block := p.addBlock(list)
|
||||
flags := ast.ListItemBeginningOfList
|
||||
// Note: this loop is intentionally explicit, not range-form. This is
|
||||
// because the body of the loop will append nested footnotes to p.notes and
|
||||
// we need to process those late additions. Range form would only walk over
|
||||
// the fixed initial set.
|
||||
for i := 0; i < len(p.notes); i++ {
|
||||
ref := p.notes[i]
|
||||
p.addChild(ref.footnote)
|
||||
block := ref.footnote
|
||||
listItem := block.(*ast.ListItem)
|
||||
listItem.ListFlags = flags | ast.ListTypeOrdered
|
||||
listItem.RefLink = ref.link
|
||||
if ref.hasBlock {
|
||||
flags |= ast.ListItemContainsBlock
|
||||
p.block(ref.title)
|
||||
} else {
|
||||
p.Inline(block, ref.title)
|
||||
}
|
||||
flags &^= ast.ListItemBeginningOfList | ast.ListItemContainsBlock
|
||||
}
|
||||
above := list.Parent
|
||||
finalizeList(list)
|
||||
p.tip = above
|
||||
|
||||
ast.WalkFunc(block, func(node ast.Node, entering bool) ast.WalkStatus {
|
||||
switch node.(type) {
|
||||
case *ast.Paragraph, *ast.Heading:
|
||||
p.Inline(node, node.AsContainer().Content)
|
||||
node.AsContainer().Content = nil
|
||||
}
|
||||
return ast.GoToNext
|
||||
})
|
||||
}
|
||||
|
||||
//
|
||||
// Link references
|
||||
//
|
||||
// This section implements support for references that (usually) appear
|
||||
// as footnotes in a document, and can be referenced anywhere in the document.
|
||||
// The basic format is:
|
||||
//
|
||||
// [1]: http://www.google.com/ "Google"
|
||||
// [2]: http://www.github.com/ "Github"
|
||||
//
|
||||
// Anywhere in the document, the reference can be linked by referring to its
|
||||
// label, i.e., 1 and 2 in this example, as in:
|
||||
//
|
||||
// This library is hosted on [Github][2], a git hosting site.
|
||||
//
|
||||
// Actual footnotes as specified in Pandoc and supported by some other Markdown
|
||||
// libraries such as php-markdown are also taken care of. They look like this:
|
||||
//
|
||||
// This sentence needs a bit of further explanation.[^note]
|
||||
//
|
||||
// [^note]: This is the explanation.
|
||||
//
|
||||
// Footnotes should be placed at the end of the document in an ordered list.
|
||||
// Inline footnotes such as:
|
||||
//
|
||||
// Inline footnotes^[Not supported.] also exist.
|
||||
//
|
||||
// are not yet supported.
|
||||
|
||||
// reference holds all information necessary for a reference-style links or
|
||||
// footnotes.
|
||||
//
|
||||
// Consider this markdown with reference-style links:
|
||||
//
|
||||
// [link][ref]
|
||||
//
|
||||
// [ref]: /url/ "tooltip title"
|
||||
//
|
||||
// It will be ultimately converted to this HTML:
|
||||
//
|
||||
// <p><a href=\"/url/\" title=\"title\">link</a></p>
|
||||
//
|
||||
// And a reference structure will be populated as follows:
|
||||
//
|
||||
// p.refs["ref"] = &reference{
|
||||
// link: "/url/",
|
||||
// title: "tooltip title",
|
||||
// }
|
||||
//
|
||||
// Alternatively, reference can contain information about a footnote. Consider
|
||||
// this markdown:
|
||||
//
|
||||
// Text needing a footnote.[^a]
|
||||
//
|
||||
// [^a]: This is the note
|
||||
//
|
||||
// A reference structure will be populated as follows:
|
||||
//
|
||||
// p.refs["a"] = &reference{
|
||||
// link: "a",
|
||||
// title: "This is the note",
|
||||
// noteID: <some positive int>,
|
||||
// }
|
||||
//
|
||||
// TODO: As you can see, it begs for splitting into two dedicated structures
|
||||
// for refs and for footnotes.
|
||||
type reference struct {
|
||||
link []byte
|
||||
title []byte
|
||||
noteID int // 0 if not a footnote ref
|
||||
hasBlock bool
|
||||
footnote ast.Node // a link to the Item node within a list of footnotes
|
||||
|
||||
text []byte // only gets populated by refOverride feature with Reference.Text
|
||||
}
|
||||
|
||||
func (r *reference) String() string {
|
||||
return fmt.Sprintf("{link: %q, title: %q, text: %q, noteID: %d, hasBlock: %v}",
|
||||
r.link, r.title, r.text, r.noteID, r.hasBlock)
|
||||
}
|
||||
|
||||
// Check whether or not data starts with a reference link.
|
||||
// If so, it is parsed and stored in the list of references
|
||||
// (in the render struct).
|
||||
// Returns the number of bytes to skip to move past it,
|
||||
// or zero if the first line is not a reference.
|
||||
func isReference(p *Parser, data []byte, tabSize int) int {
|
||||
// up to 3 optional leading spaces
|
||||
if len(data) < 4 {
|
||||
return 0
|
||||
}
|
||||
i := 0
|
||||
for i < 3 && data[i] == ' ' {
|
||||
i++
|
||||
}
|
||||
|
||||
noteID := 0
|
||||
|
||||
// id part: anything but a newline between brackets
|
||||
if data[i] != '[' {
|
||||
return 0
|
||||
}
|
||||
i++
|
||||
if p.extensions&Footnotes != 0 {
|
||||
if i < len(data) && data[i] == '^' {
|
||||
// we can set it to anything here because the proper noteIds will
|
||||
// be assigned later during the second pass. It just has to be != 0
|
||||
noteID = 1
|
||||
i++
|
||||
}
|
||||
}
|
||||
idOffset := i
|
||||
for i < len(data) && data[i] != '\n' && data[i] != '\r' && data[i] != ']' {
|
||||
i++
|
||||
}
|
||||
if i >= len(data) || data[i] != ']' {
|
||||
return 0
|
||||
}
|
||||
idEnd := i
|
||||
// footnotes can have empty ID, like this: [^], but a reference can not be
|
||||
// empty like this: []. Break early if it's not a footnote and there's no ID
|
||||
if noteID == 0 && idOffset == idEnd {
|
||||
return 0
|
||||
}
|
||||
// spacer: colon (space | tab)* newline? (space | tab)*
|
||||
i++
|
||||
if i >= len(data) || data[i] != ':' {
|
||||
return 0
|
||||
}
|
||||
i++
|
||||
for i < len(data) && (data[i] == ' ' || data[i] == '\t') {
|
||||
i++
|
||||
}
|
||||
if i < len(data) && (data[i] == '\n' || data[i] == '\r') {
|
||||
i++
|
||||
if i < len(data) && data[i] == '\n' && data[i-1] == '\r' {
|
||||
i++
|
||||
}
|
||||
}
|
||||
for i < len(data) && (data[i] == ' ' || data[i] == '\t') {
|
||||
i++
|
||||
}
|
||||
if i >= len(data) {
|
||||
return 0
|
||||
}
|
||||
|
||||
var (
|
||||
linkOffset, linkEnd int
|
||||
titleOffset, titleEnd int
|
||||
lineEnd int
|
||||
raw []byte
|
||||
hasBlock bool
|
||||
)
|
||||
|
||||
if p.extensions&Footnotes != 0 && noteID != 0 {
|
||||
linkOffset, linkEnd, raw, hasBlock = scanFootnote(p, data, i, tabSize)
|
||||
lineEnd = linkEnd
|
||||
} else {
|
||||
linkOffset, linkEnd, titleOffset, titleEnd, lineEnd = scanLinkRef(p, data, i)
|
||||
}
|
||||
if lineEnd == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// a valid ref has been found
|
||||
|
||||
ref := &reference{
|
||||
noteID: noteID,
|
||||
hasBlock: hasBlock,
|
||||
}
|
||||
|
||||
if noteID > 0 {
|
||||
// reusing the link field for the id since footnotes don't have links
|
||||
ref.link = data[idOffset:idEnd]
|
||||
// if footnote, it's not really a title, it's the contained text
|
||||
ref.title = raw
|
||||
} else {
|
||||
ref.link = data[linkOffset:linkEnd]
|
||||
ref.title = data[titleOffset:titleEnd]
|
||||
}
|
||||
|
||||
// id matches are case-insensitive
|
||||
id := string(bytes.ToLower(data[idOffset:idEnd]))
|
||||
|
||||
p.refs[id] = ref
|
||||
|
||||
return lineEnd
|
||||
}
|
||||
|
||||
func scanLinkRef(p *Parser, data []byte, i int) (linkOffset, linkEnd, titleOffset, titleEnd, lineEnd int) {
|
||||
// link: whitespace-free sequence, optionally between angle brackets
|
||||
if data[i] == '<' {
|
||||
i++
|
||||
}
|
||||
linkOffset = i
|
||||
for i < len(data) && data[i] != ' ' && data[i] != '\t' && data[i] != '\n' && data[i] != '\r' {
|
||||
i++
|
||||
}
|
||||
linkEnd = i
|
||||
if linkEnd < len(data) && data[linkOffset] == '<' && data[linkEnd-1] == '>' {
|
||||
linkOffset++
|
||||
linkEnd--
|
||||
}
|
||||
|
||||
// optional spacer: (space | tab)* (newline | '\'' | '"' | '(' )
|
||||
for i < len(data) && (data[i] == ' ' || data[i] == '\t') {
|
||||
i++
|
||||
}
|
||||
if i < len(data) && data[i] != '\n' && data[i] != '\r' && data[i] != '\'' && data[i] != '"' && data[i] != '(' {
|
||||
return
|
||||
}
|
||||
|
||||
// compute end-of-line
|
||||
if i >= len(data) || data[i] == '\r' || data[i] == '\n' {
|
||||
lineEnd = i
|
||||
}
|
||||
if i+1 < len(data) && data[i] == '\r' && data[i+1] == '\n' {
|
||||
lineEnd++
|
||||
}
|
||||
|
||||
// optional (space|tab)* spacer after a newline
|
||||
if lineEnd > 0 {
|
||||
i = lineEnd + 1
|
||||
for i < len(data) && (data[i] == ' ' || data[i] == '\t') {
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// optional title: any non-newline sequence enclosed in '"() alone on its line
|
||||
if i+1 < len(data) && (data[i] == '\'' || data[i] == '"' || data[i] == '(') {
|
||||
i++
|
||||
titleOffset = i
|
||||
|
||||
// look for EOL
|
||||
for i < len(data) && data[i] != '\n' && data[i] != '\r' {
|
||||
i++
|
||||
}
|
||||
if i+1 < len(data) && data[i] == '\n' && data[i+1] == '\r' {
|
||||
titleEnd = i + 1
|
||||
} else {
|
||||
titleEnd = i
|
||||
}
|
||||
|
||||
// step back
|
||||
i--
|
||||
for i > titleOffset && (data[i] == ' ' || data[i] == '\t') {
|
||||
i--
|
||||
}
|
||||
if i > titleOffset && (data[i] == '\'' || data[i] == '"' || data[i] == ')') {
|
||||
lineEnd = titleEnd
|
||||
titleEnd = i
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// The first bit of this logic is the same as Parser.listItem, but the rest
|
||||
// is much simpler. This function simply finds the entire block and shifts it
|
||||
// over by one tab if it is indeed a block (just returns the line if it's not).
|
||||
// blockEnd is the end of the section in the input buffer, and contents is the
|
||||
// extracted text that was shifted over one tab. It will need to be rendered at
|
||||
// the end of the document.
|
||||
func scanFootnote(p *Parser, data []byte, i, indentSize int) (blockStart, blockEnd int, contents []byte, hasBlock bool) {
|
||||
if i == 0 || len(data) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// skip leading whitespace on first line
|
||||
for i < len(data) && data[i] == ' ' {
|
||||
i++
|
||||
}
|
||||
|
||||
blockStart = i
|
||||
|
||||
// find the end of the line
|
||||
blockEnd = i
|
||||
for i < len(data) && data[i-1] != '\n' {
|
||||
i++
|
||||
}
|
||||
|
||||
// get working buffer
|
||||
var raw bytes.Buffer
|
||||
|
||||
// put the first line into the working buffer
|
||||
raw.Write(data[blockEnd:i])
|
||||
blockEnd = i
|
||||
|
||||
// process the following lines
|
||||
containsBlankLine := false
|
||||
|
||||
gatherLines:
|
||||
for blockEnd < len(data) {
|
||||
i++
|
||||
|
||||
// find the end of this line
|
||||
for i < len(data) && data[i-1] != '\n' {
|
||||
i++
|
||||
}
|
||||
|
||||
// if it is an empty line, guess that it is part of this item
|
||||
// and move on to the next line
|
||||
if p.isEmpty(data[blockEnd:i]) > 0 {
|
||||
containsBlankLine = true
|
||||
blockEnd = i
|
||||
continue
|
||||
}
|
||||
|
||||
n := 0
|
||||
if n = isIndented(data[blockEnd:i], indentSize); n == 0 {
|
||||
// this is the end of the block.
|
||||
// we don't want to include this last line in the index.
|
||||
break gatherLines
|
||||
}
|
||||
|
||||
// if there were blank lines before this one, insert a new one now
|
||||
if containsBlankLine {
|
||||
raw.WriteByte('\n')
|
||||
containsBlankLine = false
|
||||
}
|
||||
|
||||
// get rid of that first tab, write to buffer
|
||||
raw.Write(data[blockEnd+n : i])
|
||||
hasBlock = true
|
||||
|
||||
blockEnd = i
|
||||
}
|
||||
|
||||
if data[blockEnd-1] != '\n' {
|
||||
raw.WriteByte('\n')
|
||||
}
|
||||
|
||||
contents = raw.Bytes()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// isPunctuation returns true if c is a punctuation symbol.
|
||||
func isPunctuation(c byte) bool {
|
||||
for _, r := range []byte("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~") {
|
||||
if c == r {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// isSpace returns true if c is a white-space charactr
|
||||
func isSpace(c byte) bool {
|
||||
return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v'
|
||||
}
|
||||
|
||||
// isLetter returns true if c is ascii letter
|
||||
func isLetter(c byte) bool {
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
|
||||
}
|
||||
|
||||
// isAlnum returns true if c is a digit or letter
|
||||
// TODO: check when this is looking for ASCII alnum and when it should use unicode
|
||||
func isAlnum(c byte) bool {
|
||||
return (c >= '0' && c <= '9') || isLetter(c)
|
||||
}
|
||||
|
||||
// TODO: this is not used
|
||||
// Replace tab characters with spaces, aligning to the next TAB_SIZE column.
|
||||
// always ends output with a newline
|
||||
func expandTabs(out *bytes.Buffer, line []byte, tabSize int) {
|
||||
// first, check for common cases: no tabs, or only tabs at beginning of line
|
||||
i, prefix := 0, 0
|
||||
slowcase := false
|
||||
for i = 0; i < len(line); i++ {
|
||||
if line[i] == '\t' {
|
||||
if prefix == i {
|
||||
prefix++
|
||||
} else {
|
||||
slowcase = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// no need to decode runes if all tabs are at the beginning of the line
|
||||
if !slowcase {
|
||||
for i = 0; i < prefix*tabSize; i++ {
|
||||
out.WriteByte(' ')
|
||||
}
|
||||
out.Write(line[prefix:])
|
||||
return
|
||||
}
|
||||
|
||||
// the slow case: we need to count runes to figure out how
|
||||
// many spaces to insert for each tab
|
||||
column := 0
|
||||
i = 0
|
||||
for i < len(line) {
|
||||
start := i
|
||||
for i < len(line) && line[i] != '\t' {
|
||||
_, size := utf8.DecodeRune(line[i:])
|
||||
i += size
|
||||
column++
|
||||
}
|
||||
|
||||
if i > start {
|
||||
out.Write(line[start:i])
|
||||
}
|
||||
|
||||
if i >= len(line) {
|
||||
break
|
||||
}
|
||||
|
||||
for {
|
||||
out.WriteByte(' ')
|
||||
column++
|
||||
if column%tabSize == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// Find if a line counts as indented or not.
|
||||
// Returns number of characters the indent is (0 = not indented).
|
||||
func isIndented(data []byte, indentSize int) int {
|
||||
if len(data) == 0 {
|
||||
return 0
|
||||
}
|
||||
if data[0] == '\t' {
|
||||
return 1
|
||||
}
|
||||
if len(data) < indentSize {
|
||||
return 0
|
||||
}
|
||||
for i := 0; i < indentSize; i++ {
|
||||
if data[i] != ' ' {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
return indentSize
|
||||
}
|
||||
|
||||
// Create a url-safe slug for fragments
|
||||
func slugify(in []byte) []byte {
|
||||
if len(in) == 0 {
|
||||
return in
|
||||
}
|
||||
out := make([]byte, 0, len(in))
|
||||
sym := false
|
||||
|
||||
for _, ch := range in {
|
||||
if isAlnum(ch) {
|
||||
sym = false
|
||||
out = append(out, ch)
|
||||
} else if sym {
|
||||
continue
|
||||
} else {
|
||||
out = append(out, '-')
|
||||
sym = true
|
||||
}
|
||||
}
|
||||
var a, b int
|
||||
var ch byte
|
||||
for a, ch = range out {
|
||||
if ch != '-' {
|
||||
break
|
||||
}
|
||||
}
|
||||
for b = len(out) - 1; b > 0; b-- {
|
||||
if out[b] != '-' {
|
||||
break
|
||||
}
|
||||
}
|
||||
return out[a : b+1]
|
||||
}
|
||||
|
||||
func isListItem(d ast.Node) bool {
|
||||
_, ok := d.(*ast.ListItem)
|
||||
return ok
|
||||
}
|
89
vendor/github.com/gomarkdown/markdown/parser/ref.go
generated
vendored
Normal file
89
vendor/github.com/gomarkdown/markdown/parser/ref.go
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/gomarkdown/markdown/ast"
|
||||
)
|
||||
|
||||
// parse '(#r)', where r does not contain spaces. Or.
|
||||
// (!item) (!item, subitem), for an index, (!!item) signals primary.
|
||||
func maybeShortRefOrIndex(p *Parser, data []byte, offset int) (int, ast.Node) {
|
||||
if len(data[offset:]) < 4 {
|
||||
return 0, nil
|
||||
}
|
||||
// short ref first
|
||||
data = data[offset:]
|
||||
i := 1
|
||||
switch data[i] {
|
||||
case '#': // cross ref
|
||||
i++
|
||||
Loop:
|
||||
for i < len(data) {
|
||||
c := data[i]
|
||||
switch {
|
||||
case c == ')':
|
||||
break Loop
|
||||
case !isAlnum(c):
|
||||
if c == '_' || c == '-' || c == ':' {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
i = 0
|
||||
break Loop
|
||||
}
|
||||
i++
|
||||
}
|
||||
if i >= len(data) {
|
||||
return 0, nil
|
||||
}
|
||||
if data[i] != ')' {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
id := data[2:i]
|
||||
node := &ast.CrossReference{}
|
||||
node.Destination = id
|
||||
|
||||
return i + 1, node
|
||||
|
||||
case '!': // index
|
||||
i++
|
||||
start := i
|
||||
i = skipUntilChar(data, start, ')')
|
||||
|
||||
// did we reach the end of the buffer without a closing marker?
|
||||
if i >= len(data) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
if len(data[start:i]) < 1 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
idx := &ast.Index{}
|
||||
|
||||
idx.ID = fmt.Sprintf("idxref:%d", p.indexCnt)
|
||||
p.indexCnt++
|
||||
|
||||
idx.Primary = data[start] == '!'
|
||||
buf := data[start:i]
|
||||
|
||||
if idx.Primary {
|
||||
buf = buf[1:]
|
||||
}
|
||||
items := bytes.Split(buf, []byte(","))
|
||||
switch len(items) {
|
||||
case 1:
|
||||
idx.Item = bytes.TrimSpace(items[0])
|
||||
return i + 1, idx
|
||||
case 2:
|
||||
idx.Item = bytes.TrimSpace(items[0])
|
||||
idx.Subitem = bytes.TrimSpace(items[1])
|
||||
return i + 1, idx
|
||||
}
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
7
vendor/github.com/gomarkdown/markdown/todo.md
generated
vendored
Normal file
7
vendor/github.com/gomarkdown/markdown/todo.md
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
# Things to do
|
||||
|
||||
[ ] docs: add examples like https://godoc.org/github.com/dgrijalva/jwt-go (put in foo_example_test.go). Or see https://github.com/garyburd/redigo/blob/master/redis/zpop_example_test.go#L5 / https://godoc.org/github.com/garyburd/redigo/redis or https://godoc.org/github.com/go-redis/redis
|
||||
|
||||
[ ] figure out expandTabs and parser.TabSizeEight. Are those used?
|
||||
|
||||
[ ] SoftbreakData is not used
|
189
vendor/github.com/gomarkdown/markdown/tracking-perf.md
generated
vendored
Normal file
189
vendor/github.com/gomarkdown/markdown/tracking-perf.md
generated
vendored
Normal file
@@ -0,0 +1,189 @@
|
||||
## Tracking perf changes
|
||||
|
||||
Initial performance:
|
||||
```
|
||||
goos: darwin
|
||||
goarch: amd64
|
||||
pkg: github.com/gomarkdown/markdown
|
||||
BenchmarkEscapeHTML-8 2000000 823 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkSmartDoubleQuotes-8 300000 5033 ns/op 9872 B/op 56 allocs/op
|
||||
BenchmarkReferenceAmps-8 100000 19538 ns/op 26776 B/op 150 allocs/op
|
||||
BenchmarkReferenceAutoLinks-8 100000 17574 ns/op 24544 B/op 132 allocs/op
|
||||
BenchmarkReferenceBackslashEscapes-8 30000 50977 ns/op 76752 B/op 243 allocs/op
|
||||
BenchmarkReferenceBlockquotesWithCodeBlocks-8 200000 8546 ns/op 12864 B/op 65 allocs/op
|
||||
BenchmarkReferenceCodeBlocks-8 200000 9000 ns/op 14912 B/op 70 allocs/op
|
||||
BenchmarkReferenceCodeSpans-8 200000 8856 ns/op 14992 B/op 69 allocs/op
|
||||
BenchmarkReferenceHardWrappedPara-8 200000 6599 ns/op 11312 B/op 57 allocs/op
|
||||
BenchmarkReferenceHorizontalRules-8 100000 15483 ns/op 23536 B/op 98 allocs/op
|
||||
BenchmarkReferenceInlineHTMLAdvances-8 200000 6839 ns/op 12150 B/op 62 allocs/op
|
||||
BenchmarkReferenceInlineHTMLSimple-8 100000 19940 ns/op 28488 B/op 117 allocs/op
|
||||
BenchmarkReferenceInlineHTMLComments-8 200000 7455 ns/op 13440 B/op 64 allocs/op
|
||||
BenchmarkReferenceLinksInline-8 100000 16425 ns/op 23664 B/op 147 allocs/op
|
||||
BenchmarkReferenceLinksReference-8 30000 54895 ns/op 66464 B/op 416 allocs/op
|
||||
BenchmarkReferenceLinksShortcut-8 100000 17647 ns/op 23776 B/op 158 allocs/op
|
||||
BenchmarkReferenceLiterQuotesInTitles-8 200000 9367 ns/op 14832 B/op 95 allocs/op
|
||||
BenchmarkReferenceMarkdownBasics-8 10000 129772 ns/op 130848 B/op 378 allocs/op
|
||||
BenchmarkReferenceMarkdownSyntax-8 3000 502365 ns/op 461411 B/op 1411 allocs/op
|
||||
BenchmarkReferenceNestedBlockquotes-8 200000 7028 ns/op 12688 B/op 64 allocs/op
|
||||
BenchmarkReferenceOrderedAndUnorderedLists-8 20000 79686 ns/op 107520 B/op 374 allocs/op
|
||||
BenchmarkReferenceStrongAndEm-8 200000 10020 ns/op 17792 B/op 78 allocs/op
|
||||
BenchmarkReferenceTabs-8 200000 12025 ns/op 18224 B/op 81 allocs/op
|
||||
BenchmarkReferenceTidyness-8 200000 8985 ns/op 14432 B/op 71 allocs/op
|
||||
PASS
|
||||
ok github.com/gomarkdown/markdown 45.375s
|
||||
```
|
||||
|
||||
After switching to using interface{} for Node.Data:
|
||||
```
|
||||
BenchmarkEscapeHTML-8 2000000 929 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkSmartDoubleQuotes-8 300000 5126 ns/op 9248 B/op 56 allocs/op
|
||||
BenchmarkReferenceAmps-8 100000 19927 ns/op 17880 B/op 154 allocs/op
|
||||
BenchmarkReferenceAutoLinks-8 100000 20732 ns/op 17360 B/op 141 allocs/op
|
||||
BenchmarkReferenceBackslashEscapes-8 30000 50267 ns/op 38128 B/op 244 allocs/op
|
||||
BenchmarkReferenceBlockquotesWithCodeBlocks-8 200000 8988 ns/op 10912 B/op 67 allocs/op
|
||||
BenchmarkReferenceCodeBlocks-8 200000 8611 ns/op 12256 B/op 74 allocs/op
|
||||
BenchmarkReferenceCodeSpans-8 200000 8256 ns/op 11248 B/op 69 allocs/op
|
||||
BenchmarkReferenceHardWrappedPara-8 200000 6739 ns/op 9856 B/op 57 allocs/op
|
||||
BenchmarkReferenceHorizontalRules-8 100000 15503 ns/op 15600 B/op 104 allocs/op
|
||||
BenchmarkReferenceInlineHTMLAdvances-8 200000 6874 ns/op 10278 B/op 62 allocs/op
|
||||
BenchmarkReferenceInlineHTMLSimple-8 100000 22271 ns/op 18552 B/op 121 allocs/op
|
||||
BenchmarkReferenceInlineHTMLComments-8 200000 8315 ns/op 10736 B/op 64 allocs/op
|
||||
BenchmarkReferenceLinksInline-8 100000 16155 ns/op 16912 B/op 152 allocs/op
|
||||
BenchmarkReferenceLinksReference-8 30000 52387 ns/op 38192 B/op 445 allocs/op
|
||||
BenchmarkReferenceLinksShortcut-8 100000 17111 ns/op 16592 B/op 167 allocs/op
|
||||
BenchmarkReferenceLiterQuotesInTitles-8 200000 9164 ns/op 12048 B/op 97 allocs/op
|
||||
BenchmarkReferenceMarkdownBasics-8 10000 129262 ns/op 87264 B/op 416 allocs/op
|
||||
BenchmarkReferenceMarkdownSyntax-8 3000 496873 ns/op 293906 B/op 1559 allocs/op
|
||||
BenchmarkReferenceNestedBlockquotes-8 200000 6854 ns/op 10192 B/op 64 allocs/op
|
||||
BenchmarkReferenceOrderedAndUnorderedLists-8 20000 79633 ns/op 55024 B/op 447 allocs/op
|
||||
BenchmarkReferenceStrongAndEm-8 200000 9637 ns/op 12176 B/op 78 allocs/op
|
||||
BenchmarkReferenceTabs-8 100000 12164 ns/op 13776 B/op 87 allocs/op
|
||||
BenchmarkReferenceTidyness-8 200000 8677 ns/op 11296 B/op 75 allocs/op
|
||||
```
|
||||
|
||||
Not necessarily faster, but uses less bytes per op (but sometimes more allocs).
|
||||
|
||||
After tweaking the API:
|
||||
```
|
||||
$ ./s/run-bench.sh
|
||||
|
||||
go test -bench=. -test.benchmem
|
||||
goos: darwin
|
||||
goarch: amd64
|
||||
pkg: github.com/gomarkdown/markdown
|
||||
BenchmarkEscapeHTML-8 2000000 834 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkSmartDoubleQuotes-8 300000 3486 ns/op 6160 B/op 27 allocs/op
|
||||
BenchmarkReferenceAmps-8 100000 18158 ns/op 14792 B/op 125 allocs/op
|
||||
BenchmarkReferenceAutoLinks-8 100000 16824 ns/op 14272 B/op 112 allocs/op
|
||||
BenchmarkReferenceBackslashEscapes-8 30000 44066 ns/op 35040 B/op 215 allocs/op
|
||||
BenchmarkReferenceBlockquotesWithCodeBlocks-8 200000 6868 ns/op 7824 B/op 38 allocs/op
|
||||
BenchmarkReferenceCodeBlocks-8 200000 7157 ns/op 9168 B/op 45 allocs/op
|
||||
BenchmarkReferenceCodeSpans-8 200000 6663 ns/op 8160 B/op 40 allocs/op
|
||||
BenchmarkReferenceHardWrappedPara-8 300000 4821 ns/op 6768 B/op 28 allocs/op
|
||||
BenchmarkReferenceHorizontalRules-8 100000 13033 ns/op 12512 B/op 75 allocs/op
|
||||
BenchmarkReferenceInlineHTMLAdvances-8 300000 4998 ns/op 7190 B/op 33 allocs/op
|
||||
BenchmarkReferenceInlineHTMLSimple-8 100000 17696 ns/op 15464 B/op 92 allocs/op
|
||||
BenchmarkReferenceInlineHTMLComments-8 300000 5506 ns/op 7648 B/op 35 allocs/op
|
||||
BenchmarkReferenceLinksInline-8 100000 14450 ns/op 13824 B/op 123 allocs/op
|
||||
BenchmarkReferenceLinksReference-8 30000 52561 ns/op 35104 B/op 416 allocs/op
|
||||
BenchmarkReferenceLinksShortcut-8 100000 15616 ns/op 13504 B/op 138 allocs/op
|
||||
BenchmarkReferenceLiterQuotesInTitles-8 200000 7772 ns/op 8960 B/op 68 allocs/op
|
||||
BenchmarkReferenceMarkdownBasics-8 10000 121436 ns/op 84176 B/op 387 allocs/op
|
||||
BenchmarkReferenceMarkdownSyntax-8 3000 487404 ns/op 290818 B/op 1530 allocs/op
|
||||
BenchmarkReferenceNestedBlockquotes-8 300000 5098 ns/op 7104 B/op 35 allocs/op
|
||||
BenchmarkReferenceOrderedAndUnorderedLists-8 20000 74422 ns/op 51936 B/op 418 allocs/op
|
||||
BenchmarkReferenceStrongAndEm-8 200000 7888 ns/op 9088 B/op 49 allocs/op
|
||||
BenchmarkReferenceTabs-8 200000 10061 ns/op 10688 B/op 58 allocs/op
|
||||
BenchmarkReferenceTidyness-8 200000 7152 ns/op 8208 B/op 46 allocs/op
|
||||
ok github.com/gomarkdown/markdown 40.809s
|
||||
```
|
||||
|
||||
After refactoring Renderer:
|
||||
```
|
||||
BenchmarkEscapeHTML-8 2000000 883 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkSmartDoubleQuotes-8 300000 3717 ns/op 6208 B/op 29 allocs/op
|
||||
BenchmarkReferenceAmps-8 100000 19135 ns/op 14680 B/op 123 allocs/op
|
||||
BenchmarkReferenceAutoLinks-8 100000 17142 ns/op 14176 B/op 110 allocs/op
|
||||
BenchmarkReferenceBackslashEscapes-8 30000 54616 ns/op 35088 B/op 217 allocs/op
|
||||
BenchmarkReferenceBlockquotesWithCodeBlocks-8 200000 7993 ns/op 7872 B/op 40 allocs/op
|
||||
BenchmarkReferenceCodeBlocks-8 200000 8285 ns/op 9216 B/op 47 allocs/op
|
||||
BenchmarkReferenceCodeSpans-8 200000 7684 ns/op 8208 B/op 42 allocs/op
|
||||
BenchmarkReferenceHardWrappedPara-8 200000 5595 ns/op 6816 B/op 30 allocs/op
|
||||
BenchmarkReferenceHorizontalRules-8 100000 16444 ns/op 12560 B/op 77 allocs/op
|
||||
BenchmarkReferenceInlineHTMLAdvances-8 200000 5415 ns/op 7238 B/op 35 allocs/op
|
||||
BenchmarkReferenceInlineHTMLSimple-8 100000 19867 ns/op 15512 B/op 94 allocs/op
|
||||
BenchmarkReferenceInlineHTMLComments-8 200000 6026 ns/op 7696 B/op 37 allocs/op
|
||||
BenchmarkReferenceLinksInline-8 100000 14864 ns/op 13664 B/op 120 allocs/op
|
||||
BenchmarkReferenceLinksReference-8 30000 52479 ns/op 34816 B/op 401 allocs/op
|
||||
BenchmarkReferenceLinksShortcut-8 100000 15812 ns/op 13472 B/op 135 allocs/op
|
||||
BenchmarkReferenceLiterQuotesInTitles-8 200000 7767 ns/op 8880 B/op 68 allocs/op
|
||||
BenchmarkReferenceMarkdownBasics-8 10000 131065 ns/op 84048 B/op 386 allocs/op
|
||||
BenchmarkReferenceMarkdownSyntax-8 2000 515604 ns/op 289953 B/op 1501 allocs/op
|
||||
BenchmarkReferenceNestedBlockquotes-8 200000 5655 ns/op 7152 B/op 37 allocs/op
|
||||
BenchmarkReferenceOrderedAndUnorderedLists-8 20000 84188 ns/op 51984 B/op 420 allocs/op
|
||||
BenchmarkReferenceStrongAndEm-8 200000 8664 ns/op 9136 B/op 51 allocs/op
|
||||
BenchmarkReferenceTabs-8 100000 11110 ns/op 10736 B/op 60 allocs/op
|
||||
BenchmarkReferenceTidyness-8 200000 7628 ns/op 8256 B/op 48 allocs/op
|
||||
ok github.com/gomarkdown/markdown 40.841s
|
||||
```
|
||||
|
||||
After Node refactor to have Children array:
|
||||
```
|
||||
BenchmarkEscapeHTML-8 2000000 901 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkSmartDoubleQuotes-8 300000 3905 ns/op 6224 B/op 31 allocs/op
|
||||
BenchmarkReferenceAmps-8 100000 22216 ns/op 15560 B/op 157 allocs/op
|
||||
BenchmarkReferenceAutoLinks-8 100000 20335 ns/op 14824 B/op 146 allocs/op
|
||||
BenchmarkReferenceBackslashEscapes-8 20000 69174 ns/op 37392 B/op 316 allocs/op
|
||||
BenchmarkReferenceBlockquotesWithCodeBlocks-8 200000 8443 ns/op 7968 B/op 48 allocs/op
|
||||
BenchmarkReferenceCodeBlocks-8 200000 9250 ns/op 9392 B/op 58 allocs/op
|
||||
BenchmarkReferenceCodeSpans-8 200000 8515 ns/op 8432 B/op 54 allocs/op
|
||||
BenchmarkReferenceHardWrappedPara-8 200000 5738 ns/op 6856 B/op 34 allocs/op
|
||||
BenchmarkReferenceHorizontalRules-8 100000 20864 ns/op 13648 B/op 93 allocs/op
|
||||
BenchmarkReferenceInlineHTMLAdvances-8 200000 6187 ns/op 7310 B/op 40 allocs/op
|
||||
BenchmarkReferenceInlineHTMLSimple-8 50000 23793 ns/op 16128 B/op 114 allocs/op
|
||||
BenchmarkReferenceInlineHTMLComments-8 200000 7060 ns/op 7840 B/op 44 allocs/op
|
||||
BenchmarkReferenceLinksInline-8 100000 18432 ns/op 14496 B/op 153 allocs/op
|
||||
BenchmarkReferenceLinksReference-8 20000 67666 ns/op 37136 B/op 502 allocs/op
|
||||
BenchmarkReferenceLinksShortcut-8 100000 19324 ns/op 13984 B/op 162 allocs/op
|
||||
BenchmarkReferenceLiterQuotesInTitles-8 200000 8998 ns/op 9320 B/op 83 allocs/op
|
||||
BenchmarkReferenceMarkdownBasics-8 10000 160908 ns/op 88152 B/op 518 allocs/op
|
||||
BenchmarkReferenceMarkdownSyntax-8 2000 707160 ns/op 303801 B/op 2044 allocs/op
|
||||
BenchmarkReferenceNestedBlockquotes-8 200000 6740 ns/op 7248 B/op 45 allocs/op
|
||||
BenchmarkReferenceOrderedAndUnorderedLists-8 10000 115808 ns/op 55052 B/op 626 allocs/op
|
||||
BenchmarkReferenceStrongAndEm-8 100000 10540 ns/op 9416 B/op 72 allocs/op
|
||||
BenchmarkReferenceTabs-8 100000 13171 ns/op 10968 B/op 77 allocs/op
|
||||
BenchmarkReferenceTidyness-8 200000 8903 ns/op 8404 B/op 62 allocs/op
|
||||
PASS
|
||||
ok github.com/gomarkdown/markdown 43.477s
|
||||
```
|
||||
It's slower (but opens up possibilities for further improvements).
|
||||
|
||||
After refactoring to make ast.Node a top-level thing.
|
||||
```
|
||||
BenchmarkEscapeHTML-8 2000000 829 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkSmartDoubleQuotes-8 300000 3998 ns/op 6192 B/op 31 allocs/op
|
||||
BenchmarkReferenceAmps-8 50000 27389 ns/op 15480 B/op 153 allocs/op
|
||||
BenchmarkReferenceAutoLinks-8 50000 23106 ns/op 14656 B/op 137 allocs/op
|
||||
BenchmarkReferenceBackslashEscapes-8 10000 112435 ns/op 36696 B/op 315 allocs/op
|
||||
BenchmarkReferenceBlockquotesWithCodeBlocks-8 200000 9227 ns/op 7856 B/op 46 allocs/op
|
||||
BenchmarkReferenceCodeBlocks-8 200000 10469 ns/op 9248 B/op 54 allocs/op
|
||||
BenchmarkReferenceCodeSpans-8 200000 10522 ns/op 8368 B/op 54 allocs/op
|
||||
BenchmarkReferenceHardWrappedPara-8 200000 6354 ns/op 6784 B/op 34 allocs/op
|
||||
BenchmarkReferenceHorizontalRules-8 50000 32393 ns/op 13952 B/op 87 allocs/op
|
||||
BenchmarkReferenceInlineHTMLAdvances-8 200000 6894 ns/op 7238 B/op 40 allocs/op
|
||||
BenchmarkReferenceInlineHTMLSimple-8 50000 32942 ns/op 15864 B/op 110 allocs/op
|
||||
BenchmarkReferenceInlineHTMLComments-8 200000 8181 ns/op 7776 B/op 44 allocs/op
|
||||
BenchmarkReferenceLinksInline-8 100000 21679 ns/op 14400 B/op 148 allocs/op
|
||||
BenchmarkReferenceLinksReference-8 20000 83928 ns/op 36688 B/op 473 allocs/op
|
||||
BenchmarkReferenceLinksShortcut-8 100000 22053 ns/op 13872 B/op 153 allocs/op
|
||||
BenchmarkReferenceLiterQuotesInTitles-8 100000 10784 ns/op 9296 B/op 81 allocs/op
|
||||
BenchmarkReferenceMarkdownBasics-8 5000 237097 ns/op 87760 B/op 480 allocs/op
|
||||
BenchmarkReferenceMarkdownSyntax-8 1000 1465402 ns/op 300769 B/op 1896 allocs/op
|
||||
BenchmarkReferenceNestedBlockquotes-8 200000 7461 ns/op 7152 B/op 45 allocs/op
|
||||
BenchmarkReferenceOrderedAndUnorderedLists-8 5000 212256 ns/op 53724 B/op 553 allocs/op
|
||||
BenchmarkReferenceStrongAndEm-8 100000 13018 ns/op 9264 B/op 72 allocs/op
|
||||
BenchmarkReferenceTabs-8 100000 15005 ns/op 10752 B/op 71 allocs/op
|
||||
BenchmarkReferenceTidyness-8 200000 10308 ns/op 8292 B/op 58 allocs/op
|
||||
PASS
|
||||
ok github.com/gomarkdown/markdown 42.176s
|
||||
```
|
2
vendor/github.com/matterbridge/emoji/.gitignore
generated
vendored
Normal file
2
vendor/github.com/matterbridge/emoji/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.idea
|
||||
emoji.iml
|
21
vendor/github.com/matterbridge/emoji/LICENSE
generated
vendored
Normal file
21
vendor/github.com/matterbridge/emoji/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 kyokomi
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
53
vendor/github.com/matterbridge/emoji/README.md
generated
vendored
Normal file
53
vendor/github.com/matterbridge/emoji/README.md
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
# Emoji
|
||||
Emoji is a simple golang package.
|
||||
|
||||
[](https://app.wercker.com/project/byKey/7bef60de2c6d3e0e6c13d56b2393c5d8)
|
||||
[](https://coveralls.io/r/kyokomi/emoji?branch=master)
|
||||
[](https://godoc.org/github.com/kyokomi/emoji)
|
||||
|
||||
Get it:
|
||||
|
||||
```
|
||||
go get github.com/kyokomi/emoji
|
||||
```
|
||||
|
||||
Import it:
|
||||
|
||||
```
|
||||
import (
|
||||
"github.com/kyokomi/emoji"
|
||||
)
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/kyokomi/emoji"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("Hello World Emoji!")
|
||||
|
||||
emoji.Println(":beer: Beer!!!")
|
||||
|
||||
pizzaMessage := emoji.Sprint("I like a :pizza: and :sushi:!!")
|
||||
fmt.Println(pizzaMessage)
|
||||
}
|
||||
```
|
||||
|
||||
## Demo
|
||||
|
||||

|
||||
|
||||
## Reference
|
||||
|
||||
- [GitHub EMOJI CHEAT SHEET](http://www.emoji-cheat-sheet.com/)
|
||||
|
||||
## License
|
||||
|
||||
[MIT](https://github.com/kyokomi/emoji/blob/master/LICENSE)
|
133
vendor/github.com/matterbridge/emoji/emoji.go
generated
vendored
Normal file
133
vendor/github.com/matterbridge/emoji/emoji.go
generated
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
// Package emoji terminal output.
|
||||
package emoji
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
//go:generate generateEmojiCodeMap -pkg emoji
|
||||
|
||||
// Replace Padding character for emoji.
|
||||
const (
|
||||
ReplacePadding = ""
|
||||
)
|
||||
|
||||
// CodeMap gets the underlying map of emoji.
|
||||
func CodeMap() map[string]string {
|
||||
return emojiCodeMap
|
||||
}
|
||||
|
||||
// regular expression that matches :flag-[countrycode]:
|
||||
var flagRegexp = regexp.MustCompile(":flag-([a-z]{2}):")
|
||||
|
||||
func emojize(x string) string {
|
||||
str, ok := emojiCodeMap[x]
|
||||
if ok {
|
||||
return str + ReplacePadding
|
||||
}
|
||||
if match := flagRegexp.FindStringSubmatch(x); len(match) == 2 {
|
||||
return regionalIndicator(match[1][0]) + regionalIndicator(match[1][1])
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// regionalIndicator maps a lowercase letter to a unicode regional indicator
|
||||
func regionalIndicator(i byte) string {
|
||||
return string('\U0001F1E6' + rune(i) - 'a')
|
||||
}
|
||||
|
||||
func replaseEmoji(input *bytes.Buffer) string {
|
||||
emoji := bytes.NewBufferString(":")
|
||||
for {
|
||||
i, _, err := input.ReadRune()
|
||||
if err != nil {
|
||||
// not replase
|
||||
return emoji.String()
|
||||
}
|
||||
|
||||
if i == ':' && emoji.Len() == 1 {
|
||||
return emoji.String() + replaseEmoji(input)
|
||||
}
|
||||
|
||||
emoji.WriteRune(i)
|
||||
switch {
|
||||
case unicode.IsSpace(i):
|
||||
return emoji.String()
|
||||
case i == ':':
|
||||
return emojize(emoji.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func compile(x string) string {
|
||||
if x == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
input := bytes.NewBufferString(x)
|
||||
output := bytes.NewBufferString("")
|
||||
|
||||
for {
|
||||
i, _, err := input.ReadRune()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
switch i {
|
||||
default:
|
||||
output.WriteRune(i)
|
||||
case ':':
|
||||
output.WriteString(replaseEmoji(input))
|
||||
}
|
||||
}
|
||||
return output.String()
|
||||
}
|
||||
|
||||
// Print is fmt.Print which supports emoji
|
||||
func Print(a ...interface{}) (int, error) {
|
||||
return fmt.Print(compile(fmt.Sprint(a...)))
|
||||
}
|
||||
|
||||
// Println is fmt.Println which supports emoji
|
||||
func Println(a ...interface{}) (int, error) {
|
||||
return fmt.Println(compile(fmt.Sprint(a...)))
|
||||
}
|
||||
|
||||
// Printf is fmt.Printf which supports emoji
|
||||
func Printf(format string, a ...interface{}) (int, error) {
|
||||
return fmt.Printf(compile(fmt.Sprintf(format, a...)))
|
||||
}
|
||||
|
||||
// Fprint is fmt.Fprint which supports emoji
|
||||
func Fprint(w io.Writer, a ...interface{}) (int, error) {
|
||||
return fmt.Fprint(w, compile(fmt.Sprint(a...)))
|
||||
}
|
||||
|
||||
// Fprintln is fmt.Fprintln which supports emoji
|
||||
func Fprintln(w io.Writer, a ...interface{}) (int, error) {
|
||||
return fmt.Fprintln(w, compile(fmt.Sprint(a...)))
|
||||
}
|
||||
|
||||
// Fprintf is fmt.Fprintf which supports emoji
|
||||
func Fprintf(w io.Writer, format string, a ...interface{}) (int, error) {
|
||||
return fmt.Fprint(w, compile(fmt.Sprintf(format, a...)))
|
||||
}
|
||||
|
||||
// Sprint is fmt.Sprint which supports emoji
|
||||
func Sprint(a ...interface{}) string {
|
||||
return compile(fmt.Sprint(a...))
|
||||
}
|
||||
|
||||
// Sprintf is fmt.Sprintf which supports emoji
|
||||
func Sprintf(format string, a ...interface{}) string {
|
||||
return compile(fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
// Errorf is fmt.Errorf which supports emoji
|
||||
func Errorf(format string, a ...interface{}) error {
|
||||
return errors.New(compile(Sprintf(format, a...)))
|
||||
}
|
4114
vendor/github.com/matterbridge/emoji/emoji_codemap.go
generated
vendored
Normal file
4114
vendor/github.com/matterbridge/emoji/emoji_codemap.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
25
vendor/github.com/matterbridge/emoji/wercker.yml
generated
vendored
Normal file
25
vendor/github.com/matterbridge/emoji/wercker.yml
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
box: golang
|
||||
build:
|
||||
steps:
|
||||
- setup-go-workspace
|
||||
- script:
|
||||
name: install goveralls
|
||||
code: |
|
||||
go get github.com/mattn/goveralls
|
||||
- script:
|
||||
name: go get
|
||||
code: |
|
||||
go get ./...
|
||||
- script:
|
||||
name: go build
|
||||
code: |
|
||||
go build ./...
|
||||
- script:
|
||||
name: go test
|
||||
code: |
|
||||
go test ./...
|
||||
- script:
|
||||
name: coveralls
|
||||
code: |
|
||||
goveralls -v -service wercker.com -repotoken $COVERALLS_TOKEN
|
||||
|
26
vendor/github.com/matterbridge/gomatrix/tags.go
generated
vendored
Normal file
26
vendor/github.com/matterbridge/gomatrix/tags.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright 2019 Sumukha PK
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gomatrix
|
||||
|
||||
// TagContent contains the data for an m.tag message type
|
||||
// https://matrix.org/docs/spec/client_server/r0.4.0.html#m-tag
|
||||
type TagContent struct {
|
||||
Tags map[string]TagProperties `json:"tags"`
|
||||
}
|
||||
|
||||
// TagProperties contains the properties of a Tag
|
||||
type TagProperties struct {
|
||||
Order float32 `json:"order,omitempty"` // Empty values must be neglected
|
||||
}
|
4
vendor/github.com/nlopes/slack/block_conv.go
generated
vendored
4
vendor/github.com/nlopes/slack/block_conv.go
generated
vendored
@@ -61,7 +61,7 @@ func (b *Blocks) UnmarshalJSON(data []byte) error {
|
||||
case "section":
|
||||
block = &SectionBlock{}
|
||||
default:
|
||||
return errors.New("unsupported block type")
|
||||
block = &UnknownBlock{}
|
||||
}
|
||||
|
||||
err = json.Unmarshal(r, block)
|
||||
@@ -127,7 +127,7 @@ func (b *BlockElements) UnmarshalJSON(data []byte) error {
|
||||
case "static_select", "external_select", "users_select", "conversations_select", "channels_select":
|
||||
blockElement = &SelectBlockElement{}
|
||||
default:
|
||||
return errors.New("unsupported block element type")
|
||||
blockElement = &UnknownBlockElement{}
|
||||
}
|
||||
|
||||
err = json.Unmarshal(r, blockElement)
|
||||
|
14
vendor/github.com/nlopes/slack/block_element.go
generated
vendored
14
vendor/github.com/nlopes/slack/block_element.go
generated
vendored
@@ -62,6 +62,20 @@ type BlockElements struct {
|
||||
ElementSet []BlockElement `json:"elements,omitempty"`
|
||||
}
|
||||
|
||||
// UnknownBlockElement any block element that this library does not directly support.
|
||||
// See the "Rich Elements" section at the following URL:
|
||||
// https://api.slack.com/changelog/2019-09-what-they-see-is-what-you-get-and-more-and-less
|
||||
// New block element types may be introduced by Slack at any time; this is a catch-all for any such block elements.
|
||||
type UnknownBlockElement struct {
|
||||
Type MessageElementType `json:"type"`
|
||||
Elements BlockElements
|
||||
}
|
||||
|
||||
// ElementType returns the type of the Element
|
||||
func (s UnknownBlockElement) ElementType() MessageElementType {
|
||||
return s.Type
|
||||
}
|
||||
|
||||
// ImageBlockElement An element to insert an image - this element can be used
|
||||
// in section and context blocks only. If you want a block with only an image
|
||||
// in it, you're looking for the image block.
|
||||
|
14
vendor/github.com/nlopes/slack/block_unknown.go
generated
vendored
Normal file
14
vendor/github.com/nlopes/slack/block_unknown.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
package slack
|
||||
|
||||
// UnknownBlock represents a block type that is not yet known. This block type exists to prevent Slack from introducing
|
||||
// new and unknown block types that break this library.
|
||||
type UnknownBlock struct {
|
||||
Type MessageBlockType `json:"type"`
|
||||
BlockID string `json:"block_id,omitempty"`
|
||||
Elements BlockElements `json:"elements"`
|
||||
}
|
||||
|
||||
// BlockType returns the type of the block
|
||||
func (b UnknownBlock) BlockType() MessageBlockType {
|
||||
return b.Type
|
||||
}
|
2
vendor/github.com/nlopes/slack/go.mod
generated
vendored
2
vendor/github.com/nlopes/slack/go.mod
generated
vendored
@@ -1,4 +1,4 @@
|
||||
module github.com/nlopes/slack
|
||||
module github.com/matterbridge/slack
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
|
10
vendor/github.com/peterhellberg/emojilib/.travis.yml
generated
vendored
10
vendor/github.com/peterhellberg/emojilib/.travis.yml
generated
vendored
@@ -1,10 +0,0 @@
|
||||
language: go
|
||||
|
||||
dist: xenial
|
||||
|
||||
go:
|
||||
- "1.11.5"
|
||||
- "1.10.8"
|
||||
|
||||
script:
|
||||
- go test ./...
|
19
vendor/github.com/peterhellberg/emojilib/LICENSE
generated
vendored
19
vendor/github.com/peterhellberg/emojilib/LICENSE
generated
vendored
@@ -1,19 +0,0 @@
|
||||
Copyright (c) 2015-2019 Peter Hellberg https://c7.se
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
61
vendor/github.com/peterhellberg/emojilib/README.md
generated
vendored
61
vendor/github.com/peterhellberg/emojilib/README.md
generated
vendored
@@ -1,61 +0,0 @@
|
||||
# :book: emojilib
|
||||
|
||||
[](https://travis-ci.org/peterhellberg/emojilib)
|
||||
[](https://godoc.org/github.com/peterhellberg/emojilib)
|
||||
[](https://github.com/peterhellberg/emojilib#license-mit)
|
||||
|
||||
The [Emoji keyword library](https://github.com/muan/emojilib) by [@muan](https://github.com/muan/) ported to Go. (using `go generate`)
|
||||
|
||||
## Installation
|
||||
|
||||
go get -u github.com/peterhellberg/emojilib
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/peterhellberg/emojilib"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(emojilib.ReplaceWithPadding("I :green_heart: You!"))
|
||||
}
|
||||
```
|
||||
|
||||
## Generating a new version
|
||||
|
||||
```bash
|
||||
$ go generate
|
||||
```
|
||||
|
||||
This will download the latest version of [emojis.json](https://raw.githubusercontent.com/muan/emojilib/master/emojis.json)
|
||||
and generate a new version of `generated.go`
|
||||
|
||||
_You’ll need to have the [golang.org/x/tools/imports](https://golang.org/x/tools/imports) package installed in order to run the generator._
|
||||
|
||||
## License (MIT)
|
||||
|
||||
Copyright (c) 2015-2019 [Peter Hellberg](https://c7.se)
|
||||
|
||||
> Permission is hereby granted, free of charge, to any person obtaining
|
||||
> a copy of this software and associated documentation files (the
|
||||
> "Software"), to deal in the Software without restriction, including
|
||||
> without limitation the rights to use, copy, modify, merge, publish,
|
||||
> distribute, sublicense, and/or sell copies of the Software, and to
|
||||
> permit persons to whom the Software is furnished to do so, subject to
|
||||
> the following conditions:
|
||||
|
||||
> The above copyright notice and this permission notice shall be
|
||||
> included in all copies or substantial portions of the Software.
|
||||
|
||||
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
> EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
> MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
> NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
> LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
> OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
> WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
88
vendor/github.com/peterhellberg/emojilib/emojilib.go
generated
vendored
88
vendor/github.com/peterhellberg/emojilib/emojilib.go
generated
vendored
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
|
||||
Package emojilib is a port of the Emoji keyword library to Go
|
||||
|
||||
Installation
|
||||
|
||||
Just go get the package:
|
||||
|
||||
go get -u github.com/peterhellberg/emojilib
|
||||
|
||||
Usage
|
||||
|
||||
A small usage example
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/peterhellberg/emojilib"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(emojilib.ReplaceWithPadding("I :green_heart: You!"))
|
||||
}
|
||||
|
||||
*/
|
||||
package emojilib
|
||||
|
||||
import "errors"
|
||||
|
||||
//go:generate go run _generator/main.go
|
||||
|
||||
// Emojis contain emojis keyed on their name
|
||||
type Emojis map[string]Emoji
|
||||
|
||||
// Emoji contains the keywords, char and category for an emoji
|
||||
type Emoji struct {
|
||||
Keywords []string `json:"keywords"`
|
||||
Char string `json:"char"`
|
||||
Category string `json:"category"`
|
||||
}
|
||||
|
||||
// ErrUnknownEmoji is returned from Find if provided with a unknown emoji name
|
||||
var ErrUnknownEmoji = errors.New("unknown emoji")
|
||||
|
||||
// ErrUnknownKeyword is returned from Keyword if provided with a unknown keyword
|
||||
var ErrUnknownKeyword = errors.New("unknown keyword")
|
||||
|
||||
// Find returns an Emoji if provided with a known name
|
||||
func Find(n string) (Emoji, error) {
|
||||
if e, ok := emojis[n]; ok {
|
||||
return e, nil
|
||||
}
|
||||
|
||||
return Emoji{}, ErrUnknownEmoji
|
||||
}
|
||||
|
||||
// Keyword returns Emojis for the given keyword
|
||||
func Keyword(k string) ([]Emoji, error) {
|
||||
if names, ok := keywordLookup[k]; ok {
|
||||
es := []Emoji{}
|
||||
|
||||
for _, n := range names {
|
||||
es = append(es, emojis[n])
|
||||
}
|
||||
|
||||
return es, nil
|
||||
}
|
||||
|
||||
return []Emoji{}, ErrUnknownKeyword
|
||||
}
|
||||
|
||||
// All returns all the emojis
|
||||
func All() Emojis {
|
||||
return emojis
|
||||
}
|
||||
|
||||
// Replace takes a string and replaces all emoji names with their emoji character
|
||||
func Replace(s string) string {
|
||||
return emojiReplacer.Replace(s)
|
||||
}
|
||||
|
||||
// ReplaceWithPadding takes a string and replaces all emoji names with their
|
||||
// emoji character and a space in order to display better in terminals
|
||||
func ReplaceWithPadding(s string) string {
|
||||
return emojiPaddedReplacer.Replace(s)
|
||||
}
|
20414
vendor/github.com/peterhellberg/emojilib/generated.go
generated
vendored
20414
vendor/github.com/peterhellberg/emojilib/generated.go
generated
vendored
File diff suppressed because it is too large
Load Diff
33
vendor/github.com/shazow/ssh-chat/NOTICE
generated
vendored
Normal file
33
vendor/github.com/shazow/ssh-chat/NOTICE
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
## x/crypto/ssh/terminal
|
||||
|
||||
This project contains a fork of https://github.com/golang/crypto/tree/master/ssh/terminal
|
||||
under the sshd/terminal directory. The project's original license applies:
|
||||
|
||||
|
||||
Copyright (c) 2009 The Go 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:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* 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.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
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
|
||||
OWNER 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.
|
7
vendor/github.com/shazow/ssh-chat/internal/sanitize/sanitize.go
generated
vendored
7
vendor/github.com/shazow/ssh-chat/internal/sanitize/sanitize.go
generated
vendored
@@ -2,7 +2,10 @@ package sanitize
|
||||
|
||||
import "regexp"
|
||||
|
||||
var reStripName = regexp.MustCompile("[^\\w.-]")
|
||||
var (
|
||||
reStripName = regexp.MustCompile("[^\\w.-]")
|
||||
reStripData = regexp.MustCompile("[^[:ascii:]]|[[:cntrl:]]")
|
||||
)
|
||||
|
||||
const maxLength = 16
|
||||
|
||||
@@ -17,8 +20,6 @@ func Name(s string) string {
|
||||
return s
|
||||
}
|
||||
|
||||
var reStripData = regexp.MustCompile("[^[:ascii:]]|[[:cntrl:]]")
|
||||
|
||||
// Data returns a string with only allowed characters for client-provided metadata inputs.
|
||||
func Data(s string, maxlen int) string {
|
||||
if len(s) > maxlen {
|
||||
|
1
vendor/github.com/shazow/ssh-chat/sshd/logger.go
generated
vendored
1
vendor/github.com/shazow/ssh-chat/sshd/logger.go
generated
vendored
@@ -5,6 +5,7 @@ import stdlog "log"
|
||||
|
||||
var logger *stdlog.Logger
|
||||
|
||||
// SetLogger sets the package logging output to use w.
|
||||
func SetLogger(w io.Writer) {
|
||||
flags := stdlog.Flags()
|
||||
prefix := "[sshd] "
|
||||
|
7
vendor/github.com/shazow/ssh-chat/sshd/net.go
generated
vendored
7
vendor/github.com/shazow/ssh-chat/sshd/net.go
generated
vendored
@@ -7,7 +7,7 @@ import (
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// Container for the connection and ssh-related configuration
|
||||
// SSHListener is the container for the connection and ssh-related configuration
|
||||
type SSHListener struct {
|
||||
net.Listener
|
||||
config *ssh.ServerConfig
|
||||
@@ -16,7 +16,7 @@ type SSHListener struct {
|
||||
HandlerFunc func(term *Terminal)
|
||||
}
|
||||
|
||||
// Make an SSH listener socket
|
||||
// ListenSSH makes an SSH listener socket
|
||||
func ListenSSH(laddr string, config *ssh.ServerConfig) (*SSHListener, error) {
|
||||
socket, err := net.Listen("tcp", laddr)
|
||||
if err != nil {
|
||||
@@ -43,7 +43,7 @@ func (l *SSHListener) handleConn(conn net.Conn) (*Terminal, error) {
|
||||
return NewSession(sshConn, channels)
|
||||
}
|
||||
|
||||
// Accept incoming connections as terminal requests and yield them
|
||||
// Serve Accepts incoming connections as terminal requests and yield them
|
||||
func (l *SSHListener) Serve() {
|
||||
defer l.Close()
|
||||
for {
|
||||
@@ -59,6 +59,7 @@ func (l *SSHListener) Serve() {
|
||||
term, err := l.handleConn(conn)
|
||||
if err != nil {
|
||||
logger.Printf("[%s] Failed to handshake: %s", conn.RemoteAddr(), err)
|
||||
conn.Close() // Must be closed to avoid a leak
|
||||
return
|
||||
}
|
||||
l.HandlerFunc(term)
|
||||
|
9
vendor/github.com/shazow/ssh-chat/sshd/terminal.go
generated
vendored
9
vendor/github.com/shazow/ssh-chat/sshd/terminal.go
generated
vendored
@@ -7,14 +7,17 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/shazow/ssh-chat/sshd/terminal"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
)
|
||||
|
||||
var keepaliveInterval = time.Second * 30
|
||||
var keepaliveRequest = "keepalive@ssh-chat"
|
||||
|
||||
// ErrNoSessionChannel is returned when there is no session channel.
|
||||
var ErrNoSessionChannel = errors.New("no session channel")
|
||||
|
||||
// ErrNotSessionChannel is returned when a channel is not a session channel.
|
||||
var ErrNotSessionChannel = errors.New("terminal requires session channel")
|
||||
|
||||
// Connection is an interface with fields necessary to operate an sshd host.
|
||||
@@ -52,7 +55,7 @@ func (c sshConn) Name() string {
|
||||
return c.User()
|
||||
}
|
||||
|
||||
// Extending ssh/terminal to include a closer interface
|
||||
// Terminal extends ssh/terminal to include a close method
|
||||
type Terminal struct {
|
||||
terminal.Terminal
|
||||
Conn Connection
|
||||
@@ -103,7 +106,7 @@ func NewTerminal(conn *ssh.ServerConn, ch ssh.NewChannel) (*Terminal, error) {
|
||||
return &term, nil
|
||||
}
|
||||
|
||||
// Find session channel and make a Terminal from it
|
||||
// NewSession Finds a session channel and make a Terminal from it
|
||||
func NewSession(conn *ssh.ServerConn, channels <-chan ssh.NewChannel) (*Terminal, error) {
|
||||
// Make a terminal from the first session found
|
||||
for ch := range channels {
|
||||
|
27
vendor/github.com/shazow/ssh-chat/sshd/terminal/LICENSE
generated
vendored
Normal file
27
vendor/github.com/shazow/ssh-chat/sshd/terminal/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2009 The Go 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:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* 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.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
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
|
||||
OWNER 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.
|
1001
vendor/github.com/shazow/ssh-chat/sshd/terminal/terminal.go
generated
vendored
Normal file
1001
vendor/github.com/shazow/ssh-chat/sshd/terminal/terminal.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
114
vendor/github.com/shazow/ssh-chat/sshd/terminal/util.go
generated
vendored
Normal file
114
vendor/github.com/shazow/ssh-chat/sshd/terminal/util.go
generated
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build aix darwin dragonfly freebsd linux,!appengine netbsd openbsd
|
||||
|
||||
// Package terminal provides support functions for dealing with terminals, as
|
||||
// commonly found on UNIX systems.
|
||||
//
|
||||
// Putting a terminal into raw mode is the most common requirement:
|
||||
//
|
||||
// oldState, err := terminal.MakeRaw(0)
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// defer terminal.Restore(0, oldState)
|
||||
package terminal // import "golang.org/x/crypto/ssh/terminal"
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// State contains the state of a terminal.
|
||||
type State struct {
|
||||
termios unix.Termios
|
||||
}
|
||||
|
||||
// IsTerminal returns whether the given file descriptor is a terminal.
|
||||
func IsTerminal(fd int) bool {
|
||||
_, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// MakeRaw put the terminal connected to the given file descriptor into raw
|
||||
// mode and returns the previous state of the terminal so that it can be
|
||||
// restored.
|
||||
func MakeRaw(fd int) (*State, error) {
|
||||
termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oldState := State{termios: *termios}
|
||||
|
||||
// This attempts to replicate the behaviour documented for cfmakeraw in
|
||||
// the termios(3) manpage.
|
||||
termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON
|
||||
termios.Oflag &^= unix.OPOST
|
||||
termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN
|
||||
termios.Cflag &^= unix.CSIZE | unix.PARENB
|
||||
termios.Cflag |= unix.CS8
|
||||
termios.Cc[unix.VMIN] = 1
|
||||
termios.Cc[unix.VTIME] = 0
|
||||
if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, termios); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &oldState, nil
|
||||
}
|
||||
|
||||
// GetState returns the current state of a terminal which may be useful to
|
||||
// restore the terminal after a signal.
|
||||
func GetState(fd int) (*State, error) {
|
||||
termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &State{termios: *termios}, nil
|
||||
}
|
||||
|
||||
// Restore restores the terminal connected to the given file descriptor to a
|
||||
// previous state.
|
||||
func Restore(fd int, state *State) error {
|
||||
return unix.IoctlSetTermios(fd, ioctlWriteTermios, &state.termios)
|
||||
}
|
||||
|
||||
// GetSize returns the dimensions of the given terminal.
|
||||
func GetSize(fd int) (width, height int, err error) {
|
||||
ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
|
||||
if err != nil {
|
||||
return -1, -1, err
|
||||
}
|
||||
return int(ws.Col), int(ws.Row), nil
|
||||
}
|
||||
|
||||
// passwordReader is an io.Reader that reads from a specific file descriptor.
|
||||
type passwordReader int
|
||||
|
||||
func (r passwordReader) Read(buf []byte) (int, error) {
|
||||
return unix.Read(int(r), buf)
|
||||
}
|
||||
|
||||
// ReadPassword reads a line of input from a terminal without local echo. This
|
||||
// is commonly used for inputting passwords and other sensitive data. The slice
|
||||
// returned does not include the \n.
|
||||
func ReadPassword(fd int) ([]byte, error) {
|
||||
termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newState := *termios
|
||||
newState.Lflag &^= unix.ECHO
|
||||
newState.Lflag |= unix.ICANON | unix.ISIG
|
||||
newState.Iflag |= unix.ICRNL
|
||||
if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, &newState); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer unix.IoctlSetTermios(fd, ioctlWriteTermios, termios)
|
||||
|
||||
return readPasswordLine(passwordReader(fd))
|
||||
}
|
12
vendor/github.com/shazow/ssh-chat/sshd/terminal/util_aix.go
generated
vendored
Normal file
12
vendor/github.com/shazow/ssh-chat/sshd/terminal/util_aix.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2018 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 aix
|
||||
|
||||
package terminal
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
const ioctlReadTermios = unix.TCGETS
|
||||
const ioctlWriteTermios = unix.TCSETS
|
12
vendor/github.com/shazow/ssh-chat/sshd/terminal/util_bsd.go
generated
vendored
Normal file
12
vendor/github.com/shazow/ssh-chat/sshd/terminal/util_bsd.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build darwin dragonfly freebsd netbsd openbsd
|
||||
|
||||
package terminal
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
const ioctlReadTermios = unix.TIOCGETA
|
||||
const ioctlWriteTermios = unix.TIOCSETA
|
10
vendor/github.com/shazow/ssh-chat/sshd/terminal/util_linux.go
generated
vendored
Normal file
10
vendor/github.com/shazow/ssh-chat/sshd/terminal/util_linux.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package terminal
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
const ioctlReadTermios = unix.TCGETS
|
||||
const ioctlWriteTermios = unix.TCSETS
|
58
vendor/github.com/shazow/ssh-chat/sshd/terminal/util_plan9.go
generated
vendored
Normal file
58
vendor/github.com/shazow/ssh-chat/sshd/terminal/util_plan9.go
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package terminal provides support functions for dealing with terminals, as
|
||||
// commonly found on UNIX systems.
|
||||
//
|
||||
// Putting a terminal into raw mode is the most common requirement:
|
||||
//
|
||||
// oldState, err := terminal.MakeRaw(0)
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// defer terminal.Restore(0, oldState)
|
||||
package terminal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
type State struct{}
|
||||
|
||||
// IsTerminal returns whether the given file descriptor is a terminal.
|
||||
func IsTerminal(fd int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// MakeRaw put the terminal connected to the given file descriptor into raw
|
||||
// mode and returns the previous state of the terminal so that it can be
|
||||
// restored.
|
||||
func MakeRaw(fd int) (*State, error) {
|
||||
return nil, fmt.Errorf("terminal: MakeRaw not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
|
||||
// GetState returns the current state of a terminal which may be useful to
|
||||
// restore the terminal after a signal.
|
||||
func GetState(fd int) (*State, error) {
|
||||
return nil, fmt.Errorf("terminal: GetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
|
||||
// Restore restores the terminal connected to the given file descriptor to a
|
||||
// previous state.
|
||||
func Restore(fd int, state *State) error {
|
||||
return fmt.Errorf("terminal: Restore not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
|
||||
// GetSize returns the dimensions of the given terminal.
|
||||
func GetSize(fd int) (width, height int, err error) {
|
||||
return 0, 0, fmt.Errorf("terminal: GetSize not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
|
||||
// ReadPassword reads a line of input from a terminal without local echo. This
|
||||
// is commonly used for inputting passwords and other sensitive data. The slice
|
||||
// returned does not include the \n.
|
||||
func ReadPassword(fd int) ([]byte, error) {
|
||||
return nil, fmt.Errorf("terminal: ReadPassword not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
124
vendor/github.com/shazow/ssh-chat/sshd/terminal/util_solaris.go
generated
vendored
Normal file
124
vendor/github.com/shazow/ssh-chat/sshd/terminal/util_solaris.go
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
// 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 solaris
|
||||
|
||||
package terminal // import "golang.org/x/crypto/ssh/terminal"
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
"io"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// State contains the state of a terminal.
|
||||
type State struct {
|
||||
termios unix.Termios
|
||||
}
|
||||
|
||||
// IsTerminal returns whether the given file descriptor is a terminal.
|
||||
func IsTerminal(fd int) bool {
|
||||
_, err := unix.IoctlGetTermio(fd, unix.TCGETA)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// ReadPassword reads a line of input from a terminal without local echo. This
|
||||
// is commonly used for inputting passwords and other sensitive data. The slice
|
||||
// returned does not include the \n.
|
||||
func ReadPassword(fd int) ([]byte, error) {
|
||||
// see also: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libast/common/uwin/getpass.c
|
||||
val, err := unix.IoctlGetTermios(fd, unix.TCGETS)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
oldState := *val
|
||||
|
||||
newState := oldState
|
||||
newState.Lflag &^= syscall.ECHO
|
||||
newState.Lflag |= syscall.ICANON | syscall.ISIG
|
||||
newState.Iflag |= syscall.ICRNL
|
||||
err = unix.IoctlSetTermios(fd, unix.TCSETS, &newState)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer unix.IoctlSetTermios(fd, unix.TCSETS, &oldState)
|
||||
|
||||
var buf [16]byte
|
||||
var ret []byte
|
||||
for {
|
||||
n, err := syscall.Read(fd, buf[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if n == 0 {
|
||||
if len(ret) == 0 {
|
||||
return nil, io.EOF
|
||||
}
|
||||
break
|
||||
}
|
||||
if buf[n-1] == '\n' {
|
||||
n--
|
||||
}
|
||||
ret = append(ret, buf[:n]...)
|
||||
if n < len(buf) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// MakeRaw puts the terminal connected to the given file descriptor into raw
|
||||
// mode and returns the previous state of the terminal so that it can be
|
||||
// restored.
|
||||
// see http://cr.illumos.org/~webrev/andy_js/1060/
|
||||
func MakeRaw(fd int) (*State, error) {
|
||||
termios, err := unix.IoctlGetTermios(fd, unix.TCGETS)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
oldState := State{termios: *termios}
|
||||
|
||||
termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON
|
||||
termios.Oflag &^= unix.OPOST
|
||||
termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN
|
||||
termios.Cflag &^= unix.CSIZE | unix.PARENB
|
||||
termios.Cflag |= unix.CS8
|
||||
termios.Cc[unix.VMIN] = 1
|
||||
termios.Cc[unix.VTIME] = 0
|
||||
|
||||
if err := unix.IoctlSetTermios(fd, unix.TCSETS, termios); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &oldState, nil
|
||||
}
|
||||
|
||||
// Restore restores the terminal connected to the given file descriptor to a
|
||||
// previous state.
|
||||
func Restore(fd int, oldState *State) error {
|
||||
return unix.IoctlSetTermios(fd, unix.TCSETS, &oldState.termios)
|
||||
}
|
||||
|
||||
// GetState returns the current state of a terminal which may be useful to
|
||||
// restore the terminal after a signal.
|
||||
func GetState(fd int) (*State, error) {
|
||||
termios, err := unix.IoctlGetTermios(fd, unix.TCGETS)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &State{termios: *termios}, nil
|
||||
}
|
||||
|
||||
// GetSize returns the dimensions of the given terminal.
|
||||
func GetSize(fd int) (width, height int, err error) {
|
||||
ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
return int(ws.Col), int(ws.Row), nil
|
||||
}
|
103
vendor/github.com/shazow/ssh-chat/sshd/terminal/util_windows.go
generated
vendored
Normal file
103
vendor/github.com/shazow/ssh-chat/sshd/terminal/util_windows.go
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
|
||||
// Package terminal provides support functions for dealing with terminals, as
|
||||
// commonly found on UNIX systems.
|
||||
//
|
||||
// Putting a terminal into raw mode is the most common requirement:
|
||||
//
|
||||
// oldState, err := terminal.MakeRaw(0)
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// defer terminal.Restore(0, oldState)
|
||||
package terminal
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type State struct {
|
||||
mode uint32
|
||||
}
|
||||
|
||||
// IsTerminal returns whether the given file descriptor is a terminal.
|
||||
func IsTerminal(fd int) bool {
|
||||
var st uint32
|
||||
err := windows.GetConsoleMode(windows.Handle(fd), &st)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// MakeRaw put the terminal connected to the given file descriptor into raw
|
||||
// mode and returns the previous state of the terminal so that it can be
|
||||
// restored.
|
||||
func MakeRaw(fd int) (*State, error) {
|
||||
var st uint32
|
||||
if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
|
||||
if err := windows.SetConsoleMode(windows.Handle(fd), raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &State{st}, nil
|
||||
}
|
||||
|
||||
// GetState returns the current state of a terminal which may be useful to
|
||||
// restore the terminal after a signal.
|
||||
func GetState(fd int) (*State, error) {
|
||||
var st uint32
|
||||
if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &State{st}, nil
|
||||
}
|
||||
|
||||
// Restore restores the terminal connected to the given file descriptor to a
|
||||
// previous state.
|
||||
func Restore(fd int, state *State) error {
|
||||
return windows.SetConsoleMode(windows.Handle(fd), state.mode)
|
||||
}
|
||||
|
||||
// GetSize returns the dimensions of the given terminal.
|
||||
func GetSize(fd int) (width, height int, err error) {
|
||||
var info windows.ConsoleScreenBufferInfo
|
||||
if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
return int(info.Size.X), int(info.Size.Y), nil
|
||||
}
|
||||
|
||||
// ReadPassword reads a line of input from a terminal without local echo. This
|
||||
// is commonly used for inputting passwords and other sensitive data. The slice
|
||||
// returned does not include the \n.
|
||||
func ReadPassword(fd int) ([]byte, error) {
|
||||
var st uint32
|
||||
if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
old := st
|
||||
|
||||
st &^= (windows.ENABLE_ECHO_INPUT)
|
||||
st |= (windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
|
||||
if err := windows.SetConsoleMode(windows.Handle(fd), st); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer windows.SetConsoleMode(windows.Handle(fd), old)
|
||||
|
||||
var h windows.Handle
|
||||
p, _ := windows.GetCurrentProcess()
|
||||
if err := windows.DuplicateHandle(p, windows.Handle(fd), p, &h, 0, false, windows.DUPLICATE_SAME_ACCESS); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
f := os.NewFile(uintptr(h), "stdin")
|
||||
defer f.Close()
|
||||
return readPasswordLine(f)
|
||||
}
|
16
vendor/gitlab.com/golang-commonmark/html/.gitlab-ci.yml
generated
vendored
16
vendor/gitlab.com/golang-commonmark/html/.gitlab-ci.yml
generated
vendored
@@ -1,16 +0,0 @@
|
||||
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
10
vendor/gitlab.com/golang-commonmark/html/LICENSE
generated
vendored
@@ -1,10 +0,0 @@
|
||||
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
8
vendor/gitlab.com/golang-commonmark/html/README.md
generated
vendored
@@ -1,8 +0,0 @@
|
||||
html [](https://opensource.org/licenses/BSD-2-Clause) [](http://godoc.org/gitlab.com/golang-commonmark/html) [](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
211
vendor/gitlab.com/golang-commonmark/html/html.go
generated
vendored
@@ -1,211 +0,0 @@
|
||||
// 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(
|
||||
"&", "&",
|
||||
"<", "<",
|
||||
">", ">",
|
||||
`"`, """,
|
||||
)
|
||||
|
||||
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: // �
|
||||
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: // �
|
||||
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])
|
||||
}
|
2133
vendor/gitlab.com/golang-commonmark/html/html5_entities.go
generated
vendored
2133
vendor/gitlab.com/golang-commonmark/html/html5_entities.go
generated
vendored
File diff suppressed because it is too large
Load Diff
19
vendor/gitlab.com/golang-commonmark/linkify/.gitlab-ci.yml
generated
vendored
19
vendor/gitlab.com/golang-commonmark/linkify/.gitlab-ci.yml
generated
vendored
@@ -1,19 +0,0 @@
|
||||
image: golang:1.13
|
||||
|
||||
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
10
vendor/gitlab.com/golang-commonmark/linkify/LICENSE
generated
vendored
@@ -1,10 +0,0 @@
|
||||
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
12
vendor/gitlab.com/golang-commonmark/linkify/README.md
generated
vendored
@@ -1,12 +0,0 @@
|
||||
linkify [](https://opensource.org/licenses/BSD-2-Clause) [](http://godoc.org/gitlab.com/golang-commonmark/linkify) [](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
51
vendor/gitlab.com/golang-commonmark/linkify/charset.go
generated
vendored
@@ -1,51 +0,0 @@
|
||||
// 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
93
vendor/gitlab.com/golang-commonmark/linkify/email.go
generated
vendored
@@ -1,93 +0,0 @@
|
||||
// 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
18175
vendor/gitlab.com/golang-commonmark/linkify/generated.go
generated
vendored
File diff suppressed because it is too large
Load Diff
462
vendor/gitlab.com/golang-commonmark/linkify/linkify.go
generated
vendored
462
vendor/gitlab.com/golang-commonmark/linkify/linkify.go
generated
vendored
@@ -1,462 +0,0 @@
|
||||
// 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
404
vendor/gitlab.com/golang-commonmark/linkify/url.go
generated
vendored
@@ -1,404 +0,0 @@
|
||||
// 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 > 2 {
|
||||
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
20
vendor/gitlab.com/golang-commonmark/linkify/util.go
generated
vendored
@@ -1,20 +0,0 @@
|
||||
// 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
|
||||
}
|
25
vendor/gitlab.com/golang-commonmark/markdown/.gitlab-ci.yml
generated
vendored
25
vendor/gitlab.com/golang-commonmark/markdown/.gitlab-ci.yml
generated
vendored
@@ -1,25 +0,0 @@
|
||||
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
1
vendor/gitlab.com/golang-commonmark/markdown/AUTHORS
generated
vendored
@@ -1 +0,0 @@
|
||||
opennota@gmail.com
|
10
vendor/gitlab.com/golang-commonmark/markdown/LICENSE
generated
vendored
10
vendor/gitlab.com/golang-commonmark/markdown/LICENSE
generated
vendored
@@ -1,10 +0,0 @@
|
||||
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
64
vendor/gitlab.com/golang-commonmark/markdown/README.md
generated
vendored
@@ -1,64 +0,0 @@
|
||||
markdown [](http://godoc.org/gitlab.com/golang-commonmark/markdown) [](https://opensource.org/licenses/BSD-2-Clause) [](https://gitlab.com/golang-commonmark/markdown/commits/master) [](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
26
vendor/gitlab.com/golang-commonmark/markdown/align.go
generated
vendored
@@ -1,26 +0,0 @@
|
||||
// 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 ""
|
||||
}
|
70
vendor/gitlab.com/golang-commonmark/markdown/autolink.go
generated
vendored
70
vendor/gitlab.com/golang-commonmark/markdown/autolink.go
generated
vendored
@@ -1,70 +0,0 @@
|
||||
// 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
|
||||
}
|
60
vendor/gitlab.com/golang-commonmark/markdown/backticks.go
generated
vendored
60
vendor/gitlab.com/golang-commonmark/markdown/backticks.go
generated
vendored
@@ -1,60 +0,0 @@
|
||||
// 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
|
||||
}
|
43
vendor/gitlab.com/golang-commonmark/markdown/balance_pairs.go
generated
vendored
43
vendor/gitlab.com/golang-commonmark/markdown/balance_pairs.go
generated
vendored
@@ -1,43 +0,0 @@
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user