1
1
Fork 0
mirror of https://gitea.com/gitea/tea synced 2024-05-12 18:56:06 +02:00

Detect markdown line width, resolve relative URLs (#332)

~~this is semi-blocked by https://github.com/charmbracelet/glamour/pull/96, but behaviour isn't really worse than the previous behaviour (most links work, some are still broken)~~

#### testcase for link resolver
```
tea pr 332
tea checkout 332 && make install && tea pr 332
```

- [rel](./332)
- [abs](/gitea/tea/pulls/332)
- [full](https://gitea.com/gitea/tea/pulls/332)

Co-authored-by: Norwin Roosen <git@nroo.de>
Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://gitea.com/gitea/tea/pulls/332
Reviewed-by: 6543 <6543@obermui.de>
Reviewed-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: Norwin <noerw@noreply.gitea.io>
Co-committed-by: Norwin <noerw@noreply.gitea.io>
This commit is contained in:
Norwin 2021-03-12 20:28:46 +08:00 committed by Andrew Thornton
parent cb404b53b5
commit 222d0501df
82 changed files with 7709 additions and 316 deletions

2
go.mod
View File

@ -34,3 +34,5 @@ require (
golang.org/x/tools v0.1.0 // indirect
gopkg.in/yaml.v2 v2.4.0
)
replace github.com/charmbracelet/glamour => github.com/noerw/glamour v0.2.1-0.20210305125354-f0a29f1de0c2

30
go.sum
View File

@ -18,8 +18,8 @@ github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBb
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U=
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
github.com/alecthomas/chroma v0.7.3 h1:NfdAERMy+esYQs8OXk0I868/qDxxCEo7FMz1WIqMAeI=
github.com/alecthomas/chroma v0.7.3/go.mod h1:sko8vR34/90zvl5QdcUdvzL3J8NKjAUx9va9jPuFNoM=
github.com/alecthomas/chroma v0.8.1 h1:ym20sbvyC6RXz45u4qDglcgr8E313oPROshcuCHqiEE=
github.com/alecthomas/chroma v0.8.1/go.mod h1:sko8vR34/90zvl5QdcUdvzL3J8NKjAUx9va9jPuFNoM=
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo=
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
github.com/alecthomas/kong v0.2.4/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE=
@ -31,8 +31,10 @@ github.com/araddon/dateparse v0.0.0-20210207001429-0eec95c9db7e h1:OjdSMCht0ZVX7
github.com/araddon/dateparse v0.0.0-20210207001429-0eec95c9db7e/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/charmbracelet/glamour v0.2.0 h1:mTgaiNiumpqTZp3qVM6DH9UB0NlbY17wejoMf1kM8Pg=
github.com/charmbracelet/glamour v0.2.0/go.mod h1:UA27Kwj3QHialP74iU6C+Gpc8Y7IOAKupeKMLLBURWM=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/chris-ramon/douceur v0.2.0 h1:IDMEdxlEUUBYBKE4z/mJnFyVXox+MjuEVDJNN27glkU=
github.com/chris-ramon/douceur v0.2.0/go.mod h1:wDW5xjJdeoMm1mRt4sD4c/LbF/mWdEpRXQKjTR8nIBE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
@ -60,7 +62,8 @@ github.com/go-git/go-git/v5 v5.2.0 h1:YPBLG/3UK1we1ohRkncLjaXWLW+HKp5QNM/jTli2Jg
github.com/go-git/go-git/v5 v5.2.0/go.mod h1:kh02eMX+wdqqxgNMEyq8YgwlIOsDOa9homkUq1PoTMs=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f/go.mod h1:nOFQdrUlIlx6M6ODdSpBj1NVA+VgLC6kmw60mkw34H4=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI=
github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ=
@ -101,17 +104,18 @@ github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRC
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/microcosm-cc/bluemonday v1.0.2 h1:5lPfLTTAvAbtS0VqT+94yOtFnGfUWYyx0+iToC3Os3s=
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
github.com/microcosm-cc/bluemonday v1.0.4 h1:p0L+CTpo/PLFdkoPcJemLXG+fpMD7pYOoDEq1axMbGg=
github.com/microcosm-cc/bluemonday v1.0.4/go.mod h1:8iwZnFn2CDDNZ0r6UXhF4xawGvzaqzCRa1n3/lO3W2w=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/muesli/reflow v0.1.0 h1:oQdpLfO56lr5pgLvqD0TcjW85rDjSYSBVdiG1Ch1ddM=
github.com/muesli/reflow v0.1.0/go.mod h1:I9bWAt7QTg/que/qmUCJBGlj7wEq8OAFBjPNjc6xK4I=
github.com/muesli/termenv v0.6.0/go.mod h1:SohX91w6swWA4AYU+QmPx+aSgXhWO0juiyID9UZmbpA=
github.com/muesli/reflow v0.2.0 h1:2o0UBJPHHH4fa2GCXU4Rg4DwOtWPMekCeyc5EWbAQp0=
github.com/muesli/reflow v0.2.0/go.mod h1:qT22vjVmM9MIUeLgsVYe/Ye7eZlbv9dZjL3dVhUqLX8=
github.com/muesli/termenv v0.7.4 h1:/pBqvU5CpkY53tU0vVn+xgs2ZTX63aH5nY+SSps5Xa8=
github.com/muesli/termenv v0.7.4/go.mod h1:pZ7qY9l3F7e5xsAOS0zCew2tME+p7bWeBkotCEcIIcc=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/noerw/glamour v0.2.1-0.20210305125354-f0a29f1de0c2 h1:ACjOTGUGi7rt3JQU9GIFFs8sueFGShy6GcGjQhMmKjs=
github.com/noerw/glamour v0.2.1-0.20210305125354-f0a29f1de0c2/go.mod h1:WIVFX8Y2VIK1Y/1qtXYL/Vvzqlcbo3VgVop9i2piPkE=
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
@ -151,9 +155,11 @@ github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0B
github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.0/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.1 h1:eVwehsLsZlCJCwXyGLgg+Q4iFWE/eTIMG0e8waCmm/I=
github.com/yuin/goldmark v1.3.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os=
github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=

View File

@ -13,20 +13,27 @@ import (
// Comments renders a list of comments to stdout
func Comments(comments []*gitea.Comment) {
var baseURL string
if len(comments) != 0 {
baseURL = comments[0].HTMLURL
}
var out = make([]string, len(comments))
for i, c := range comments {
out[i] = formatComment(c)
baseURL = comments[i].HTMLURL
}
outputMarkdown(fmt.Sprintf(
// this will become a heading by means of the first --- from a comment
"Comments\n%s",
strings.Join(out, "\n"),
))
), baseURL)
}
// Comment renders a comment to stdout
func Comment(c *gitea.Comment) {
outputMarkdown(formatComment(c))
outputMarkdown(formatComment(c), c.HTMLURL)
}
func formatComment(c *gitea.Comment) string {

View File

@ -21,7 +21,7 @@ func IssueDetails(issue *gitea.Issue) {
issue.Poster.UserName,
FormatTime(issue.Created),
issue.Body,
))
), issue.HTMLURL)
}
// IssuesPullsList prints a listing of issues & pulls

View File

@ -28,7 +28,7 @@ func LoginDetails(login *config.Login) {
}
in += fmt.Sprintf("\nCreated: %s", time.Unix(login.Created, 0).Format(time.RFC822))
outputMarkdown(in)
outputMarkdown(in, "")
}
// LoginsList prints a listing of logins

View File

@ -6,15 +6,27 @@ package print
import (
"fmt"
"os"
"github.com/charmbracelet/glamour"
"golang.org/x/crypto/ssh/terminal"
)
// outputMarkdown prints markdown to stdout, formatted for terminals.
// If the input could not be parsed, it is printed unformatted, the error
// is returned anyway.
func outputMarkdown(markdown string) error {
out, err := glamour.Render(markdown, "auto")
func outputMarkdown(markdown string, baseURL string) error {
renderer, err := glamour.NewTermRenderer(
glamour.WithAutoStyle(),
glamour.WithBaseURL(baseURL),
glamour.WithWordWrap(getWordWrap()),
)
if err != nil {
fmt.Printf(markdown)
return err
}
out, err := renderer.Render(markdown)
if err != nil {
fmt.Printf(markdown)
return err
@ -22,3 +34,18 @@ func outputMarkdown(markdown string) error {
fmt.Print(out)
return nil
}
// stolen from https://github.com/charmbracelet/glow/blob/e9d728c/main.go#L152-L165
func getWordWrap() int {
fd := int(os.Stdout.Fd())
width := 80
if terminal.IsTerminal(fd) {
if w, _, err := terminal.GetSize(fd); err == nil {
width = w
}
}
if width > 120 {
width = 120
}
return width
}

View File

@ -76,7 +76,7 @@ func PullDetails(pr *gitea.PullRequest, reviews []*gitea.PullReview, ciStatus *g
}
}
outputMarkdown(out)
outputMarkdown(out, pr.HTMLURL)
}
func formatReviews(reviews []*gitea.PullReview) string {

View File

@ -87,7 +87,7 @@ func RepoDetails(repo *gitea.Repository, topics []string) {
urls,
perm,
tops,
))
), repo.HTMLURL)
}
// RepoFields are the available fields to print with ReposList()

View File

@ -20,6 +20,11 @@ linters:
- wsl
- gomnd
- gocognit
- goerr113
- nolintlint
- testpackage
- godot
- nestif
linters-settings:
govet:

View File

@ -4,7 +4,7 @@ go:
- "1.13.x"
script:
- go test -v ./...
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s v1.22.2
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s v1.26.0
- ./bin/golangci-lint run
- git clean -fdx .
after_success:

View File

@ -30,14 +30,14 @@ var Awk = internal.Register(MustNewLexer(
"root": {
{`^(?=\s|/)`, Text, Push("slashstartsregex")},
Include("commentsandwhitespace"),
{`\+\+|--|\|\||&&|in\b|\$|!?~|(\*\*|[-<>+*%\^/!=|])=?`, Operator, Push("slashstartsregex")},
{`\+\+|--|\|\||&&|in\b|\$|!?~|\|&|(\*\*|[-<>+*%\^/!=|])=?`, Operator, Push("slashstartsregex")},
{`[{(\[;,]`, Punctuation, Push("slashstartsregex")},
{`[})\].]`, Punctuation, nil},
{`(break|continue|do|while|exit|for|if|else|return)\b`, Keyword, Push("slashstartsregex")},
{`(break|continue|do|while|exit|for|if|else|return|switch|case|default)\b`, Keyword, Push("slashstartsregex")},
{`function\b`, KeywordDeclaration, Push("slashstartsregex")},
{`(atan2|cos|exp|int|log|rand|sin|sqrt|srand|gensub|gsub|index|length|match|split|sprintf|sub|substr|tolower|toupper|close|fflush|getline|next|nextfile|print|printf|strftime|systime|delete|system)\b`, KeywordReserved, nil},
{`(ARGC|ARGIND|ARGV|BEGIN|CONVFMT|ENVIRON|END|ERRNO|FIELDWIDTHS|FILENAME|FNR|FS|IGNORECASE|NF|NR|OFMT|OFS|ORFS|RLENGTH|RS|RSTART|RT|SUBSEP)\b`, NameBuiltin, nil},
{`[$a-zA-Z_]\w*`, NameOther, nil},
{`(atan2|cos|exp|int|log|rand|sin|sqrt|srand|gensub|gsub|index|length|match|split|patsplit|sprintf|sub|substr|tolower|toupper|close|fflush|getline|next(file)|print|printf|strftime|systime|mktime|delete|system|strtonum|and|compl|lshift|or|rshift|asorti?|isarray|bindtextdomain|dcn?gettext|@(include|load|namespace))\b`, KeywordReserved, nil},
{`(ARGC|ARGIND|ARGV|BEGIN(FILE)?|BINMODE|CONVFMT|ENVIRON|END(FILE)?|ERRNO|FIELDWIDTHS|FILENAME|FNR|FPAT|FS|IGNORECASE|LINT|NF|NR|OFMT|OFS|ORS|PROCINFO|RLENGTH|RS|RSTART|RT|SUBSEP|TEXTDOMAIN)\b`, NameBuiltin, nil},
{`[@$a-zA-Z_]\w*`, NameOther, nil},
{`[0-9][0-9]*\.[0-9]+([eE][0-9]+)?[fd]?`, LiteralNumberFloat, nil},
{`0x[0-9a-fA-F]+`, LiteralNumberHex, nil},
{`[0-9]+`, LiteralNumberInteger, nil},

View File

@ -36,7 +36,7 @@ var Bash = internal.Register(MustNewLexer(
{`\b(if|fi|else|while|do|done|for|then|return|function|case|select|continue|until|esac|elif)(\s*)\b`, ByGroups(Keyword, Text), nil},
{"\\b(alias|bg|bind|break|builtin|caller|cd|command|compgen|complete|declare|dirs|disown|echo|enable|eval|exec|exit|export|false|fc|fg|getopts|hash|help|history|jobs|kill|let|local|logout|popd|printf|pushd|pwd|read|readonly|set|shift|shopt|source|suspend|test|time|times|trap|true|type|typeset|ulimit|umask|unalias|unset|wait)(?=[\\s)`])", NameBuiltin, nil},
{`\A#!.+\n`, CommentPreproc, nil},
{`#.*\S`, CommentSingle, nil},
{`#.*(\S|$)`, CommentSingle, nil},
{`\\[\w\W]`, LiteralStringEscape, nil},
{`(\b\w+)(\s*)(\+?=)`, ByGroups(NameVariable, Text, Operator), nil},
{`[\[\]{}()=]`, Operator, nil},

View File

@ -0,0 +1,206 @@
package c
import (
. "github.com/alecthomas/chroma" // nolint
"github.com/alecthomas/chroma/lexers/internal"
)
// caddyfileCommon are the rules common to both of the lexer variants
var caddyfileCommon = Rules{
"site_block_common": {
// Import keyword
{`(import)(\s+)([^\s]+)`, ByGroups(Keyword, Text, NameVariableMagic), nil},
// Matcher definition
{`@[^\s]+(?=\s)`, NameDecorator, Push("matcher")},
// Matcher token stub for docs
{`\[\<matcher\>\]`, NameDecorator, Push("matcher")},
// These cannot have matchers but may have things that look like
// matchers in their arguments, so we just parse as a subdirective.
{`try_files`, Keyword, Push("subdirective")},
// These are special, they can nest more directives
{`handle_errors|handle|route|handle_path|not`, Keyword, Push("nested_directive")},
// Any other directive
{`[^\s#]+`, Keyword, Push("directive")},
Include("base"),
},
"matcher": {
{`\{`, Punctuation, Push("block")},
// Not can be one-liner
{`not`, Keyword, Push("deep_not_matcher")},
// Any other same-line matcher
{`[^\s#]+`, Keyword, Push("arguments")},
// Terminators
{`\n`, Text, Pop(1)},
{`\}`, Punctuation, Pop(1)},
Include("base"),
},
"block": {
{`\}`, Punctuation, Pop(2)},
// Not can be one-liner
{`not`, Keyword, Push("not_matcher")},
// Any other subdirective
{`[^\s#]+`, Keyword, Push("subdirective")},
Include("base"),
},
"nested_block": {
{`\}`, Punctuation, Pop(2)},
// Matcher definition
{`@[^\s]+(?=\s)`, NameDecorator, Push("matcher")},
// Something that starts with literally < is probably a docs stub
{`\<[^#]+\>`, Keyword, Push("nested_directive")},
// Any other directive
{`[^\s#]+`, Keyword, Push("nested_directive")},
Include("base"),
},
"not_matcher": {
{`\}`, Punctuation, Pop(2)},
{`\{(?=\s)`, Punctuation, Push("block")},
{`[^\s#]+`, Keyword, Push("arguments")},
{`\s+`, Text, nil},
},
"deep_not_matcher": {
{`\}`, Punctuation, Pop(2)},
{`\{(?=\s)`, Punctuation, Push("block")},
{`[^\s#]+`, Keyword, Push("deep_subdirective")},
{`\s+`, Text, nil},
},
"directive": {
{`\{(?=\s)`, Punctuation, Push("block")},
Include("matcher_token"),
Include("comments_pop_1"),
{`\n`, Text, Pop(1)},
Include("base"),
},
"nested_directive": {
{`\{(?=\s)`, Punctuation, Push("nested_block")},
Include("matcher_token"),
Include("comments_pop_1"),
{`\n`, Text, Pop(1)},
Include("base"),
},
"subdirective": {
{`\{(?=\s)`, Punctuation, Push("block")},
Include("comments_pop_1"),
{`\n`, Text, Pop(1)},
Include("base"),
},
"arguments": {
{`\{(?=\s)`, Punctuation, Push("block")},
Include("comments_pop_2"),
{`\\\n`, Text, nil}, // Skip escaped newlines
{`\n`, Text, Pop(2)},
Include("base"),
},
"deep_subdirective": {
{`\{(?=\s)`, Punctuation, Push("block")},
Include("comments_pop_3"),
{`\n`, Text, Pop(3)},
Include("base"),
},
"matcher_token": {
{`@[^\s]+`, NameDecorator, Push("arguments")}, // Named matcher
{`/[^\s]+`, NameDecorator, Push("arguments")}, // Path matcher
{`\*`, NameDecorator, Push("arguments")}, // Wildcard path matcher
{`\[\<matcher\>\]`, NameDecorator, Push("arguments")}, // Matcher token stub for docs
},
"comments": {
{`^#.*\n`, CommentSingle, nil}, // Comment at start of line
{`\s+#.*\n`, CommentSingle, nil}, // Comment preceded by whitespace
},
"comments_pop_1": {
{`^#.*\n`, CommentSingle, Pop(1)}, // Comment at start of line
{`\s+#.*\n`, CommentSingle, Pop(1)}, // Comment preceded by whitespace
},
"comments_pop_2": {
{`^#.*\n`, CommentSingle, Pop(2)}, // Comment at start of line
{`\s+#.*\n`, CommentSingle, Pop(2)}, // Comment preceded by whitespace
},
"comments_pop_3": {
{`^#.*\n`, CommentSingle, Pop(3)}, // Comment at start of line
{`\s+#.*\n`, CommentSingle, Pop(3)}, // Comment preceded by whitespace
},
"base": {
Include("comments"),
{`(on|off|first|last|before|after|internal|strip_prefix|strip_suffix|replace)\b`, NameConstant, nil},
{`(https?://)?([a-z0-9.-]+)(:)([0-9]+)`, ByGroups(Name, Name, Punctuation, LiteralNumberInteger), nil},
{`[a-z-]+/[a-z-+]+`, LiteralString, nil},
{`[0-9]+[km]?\b`, LiteralNumberInteger, nil},
{`\{[\w+.\$-]+\}`, LiteralStringEscape, nil}, // Placeholder
{`\[(?=[^#{}$]+\])`, Punctuation, nil},
{`\]|\|`, Punctuation, nil},
{`[^\s#{}$\]]+`, LiteralString, nil},
{`/[^\s#]*`, Name, nil},
{`\s+`, Text, nil},
},
}
// Caddyfile lexer.
var Caddyfile = internal.Register(MustNewLexer(
&Config{
Name: "Caddyfile",
Aliases: []string{"caddyfile", "caddy"},
Filenames: []string{"Caddyfile*"},
MimeTypes: []string{},
},
Rules{
"root": {
Include("comments"),
// Global options block
{`^\s*(\{)\s*$`, ByGroups(Punctuation), Push("globals")},
// Snippets
{`(\([^\s#]+\))(\s*)(\{)`, ByGroups(NameVariableAnonymous, Text, Punctuation), Push("snippet")},
// Site label
{`[^#{(\s,]+`, GenericHeading, Push("label")},
// Site label with placeholder
{`\{[\w+.\$-]+\}`, LiteralStringEscape, Push("label")},
{`\s+`, Text, nil},
},
"globals": {
{`\}`, Punctuation, Pop(1)},
{`[^\s#]+`, Keyword, Push("directive")},
Include("base"),
},
"snippet": {
{`\}`, Punctuation, Pop(1)},
// Matcher definition
{`@[^\s]+(?=\s)`, NameDecorator, Push("matcher")},
// Any directive
{`[^\s#]+`, Keyword, Push("directive")},
Include("base"),
},
"label": {
// Allow multiple labels, comma separated, newlines after
// a comma means another label is coming
{`,\s*\n?`, Text, nil},
{` `, Text, nil},
// Site label with placeholder
{`\{[\w+.\$-]+\}`, LiteralStringEscape, nil},
// Site label
{`[^#{(\s,]+`, GenericHeading, nil},
// Comment after non-block label (hack because comments end in \n)
{`#.*\n`, CommentSingle, Push("site_block")},
// Note: if \n, we'll never pop out of the site_block, it's valid
{`\{(?=\s)|\n`, Punctuation, Push("site_block")},
},
"site_block": {
{`\}`, Punctuation, Pop(2)},
Include("site_block_common"),
},
}.Merge(caddyfileCommon),
))
// Caddyfile directive-only lexer.
var CaddyfileDirectives = internal.Register(MustNewLexer(
&Config{
Name: "Caddyfile Directives",
Aliases: []string{"caddyfile-directives", "caddyfile-d", "caddy-d"},
Filenames: []string{},
MimeTypes: []string{},
},
Rules{
// Same as "site_block" in Caddyfile
"root": {
Include("site_block_common"),
},
}.Merge(caddyfileCommon),
))

View File

@ -1,15 +1,12 @@
package circular
import (
"strings"
. "github.com/alecthomas/chroma" // nolint
"github.com/alecthomas/chroma/lexers/h"
"github.com/alecthomas/chroma/lexers/internal"
)
// PHP lexer.
var PHP = internal.Register(DelegatingLexer(h.HTML, MustNewLexer(
// PHP lexer for pure PHP code (not embedded in HTML).
var PHP = internal.Register(MustNewLexer(
&Config{
Name: "PHP",
Aliases: []string{"php", "php3", "php4", "php5"},
@ -19,73 +16,65 @@ var PHP = internal.Register(DelegatingLexer(h.HTML, MustNewLexer(
CaseInsensitive: true,
EnsureNL: true,
},
Rules{
"root": {
{`<\?(php)?`, CommentPreproc, Push("php")},
{`[^<]+`, Other, nil},
{`<`, Other, nil},
},
"php": {
{`\?>`, CommentPreproc, Pop(1)},
{`(<<<)([\'"]?)((?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*)(\2\n.*?\n\s*)(\3)(;?)(\n)`, ByGroups(LiteralString, LiteralString, LiteralStringDelimiter, LiteralString, LiteralStringDelimiter, Punctuation, Text), nil},
{`\s+`, Text, nil},
{`#.*?\n`, CommentSingle, nil},
{`//.*?\n`, CommentSingle, nil},
{`/\*\*/`, CommentMultiline, nil},
{`/\*\*.*?\*/`, LiteralStringDoc, nil},
{`/\*.*?\*/`, CommentMultiline, nil},
{`(->|::)(\s*)((?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*)`, ByGroups(Operator, Text, NameAttribute), nil},
{`[~!%^&*+=|:.<>/@-]+`, Operator, nil},
{`\?`, Operator, nil},
{`[\[\]{}();,]+`, Punctuation, nil},
{`(class)(\s+)`, ByGroups(Keyword, Text), Push("classname")},
{`(function)(\s*)(?=\()`, ByGroups(Keyword, Text), nil},
{`(function)(\s+)(&?)(\s*)`, ByGroups(Keyword, Text, Operator, Text), Push("functionname")},
{`(const)(\s+)((?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*)`, ByGroups(Keyword, Text, NameConstant), nil},
{`(and|E_PARSE|old_function|E_ERROR|or|as|E_WARNING|parent|eval|PHP_OS|break|exit|case|extends|PHP_VERSION|cfunction|FALSE|print|for|require|continue|foreach|require_once|declare|return|default|static|do|switch|die|stdClass|echo|else|TRUE|elseif|var|empty|if|xor|enddeclare|include|virtual|endfor|include_once|while|endforeach|global|endif|list|endswitch|new|endwhile|not|array|E_ALL|NULL|final|php_user_filter|interface|implements|public|private|protected|abstract|clone|try|catch|throw|this|use|namespace|trait|yield|finally)\b`, Keyword, nil},
{`(true|false|null)\b`, KeywordConstant, nil},
Include("magicconstants"),
{`\$\{\$+(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*\}`, NameVariable, nil},
{`\$+(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*`, NameVariable, nil},
{`(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*`, NameOther, nil},
{`(\d+\.\d*|\d*\.\d+)(e[+-]?[0-9]+)?`, LiteralNumberFloat, nil},
{`\d+e[+-]?[0-9]+`, LiteralNumberFloat, nil},
{`0[0-7]+`, LiteralNumberOct, nil},
{`0x[a-f0-9]+`, LiteralNumberHex, nil},
{`\d+`, LiteralNumberInteger, nil},
{`0b[01]+`, LiteralNumberBin, nil},
{`'([^'\\]*(?:\\.[^'\\]*)*)'`, LiteralStringSingle, nil},
{"`([^`\\\\]*(?:\\\\.[^`\\\\]*)*)`", LiteralStringBacktick, nil},
{`"`, LiteralStringDouble, Push("string")},
},
"magicfuncs": {
{Words(``, `\b`, `__construct`, `__destruct`, `__call`, `__callStatic`, `__get`, `__set`, `__isset`, `__unset`, `__sleep`, `__wakeup`, `__toString`, `__invoke`, `__set_state`, `__clone`, `__debugInfo`), NameFunctionMagic, nil},
},
"magicconstants": {
{Words(``, `\b`, `__LINE__`, `__FILE__`, `__DIR__`, `__FUNCTION__`, `__CLASS__`, `__TRAIT__`, `__METHOD__`, `__NAMESPACE__`), NameConstant, nil},
},
"classname": {
{`(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*`, NameClass, Pop(1)},
},
"functionname": {
Include("magicfuncs"),
{`(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*`, NameFunction, Pop(1)},
Default(Pop(1)),
},
"string": {
{`"`, LiteralStringDouble, Pop(1)},
{`[^{$"\\]+`, LiteralStringDouble, nil},
{`\\([nrt"$\\]|[0-7]{1,3}|x[0-9a-f]{1,2})`, LiteralStringEscape, nil},
{`\$(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*(\[\S+?\]|->(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*)?`, LiteralStringInterpol, nil},
{`(\{\$\{)(.*?)(\}\})`, ByGroups(LiteralStringInterpol, UsingSelf("root"), LiteralStringInterpol), nil},
{`(\{)(\$.*?)(\})`, ByGroups(LiteralStringInterpol, UsingSelf("root"), LiteralStringInterpol), nil},
{`(\$\{)(\S+)(\})`, ByGroups(LiteralStringInterpol, NameVariable, LiteralStringInterpol), nil},
{`[${\\]`, LiteralStringDouble, nil},
},
phpCommonRules.Rename("php", "root"),
))
var phpCommonRules = Rules{
"php": {
{`\?>`, CommentPreproc, Pop(1)},
{`(<<<)([\'"]?)((?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*)(\2\n.*?\n\s*)(\3)(;?)(\n)`, ByGroups(LiteralString, LiteralString, LiteralStringDelimiter, LiteralString, LiteralStringDelimiter, Punctuation, Text), nil},
{`\s+`, Text, nil},
{`#.*?\n`, CommentSingle, nil},
{`//.*?\n`, CommentSingle, nil},
{`/\*\*/`, CommentMultiline, nil},
{`/\*\*.*?\*/`, LiteralStringDoc, nil},
{`/\*.*?\*/`, CommentMultiline, nil},
{`(->|::)(\s*)((?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*)`, ByGroups(Operator, Text, NameAttribute), nil},
{`[~!%^&*+=|:.<>/@-]+`, Operator, nil},
{`\?`, Operator, nil},
{`[\[\]{}();,]+`, Punctuation, nil},
{`(class)(\s+)`, ByGroups(Keyword, Text), Push("classname")},
{`(function)(\s*)(?=\()`, ByGroups(Keyword, Text), nil},
{`(function)(\s+)(&?)(\s*)`, ByGroups(Keyword, Text, Operator, Text), Push("functionname")},
{`(const)(\s+)((?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*)`, ByGroups(Keyword, Text, NameConstant), nil},
{`(and|E_PARSE|old_function|E_ERROR|or|as|E_WARNING|parent|eval|PHP_OS|break|exit|case|extends|PHP_VERSION|cfunction|FALSE|print|for|require|continue|foreach|require_once|declare|return|default|static|do|switch|die|stdClass|echo|else|TRUE|elseif|var|empty|if|xor|enddeclare|include|virtual|endfor|include_once|while|endforeach|global|endif|list|endswitch|new|endwhile|not|array|E_ALL|NULL|final|php_user_filter|interface|implements|public|private|protected|abstract|clone|try|catch|throw|this|use|namespace|trait|yield|finally)\b`, Keyword, nil},
{`(true|false|null)\b`, KeywordConstant, nil},
Include("magicconstants"),
{`\$\{\$+(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*\}`, NameVariable, nil},
{`\$+(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*`, NameVariable, nil},
{`(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*`, NameOther, nil},
{`(\d+\.\d*|\d*\.\d+)(e[+-]?[0-9]+)?`, LiteralNumberFloat, nil},
{`\d+e[+-]?[0-9]+`, LiteralNumberFloat, nil},
{`0[0-7]+`, LiteralNumberOct, nil},
{`0x[a-f0-9]+`, LiteralNumberHex, nil},
{`\d+`, LiteralNumberInteger, nil},
{`0b[01]+`, LiteralNumberBin, nil},
{`'([^'\\]*(?:\\.[^'\\]*)*)'`, LiteralStringSingle, nil},
{"`([^`\\\\]*(?:\\\\.[^`\\\\]*)*)`", LiteralStringBacktick, nil},
{`"`, LiteralStringDouble, Push("string")},
},
).SetAnalyser(func(text string) float32 {
if strings.Contains(text, "<?php") {
return 0.5
}
return 0.0
})))
"magicfuncs": {
{Words(``, `\b`, `__construct`, `__destruct`, `__call`, `__callStatic`, `__get`, `__set`, `__isset`, `__unset`, `__sleep`, `__wakeup`, `__toString`, `__invoke`, `__set_state`, `__clone`, `__debugInfo`), NameFunctionMagic, nil},
},
"magicconstants": {
{Words(``, `\b`, `__LINE__`, `__FILE__`, `__DIR__`, `__FUNCTION__`, `__CLASS__`, `__TRAIT__`, `__METHOD__`, `__NAMESPACE__`), NameConstant, nil},
},
"classname": {
{`(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*`, NameClass, Pop(1)},
},
"functionname": {
Include("magicfuncs"),
{`(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*`, NameFunction, Pop(1)},
Default(Pop(1)),
},
"string": {
{`"`, LiteralStringDouble, Pop(1)},
{`[^{$"\\]+`, LiteralStringDouble, nil},
{`\\([nrt"$\\]|[0-7]{1,3}|x[0-9a-f]{1,2})`, LiteralStringEscape, nil},
{`\$(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*(\[\S+?\]|->(?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*)?`, LiteralStringInterpol, nil},
{`(\{\$\{)(.*?)(\}\})`, ByGroups(LiteralStringInterpol, UsingSelf("root"), LiteralStringInterpol), nil},
{`(\{)(\$.*?)(\})`, ByGroups(LiteralStringInterpol, UsingSelf("root"), LiteralStringInterpol), nil},
{`(\$\{)(\S+)(\})`, ByGroups(LiteralStringInterpol, NameVariable, LiteralStringInterpol), nil},
{`[${\\]`, LiteralStringDouble, nil},
},
}

View File

@ -0,0 +1,34 @@
package circular
import (
"strings"
. "github.com/alecthomas/chroma" // nolint
"github.com/alecthomas/chroma/lexers/h"
"github.com/alecthomas/chroma/lexers/internal"
)
// PHTML lexer is PHP in HTML.
var PHTML = internal.Register(DelegatingLexer(h.HTML, MustNewLexer(
&Config{
Name: "PHTML",
Aliases: []string{"phtml"},
Filenames: []string{"*.phtml"},
MimeTypes: []string{"application/x-php", "application/x-httpd-php", "application/x-httpd-php3", "application/x-httpd-php4", "application/x-httpd-php5"},
DotAll: true,
CaseInsensitive: true,
EnsureNL: true,
},
Rules{
"root": {
{`<\?(php)?`, CommentPreproc, Push("php")},
{`[^<]+`, Other, nil},
{`<`, Other, nil},
},
}.Merge(phpCommonRules),
).SetAnalyser(func(text string) float32 {
if strings.Contains(text, "<?php") {
return 0.5
}
return 0.0
})))

View File

@ -28,6 +28,13 @@ var Elixir = internal.Register(MustNewLexer(
{`:"`, LiteralStringSymbol, Push("string_double_atom")},
{`:'`, LiteralStringSymbol, Push("string_single_atom")},
{`((?:\.\.\.|<<>>|%\{\}|%|\{\})|(?:(?:\.\.\.|[a-z_]\w*[!?]?)|[A-Z]\w*(?:\.[A-Z]\w*)*|(?:\<\<\<|\>\>\>|\|\|\||\&\&\&|\^\^\^|\~\~\~|\=\=\=|\!\=\=|\~\>\>|\<\~\>|\|\~\>|\<\|\>|\=\=|\!\=|\<\=|\>\=|\&\&|\|\||\<\>|\+\+|\-\-|\|\>|\=\~|\-\>|\<\-|\||\.|\=|\~\>|\<\~|\<|\>|\+|\-|\*|\/|\!|\^|\&)))(:)(?=\s|\n)`, ByGroups(LiteralStringSymbol, Punctuation), nil},
{`(fn|do|end|after|else|rescue|catch)\b`, Keyword, nil},
{`(not|and|or|when|in)\b`, OperatorWord, nil},
{`(case|cond|for|if|unless|try|receive|raise|quote|unquote|unquote_splicing|throw|super|while)\b`, Keyword, nil},
{`(def|defp|defmodule|defprotocol|defmacro|defmacrop|defdelegate|defexception|defstruct|defimpl|defcallback)\b`, KeywordDeclaration, nil},
{`(import|require|use|alias)\b`, KeywordNamespace, nil},
{`(nil|true|false)\b`, NameConstant, nil},
{`(_|__MODULE__|__DIR__|__ENV__|__CALLER__)\b`, NamePseudo, nil},
{`@(?:\.\.\.|[a-z_]\w*[!?]?)`, NameAttribute, nil},
{`(?:\.\.\.|[a-z_]\w*[!?]?)`, Name, nil},
{`(%?)([A-Z]\w*(?:\.[A-Z]\w*)*)`, ByGroups(Punctuation, NameClass), nil},

View File

@ -15,6 +15,7 @@ var Go = internal.Register(MustNewLexer(
Aliases: []string{"go", "golang"},
Filenames: []string{"*.go"},
MimeTypes: []string{"text/x-gosrc"},
EnsureNL: true,
},
Rules{
"root": {

View File

@ -19,8 +19,8 @@ var HTTP = internal.Register(httpBodyContentTypeLexer(MustNewLexer(
},
Rules{
"root": {
{`(GET|POST|PUT|DELETE|HEAD|OPTIONS|TRACE|PATCH|CONNECT)( +)([^ ]+)( +)(HTTP)(/)(1\.[01])(\r?\n|\Z)`, ByGroups(NameFunction, Text, NameNamespace, Text, KeywordReserved, Operator, LiteralNumber, Text), Push("headers")},
{`(HTTP)(/)(1\.[01])( +)(\d{3})( +)([^\r\n]+)(\r?\n|\Z)`, ByGroups(KeywordReserved, Operator, LiteralNumber, Text, LiteralNumber, Text, NameException, Text), Push("headers")},
{`(GET|POST|PUT|DELETE|HEAD|OPTIONS|TRACE|PATCH|CONNECT)( +)([^ ]+)( +)(HTTP)(/)([12]\.[01])(\r?\n|\Z)`, ByGroups(NameFunction, Text, NameNamespace, Text, KeywordReserved, Operator, LiteralNumber, Text), Push("headers")},
{`(HTTP)(/)([12]\.[01])( +)(\d{3})( +)([^\r\n]+)(\r?\n|\Z)`, ByGroups(KeywordReserved, Operator, LiteralNumber, Text, LiteralNumber, Text, NameException, Text), Push("headers")},
},
"headers": {
{`([^\s:]+)( *)(:)( *)([^\r\n]+)(\r?\n|\Z)`, EmitterFunc(httpHeaderBlock), nil},

View File

@ -10,7 +10,7 @@ var Ini = internal.Register(MustNewLexer(
&Config{
Name: "INI",
Aliases: []string{"ini", "cfg", "dosini"},
Filenames: []string{"*.ini", "*.cfg", "*.inf", ".gitconfig"},
Filenames: []string{"*.ini", "*.cfg", "*.inf", ".gitconfig", ".editorconfig"},
MimeTypes: []string{"text/x-ini", "text/inf"},
},
Rules{

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -24,32 +24,71 @@ var Kotlin = internal.Register(MustNewLexer(
{`//[^\n]*\n?`, CommentSingle, nil},
{`/[*].*?[*]/`, CommentMultiline, nil},
{`\n`, Text, nil},
{`::|!!|\?[:.]`, Operator, nil},
{`[~!%^&*()+=|\[\]:;,.<>/?-]`, Punctuation, nil},
{`!==|!in|!is|===`, Operator, nil},
{`%=|&&|\*=|\+\+|\+=|--|-=|->|\.\.|\/=|::|<=|==|>=|!!|!=|\|\||\?[:.]`, Operator, nil},
{`[~!%^&*()+=|\[\]:;,.<>\/?-]`, Punctuation, nil},
{`[{}]`, Punctuation, nil},
{`"""[^"]*"""`, LiteralString, nil},
{`"(\\\\|\\"|[^"\n])*["\n]`, LiteralString, nil},
{`"""`, LiteralString, Push("rawstring")},
{`"`, LiteralStringDouble, Push("string")},
{`(')(\\u[0-9a-fA-F]{4})(')`, ByGroups(LiteralStringChar, LiteralStringEscape, LiteralStringChar), nil},
{`'\\.'|'[^\\]'`, LiteralStringChar, nil},
{`0[xX][0-9a-fA-F]+[Uu]?[Ll]?|[0-9]+(\.[0-9]*)?([eE][+-][0-9]+)?[fF]?[Uu]?[Ll]?`, LiteralNumber, nil},
{`(companion)(\s+)(object)`, ByGroups(Keyword, Text, Keyword), nil},
{`(class|interface|object)(\s+)`, ByGroups(Keyword, Text), Push("class")},
{`(package|import)(\s+)`, ByGroups(Keyword, Text), Push("package")},
{`(val|var)(\s+)`, ByGroups(Keyword, Text), Push("property")},
{`(fun)(\s+)(<[^>]*>\s+)?`, ByGroups(Keyword, Text, Text), Push("function")},
{`(abstract|actual|annotation|as|break|by|catch|class|companion|const|constructor|continue|crossinline|data|do|dynamic|else|enum|expect|external|false|final|finally|for|fun|get|if|import|in|infix|inline|inner|interface|internal|is|lateinit|noinline|null|object|open|operator|out|override|package|private|protected|public|reified|return|sealed|set|super|suspend|tailrec|this|throw|true|try|val|var|vararg|when|where|while)\b`, Keyword, nil},
{"(@?[" + kotlinIdentifier + "]*`)", Name, nil},
{`(fun)(\s+)`, ByGroups(Keyword, Text), Push("function")},
{`(abstract|actual|annotation|as|as\?|break|by|catch|class|companion|const|constructor|continue|crossinline|data|delegate|do|dynamic|else|enum|expect|external|false|field|file|final|finally|for|fun|get|if|import|in|infix|init|inline|inner|interface|internal|is|it|lateinit|noinline|null|object|open|operator|out|override|package|param|private|property|protected|public|receiver|reified|return|sealed|set|setparam|super|suspend|tailrec|this|throw|true|try|typealias|typeof|val|var|vararg|when|where|while)\b`, Keyword, nil},
{`@[` + kotlinIdentifier + `]+`, NameDecorator, nil},
{`[` + kotlinIdentifier + `]+`, Name, nil},
},
"package": {
{`\S+`, NameNamespace, Pop(1)},
},
"class": {
{"(@?[" + kotlinIdentifier + "]*`)", NameClass, Pop(1)},
// \x60 is the back tick character (`)
{`\x60[^\x60]+?\x60`, NameClass, Pop(1)},
{`[` + kotlinIdentifier + `]+`, NameClass, Pop(1)},
},
"property": {
{"(@?[" + kotlinIdentifier + " ]*`)", NameProperty, Pop(1)},
{`\x60[^\x60]+?\x60`, NameProperty, Pop(1)},
{`[` + kotlinIdentifier + `]+`, NameProperty, Pop(1)},
},
"generics-specification": {
{`<`, Punctuation, Push("generics-specification")}, // required for generics inside generics e.g. <T : List<Int> >
{`>`, Punctuation, Pop(1)},
{`[,:*?]`, Punctuation, nil},
{`(in|out|reified)`, Keyword, nil},
{`\x60[^\x60]+?\x60`, NameClass, nil},
{`[` + kotlinIdentifier + `]+`, NameClass, nil},
{`\s+`, Text, nil},
},
"function": {
{"(@?[" + kotlinIdentifier + " ]*`)", NameFunction, Pop(1)},
{`<`, Punctuation, Push("generics-specification")},
{`\x60[^\x60]+?\x60`, NameFunction, Pop(1)},
{`[` + kotlinIdentifier + `]+`, NameFunction, Pop(1)},
{`\s+`, Text, nil},
},
"rawstring": {
// raw strings don't allow character escaping
{`"""`, LiteralString, Pop(1)},
{`(?:[^$"]+|\"{1,2}[^"])+`, LiteralString, nil},
Include("string-interpol"),
// remaining dollar signs are just a string
{`\$`, LiteralString, nil},
},
"string": {
{`\\[tbnr'"\\\$]`, LiteralStringEscape, nil},
{`\\u[0-9a-fA-F]{4}`, LiteralStringEscape, nil},
{`"`, LiteralStringDouble, Pop(1)},
Include("string-interpol"),
{`[^\n\\"$]+`, LiteralStringDouble, nil},
// remaining dollar signs are just a string
{`\$`, LiteralStringDouble, nil},
},
"string-interpol": {
{`\$[` + kotlinIdentifier + `]+`, LiteralStringInterpol, nil},
{`\${[^}\n]*}`, LiteralStringInterpol, nil},
},
},
))

View File

@ -32,6 +32,7 @@ import (
_ "github.com/alecthomas/chroma/lexers/w"
_ "github.com/alecthomas/chroma/lexers/x"
_ "github.com/alecthomas/chroma/lexers/y"
_ "github.com/alecthomas/chroma/lexers/z"
)
// Registry of Lexers.

59
vendor/github.com/alecthomas/chroma/lexers/p/pony.go generated vendored Normal file
View File

@ -0,0 +1,59 @@
package p
import (
. "github.com/alecthomas/chroma" // nolint
"github.com/alecthomas/chroma/lexers/internal"
)
// Pony lexer.
var Pony = internal.Register(MustNewLexer(
&Config{
Name: "Pony",
Aliases: []string{"pony"},
Filenames: []string{"*.pony"},
MimeTypes: []string{},
},
Rules{
"root": {
{`\n`, Text, nil},
{`[^\S\n]+`, Text, nil},
{`//.*\n`, CommentSingle, nil},
{`/\*`, CommentMultiline, Push("nested_comment")},
{`"""(?:.|\n)*?"""`, LiteralStringDoc, nil},
{`"`, LiteralString, Push("string")},
{`\'.*\'`, LiteralStringChar, nil},
{`=>|[]{}:().~;,|&!^?[]`, Punctuation, nil},
{Words(``, `\b`, `addressof`, `and`, `as`, `consume`, `digestof`, `is`, `isnt`, `not`, `or`), OperatorWord, nil},
{`!=|==|<<|>>|[-+/*%=<>]`, Operator, nil},
{Words(``, `\b`, `box`, `break`, `compile_error`, `compile_intrinsic`, `continue`, `do`, `else`, `elseif`, `embed`, `end`, `error`, `for`, `if`, `ifdef`, `in`, `iso`, `lambda`, `let`, `match`, `object`, `recover`, `ref`, `repeat`, `return`, `tag`, `then`, `this`, `trn`, `try`, `until`, `use`, `var`, `val`, `where`, `while`, `with`, `#any`, `#read`, `#send`, `#share`), Keyword, nil},
{`(actor|class|struct|primitive|interface|trait|type)((?:\s)+)`, ByGroups(Keyword, Text), Push("typename")},
{`(new|fun|be)((?:\s)+)`, ByGroups(Keyword, Text), Push("methodname")},
{Words(``, `\b`, `U8`, `U16`, `U32`, `U64`, `ULong`, `USize`, `U128`, `Unsigned`, `Stringable`, `String`, `StringBytes`, `StringRunes`, `InputNotify`, `InputStream`, `Stdin`, `ByteSeq`, `ByteSeqIter`, `OutStream`, `StdStream`, `SourceLoc`, `I8`, `I16`, `I32`, `I64`, `ILong`, `ISize`, `I128`, `Signed`, `Seq`, `RuntimeOptions`, `Real`, `Integer`, `SignedInteger`, `UnsignedInteger`, `FloatingPoint`, `Number`, `Int`, `ReadSeq`, `ReadElement`, `Pointer`, `Platform`, `NullablePointer`, `None`, `Iterator`, `F32`, `F64`, `Float`, `Env`, `DoNotOptimise`, `DisposableActor`, `Less`, `Equal`, `Greater`, `Compare`, `HasEq`, `Equatable`, `Comparable`, `Bool`, `AsioEventID`, `AsioEventNotify`, `AsioEvent`, `Array`, `ArrayKeys`, `ArrayValues`, `ArrayPairs`, `Any`, `AmbientAuth`), KeywordType, nil},
{`_?[A-Z]\w*`, NameClass, nil},
{`string\(\)`, NameOther, nil},
{`(\d+\.\d*|\.\d+|\d+)[eE][+-]?\d+`, LiteralNumberFloat, nil},
{`0x[0-9a-fA-F]+`, LiteralNumberHex, nil},
{`\d+`, LiteralNumberInteger, nil},
{`(true|false)\b`, Keyword, nil},
{`_\d*`, Name, nil},
{`_?[a-z][\w\'_]*`, Name, nil},
},
"typename": {
{`(iso|trn|ref|val|box|tag)?((?:\s)*)(_?[A-Z]\w*)`, ByGroups(Keyword, Text, NameClass), Pop(1)},
},
"methodname": {
{`(iso|trn|ref|val|box|tag)?((?:\s)*)(_?[a-z]\w*)`, ByGroups(Keyword, Text, NameFunction), Pop(1)},
},
"nested_comment": {
{`[^*/]+`, CommentMultiline, nil},
{`/\*`, CommentMultiline, Push()},
{`\*/`, CommentMultiline, Pop(1)},
{`[*/]`, CommentMultiline, nil},
},
"string": {
{`"`, LiteralString, Pop(1)},
{`\\"`, LiteralString, nil},
{`[^\\"]+`, LiteralString, nil},
},
},
))

View File

@ -22,7 +22,7 @@ var TOML = internal.Register(MustNewLexer(
{`[+-]?[0-9](_?\d)*`, LiteralNumberInteger, nil},
{`"(\\\\|\\"|[^"])*"`, StringDouble, nil},
{`'(\\\\|\\'|[^'])*'`, StringSingle, nil},
{`[.,=\[\]]`, Punctuation, nil},
{`[.,=\[\]{}]`, Punctuation, nil},
{`[^\W\d]\w*`, NameOther, nil},
},
},

View File

@ -38,14 +38,14 @@ var TypeScript = internal.Register(MustNewLexer(
{`\+\+|--|~|&&|\?|:|\|\||\\(?=\n)|(<<|>>>?|==?|!=?|[-<>+*%&|^/])=?`, Operator, Push("slashstartsregex")},
{`[{(\[;,]`, Punctuation, Push("slashstartsregex")},
{`[})\].]`, Punctuation, nil},
{`(for|in|while|do|break|return|continue|switch|case|default|if|else|throw|try|catch|finally|new|delete|typeof|instanceof|void|this)\b`, Keyword, Push("slashstartsregex")},
{`(for|in|of|while|do|break|return|yield|continue|switch|case|default|if|else|throw|try|catch|finally|new|delete|typeof|instanceof|keyof|asserts|is|infer|await|void|this)\b`, Keyword, Push("slashstartsregex")},
{`(var|let|with|function)\b`, KeywordDeclaration, Push("slashstartsregex")},
{`(abstract|boolean|byte|char|class|const|debugger|double|enum|export|extends|final|float|goto|implements|import|int|interface|long|native|package|private|protected|public|short|static|super|synchronized|throws|transient|volatile)\b`, KeywordReserved, nil},
{`(abstract|async|boolean|class|const|debugger|enum|export|extends|from|get|global|goto|implements|import|interface|namespace|package|private|protected|public|readonly|require|set|static|super|type)\b`, KeywordReserved, nil},
{`(true|false|null|NaN|Infinity|undefined)\b`, KeywordConstant, nil},
{`(Array|Boolean|Date|Error|Function|Math|netscape|Number|Object|Packages|RegExp|String|sun|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|Error|eval|isFinite|isNaN|parseFloat|parseInt|document|this|window)\b`, NameBuiltin, nil},
{`(Array|Boolean|Date|Error|Function|Math|Number|Object|Packages|RegExp|String|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|eval|isFinite|isNaN|parseFloat|parseInt|document|this|window)\b`, NameBuiltin, nil},
{`\b(module)(\s*)(\s*[\w?.$][\w?.$]*)(\s*)`, ByGroups(KeywordReserved, Text, NameOther, Text), Push("slashstartsregex")},
{`\b(string|bool|number)\b`, KeywordType, nil},
{`\b(constructor|declare|interface|as|AS)\b`, KeywordReserved, nil},
{`\b(string|bool|number|any|never|object|symbol|unique|unknown|bigint)\b`, KeywordType, nil},
{`\b(constructor|declare|interface|as)\b`, KeywordReserved, nil},
{`(super)(\s*)(\([\w,?.$\s]+\s*\))`, ByGroups(KeywordReserved, Text), Push("slashstartsregex")},
{`([a-zA-Z_?.$][\w?.$]*)\(\) \{`, NameOther, Push("slashstartsregex")},
{`([\w?.$][\w?.$]*)(\s*:\s*)([\w?.$][\w?.$]*)`, ByGroups(NameOther, Text, KeywordType), nil},

View File

@ -15,32 +15,36 @@ var YAML = internal.Register(MustNewLexer(
Rules{
"root": {
Include("whitespace"),
{`^---`, Text, nil},
{`^---`, NameNamespace, nil},
{`^\.\.\.`, NameNamespace, nil},
{`[\n?]?\s*- `, Text, nil},
{`#.*$`, Comment, nil},
{`!![^\s]+`, CommentPreproc, nil},
{`&[^\s]+`, CommentPreproc, nil},
{`\*[^\s]+`, CommentPreproc, nil},
{`^%include\s+[^\n\r]+`, CommentPreproc, nil},
{`([>|+-]\s+)(\s+)((?:(?:.*?$)(?:[\n\r]*?)?)*)`, ByGroups(StringDoc, StringDoc, StringDoc), nil},
Include("key"),
Include("value"),
{`[?:,\[\]]`, Punctuation, nil},
{`.`, Text, nil},
},
"value": {
{Words(``, `\b`, "true", "false", "null"), KeywordConstant, nil},
{`([>|](?:[+-])?)(\n(^ {1,})(?:.*\n*(?:^\3 *).*)*)`, ByGroups(Punctuation, StringDoc, Whitespace), nil},
{Words(``, `\b`, "true", "True", "TRUE", "false", "False", "FALSE", "null",
"y", "Y", "yes", "Yes", "YES", "n", "N", "no", "No", "NO",
"on", "On", "ON", "off", "Off", "OFF"), KeywordConstant, nil},
{`"(?:\\.|[^"])*"`, StringDouble, nil},
{`'(?:\\.|[^'])*'`, StringSingle, nil},
{`\d\d\d\d-\d\d-\d\d([T ]\d\d:\d\d:\d\d(\.\d+)?(Z|\s+[-+]\d+)?)?`, LiteralDate, nil},
{`\b[+\-]?(0x[\da-f]+|0o[0-7]+|(\d+\.?\d*|\.?\d+)(e[\+\-]?\d+)?|\.inf|\.nan)\b`, Number, nil},
{`\b[\w]+\b`, Text, nil},
{`([^\{\}\[\]\?,\:\!\-\*&\@].*)( )+(#.*)`, ByGroups(Literal, Whitespace, Comment), nil},
{`[^\{\}\[\]\?,\:\!\-\*&\@].*`, Literal, nil},
},
"key": {
{`"[^"\n].*": `, Keyword, nil},
{`(-)( )([^"\n{]*)(:)( )`, ByGroups(Punctuation, Whitespace, Keyword, Punctuation, Whitespace), nil},
{`([^"\n{]*)(:)( )`, ByGroups(Keyword, Punctuation, Whitespace), nil},
{`([^"\n{]*)(:)(\n)`, ByGroups(Keyword, Punctuation, Whitespace), nil},
{`"[^"\n].*": `, NameTag, nil},
{`(-)( )([^"\n{]*)(:)( )`, ByGroups(Punctuation, Whitespace, NameTag, Punctuation, Whitespace), nil},
{`([^"\n{]*)(:)( )`, ByGroups(NameTag, Punctuation, Whitespace), nil},
{`([^"\n{]*)(:)(\n)`, ByGroups(NameTag, Punctuation, Whitespace), nil},
},
"whitespace": {
{`\s+`, Whitespace, nil},

54
vendor/github.com/alecthomas/chroma/lexers/z/zig.go generated vendored Normal file
View File

@ -0,0 +1,54 @@
package z
import (
. "github.com/alecthomas/chroma" // nolint
"github.com/alecthomas/chroma/lexers/internal"
)
// Zig lexer.
var Zig = internal.Register(MustNewLexer(
&Config{
Name: "Zig",
Aliases: []string{"zig"},
Filenames: []string{"*.zig"},
MimeTypes: []string{"text/zig"},
},
Rules{
"root": {
{`\n`, TextWhitespace, nil},
{`\s+`, TextWhitespace, nil},
{`//.*?\n`, CommentSingle, nil},
{Words(``, `\b`, `break`, `return`, `continue`, `asm`, `defer`, `errdefer`, `unreachable`, `try`, `catch`, `async`, `await`, `suspend`, `resume`, `cancel`), Keyword, nil},
{Words(``, `\b`, `const`, `var`, `extern`, `packed`, `export`, `pub`, `noalias`, `inline`, `comptime`, `nakedcc`, `stdcallcc`, `volatile`, `allowzero`, `align`, `linksection`, `threadlocal`), KeywordReserved, nil},
{Words(``, `\b`, `struct`, `enum`, `union`, `error`), Keyword, nil},
{Words(``, `\b`, `while`, `for`), Keyword, nil},
{Words(``, `\b`, `bool`, `f16`, `f32`, `f64`, `f128`, `void`, `noreturn`, `type`, `anyerror`, `promise`, `i0`, `u0`, `isize`, `usize`, `comptime_int`, `comptime_float`, `c_short`, `c_ushort`, `c_int`, `c_uint`, `c_long`, `c_ulong`, `c_longlong`, `c_ulonglong`, `c_longdouble`, `c_voidi8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`), KeywordType, nil},
{Words(``, `\b`, `true`, `false`, `null`, `undefined`), KeywordConstant, nil},
{Words(``, `\b`, `if`, `else`, `switch`, `and`, `or`, `orelse`), Keyword, nil},
{Words(``, `\b`, `fn`, `usingnamespace`, `test`), Keyword, nil},
{`0x[0-9a-fA-F]+\.[0-9a-fA-F]+([pP][\-+]?[0-9a-fA-F]+)?`, LiteralNumberFloat, nil},
{`0x[0-9a-fA-F]+\.?[pP][\-+]?[0-9a-fA-F]+`, LiteralNumberFloat, nil},
{`[0-9]+\.[0-9]+([eE][-+]?[0-9]+)?`, LiteralNumberFloat, nil},
{`[0-9]+\.?[eE][-+]?[0-9]+`, LiteralNumberFloat, nil},
{`0b[01]+`, LiteralNumberBin, nil},
{`0o[0-7]+`, LiteralNumberOct, nil},
{`0x[0-9a-fA-F]+`, LiteralNumberHex, nil},
{`[0-9]+`, LiteralNumberInteger, nil},
{`@[a-zA-Z_]\w*`, NameBuiltin, nil},
{`[a-zA-Z_]\w*`, Name, nil},
{`\'\\\'\'`, LiteralStringEscape, nil},
{`\'\\(|x[a-fA-F0-9]{2}|u[a-fA-F0-9]{4}|U[a-fA-F0-9]{6}|[nr\\t\'"])\'`, LiteralStringEscape, nil},
{`\'[^\\\']\'`, LiteralString, nil},
{`\\\\[^\n]*`, LiteralStringHeredoc, nil},
{`c\\\\[^\n]*`, LiteralStringHeredoc, nil},
{`c?"`, LiteralString, Push("string")},
{`[+%=><|^!?/\-*&~:]`, Operator, nil},
{`[{}()\[\],.;]`, Punctuation, nil},
},
"string": {
{`\\(x[a-fA-F0-9]{2}|u[a-fA-F0-9]{4}|U[a-fA-F0-9]{6}|[nr\\t\'"])`, LiteralStringEscape, nil},
{`[^\\"\n]+`, LiteralString, nil},
{`"`, LiteralString, Pop(1)},
},
},
))

View File

@ -6,6 +6,7 @@ import (
"regexp"
"strings"
"sync"
"time"
"unicode/utf8"
"github.com/dlclark/regexp2"
@ -160,6 +161,14 @@ func Tokenise(lexer Lexer, options *TokeniseOptions, text string) ([]Token, erro
// Rules maps from state to a sequence of Rules.
type Rules map[string][]Rule
// Rename clones rules then a rule.
func (r Rules) Rename(old, new string) Rules {
r = r.Clone()
r[new] = r[old]
delete(r, old)
return r
}
// Clone returns a clone of the Rules.
func (r Rules) Clone() Rules {
out := map[string][]Rule{}
@ -170,6 +179,15 @@ func (r Rules) Clone() Rules {
return out
}
// Merge creates a clone of "r" then merges "rules" into the clone.
func (r Rules) Merge(rules Rules) Rules {
out := r.Clone()
for k, v := range rules.Clone() {
out[k] = v
}
return out
}
// MustNewLexer creates a new Lexer or panics.
func MustNewLexer(config *Config, rules Rules) *RegexLexer {
lexer, err := NewLexer(config, rules)
@ -376,6 +394,7 @@ func (r *RegexLexer) maybeCompile() (err error) {
if err != nil {
return fmt.Errorf("failed to compile rule %s.%d: %s", state, i, err)
}
rule.Regexp.MatchTimeout = time.Millisecond * 250
}
}
}

22
vendor/github.com/aymerick/douceur/LICENSE generated vendored Normal file
View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Aymerick JEHANNE
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.

60
vendor/github.com/aymerick/douceur/css/declaration.go generated vendored Normal file
View File

@ -0,0 +1,60 @@
package css
import "fmt"
// Declaration represents a parsed style property
type Declaration struct {
Property string
Value string
Important bool
}
// NewDeclaration instanciates a new Declaration
func NewDeclaration() *Declaration {
return &Declaration{}
}
// Returns string representation of the Declaration
func (decl *Declaration) String() string {
return decl.StringWithImportant(true)
}
// StringWithImportant returns string representation with optional !important part
func (decl *Declaration) StringWithImportant(option bool) string {
result := fmt.Sprintf("%s: %s", decl.Property, decl.Value)
if option && decl.Important {
result += " !important"
}
result += ";"
return result
}
// Equal returns true if both Declarations are equals
func (decl *Declaration) Equal(other *Declaration) bool {
return (decl.Property == other.Property) && (decl.Value == other.Value) && (decl.Important == other.Important)
}
//
// DeclarationsByProperty
//
// DeclarationsByProperty represents sortable style declarations
type DeclarationsByProperty []*Declaration
// Implements sort.Interface
func (declarations DeclarationsByProperty) Len() int {
return len(declarations)
}
// Implements sort.Interface
func (declarations DeclarationsByProperty) Swap(i, j int) {
declarations[i], declarations[j] = declarations[j], declarations[i]
}
// Implements sort.Interface
func (declarations DeclarationsByProperty) Less(i, j int) bool {
return declarations[i].Property < declarations[j].Property
}

230
vendor/github.com/aymerick/douceur/css/rule.go generated vendored Normal file
View File

@ -0,0 +1,230 @@
package css
import (
"fmt"
"strings"
)
const (
indentSpace = 2
)
// RuleKind represents a Rule kind
type RuleKind int
// Rule kinds
const (
QualifiedRule RuleKind = iota
AtRule
)
// At Rules than have Rules inside their block instead of Declarations
var atRulesWithRulesBlock = []string{
"@document", "@font-feature-values", "@keyframes", "@media", "@supports",
}
// Rule represents a parsed CSS rule
type Rule struct {
Kind RuleKind
// At Rule name (eg: "@media")
Name string
// Raw prelude
Prelude string
// Qualified Rule selectors parsed from prelude
Selectors []string
// Style properties
Declarations []*Declaration
// At Rule embedded rules
Rules []*Rule
// Current rule embedding level
EmbedLevel int
}
// NewRule instanciates a new Rule
func NewRule(kind RuleKind) *Rule {
return &Rule{
Kind: kind,
}
}
// Returns string representation of rule kind
func (kind RuleKind) String() string {
switch kind {
case QualifiedRule:
return "Qualified Rule"
case AtRule:
return "At Rule"
default:
return "WAT"
}
}
// EmbedsRules returns true if this rule embeds another rules
func (rule *Rule) EmbedsRules() bool {
if rule.Kind == AtRule {
for _, atRuleName := range atRulesWithRulesBlock {
if rule.Name == atRuleName {
return true
}
}
}
return false
}
// Equal returns true if both rules are equals
func (rule *Rule) Equal(other *Rule) bool {
if (rule.Kind != other.Kind) ||
(rule.Prelude != other.Prelude) ||
(rule.Name != other.Name) {
return false
}
if (len(rule.Selectors) != len(other.Selectors)) ||
(len(rule.Declarations) != len(other.Declarations)) ||
(len(rule.Rules) != len(other.Rules)) {
return false
}
for i, sel := range rule.Selectors {
if sel != other.Selectors[i] {
return false
}
}
for i, decl := range rule.Declarations {
if !decl.Equal(other.Declarations[i]) {
return false
}
}
for i, rule := range rule.Rules {
if !rule.Equal(other.Rules[i]) {
return false
}
}
return true
}
// Diff returns a string representation of rules differences
func (rule *Rule) Diff(other *Rule) []string {
result := []string{}
if rule.Kind != other.Kind {
result = append(result, fmt.Sprintf("Kind: %s | %s", rule.Kind.String(), other.Kind.String()))
}
if rule.Prelude != other.Prelude {
result = append(result, fmt.Sprintf("Prelude: \"%s\" | \"%s\"", rule.Prelude, other.Prelude))
}
if rule.Name != other.Name {
result = append(result, fmt.Sprintf("Name: \"%s\" | \"%s\"", rule.Name, other.Name))
}
if len(rule.Selectors) != len(other.Selectors) {
result = append(result, fmt.Sprintf("Selectors: %v | %v", strings.Join(rule.Selectors, ", "), strings.Join(other.Selectors, ", ")))
} else {
for i, sel := range rule.Selectors {
if sel != other.Selectors[i] {
result = append(result, fmt.Sprintf("Selector: \"%s\" | \"%s\"", sel, other.Selectors[i]))
}
}
}
if len(rule.Declarations) != len(other.Declarations) {
result = append(result, fmt.Sprintf("Declarations Nb: %d | %d", len(rule.Declarations), len(other.Declarations)))
} else {
for i, decl := range rule.Declarations {
if !decl.Equal(other.Declarations[i]) {
result = append(result, fmt.Sprintf("Declaration: \"%s\" | \"%s\"", decl.String(), other.Declarations[i].String()))
}
}
}
if len(rule.Rules) != len(other.Rules) {
result = append(result, fmt.Sprintf("Rules Nb: %d | %d", len(rule.Rules), len(other.Rules)))
} else {
for i, rule := range rule.Rules {
if !rule.Equal(other.Rules[i]) {
result = append(result, fmt.Sprintf("Rule: \"%s\" | \"%s\"", rule.String(), other.Rules[i].String()))
}
}
}
return result
}
// Returns the string representation of a rule
func (rule *Rule) String() string {
result := ""
if rule.Kind == QualifiedRule {
for i, sel := range rule.Selectors {
if i != 0 {
result += ", "
}
result += sel
}
} else {
// AtRule
result += fmt.Sprintf("%s", rule.Name)
if rule.Prelude != "" {
if result != "" {
result += " "
}
result += fmt.Sprintf("%s", rule.Prelude)
}
}
if (len(rule.Declarations) == 0) && (len(rule.Rules) == 0) {
result += ";"
} else {
result += " {\n"
if rule.EmbedsRules() {
for _, subRule := range rule.Rules {
result += fmt.Sprintf("%s%s\n", rule.indent(), subRule.String())
}
} else {
for _, decl := range rule.Declarations {
result += fmt.Sprintf("%s%s\n", rule.indent(), decl.String())
}
}
result += fmt.Sprintf("%s}", rule.indentEndBlock())
}
return result
}
// Returns identation spaces for declarations and rules
func (rule *Rule) indent() string {
result := ""
for i := 0; i < ((rule.EmbedLevel + 1) * indentSpace); i++ {
result += " "
}
return result
}
// Returns identation spaces for end of block character
func (rule *Rule) indentEndBlock() string {
result := ""
for i := 0; i < (rule.EmbedLevel * indentSpace); i++ {
result += " "
}
return result
}

25
vendor/github.com/aymerick/douceur/css/stylesheet.go generated vendored Normal file
View File

@ -0,0 +1,25 @@
package css
// Stylesheet represents a parsed stylesheet
type Stylesheet struct {
Rules []*Rule
}
// NewStylesheet instanciate a new Stylesheet
func NewStylesheet() *Stylesheet {
return &Stylesheet{}
}
// Returns string representation of the Stylesheet
func (sheet *Stylesheet) String() string {
result := ""
for _, rule := range sheet.Rules {
if result != "" {
result += "\n"
}
result += rule.String()
}
return result
}

26
vendor/github.com/charmbracelet/glamour/.golangci.yml generated vendored Normal file
View File

@ -0,0 +1,26 @@
run:
tests: false
issues:
max-issues-per-linter: 0
max-same-issues: 0
linters:
enable:
- bodyclose
- dupl
- exportloopref
- goconst
- godot
- godox
- goimports
- gomnd
- goprintffuncname
- gosec
- misspell
- prealloc
- rowserrcheck
- sqlclosecheck
- unconvert
- unparam
- whitespace

View File

@ -1,10 +1,13 @@
# Glamour
[![Latest Release](https://img.shields.io/github/release/charmbracelet/glamour.svg)](https://github.com/charmbracelet/glamour/releases)
[![GoDoc](https://godoc.org/github.com/golang/gddo?status.svg)](https://pkg.go.dev/github.com/charmbracelet/glamour?tab=doc)
[![Build Status](https://github.com/charmbracelet/glamour/workflows/build/badge.svg)](https://github.com/charmbracelet/glamour/actions)
[![Coverage Status](https://coveralls.io/repos/github/charmbracelet/glamour/badge.svg?branch=master)](https://coveralls.io/github/charmbracelet/glamour?branch=master)
[![Go ReportCard](http://goreportcard.com/badge/charmbracelet/glamour)](http://goreportcard.com/report/charmbracelet/glamour)
<p>
<img src="https://stuff.charm.sh/glamour/glamour-github-header.png" width="245" alt="Glamour Title Treatment"><br>
<a href="https://github.com/charmbracelet/glamour/releases"><img src="https://img.shields.io/github/release/charmbracelet/glamour.svg" alt="Latest Release"></a>
<a href="https://pkg.go.dev/github.com/charmbracelet/glamour?tab=doc"><img src="https://godoc.org/github.com/golang/gddo?status.svg" alt="GoDoc"></a>
<a href="https://github.com/charmbracelet/glamour/actions"><img src="https://github.com/charmbracelet/glamour/workflows/build/badge.svg" alt="Build Status"></a>
<a href="https://coveralls.io/github/charmbracelet/glamour?branch=master"><img src="https://coveralls.io/repos/github/charmbracelet/glamour/badge.svg?branch=master" alt="Coverage Status"></a>
<a href="http://goreportcard.com/report/charmbracelet/glamour"><img src="http://goreportcard.com/badge/charmbracelet/glamour" alt="Go ReportCard"></a>
</p>
Write handsome command-line tools with *glamour*!
@ -63,10 +66,22 @@ There are a few options for using a custom style:
## Glamourous Projects
Check out [Glow](https://github.com/charmbracelet/glow), a markdown renderer for
the command-line, which uses `glamour`.
Check out these projects, which use `glamour`:
- [Glow](https://github.com/charmbracelet/glow), a markdown renderer for
the command-line.
- [GitHub CLI](https://github.com/cli/cli), GitHubs official command line tool.
- [GLab](https://github.com/profclems/glab), An open source GitLab command line tool.
## License
[MIT](https://github.com/charmbracelet/glamour/raw/master/LICENSE)
***
Part of [Charm](https://charm.sh).
<a href="https://charm.sh/"><img alt="the Charm logo" src="https://stuff.charm.sh/charm-badge.jpg" width="400"></a>
Charm热爱开源! / Charm loves open source!

View File

@ -7,6 +7,7 @@ import (
"io"
"strings"
east "github.com/yuin/goldmark-emoji/ast"
"github.com/yuin/goldmark/ast"
astext "github.com/yuin/goldmark/extension/ast"
)
@ -375,6 +376,14 @@ func (tr *ANSIRenderer) NewElement(node ast.Node, source []byte) Element {
case ast.KindTextBlock:
return Element{}
case east.KindEmoji:
n := node.(*east.Emoji)
return Element{
Renderer: &BaseElement{
Token: string(n.Value.Unicode),
},
}
// Unknown case
default:
fmt.Println("Warning: unhandled element", node.Kind().String())

View File

@ -25,7 +25,7 @@ func (e *ImageElement) Render(w io.Writer, ctx RenderContext) error {
}
if len(e.URL) > 0 {
el := &BaseElement{
Token: resolveRelativeURL(e.BaseURL, e.URL),
Token: resolveURL(e.BaseURL, e.URL),
Prefix: " ",
Style: ctx.options.Styles.Image,
}

View File

@ -64,7 +64,7 @@ func (e *LinkElement) Render(w io.Writer, ctx RenderContext) error {
}
el := &BaseElement{
Token: resolveRelativeURL(e.BaseURL, e.URL),
Token: resolveURL(e.BaseURL, e.URL),
Prefix: pre,
Style: style,
}

View File

@ -3,9 +3,9 @@ package ansi
import (
"io"
"net/url"
"strings"
"github.com/muesli/termenv"
east "github.com/yuin/goldmark-emoji/ast"
"github.com/yuin/goldmark/ast"
astext "github.com/yuin/goldmark/extension/ast"
"github.com/yuin/goldmark/renderer"
@ -72,13 +72,16 @@ func (r *ANSIRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
reg.Register(astext.KindFootnote, r.renderNode)
reg.Register(astext.KindFootnoteList, r.renderNode)
reg.Register(astext.KindFootnoteLink, r.renderNode)
reg.Register(astext.KindFootnoteBackLink, r.renderNode)
reg.Register(astext.KindFootnoteBacklink, r.renderNode)
// checkboxes
reg.Register(astext.KindTaskCheckBox, r.renderNode)
// strikethrough
reg.Register(astext.KindStrikethrough, r.renderNode)
// emoji
reg.Register(east.KindEmoji, r.renderNode)
}
func (r *ANSIRenderer) renderNode(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
@ -145,7 +148,7 @@ func isChild(node ast.Node) bool {
return false
}
func resolveRelativeURL(baseURL string, rel string) string {
func resolveURL(baseURL string, rel string) string {
u, err := url.Parse(rel)
if err != nil {
return rel
@ -153,7 +156,6 @@ func resolveRelativeURL(baseURL string, rel string) string {
if u.IsAbs() {
return rel
}
u.Path = strings.TrimPrefix(u.Path, "/")
base, err := url.Parse(baseURL)
if err != nil {

View File

@ -9,6 +9,7 @@ import (
"github.com/muesli/termenv"
"github.com/yuin/goldmark"
emoji "github.com/yuin/goldmark-emoji"
"github.com/yuin/goldmark/extension"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer"
@ -135,20 +136,17 @@ func WithEnvironmentConfig() TermRendererOption {
// standard style.
func WithStylePath(stylePath string) TermRendererOption {
return func(tr *TermRenderer) error {
jsonBytes, err := ioutil.ReadFile(stylePath)
switch {
case err == nil:
return json.Unmarshal(jsonBytes, &tr.ansiOptions.Styles)
case os.IsNotExist(err):
styles, err := getDefaultStyle(stylePath)
styles, err := getDefaultStyle(stylePath)
if err != nil {
jsonBytes, err := ioutil.ReadFile(stylePath)
if err != nil {
return err
}
tr.ansiOptions.Styles = *styles
return nil
default:
return err
return json.Unmarshal(jsonBytes, &tr.ansiOptions.Styles)
}
tr.ansiOptions.Styles = *styles
return nil
}
}
@ -187,6 +185,14 @@ func WithWordWrap(wordWrap int) TermRendererOption {
}
}
// WithEmoji sets a TermRenderer's emoji rendering.
func WithEmoji() TermRendererOption {
return func(tr *TermRenderer) error {
emoji.New().Extend(tr.md)
return nil
}
}
func (tr *TermRenderer) Read(b []byte) (int, error) {
return tr.renderBuf.Read(b)
}

View File

@ -3,10 +3,11 @@ module github.com/charmbracelet/glamour
go 1.13
require (
github.com/alecthomas/chroma v0.7.3
github.com/microcosm-cc/bluemonday v1.0.2
github.com/muesli/reflow v0.1.0
github.com/muesli/termenv v0.6.0
github.com/alecthomas/chroma v0.8.1
github.com/microcosm-cc/bluemonday v1.0.4
github.com/muesli/reflow v0.2.0
github.com/muesli/termenv v0.7.4
github.com/olekukonko/tablewriter v0.0.4
github.com/yuin/goldmark v1.2.0
github.com/yuin/goldmark v1.3.1
github.com/yuin/goldmark-emoji v1.0.1
)

View File

@ -1,12 +1,16 @@
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U=
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
github.com/alecthomas/chroma v0.7.3 h1:NfdAERMy+esYQs8OXk0I868/qDxxCEo7FMz1WIqMAeI=
github.com/alecthomas/chroma v0.7.3/go.mod h1:sko8vR34/90zvl5QdcUdvzL3J8NKjAUx9va9jPuFNoM=
github.com/alecthomas/chroma v0.8.1 h1:ym20sbvyC6RXz45u4qDglcgr8E313oPROshcuCHqiEE=
github.com/alecthomas/chroma v0.8.1/go.mod h1:sko8vR34/90zvl5QdcUdvzL3J8NKjAUx9va9jPuFNoM=
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721 h1:JHZL0hZKJ1VENNfmXvHbgYlbUOvpzYzvy2aZU5gXVeo=
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
github.com/alecthomas/kong v0.2.4/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE=
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 h1:p9Sln00KOTlrYkxI1zYWl1QLnEqAqEARBEYa8FQnQcY=
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/chris-ramon/douceur v0.2.0 h1:IDMEdxlEUUBYBKE4z/mJnFyVXox+MjuEVDJNN27glkU=
github.com/chris-ramon/douceur v0.2.0/go.mod h1:wDW5xjJdeoMm1mRt4sD4c/LbF/mWdEpRXQKjTR8nIBE=
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ=
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -14,8 +18,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk=
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f h1:5CjVwnuUcp5adK4gmY6i72gpVFVnZDP2h5TmPScB6u4=
github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f/go.mod h1:nOFQdrUlIlx6M6ODdSpBj1NVA+VgLC6kmw60mkw34H4=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
@ -25,12 +29,12 @@ github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+tw
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/microcosm-cc/bluemonday v1.0.2 h1:5lPfLTTAvAbtS0VqT+94yOtFnGfUWYyx0+iToC3Os3s=
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
github.com/muesli/reflow v0.1.0 h1:oQdpLfO56lr5pgLvqD0TcjW85rDjSYSBVdiG1Ch1ddM=
github.com/muesli/reflow v0.1.0/go.mod h1:I9bWAt7QTg/que/qmUCJBGlj7wEq8OAFBjPNjc6xK4I=
github.com/muesli/termenv v0.6.0 h1:zxvzTBmo4ZcxhNGGWeMz+Tttm51eF5bmPjfy4MCRYlk=
github.com/muesli/termenv v0.6.0/go.mod h1:SohX91w6swWA4AYU+QmPx+aSgXhWO0juiyID9UZmbpA=
github.com/microcosm-cc/bluemonday v1.0.4 h1:p0L+CTpo/PLFdkoPcJemLXG+fpMD7pYOoDEq1axMbGg=
github.com/microcosm-cc/bluemonday v1.0.4/go.mod h1:8iwZnFn2CDDNZ0r6UXhF4xawGvzaqzCRa1n3/lO3W2w=
github.com/muesli/reflow v0.2.0 h1:2o0UBJPHHH4fa2GCXU4Rg4DwOtWPMekCeyc5EWbAQp0=
github.com/muesli/reflow v0.2.0/go.mod h1:qT22vjVmM9MIUeLgsVYe/Ye7eZlbv9dZjL3dVhUqLX8=
github.com/muesli/termenv v0.7.4 h1:/pBqvU5CpkY53tU0vVn+xgs2ZTX63aH5nY+SSps5Xa8=
github.com/muesli/termenv v0.7.4/go.mod h1:pZ7qY9l3F7e5xsAOS0zCew2tME+p7bWeBkotCEcIIcc=
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -43,8 +47,12 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/yuin/goldmark v1.2.0 h1:WOOcyaJPlzb8fZ8TloxFe8QZkhOOJx87leDa9MIT9dc=
github.com/yuin/goldmark v1.2.0/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.1 h1:eVwehsLsZlCJCwXyGLgg+Q4iFWE/eTIMG0e8waCmm/I=
github.com/yuin/goldmark v1.3.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os=
github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3 h1:eH6Eip3UpmR+yM/qI9Ijluzb1bNv/cAU/n+6l8tRSis=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg=

22
vendor/github.com/chris-ramon/douceur/LICENSE generated vendored Normal file
View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015 Aymerick JEHANNE
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.

409
vendor/github.com/chris-ramon/douceur/parser/parser.go generated vendored Normal file
View File

@ -0,0 +1,409 @@
package parser
import (
"errors"
"fmt"
"regexp"
"strings"
"github.com/gorilla/css/scanner"
"github.com/aymerick/douceur/css"
)
const (
importantSuffixRegexp = `(?i)\s*!important\s*$`
)
var (
importantRegexp *regexp.Regexp
)
// Parser represents a CSS parser
type Parser struct {
scan *scanner.Scanner // Tokenizer
// Tokens parsed but not consumed yet
tokens []*scanner.Token
// Rule embedding level
embedLevel int
}
func init() {
importantRegexp = regexp.MustCompile(importantSuffixRegexp)
}
// NewParser instanciates a new parser
func NewParser(txt string) *Parser {
return &Parser{
scan: scanner.New(txt),
}
}
// Parse parses a whole stylesheet
func Parse(text string) (*css.Stylesheet, error) {
result, err := NewParser(text).ParseStylesheet()
if err != nil {
return nil, err
}
return result, nil
}
// ParseDeclarations parses CSS declarations
func ParseDeclarations(text string) ([]*css.Declaration, error) {
result, err := NewParser(text).ParseDeclarations()
if err != nil {
return nil, err
}
return result, nil
}
// ParseStylesheet parses a stylesheet
func (parser *Parser) ParseStylesheet() (*css.Stylesheet, error) {
result := css.NewStylesheet()
// Parse BOM
if _, err := parser.parseBOM(); err != nil {
return result, err
}
// Parse list of rules
rules, err := parser.ParseRules()
if err != nil {
return result, err
}
result.Rules = rules
return result, nil
}
// ParseRules parses a list of rules
func (parser *Parser) ParseRules() ([]*css.Rule, error) {
result := []*css.Rule{}
inBlock := false
if parser.tokenChar("{") {
// parsing a block of rules
inBlock = true
parser.embedLevel++
parser.shiftToken()
}
for parser.tokenParsable() {
if parser.tokenIgnorable() {
parser.shiftToken()
} else if parser.tokenChar("}") {
if !inBlock {
errMsg := fmt.Sprintf("Unexpected } character: %s", parser.nextToken().String())
return result, errors.New(errMsg)
}
parser.shiftToken()
parser.embedLevel--
// finished
break
} else {
rule, err := parser.ParseRule()
if err != nil {
return result, err
}
rule.EmbedLevel = parser.embedLevel
result = append(result, rule)
}
}
return result, parser.err()
}
// ParseRule parses a rule
func (parser *Parser) ParseRule() (*css.Rule, error) {
if parser.tokenAtKeyword() {
return parser.parseAtRule()
}
return parser.parseQualifiedRule()
}
// ParseDeclarations parses a list of declarations
func (parser *Parser) ParseDeclarations() ([]*css.Declaration, error) {
result := []*css.Declaration{}
if parser.tokenChar("{") {
parser.shiftToken()
}
for parser.tokenParsable() {
if parser.tokenIgnorable() {
parser.shiftToken()
} else if parser.tokenChar("}") {
// end of block
parser.shiftToken()
break
} else {
declaration, err := parser.ParseDeclaration()
if err != nil {
return result, err
}
result = append(result, declaration)
}
}
return result, parser.err()
}
// ParseDeclaration parses a declaration
func (parser *Parser) ParseDeclaration() (*css.Declaration, error) {
result := css.NewDeclaration()
curValue := ""
for parser.tokenParsable() {
if parser.tokenChar(":") {
result.Property = strings.TrimSpace(curValue)
curValue = ""
parser.shiftToken()
} else if parser.tokenChar(";") || parser.tokenChar("}") {
if result.Property == "" {
errMsg := fmt.Sprintf("Unexpected ; character: %s", parser.nextToken().String())
return result, errors.New(errMsg)
}
if importantRegexp.MatchString(curValue) {
result.Important = true
curValue = importantRegexp.ReplaceAllString(curValue, "")
}
result.Value = strings.TrimSpace(curValue)
if parser.tokenChar(";") {
parser.shiftToken()
}
// finished
break
} else {
token := parser.shiftToken()
curValue += token.Value
}
}
// log.Printf("[parsed] Declaration: %s", result.String())
return result, parser.err()
}
// Parse an At Rule
func (parser *Parser) parseAtRule() (*css.Rule, error) {
// parse rule name (eg: "@import")
token := parser.shiftToken()
result := css.NewRule(css.AtRule)
result.Name = token.Value
for parser.tokenParsable() {
if parser.tokenChar(";") {
parser.shiftToken()
// finished
break
} else if parser.tokenChar("{") {
if result.EmbedsRules() {
// parse rules block
rules, err := parser.ParseRules()
if err != nil {
return result, err
}
result.Rules = rules
} else {
// parse declarations block
declarations, err := parser.ParseDeclarations()
if err != nil {
return result, err
}
result.Declarations = declarations
}
// finished
break
} else {
// parse prelude
prelude, err := parser.parsePrelude()
if err != nil {
return result, err
}
result.Prelude = prelude
}
}
// log.Printf("[parsed] Rule: %s", result.String())
return result, parser.err()
}
// Parse a Qualified Rule
func (parser *Parser) parseQualifiedRule() (*css.Rule, error) {
result := css.NewRule(css.QualifiedRule)
for parser.tokenParsable() {
if parser.tokenChar("{") {
if result.Prelude == "" {
errMsg := fmt.Sprintf("Unexpected { character: %s", parser.nextToken().String())
return result, errors.New(errMsg)
}
// parse declarations block
declarations, err := parser.ParseDeclarations()
if err != nil {
return result, err
}
result.Declarations = declarations
// finished
break
} else {
// parse prelude
prelude, err := parser.parsePrelude()
if err != nil {
return result, err
}
result.Prelude = prelude
}
}
result.Selectors = strings.Split(result.Prelude, ",")
for i, sel := range result.Selectors {
result.Selectors[i] = strings.TrimSpace(sel)
}
// log.Printf("[parsed] Rule: %s", result.String())
return result, parser.err()
}
// Parse Rule prelude
func (parser *Parser) parsePrelude() (string, error) {
result := ""
for parser.tokenParsable() && !parser.tokenEndOfPrelude() {
token := parser.shiftToken()
result += token.Value
}
result = strings.TrimSpace(result)
// log.Printf("[parsed] prelude: %s", result)
return result, parser.err()
}
// Parse BOM
func (parser *Parser) parseBOM() (bool, error) {
if parser.nextToken().Type == scanner.TokenBOM {
parser.shiftToken()
return true, nil
}
return false, parser.err()
}
// Returns next token without removing it from tokens buffer
func (parser *Parser) nextToken() *scanner.Token {
if len(parser.tokens) == 0 {
// fetch next token
nextToken := parser.scan.Next()
// log.Printf("[token] %s => %v", nextToken.Type.String(), nextToken.Value)
// queue it
parser.tokens = append(parser.tokens, nextToken)
}
return parser.tokens[0]
}
// Returns next token and remove it from the tokens buffer
func (parser *Parser) shiftToken() *scanner.Token {
var result *scanner.Token
result, parser.tokens = parser.tokens[0], parser.tokens[1:]
return result
}
// Returns tokenizer error, or nil if no error
func (parser *Parser) err() error {
if parser.tokenError() {
token := parser.nextToken()
return fmt.Errorf("Tokenizer error: %s", token.String())
}
return nil
}
// Returns true if next token is Error
func (parser *Parser) tokenError() bool {
return parser.nextToken().Type == scanner.TokenError
}
// Returns true if next token is EOF
func (parser *Parser) tokenEOF() bool {
return parser.nextToken().Type == scanner.TokenEOF
}
// Returns true if next token is a whitespace
func (parser *Parser) tokenWS() bool {
return parser.nextToken().Type == scanner.TokenS
}
// Returns true if next token is a comment
func (parser *Parser) tokenComment() bool {
return parser.nextToken().Type == scanner.TokenComment
}
// Returns true if next token is a CDO or a CDC
func (parser *Parser) tokenCDOorCDC() bool {
switch parser.nextToken().Type {
case scanner.TokenCDO, scanner.TokenCDC:
return true
default:
return false
}
}
// Returns true if next token is ignorable
func (parser *Parser) tokenIgnorable() bool {
return parser.tokenWS() || parser.tokenComment() || parser.tokenCDOorCDC()
}
// Returns true if next token is parsable
func (parser *Parser) tokenParsable() bool {
return !parser.tokenEOF() && !parser.tokenError()
}
// Returns true if next token is an At Rule keyword
func (parser *Parser) tokenAtKeyword() bool {
return parser.nextToken().Type == scanner.TokenAtKeyword
}
// Returns true if next token is given character
func (parser *Parser) tokenChar(value string) bool {
token := parser.nextToken()
return (token.Type == scanner.TokenChar) && (token.Value == value)
}
// Returns true if next token marks the end of a prelude
func (parser *Parser) tokenEndOfPrelude() bool {
return parser.tokenChar(";") || parser.tokenChar("{")
}

27
vendor/github.com/gorilla/css/LICENSE generated vendored Normal file
View File

@ -0,0 +1,27 @@
Copyright (c) 2013, Gorilla web toolkit
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 the {organization} 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 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.

33
vendor/github.com/gorilla/css/scanner/doc.go generated vendored Normal file
View File

@ -0,0 +1,33 @@
// Copyright 2012 The Gorilla 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 gorilla/css/scanner generates tokens for a CSS3 input.
It follows the CSS3 specification located at:
http://www.w3.org/TR/css3-syntax/
To use it, create a new scanner for a given CSS string and call Next() until
the token returned has type TokenEOF or TokenError:
s := scanner.New(myCSS)
for {
token := s.Next()
if token.Type == scanner.TokenEOF || token.Type == scanner.TokenError {
break
}
// Do something with the token...
}
Following the CSS3 specification, an error can only occur when the scanner
finds an unclosed quote or unclosed comment. In these cases the text becomes
"untokenizable". Everything else is tokenizable and it is up to a parser
to make sense of the token stream (or ignore nonsensical token sequences).
Note: the scanner doesn't perform lexical analysis or, in other words, it
doesn't care about the token context. It is intended to be used by a
lexer or parser.
*/
package scanner

356
vendor/github.com/gorilla/css/scanner/scanner.go generated vendored Normal file
View File

@ -0,0 +1,356 @@
// Copyright 2012 The Gorilla 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 scanner
import (
"fmt"
"regexp"
"strings"
"unicode"
"unicode/utf8"
)
// tokenType identifies the type of lexical tokens.
type tokenType int
// String returns a string representation of the token type.
func (t tokenType) String() string {
return tokenNames[t]
}
// Token represents a token and the corresponding string.
type Token struct {
Type tokenType
Value string
Line int
Column int
}
// String returns a string representation of the token.
func (t *Token) String() string {
if len(t.Value) > 10 {
return fmt.Sprintf("%s (line: %d, column: %d): %.10q...",
t.Type, t.Line, t.Column, t.Value)
}
return fmt.Sprintf("%s (line: %d, column: %d): %q",
t.Type, t.Line, t.Column, t.Value)
}
// All tokens -----------------------------------------------------------------
// The complete list of tokens in CSS3.
const (
// Scanner flags.
TokenError tokenType = iota
TokenEOF
// From now on, only tokens from the CSS specification.
TokenIdent
TokenAtKeyword
TokenString
TokenHash
TokenNumber
TokenPercentage
TokenDimension
TokenURI
TokenUnicodeRange
TokenCDO
TokenCDC
TokenS
TokenComment
TokenFunction
TokenIncludes
TokenDashMatch
TokenPrefixMatch
TokenSuffixMatch
TokenSubstringMatch
TokenChar
TokenBOM
)
// tokenNames maps tokenType's to their names. Used for conversion to string.
var tokenNames = map[tokenType]string{
TokenError: "error",
TokenEOF: "EOF",
TokenIdent: "IDENT",
TokenAtKeyword: "ATKEYWORD",
TokenString: "STRING",
TokenHash: "HASH",
TokenNumber: "NUMBER",
TokenPercentage: "PERCENTAGE",
TokenDimension: "DIMENSION",
TokenURI: "URI",
TokenUnicodeRange: "UNICODE-RANGE",
TokenCDO: "CDO",
TokenCDC: "CDC",
TokenS: "S",
TokenComment: "COMMENT",
TokenFunction: "FUNCTION",
TokenIncludes: "INCLUDES",
TokenDashMatch: "DASHMATCH",
TokenPrefixMatch: "PREFIXMATCH",
TokenSuffixMatch: "SUFFIXMATCH",
TokenSubstringMatch: "SUBSTRINGMATCH",
TokenChar: "CHAR",
TokenBOM: "BOM",
}
// Macros and productions -----------------------------------------------------
// http://www.w3.org/TR/css3-syntax/#tokenization
var macroRegexp = regexp.MustCompile(`\{[a-z]+\}`)
// macros maps macro names to patterns to be expanded.
var macros = map[string]string{
// must be escaped: `\.+*?()|[]{}^$`
"ident": `-?{nmstart}{nmchar}*`,
"name": `{nmchar}+`,
"nmstart": `[a-zA-Z_]|{nonascii}|{escape}`,
"nonascii": "[\u0080-\uD7FF\uE000-\uFFFD\U00010000-\U0010FFFF]",
"unicode": `\\[0-9a-fA-F]{1,6}{wc}?`,
"escape": "{unicode}|\\\\[\u0020-\u007E\u0080-\uD7FF\uE000-\uFFFD\U00010000-\U0010FFFF]",
"nmchar": `[a-zA-Z0-9_-]|{nonascii}|{escape}`,
"num": `[0-9]*\.[0-9]+|[0-9]+`,
"string": `"(?:{stringchar}|')*"|'(?:{stringchar}|")*'`,
"stringchar": `{urlchar}|[ ]|\\{nl}`,
"nl": `[\n\r\f]|\r\n`,
"w": `{wc}*`,
"wc": `[\t\n\f\r ]`,
// urlchar should accept [(ascii characters minus those that need escaping)|{nonascii}|{escape}]
// ASCII characters range = `[\u0020-\u007e]`
// Skip space \u0020 = `[\u0021-\u007e]`
// Skip quotation mark \0022 = `[\u0021\u0023-\u007e]`
// Skip apostrophe \u0027 = `[\u0021\u0023-\u0026\u0028-\u007e]`
// Skip reverse solidus \u005c = `[\u0021\u0023-\u0026\u0028-\u005b\u005d\u007e]`
// Finally, the left square bracket (\u005b) and right (\u005d) needs escaping themselves
"urlchar": "[\u0021\u0023-\u0026\u0028-\\\u005b\\\u005d-\u007E]|{nonascii}|{escape}",
}
// productions maps the list of tokens to patterns to be expanded.
var productions = map[tokenType]string{
// Unused regexps (matched using other methods) are commented out.
TokenIdent: `{ident}`,
TokenAtKeyword: `@{ident}`,
TokenString: `{string}`,
TokenHash: `#{name}`,
TokenNumber: `{num}`,
TokenPercentage: `{num}%`,
TokenDimension: `{num}{ident}`,
TokenURI: `url\({w}(?:{string}|{urlchar}*?){w}\)`,
TokenUnicodeRange: `U\+[0-9A-F\?]{1,6}(?:-[0-9A-F]{1,6})?`,
//TokenCDO: `<!--`,
TokenCDC: `-->`,
TokenS: `{wc}+`,
TokenComment: `/\*[^\*]*[\*]+(?:[^/][^\*]*[\*]+)*/`,
TokenFunction: `{ident}\(`,
//TokenIncludes: `~=`,
//TokenDashMatch: `\|=`,
//TokenPrefixMatch: `\^=`,
//TokenSuffixMatch: `\$=`,
//TokenSubstringMatch: `\*=`,
//TokenChar: `[^"']`,
//TokenBOM: "\uFEFF",
}
// matchers maps the list of tokens to compiled regular expressions.
//
// The map is filled on init() using the macros and productions defined in
// the CSS specification.
var matchers = map[tokenType]*regexp.Regexp{}
// matchOrder is the order to test regexps when first-char shortcuts
// can't be used.
var matchOrder = []tokenType{
TokenURI,
TokenFunction,
TokenUnicodeRange,
TokenIdent,
TokenDimension,
TokenPercentage,
TokenNumber,
TokenCDC,
}
func init() {
// replace macros and compile regexps for productions.
replaceMacro := func(s string) string {
return "(?:" + macros[s[1:len(s)-1]] + ")"
}
for t, s := range productions {
for macroRegexp.MatchString(s) {
s = macroRegexp.ReplaceAllStringFunc(s, replaceMacro)
}
matchers[t] = regexp.MustCompile("^(?:" + s + ")")
}
}
// Scanner --------------------------------------------------------------------
// New returns a new CSS scanner for the given input.
func New(input string) *Scanner {
// Normalize newlines.
input = strings.Replace(input, "\r\n", "\n", -1)
return &Scanner{
input: input,
row: 1,
col: 1,
}
}
// Scanner scans an input and emits tokens following the CSS3 specification.
type Scanner struct {
input string
pos int
row int
col int
err *Token
}
// Next returns the next token from the input.
//
// At the end of the input the token type is TokenEOF.
//
// If the input can't be tokenized the token type is TokenError. This occurs
// in case of unclosed quotation marks or comments.
func (s *Scanner) Next() *Token {
if s.err != nil {
return s.err
}
if s.pos >= len(s.input) {
s.err = &Token{TokenEOF, "", s.row, s.col}
return s.err
}
if s.pos == 0 {
// Test BOM only once, at the beginning of the file.
if strings.HasPrefix(s.input, "\uFEFF") {
return s.emitSimple(TokenBOM, "\uFEFF")
}
}
// There's a lot we can guess based on the first byte so we'll take a
// shortcut before testing multiple regexps.
input := s.input[s.pos:]
switch input[0] {
case '\t', '\n', '\f', '\r', ' ':
// Whitespace.
return s.emitToken(TokenS, matchers[TokenS].FindString(input))
case '.':
// Dot is too common to not have a quick check.
// We'll test if this is a Char; if it is followed by a number it is a
// dimension/percentage/number, and this will be matched later.
if len(input) > 1 && !unicode.IsDigit(rune(input[1])) {
return s.emitSimple(TokenChar, ".")
}
case '#':
// Another common one: Hash or Char.
if match := matchers[TokenHash].FindString(input); match != "" {
return s.emitToken(TokenHash, match)
}
return s.emitSimple(TokenChar, "#")
case '@':
// Another common one: AtKeyword or Char.
if match := matchers[TokenAtKeyword].FindString(input); match != "" {
return s.emitSimple(TokenAtKeyword, match)
}
return s.emitSimple(TokenChar, "@")
case ':', ',', ';', '%', '&', '+', '=', '>', '(', ')', '[', ']', '{', '}':
// More common chars.
return s.emitSimple(TokenChar, string(input[0]))
case '"', '\'':
// String or error.
match := matchers[TokenString].FindString(input)
if match != "" {
return s.emitToken(TokenString, match)
}
s.err = &Token{TokenError, "unclosed quotation mark", s.row, s.col}
return s.err
case '/':
// Comment, error or Char.
if len(input) > 1 && input[1] == '*' {
match := matchers[TokenComment].FindString(input)
if match != "" {
return s.emitToken(TokenComment, match)
} else {
s.err = &Token{TokenError, "unclosed comment", s.row, s.col}
return s.err
}
}
return s.emitSimple(TokenChar, "/")
case '~':
// Includes or Char.
return s.emitPrefixOrChar(TokenIncludes, "~=")
case '|':
// DashMatch or Char.
return s.emitPrefixOrChar(TokenDashMatch, "|=")
case '^':
// PrefixMatch or Char.
return s.emitPrefixOrChar(TokenPrefixMatch, "^=")
case '$':
// SuffixMatch or Char.
return s.emitPrefixOrChar(TokenSuffixMatch, "$=")
case '*':
// SubstringMatch or Char.
return s.emitPrefixOrChar(TokenSubstringMatch, "*=")
case '<':
// CDO or Char.
return s.emitPrefixOrChar(TokenCDO, "<!--")
}
// Test all regexps, in order.
for _, token := range matchOrder {
if match := matchers[token].FindString(input); match != "" {
return s.emitToken(token, match)
}
}
// We already handled unclosed quotation marks and comments,
// so this can only be a Char.
r, width := utf8.DecodeRuneInString(input)
token := &Token{TokenChar, string(r), s.row, s.col}
s.col += width
s.pos += width
return token
}
// updatePosition updates input coordinates based on the consumed text.
func (s *Scanner) updatePosition(text string) {
width := utf8.RuneCountInString(text)
lines := strings.Count(text, "\n")
s.row += lines
if lines == 0 {
s.col += width
} else {
s.col = utf8.RuneCountInString(text[strings.LastIndex(text, "\n"):])
}
s.pos += len(text) // while col is a rune index, pos is a byte index
}
// emitToken returns a Token for the string v and updates the scanner position.
func (s *Scanner) emitToken(t tokenType, v string) *Token {
token := &Token{t, v, s.row, s.col}
s.updatePosition(v)
return token
}
// emitSimple returns a Token for the string v and updates the scanner
// position in a simplified manner.
//
// The string is known to have only ASCII characters and to not have a newline.
func (s *Scanner) emitSimple(t tokenType, v string) *Token {
token := &Token{t, v, s.row, s.col}
s.col += len(v)
s.pos += len(v)
return token
}
// emitPrefixOrChar returns a Token for type t if the current position
// matches the given prefix. Otherwise it returns a Char token using the
// first character from the prefix.
//
// The prefix is known to have only ASCII characters and to not have a newline.
func (s *Scanner) emitPrefixOrChar(t tokenType, prefix string) *Token {
if strings.HasPrefix(s.input[s.pos:], prefix) {
return s.emitSimple(t, prefix)
}
return s.emitSimple(TokenChar, string(prefix[0]))
}

15
vendor/github.com/microcosm-cc/bluemonday/.gitignore generated vendored Normal file
View File

@ -0,0 +1,15 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# goland idea folder
*.idea

View File

@ -1,6 +1,5 @@
language: go
go:
- 1.1.x
- 1.2.x
- 1.3.x
- 1.4.x
@ -11,6 +10,7 @@ go:
- 1.9.x
- 1.10.x
- 1.11.x
- 1.12.x
- tip
matrix:
allow_failures:

View File

@ -1,6 +1,7 @@
1. Andrew Krasichkov @buglloc https://github.com/buglloc
1. John Graham-Cumming http://jgc.org/
1. Mohammad Gufran https://github.com/Gufran
1. Steven Gutzwiller https://github.com/StevenGutzwiller
1. Andrew Krasichkov @buglloc https://github.com/buglloc
1. Mike Samuel mikesamuel@gmail.com
1. Dmitri Shuralyov shurcooL@gmail.com
1. https://github.com/opennota
1. https://github.com/Gufran
1. https://github.com/opennota

View File

@ -58,10 +58,12 @@ We expect to be supplied with well-formatted HTML (closing elements for every ap
### Supported Go Versions
bluemonday is tested against Go 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, and tip.
bluemonday is tested against Go 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 1.10, 1.11, 1.12, and tip.
We do not support Go 1.0 as we depend on `golang.org/x/net/html` which includes a reference to `io.ErrNoProgress` which did not exist in Go 1.0.
We support Go 1.1 but Travis no longer tests against it.
## Is it production ready?
*Yes*
@ -90,7 +92,7 @@ func main() {
// Do this once for each unique policy, and use the policy for the life of the program
// Policy creation/editing is not safe to use in multiple goroutines
p := bluemonday.UGCPolicy()
// The policy can then be used to sanitize lots of input and it is safe to use the policy in multiple goroutines
html := p.Sanitize(
`<a onblur="alert(secret)" href="http://www.google.com">Google</a>`,
@ -167,12 +169,26 @@ To add elements to a policy either add just the elements:
p.AllowElements("b", "strong")
```
Or using a regex:
_Note: if an element is added by name as shown above, any matching regex will be ignored_
It is also recommended to ensure multiple patterns don't overlap as order of execution is not guaranteed and can result in some rules being missed.
```go
p.AllowElementsMatching(regex.MustCompile(`^my-element-`))
```
Or add elements as a virtue of adding an attribute:
```go
// Not the recommended pattern, see the recommendation on using .Matching() below
p.AllowAttrs("nowrap").OnElements("td", "th")
```
Again, this also supports a regex pattern match alternative:
```go
p.AllowAttrs("nowrap").OnElementsMatching(regex.MustCompile(`^my-element-`))
```
Attributes can either be added to all elements:
```go
p.AllowAttrs("dir").Matching(regexp.MustCompile("(?i)rtl|ltr")).Globally()
@ -202,6 +218,49 @@ p := bluemonday.UGCPolicy()
p.AllowElements("fieldset", "select", "option")
```
### Inline CSS
Although it's possible to handle inline CSS using `AllowAttrs` with a `Matching` rule, writing a single monolithic regular expression to safely process all inline CSS which you wish to allow is not a trivial task. Instead of attempting to do so, you can whitelist the `style` attribute on whichever element(s) you desire and use style policies to control and sanitize inline styles.
It is suggested that you use `Matching` (with a suitable regular expression)
`MatchingEnum`, or `MatchingHandler` to ensure each style matches your needs,
but default handlers are supplied for most widely used styles.
Similar to attributes, you can allow specific CSS properties to be set inline:
```go
p.AllowAttrs("style").OnElements("span", "p")
// Allow the 'color' property with valid RGB(A) hex values only (on any element allowed a 'style' attribute)
p.AllowStyles("color").Matching(regexp.MustCompile("(?i)^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$")).Globally()
```
Additionally, you can allow a CSS property to be set only to an allowed value:
```go
p.AllowAttrs("style").OnElements("span", "p")
// Allow the 'text-decoration' property to be set to 'underline', 'line-through' or 'none'
// on 'span' elements only
p.AllowStyles("text-decoration").MatchingEnum("underline", "line-through", "none").OnElements("span")
```
Or you can specify elements based on a regex patterm match:
```go
p.AllowAttrs("style").OnElementsMatching(regex.MustCompile(`^my-element-`))
// Allow the 'text-decoration' property to be set to 'underline', 'line-through' or 'none'
// on 'span' elements only
p.AllowStyles("text-decoration").MatchingEnum("underline", "line-through", "none").OnElementsMatching(regex.MustCompile(`^my-element-`))
```
If you need more specific checking, you can create a handler that takes in a string and returns a bool to
validate the values for a given property. The string parameter has been
converted to lowercase and unicode code points have been converted.
```go
myHandler := func(value string) bool{
return true
}
p.AllowAttrs("style").OnElements("span", "p")
// Allow the 'color' property with values validated by the handler (on any element allowed a 'style' attribute)
p.AllowStyles("color").MatchingHandler(myHandler).Globally()
```
### Links
Links are difficult beasts to sanitise safely and also one of the biggest attack vectors for malicious content.
@ -236,6 +295,13 @@ Regardless of whether you have enabled parseable URLs, you can force all URLs to
p.RequireNoFollowOnLinks(true)
```
Similarly, you can force all URLs to have "noreferrer" in their rel attribute.
```go
// This applies to "a" "area" "link" elements that have a "href" attribute
p.RequireNoReferrerOnLinks(true)
```
We provide a convenience method that applies all of the above, but you will still need to whitelist the linkable elements for the URL rules to be applied to:
```go
p.AllowStandardURLs()
@ -316,7 +382,6 @@ It is not the job of bluemonday to fix your bad HTML, it is merely the job of bl
## TODO
* Add support for CSS sanitisation to allow some CSS properties based on a whitelist, possibly using the [Gorilla CSS3 scanner](http://www.gorillatoolkit.org/pkg/css/scanner) - PRs welcome so long as testing covers XSS and demonstrates safety first
* Investigate whether devs want to blacklist elements and attributes. This would allow devs to take an existing policy (such as the `bluemonday.UGCPolicy()` ) that encapsulates 90% of what they're looking for but does more than they need, and to remove the extra things they do not want to make it 100% what they want
* Investigate whether devs want a validating HTML mode, in which the HTML elements are not just transformed into a balanced tree (every start tag has a closing tag at the correct depth) but also that elements and character data appear only in their allowed context (i.e. that a `table` element isn't a descendent of a `caption`, that `colgroup`, `thead`, `tbody`, `tfoot` and `tr` are permitted, and that character data is not permitted)

View File

@ -2,4 +2,9 @@ module github.com/microcosm-cc/bluemonday
go 1.9
require golang.org/x/net v0.0.0-20181220203305-927f97764cc3
require (
github.com/aymerick/douceur v0.2.0 // indirect
github.com/chris-ramon/douceur v0.2.0
github.com/gorilla/css v1.0.0 // indirect
golang.org/x/net v0.0.0-20181220203305-927f97764cc3
)

View File

@ -1,2 +1,8 @@
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/chris-ramon/douceur v0.2.0 h1:IDMEdxlEUUBYBKE4z/mJnFyVXox+MjuEVDJNN27glkU=
github.com/chris-ramon/douceur v0.2.0/go.mod h1:wDW5xjJdeoMm1mRt4sD4c/LbF/mWdEpRXQKjTR8nIBE=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3 h1:eH6Eip3UpmR+yM/qI9Ijluzb1bNv/cAU/n+6l8tRSis=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=

2085
vendor/github.com/microcosm-cc/bluemonday/handlers.go generated vendored Normal file
View File

@ -0,0 +1,2085 @@
// Copyright (c) 2019, David Kitchen <david@buro9.com>
//
// 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 the organisation (Microcosm) 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 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.
package bluemonday
import (
"regexp"
"strings"
)
var (
defaultStyleHandlers = map[string]func(string) bool{
"align-content": AlignContentHandler,
"align-items": AlignItemsHandler,
"align-self": AlignSelfHandler,
"all": AllHandler,
"animation": AnimationHandler,
"animation-delay": AnimationDelayHandler,
"animation-direction": AnimationDirectionHandler,
"animation-duration": AnimationDurationHandler,
"animation-fill-mode": AnimationFillModeHandler,
"animation-iteration-count": AnimationIterationCountHandler,
"animation-name": AnimationNameHandler,
"animation-play-state": AnimationPlayStateHandler,
"animation-timing-function": TimingFunctionHandler,
"backface-visibility": BackfaceVisibilityHandler,
"background": BackgroundHandler,
"background-attachment": BackgroundAttachmentHandler,
"background-blend-mode": BackgroundBlendModeHandler,
"background-clip": BackgroundClipHandler,
"background-color": ColorHandler,
"background-image": ImageHandler,
"background-origin": BackgroundOriginHandler,
"background-position": BackgroundPositionHandler,
"background-repeat": BackgroundRepeatHandler,
"background-size": BackgroundSizeHandler,
"border": BorderHandler,
"border-bottom": BorderSideHandler,
"border-bottom-color": ColorHandler,
"border-bottom-left-radius": BorderSideRadiusHandler,
"border-bottom-right-radius": BorderSideRadiusHandler,
"border-bottom-style": BorderSideStyleHandler,
"border-bottom-width": BorderSideWidthHandler,
"border-collapse": BorderCollapseHandler,
"border-color": ColorHandler,
"border-image": BorderImageHandler,
"border-image-outset": BorderImageOutsetHandler,
"border-image-repeat": BorderImageRepeatHandler,
"border-image-slice": BorderImageSliceHandler,
"border-image-source": ImageHandler,
"border-image-width": BorderImageWidthHandler,
"border-left": BorderSideHandler,
"border-left-color": ColorHandler,
"border-left-style": BorderSideStyleHandler,
"border-left-width": BorderSideWidthHandler,
"border-radius": BorderRadiusHandler,
"border-right": BorderSideHandler,
"border-right-color": ColorHandler,
"border-right-style": BorderSideStyleHandler,
"border-right-width": BorderSideWidthHandler,
"border-spacing": BorderSpacingHandler,
"border-style": BorderStyleHandler,
"border-top": BorderSideHandler,
"border-top-color": ColorHandler,
"border-top-left-radius": BorderSideRadiusHandler,
"border-top-right-radius": BorderSideRadiusHandler,
"border-top-style": BorderSideStyleHandler,
"border-top-width": BorderSideWidthHandler,
"border-width": BorderWidthHandler,
"bottom": SideHandler,
"box-decoration-break": BoxDecorationBreakHandler,
"box-shadow": BoxShadowHandler,
"box-sizing": BoxSizingHandler,
"break-after": BreakBeforeAfterHandler,
"break-before": BreakBeforeAfterHandler,
"break-inside": BreakInsideHandler,
"caption-side": CaptionSideHandler,
"caret-color": CaretColorHandler,
"clear": ClearHandler,
"clip": ClipHandler,
"color": ColorHandler,
"column-count": ColumnCountHandler,
"column-fill": ColumnFillHandler,
"column-gap": ColumnGapHandler,
"column-rule": ColumnRuleHandler,
"column-rule-color": ColorHandler,
"column-rule-style": BorderSideStyleHandler,
"column-rule-width": ColumnRuleWidthHandler,
"column-span": ColumnSpanHandler,
"column-width": ColumnWidthHandler,
"columns": ColumnsHandler,
"cursor": CursorHandler,
"direction": DirectionHandler,
"display": DisplayHandler,
"empty-cells": EmptyCellsHandler,
"filter": FilterHandler,
"flex": FlexHandler,
"flex-basis": FlexBasisHandler,
"flex-direction": FlexDirectionHandler,
"flex-flow": FlexFlowHandler,
"flex-grow": FlexGrowHandler,
"flex-shrink": FlexGrowHandler,
"flex-wrap": FlexWrapHandler,
"float": FloatHandler,
"font": FontHandler,
"font-family": FontFamilyHandler,
"font-kerning": FontKerningHandler,
"font-language-override": FontLanguageOverrideHandler,
"font-size": FontSizeHandler,
"font-size-adjust": FontSizeAdjustHandler,
"font-stretch": FontStretchHandler,
"font-style": FontStyleHandler,
"font-synthesis": FontSynthesisHandler,
"font-variant": FontVariantHandler,
"font-variant-caps": FontVariantCapsHandler,
"font-variant-position": FontVariantPositionHandler,
"font-weight": FontWeightHandler,
"grid": GridHandler,
"grid-area": GridAreaHandler,
"grid-auto-columns": GridAutoColumnsHandler,
"grid-auto-flow": GridAutoFlowHandler,
"grid-auto-rows": GridAutoColumnsHandler,
"grid-column": GridColumnHandler,
"grid-column-end": GridAxisStartEndHandler,
"grid-column-gap": LengthHandler,
"grid-column-start": GridAxisStartEndHandler,
"grid-gap": GridGapHandler,
"grid-row": GridRowHandler,
"grid-row-end": GridAxisStartEndHandler,
"grid-row-gap": LengthHandler,
"grid-row-start": GridAxisStartEndHandler,
"grid-template": GridTemplateHandler,
"grid-template-areas": GridTemplateAreasHandler,
"grid-template-columns": GridTemplateColumnsHandler,
"grid-template-rows": GridTemplateRowsHandler,
"hanging-punctuation": HangingPunctuationHandler,
"height": HeightHandler,
"hyphens": HyphensHandler,
"image-rendering": ImageRenderingHandler,
"isolation": IsolationHandler,
"justify-content": JustifyContentHandler,
"left": SideHandler,
"letter-spacing": LetterSpacingHandler,
"line-break": LineBreakHandler,
"line-height": LineHeightHandler,
"list-style": ListStyleHandler,
"list-style-image": ImageHandler,
"list-style-position": ListStylePositionHandler,
"list-style-type": ListStyleTypeHandler,
"margin": MarginHandler,
"margin-bottom": MarginSideHandler,
"margin-left": MarginSideHandler,
"margin-right": MarginSideHandler,
"margin-top": MarginSideHandler,
"max-height": MaxHeightWidthHandler,
"max-width": MaxHeightWidthHandler,
"min-height": MinHeightWidthHandler,
"min-width": MinHeightWidthHandler,
"mix-blend-mode": MixBlendModeHandler,
"object-fit": ObjectFitHandler,
"object-position": ObjectPositionHandler,
"opacity": OpacityHandler,
"order": OrderHandler,
"orphans": OrphansHandler,
"outline": OutlineHandler,
"outline-color": ColorHandler,
"outline-offset": OutlineOffsetHandler,
"outline-style": OutlineStyleHandler,
"outline-width": OutlineWidthHandler,
"overflow": OverflowHandler,
"overflow-wrap": OverflowWrapHandler,
"overflow-x": OverflowXYHandler,
"overflow-y": OverflowXYHandler,
"padding": PaddingHandler,
"padding-bottom": PaddingSideHandler,
"padding-left": PaddingSideHandler,
"padding-right": PaddingSideHandler,
"padding-top": PaddingSideHandler,
"page-break-after": PageBreakBeforeAfterHandler,
"page-break-before": PageBreakBeforeAfterHandler,
"page-break-inside": PageBreakInsideHandler,
"perspective": PerspectiveHandler,
"perspective-origin": PerspectiveOriginHandler,
"pointer-events": PointerEventsHandler,
"position": PositionHandler,
"quotes": QuotesHandler,
"resize": ResizeHandler,
"right": SideHandler,
"scroll-behavior": ScrollBehaviorHandler,
"tab-size": TabSizeHandler,
"table-layout": TableLayoutHandler,
"text-align": TextAlignHandler,
"text-align-last": TextAlignLastHandler,
"text-combine-upright": TextCombineUprightHandler,
"text-decoration": TextDecorationHandler,
"text-decoration-color": ColorHandler,
"text-decoration-line": TextDecorationLineHandler,
"text-decoration-style": TextDecorationStyleHandler,
"text-indent": TextIndentHandler,
"text-justify": TextJustifyHandler,
"text-orientation": TextOrientationHandler,
"text-overflow": TextOverflowHandler,
"text-shadow": TextShadowHandler,
"text-transform": TextTransformHandler,
"top": SideHandler,
"transform": TransformHandler,
"transform-origin": TransformOriginHandler,
"transform-style": TransformStyleHandler,
"transition": TransitionHandler,
"transition-delay": TransitionDelayHandler,
"transition-duration": TransitionDurationHandler,
"transition-property": TransitionPropertyHandler,
"transition-timing-function": TimingFunctionHandler,
"unicode-bidi": UnicodeBidiHandler,
"user-select": UserSelectHandler,
"vertical-align": VerticalAlignHandler,
"visibility": VisiblityHandler,
"white-space": WhiteSpaceHandler,
"widows": OrphansHandler,
"width": WidthHandler,
"word-break": WordBreakHandler,
"word-spacing": WordSpacingHandler,
"word-wrap": WordWrapHandler,
"writing-mode": WritingModeHandler,
"z-index": ZIndexHandler,
}
colorValues = []string{"initial", "inherit", "aliceblue", "antiquewhite",
"aqua", "aquamarine", "azure", "beige", "bisque", "black",
"blanchedalmond", "blue", "blueviolet", "brown", "burlywood",
"cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
"cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
"darkgray", "darkgrey", "darkgreen", "darkkhaki", "darkmagenta",
"darkolivegreen", "darkorange", "darkorchid", "darkred", "darksalmon",
"darkseagreen", "darkslateblue", "darkslategrey", "darkslategray",
"darkturquoise", "darkviolet", "deeppink", "deepskyblue", "dimgray",
"dimgrey", "dodgerblue", "firebrick", "floralwhite", "forestgreen",
"fuchsia", "gainsboro", "ghostwhite", "gold", "goldenrod", "gray",
"grey", "green", "greenyellow", "honeydew", "hotpink", "indianred",
"indigo", "ivory", "khaki", "lavender", "lavenderblush",
"lemonchiffon", "lightblue", "lightcoral", "lightcyan",
"lightgoldenrodyellow", "lightgray", "lightgrey", "lightgreen",
"lightpink", "lightsalmon", "lightseagreen", "lightskyblue",
"lightslategray", "lightslategrey", "lightsteeelblue", "lightyellow",
"lime", "limegreen", "linen", "magenta", "maroon", "mediumaquamarine",
"mediumblue", "mediumorchid", "mediumpurple", "mediumseagreen",
"mediumslateblue", "mediumspringgreen", "mediumturquoise",
"mediumvioletred", "midnightblue", "mintcream", "mistyrose",
"moccasin", "navajowhite", "navy", "oldlace", "olive", "olivedrab",
"orange", "orangered", "orchid", "palegoldenrod", "palegreen",
"paleturquoise", "palevioletred", "papayawhip", "peachpuff", "peru",
"pink", "plum", "powderblue", "purple", "rebeccapurple", "red",
"rosybrown", "royalblue", "saddlebrown", "salmon", "sandybrown",
"seagreen", "seashell", "sienna", "silver", "skyblue", "slateblue",
"slategray", "slategrey", "snow", "springgreen", "steelblue", "tan",
"teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
"whitesmoke", "yellow", "yellowgreen"}
)
func multiSplit(value string, seps ...string) []string {
curArray := []string{value}
for _, i := range seps {
newArray := []string{}
for _, j := range curArray {
newArray = append(newArray, strings.Split(j, i)...)
}
curArray = newArray
}
return curArray
}
func recursiveCheck(value []string, funcs []func(string) bool) bool {
for i := 0; i < len(value); i++ {
tempVal := strings.Join(value[:i+1], " ")
for _, j := range funcs {
if j(tempVal) && (len(value[i+1:]) == 0 || recursiveCheck(value[i+1:], funcs)) {
return true
}
}
}
return false
}
func in(value []string, arr []string) bool {
for _, i := range value {
foundString := false
for _, j := range arr {
if j == i {
foundString = true
}
}
if !foundString {
return false
}
}
return true
}
func splitValues(value string) []string {
values := strings.Split(value, ",")
for _, strippedValue := range values {
strippedValue = strings.ToLower(strings.TrimSpace(strippedValue))
}
return values
}
func getDefaultHandler(attr string) func(string) bool {
if defaultStyleHandlers[attr] != nil {
return defaultStyleHandlers[attr]
}
return BaseHandler
}
func BaseHandler(value string) bool {
return false
}
func AlignContentHandler(value string) bool {
values := []string{"stretch", "center", "flex-start",
"flex-end", "space-between", "space-around", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func AlignItemsHandler(value string) bool {
values := []string{"stretch", "center", "flex-start",
"flex-end", "baseline", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func AlignSelfHandler(value string) bool {
values := []string{"auto", "stretch", "center", "flex-start",
"flex-end", "baseline", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func AllHandler(value string) bool {
values := []string{"initial", "inherit", "unset"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func AnimationHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
usedFunctions := []func(string) bool{
AnimationNameHandler,
AnimationDurationHandler,
TimingFunctionHandler,
AnimationDelayHandler,
AnimationIterationCountHandler,
AnimationDirectionHandler,
AnimationFillModeHandler,
AnimationPlayStateHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func AnimationDelayHandler(value string) bool {
reg := regexp.MustCompile(`[\-]?[0-9]+[\.]?[0-9]*[s|ms]?`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
values := []string{"initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func AnimationDirectionHandler(value string) bool {
values := []string{"normal", "reverse", "alternate", "alternate-reverse", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func AnimationDurationHandler(value string) bool {
reg := regexp.MustCompile(`[0-9]+[\.]?[0-9]*[s|ms]?`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
values := []string{"initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func AnimationFillModeHandler(value string) bool {
values := []string{"none", "forwards", "backwards", "both", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func AnimationIterationCountHandler(value string) bool {
reg := regexp.MustCompile(`[0-9]+[\.]?[0-9]*`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
values := []string{"infinite", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func AnimationNameHandler(value string) bool {
reg := regexp.MustCompile(`[a-z]+`)
reg.Longest()
return reg.FindString(value) == value && value != ""
}
func AnimationPlayStateHandler(value string) bool {
values := []string{"paused", "running", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TimingFunctionHandler(value string) bool {
values := []string{"linear", "ease", "ease-in", "ease-out", "ease-in-out", "step-start", "step-end", "initial", "inherit"}
splitVals := splitValues(value)
if in(splitVals, values) {
return true
}
reg := regexp.MustCompile(`cubic-bezier\(([ ]*(0(.[0-9]+)?|1(.0)?),){3}[ ]*(0(.[0-9]+)?|1)\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
reg = regexp.MustCompile(`steps\([ ]*[0-9]+([ ]*,[ ]*(start|end)?)\)`)
reg.Longest()
return reg.FindString(value) == value && value != ""
}
func BackfaceVisibilityHandler(value string) bool {
values := []string{"visible", "hidden", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func BackgroundHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
newSplitVals := []string{}
for _, i := range splitVals {
if len(strings.Split(i, "/")) == 2 {
newSplitVals = append(newSplitVals, strings.Split(i, "/")...)
} else {
newSplitVals = append(newSplitVals, i)
}
}
usedFunctions := []func(string) bool{
ColorHandler,
ImageHandler,
BackgroundPositionHandler,
BackgroundSizeHandler,
BackgroundRepeatHandler,
BackgroundOriginHandler,
BackgroundClipHandler,
BackgroundAttachmentHandler,
}
return recursiveCheck(newSplitVals, usedFunctions)
}
func BackgroundAttachmentHandler(value string) bool {
values := []string{"scroll", "fixed", "local", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func BackgroundClipHandler(value string) bool {
values := []string{"border-box", "padding-box", "content-box", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func BackgroundBlendModeHandler(value string) bool {
values := []string{"normal", "multiply", "screen", "overlay", "darken",
"lighten", "color-dodge", "saturation", "color", "luminosity"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func ImageHandler(value string) bool {
values := []string{"none", "initial", "inherit"}
splitVals := splitValues(value)
if in(splitVals, values) {
return true
}
reg := regexp.MustCompile(`url\([\"\']?((https|http)[a-z0-9\.\\/_:]+[\"\']?)\)`)
reg.Longest()
return reg.FindString(value) == value && value != ""
}
func BackgroundOriginHandler(value string) bool {
values := []string{"padding-box", "border-box", "content-box", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func BackgroundPositionHandler(value string) bool {
splitVals := strings.Split(value, ";")
values := []string{"left", "left top", "left bottom", "right", "right top", "right bottom", "right center", "center top", "center center", "center bottom", "center", "top", "bottom", "initial", "inherit"}
if in(splitVals, values) {
return true
}
reg := regexp.MustCompile(`[\-]*[0-9]+[cm|mm|in|px|pt|pc\%]* [[\-]*[0-9]+[cm|mm|in|px|pt|pc\%]*]*`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
return false
}
func BackgroundRepeatHandler(value string) bool {
values := []string{"repeat", "repeat-x", "repeat-y", "no-repeat", "space", "round", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func BackgroundSizeHandler(value string) bool {
splitVals := strings.Split(value, " ")
values := []string{"auto", "cover", "contain", "initial", "inherit"}
if in(splitVals, values) {
return true
}
if len(splitVals) > 0 && LengthHandler(splitVals[0]) {
if len(splitVals) < 2 || (len(splitVals) == 2 && LengthHandler(splitVals[1])) {
return true
}
}
return false
}
func BorderHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := multiSplit(value, " ", "/")
usedFunctions := []func(string) bool{
BorderWidthHandler,
BorderStyleHandler,
ColorHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func BorderSideHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
usedFunctions := []func(string) bool{
BorderSideWidthHandler,
BorderSideStyleHandler,
ColorHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func BorderSideRadiusHandler(value string) bool {
splitVals := strings.Split(value, " ")
valid := true
for _, i := range splitVals {
if !LengthHandler(i) {
valid = false
break
}
}
if valid {
return true
}
splitVals = splitValues(value)
values := []string{"initial", "inherit"}
return in(splitVals, values)
}
func BorderSideStyleHandler(value string) bool {
values := []string{"none", "hidden", "dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func BorderSideWidthHandler(value string) bool {
if LengthHandler(value) {
return true
}
splitVals := strings.Split(value, ";")
values := []string{"medium", "thin", "thick", "initial", "inherit"}
return in(splitVals, values)
}
func BorderCollapseHandler(value string) bool {
values := []string{"separate", "collapse", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func BorderImageHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := multiSplit(value, " ", " / ")
usedFunctions := []func(string) bool{
ImageHandler,
BorderImageSliceHandler,
BorderImageWidthHandler,
BorderImageOutsetHandler,
BorderImageRepeatHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func BorderImageOutsetHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func BorderImageRepeatHandler(value string) bool {
values := []string{"stretch", "repeat", "round", "space", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func BorderImageSliceHandler(value string) bool {
values := []string{"fill", "initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
if len(splitVals) > 4 {
return false
}
usedFunctions := []func(string) bool{
LengthHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func BorderImageWidthHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"auto", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func BorderRadiusHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
if len(splitVals) > 4 {
return false
}
usedFunctions := []func(string) bool{
LengthHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func BorderSpacingHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
if len(splitVals) > 2 {
return false
}
usedFunctions := []func(string) bool{
LengthHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func BorderStyleHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
if len(splitVals) > 4 {
return false
}
usedFunctions := []func(string) bool{
BorderSideStyleHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func BorderWidthHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
if len(splitVals) > 4 {
return false
}
usedFunctions := []func(string) bool{
BorderSideWidthHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func SideHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"auto", "inherit", "unset"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func BoxDecorationBreakHandler(value string) bool {
values := []string{"slice", "clone", "initial", "initial", "inherit", "unset"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func BoxShadowHandler(value string) bool {
values := []string{"none", "initial", "inherit"}
if in([]string{value}, values) {
return true
}
commaSplitVals := strings.Split(value, ",")
for _, val := range commaSplitVals {
splitVals := strings.Split(val, " ")
if len(splitVals) > 6 || len(splitVals) < 2 {
return false
}
if !LengthHandler(splitVals[0]) {
return false
}
if !LengthHandler(splitVals[1]) {
return false
}
usedFunctions := []func(string) bool{
LengthHandler,
ColorHandler,
}
if len(splitVals) > 2 && !recursiveCheck(splitVals[2:], usedFunctions) {
return false
}
}
return true
}
func BoxSizingHandler(value string) bool {
values := []string{"slicontent-box", "border-box", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func BreakBeforeAfterHandler(value string) bool {
values := []string{"auto", "avoid", "always", "all", "avoid-page", "page", "left", "right", "recto", "verso", "avoid-column", "column", "avoid-region", "region"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func BreakInsideHandler(value string) bool {
values := []string{"auto", "avoid", "avoid-page", "avoid-column", "avoid-region"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func CaptionSideHandler(value string) bool {
values := []string{"top", "bottom", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func CaretColorHandler(value string) bool {
splitVals := splitValues(value)
if in(splitVals, colorValues) {
return true
}
reg := regexp.MustCompile(`#[0-9abcdef]{6}`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
reg = regexp.MustCompile(`rgb\(([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))),){2}([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))))\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
reg = regexp.MustCompile(`rgba\(([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))),){3}[ ]*(1(\.0)?|0|(0\.[0-9]+))\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
reg = regexp.MustCompile(`hsl\([ ]*([012]?[0-9]{1,2}|3[0-5][0-9]|360),[ ]*([0-9]{0,2}|100)\%,[ ]*([0-9]{0,2}|100)\%\)`)
if reg.FindString(value) == value && value != "" {
return true
}
reg = regexp.MustCompile(`hsla\(([ ]*[012]?[0-9]{1,2}|3[0-5][0-9]|360),[ ]*([0-9]{0,2}|100)\%,[ ]*([0-9]{0,2}|100)\%,[ ]*(1|1\.0|0|(0\.[0-9]+))\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
return false
}
func ClearHandler(value string) bool {
values := []string{"none", "left", "right", "both", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func ClipHandler(value string) bool {
reg := regexp.MustCompile(`rect\([0-9]+px,[ ]*[0-9]+px,[ ]*[0-9]+px,[ ]*[0-9]+px\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
values := []string{"auto", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func ColorHandler(value string) bool {
splitVals := splitValues(value)
if in(splitVals, colorValues) {
return true
}
reg := regexp.MustCompile(`#[0-9abcdef]{6}`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
reg = regexp.MustCompile(`rgb\(([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))),){2}([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))))\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
reg = regexp.MustCompile(`rgba\(([ ]*((([0-9]{1,2}|100)\%)|(([01]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))),){3}[ ]*(1(\.0)?|0|(0\.[0-9]+))\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
reg = regexp.MustCompile(`hsl\([ ]*([012]?[0-9]{1,2}|3[0-5][0-9]|360),[ ]*([0-9]{0,2}|100)\%,[ ]*([0-9]{0,2}|100)\%\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
reg = regexp.MustCompile(`hsla\(([ ]*[012]?[0-9]{1,2}|3[0-5][0-9]|360),[ ]*([0-9]{0,2}|100)\%,[ ]*([0-9]{0,2}|100)\%,[ ]*(1|1\.0|0|(0\.[0-9]+))\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
return false
}
func ColumnCountHandler(value string) bool {
reg := regexp.MustCompile(`[0-9]+`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
values := []string{"auto", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func ColumnFillHandler(value string) bool {
values := []string{"balance", "auto", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func ColumnGapHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"normal", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func ColumnRuleHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
usedFunctions := []func(string) bool{
ColumnRuleWidthHandler,
BorderSideStyleHandler,
ColorHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func ColumnRuleWidthHandler(value string) bool {
if LengthHandler(value) {
return true
}
splitVals := strings.Split(value, ";")
values := []string{"medium", "thin", "thick", "initial", "inherit"}
return in(splitVals, values)
}
func ColumnSpanHandler(value string) bool {
values := []string{"none", "all", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func ColumnWidthHandler(value string) bool {
if LengthHandler(value) {
return true
}
splitVals := strings.Split(value, ";")
values := []string{"auto", "initial", "inherit"}
return in(splitVals, values)
}
func ColumnsHandler(value string) bool {
values := []string{"auto", "initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
usedFunctions := []func(string) bool{
ColumnWidthHandler,
ColumnCountHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func CursorHandler(value string) bool {
values := []string{"alias", "all-scroll", "auto", "cell", "context-menu", "col-resize", "copy", "crosshair", "default", "e-resize", "ew-resize", "grab", "grabbing", "help", "move", "n-resize", "ne-resize", "nesw-resize", "ns-resize", "nw-resize", "nwse-resize", "no-drop", "none", "not-allowed", "pointer", "progress", "row-resize", "s-resize", "se-resize", "sw-resize", "text", "vertical-text", "w-resize", "wait", "zoom-in", "zoom-out", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func DirectionHandler(value string) bool {
values := []string{"ltr", "rtl", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func DisplayHandler(value string) bool {
values := []string{"inline", "block", "contents", "flex", "grid", "inline-block", "inline-flex", "inline-grid", "inline-table", "list-item", "run-in", "table", "table-caption", "table-column-group", "table-header-group", "table-footer-group", "table-row-group", "table-cell", "table-column", "table-row", "none", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func EmptyCellsHandler(value string) bool {
values := []string{"show", "hide", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func FilterHandler(value string) bool {
values := []string{"none", "initial", "inherit"}
splitVals := splitValues(value)
if in(splitVals, values) {
return true
}
reg := regexp.MustCompile(`blur\([0-9]+px\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
reg = regexp.MustCompile(`(brightness|contrast)\([0-9]+\%\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
reg = regexp.MustCompile(`drop-shadow\(([-]?[0-9]+px) ([-]?[0-9]+px)( [-]?[0-9]+px)?( ([-]?[0-9]+px))?`)
reg.Longest()
colorValue := strings.TrimSuffix(string(reg.ReplaceAll([]byte(value), []byte{})), ")")
if ColorHandler(colorValue) {
return true
}
reg = regexp.MustCompile(`grayscale\(([0-9]{1,2}|100)%\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
reg = regexp.MustCompile(`hue-rotate\(([12]?[0-9]{1,2}|3[0-5][0-9]|360)?\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
reg = regexp.MustCompile(`invert\(([0-9]{1,2}|100)%\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
reg = regexp.MustCompile(`opacity\(([0-9]{1,2}|100)%\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
reg = regexp.MustCompile(`saturate\([0-9]+%\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
reg = regexp.MustCompile(`sepia\(([0-9]{1,2}|100)%\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
//Not allowing URLs
return false
}
func FlexHandler(value string) bool {
values := []string{"auto", "initial", "initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
usedFunctions := []func(string) bool{
FlexGrowHandler,
FlexBasisHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func FlexBasisHandler(value string) bool {
if LengthHandler(value) {
return true
}
splitVals := strings.Split(value, ";")
values := []string{"auto", "initial", "inherit"}
return in(splitVals, values)
}
func FlexDirectionHandler(value string) bool {
values := []string{"row", "row-reverse", "column", "column-reverse", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func FlexFlowHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
usedFunctions := []func(string) bool{
FlexDirectionHandler,
FlexWrapHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func FlexGrowHandler(value string) bool {
reg := regexp.MustCompile(`[0-9\.]+`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
splitVals := strings.Split(value, ";")
values := []string{"initial", "inherit"}
return in(splitVals, values)
}
func FlexWrapHandler(value string) bool {
values := []string{"nowrap", "wrap", "wrap-reverse", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func FloatHandler(value string) bool {
values := []string{"none", "left", "right", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func FontHandler(value string) bool {
values := []string{"caption", "icon", "menu", "message-box", "small-caption", "status-bar", "initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
newSplitVals := []string{}
for _, i := range splitVals {
if len(strings.Split(i, "/")) == 2 {
newSplitVals = append(newSplitVals, strings.Split(i, "/")...)
} else {
newSplitVals = append(newSplitVals, i)
}
}
usedFunctions := []func(string) bool{
FontStyleHandler,
FontVariantHandler,
FontWeightHandler,
FontSizeHandler,
FontFamilyHandler,
}
return recursiveCheck(newSplitVals, usedFunctions)
}
func FontFamilyHandler(value string) bool {
values := []string{"initial", "inherit"}
splitVals := splitValues(value)
if in(splitVals, values) {
return true
}
reg := regexp.MustCompile(`('[a-z \-]+'|[a-z \-]+)`)
reg.Longest()
for _, i := range splitVals {
i = strings.TrimSpace(i)
if reg.FindString(i) != i {
return false
}
}
return true
}
func FontKerningHandler(value string) bool {
values := []string{"auto", "normal", "none"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func FontLanguageOverrideHandler(value string) bool {
reg := regexp.MustCompile(`[a-z]+`)
reg.Longest()
return reg.FindString(value) == value && value != ""
}
func FontSizeHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"medium", "xx-small", "x-small", "small", "large", "x-large", "xx-large", "smaller", "larger", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func FontSizeAdjustHandler(value string) bool {
reg := regexp.MustCompile(`[0-9]+[\.]?[0-9]*`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
values := []string{"auto", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func FontStretchHandler(value string) bool {
values := []string{"ultra-condensed", "extra-condensed", "condensed", "semi-condensed", "normal", "semi-expanded", "expanded", "extra-expanded", "ultra-expanded", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func FontStyleHandler(value string) bool {
values := []string{"normal", "italic", "oblique", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func FontSynthesisHandler(value string) bool {
values := []string{"none", "style", "weight"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func FontVariantCapsHandler(value string) bool {
values := []string{"normal", "small-caps", "all-small-caps", "petite-caps", "all-petite-caps", "unicase", "titling-caps"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func FontVariantHandler(value string) bool {
values := []string{"normal", "small-caps", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func FontVariantPositionHandler(value string) bool {
values := []string{"normal", "sub", "super"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func FontWeightHandler(value string) bool {
values := []string{"normal", "bold", "bolder", "lighter", "100", "200", "300", "400", "500", "600", "700", "800", "900", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func GridHandler(value string) bool {
values := []string{"none", "initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
newSplitVals := []string{}
for _, i := range splitVals {
if i != "/" {
newSplitVals = append(newSplitVals, i)
}
}
usedFunctions := []func(string) bool{
GridTemplateRowsHandler,
GridTemplateColumnsHandler,
GridTemplateAreasHandler,
GridAutoColumnsHandler,
GridAutoFlowHandler,
}
return recursiveCheck(newSplitVals, usedFunctions)
}
func GridAreaHandler(value string) bool {
values := []string{"none", "initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " / ")
usedFunctions := []func(string) bool{
GridAxisStartEndHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func GridAutoColumnsHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"auto", "max-content", "min-content", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func GridAutoFlowHandler(value string) bool {
values := []string{"row", "column", "dense", "row dense", "column dense"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func GridColumnHandler(value string) bool {
values := []string{"none", "initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " / ")
if len(splitVals) > 2 {
return false
}
usedFunctions := []func(string) bool{
GridAxisStartEndHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func GridColumnGapHandler(value string) bool {
return LengthHandler(value)
}
func LengthHandler(value string) bool {
reg := regexp.MustCompile(`[\-]?[0-9]+[\.]?[0-9]*(%|cm|mm|in|px|pt|pc|em|ex|ch|rem|vw|vh|vmin|vmax|deg|rad|turn)?`)
reg.Longest()
return reg.FindString(value) == value && value != ""
}
func LineBreakHandler(value string) bool {
values := []string{"auto", "loose", "normal", "strict"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func GridAxisStartEndHandler(value string) bool {
reg := regexp.MustCompile(`[0-9]+`)
if reg.FindString(value) == value && value != "" {
return true
}
reg = regexp.MustCompile(`span [0-9]+`)
if reg.FindString(value) == value && value != "" {
return true
}
values := []string{"auto"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func GridGapHandler(value string) bool {
splitVals := strings.Split(value, " ")
if len(splitVals) > 2 {
return false
}
usedFunctions := []func(string) bool{
GridColumnGapHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func GridRowHandler(value string) bool {
splitVals := strings.Split(value, " / ")
if len(splitVals) > 2 {
return false
}
usedFunctions := []func(string) bool{
GridAxisStartEndHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func GridTemplateHandler(value string) bool {
values := []string{"none", "initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " / ")
if len(splitVals) > 2 {
return false
}
usedFunctions := []func(string) bool{
GridTemplateColumnsHandler,
GridTemplateRowsHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func GridTemplateAreasHandler(value string) bool {
values := []string{"none"}
if in([]string{value}, values) {
return true
}
reg := regexp.MustCompile(`['"]?[a-z ]+['"]?`)
reg.Longest()
return reg.FindString(value) == value && value != ""
}
func GridTemplateColumnsHandler(value string) bool {
splitVals := strings.Split(value, " ")
values := []string{"none", "auto", "max-content", "min-content", "initial", "inherit"}
for _, val := range splitVals {
if LengthHandler(val) {
continue
}
valArr := []string{val}
if !in(valArr, values) {
return false
}
}
return true
}
func GridTemplateRowsHandler(value string) bool {
splitVals := strings.Split(value, " ")
values := []string{"none", "auto", "max-content", "min-content"}
for _, val := range splitVals {
if LengthHandler(val) {
continue
}
valArr := []string{val}
if !in(valArr, values) {
return false
}
}
return true
}
func HangingPunctuationHandler(value string) bool {
values := []string{"none", "first", "last", "allow-end", "force-end", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func HeightHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"auto", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func HyphensHandler(value string) bool {
values := []string{"none", "manual", "auto", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func ImageRenderingHandler(value string) bool {
values := []string{"auto", "smooth", "high-quality", "crisp-edges", "pixelated"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func IsolationHandler(value string) bool {
values := []string{"auto", "isolate", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func JustifyContentHandler(value string) bool {
values := []string{"flex-start", "flex-end", "center", "space-between", "space-around", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func LetterSpacingHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"normal", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func LineHeightHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"normal", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func ListStyleHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
usedFunctions := []func(string) bool{
ListStyleTypeHandler,
ListStylePositionHandler,
ImageHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func ListStylePositionHandler(value string) bool {
values := []string{"inside", "outside", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func ListStyleTypeHandler(value string) bool {
values := []string{"disc", "armenian", "circle", "cjk-ideographic", "decimal", "decimal-leading-zero", "georgian", "hebrew", "hiragana", "hiragana-iroha", "katakana", "katakana-iroha", "lower-alpha", "lower-greek", "lower-latin", "lower-roman", "none", "square", "upper-alpha", "upper-greek", "upper-latin", "upper-roman", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func MarginHandler(value string) bool {
values := []string{"auto", "initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
usedFunctions := []func(string) bool{
MarginSideHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func MarginSideHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"auto", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func MaxHeightWidthHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"none", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func MinHeightWidthHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func MixBlendModeHandler(value string) bool {
values := []string{"normal", "multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn", "difference", "exclusion", "hue", "saturation", "color", "luminosity"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func ObjectFitHandler(value string) bool {
values := []string{"fill", "contain", "cover", "none", "scale-down", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func ObjectPositionHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
if len(splitVals) > 2 {
return false
}
usedFunctions := []func(string) bool{
LengthHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func OpacityHandler(value string) bool {
reg := regexp.MustCompile("(0[.]?[0-9]*)|(1.0)")
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
values := []string{"initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func OrderHandler(value string) bool {
reg := regexp.MustCompile("[0-9]+")
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
values := []string{"initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func OutlineHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
usedFunctions := []func(string) bool{
ColorHandler,
OutlineWidthHandler,
OutlineStyleHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func OutlineOffsetHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func OutlineStyleHandler(value string) bool {
values := []string{"none", "hidden", "dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func OutlineWidthHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"medium", "thin", "thick", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func OverflowHandler(value string) bool {
values := []string{"visible", "hidden", "scroll", "auto", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func OverflowXYHandler(value string) bool {
values := []string{"visible", "hidden", "scroll", "auto", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func OverflowWrapHandler(value string) bool {
values := []string{"normal", "break-word", "anywhere"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func OrphansHandler(value string) bool {
reg := regexp.MustCompile(`[0-9]+`)
reg.Longest()
return reg.FindString(value) == value && value != ""
}
func PaddingHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
if len(splitVals) > 4 {
return false
}
usedFunctions := []func(string) bool{
PaddingSideHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func PaddingSideHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func PageBreakBeforeAfterHandler(value string) bool {
values := []string{"auto", "always", "avoid", "left", "right", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func PageBreakInsideHandler(value string) bool {
values := []string{"auto", "avoid", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func PerspectiveHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"none", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func PerspectiveOriginHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
xValues := []string{"left", "center", "right"}
yValues := []string{"top", "center", "bottom"}
if len(splitVals) > 1 {
if !in([]string{splitVals[0]}, xValues) && !LengthHandler(splitVals[0]) {
return false
}
return in([]string{splitVals[1]}, yValues) || LengthHandler(splitVals[1])
} else if len(splitVals) == 1 {
return in(splitVals, xValues) || in(splitVals, yValues) || LengthHandler(splitVals[0])
}
return false
}
func PointerEventsHandler(value string) bool {
values := []string{"auto", "none", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func PositionHandler(value string) bool {
values := []string{"static", "absolute", "fixed", "relative", "sticky", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func QuotesHandler(value string) bool {
values := []string{"none", "initial", "inherit"}
splitVals := splitValues(value)
if in(splitVals, values) {
return true
}
reg := regexp.MustCompile(`([ ]*["'][\x{0022}\x{0027}\x{2039}\x{2039}\x{203A}\x{00AB}\x{00BB}\x{2018}\x{2019}\x{201C}-\x{201E}]["'] ["'][\x{0022}\x{0027}\x{2039}\x{2039}\x{203A}\x{00AB}\x{00BB}\x{2018}\x{2019}\x{201C}-\x{201E}]["'])+`)
reg.Longest()
return reg.FindString(value) == value && value != ""
}
func ResizeHandler(value string) bool {
values := []string{"none", "both", "horizontal", "vertical", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func ScrollBehaviorHandler(value string) bool {
values := []string{"auto", "smooth", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TabSizeHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TableLayoutHandler(value string) bool {
values := []string{"auto", "fixed", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TextAlignHandler(value string) bool {
values := []string{"left", "right", "center", "justify", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TextAlignLastHandler(value string) bool {
values := []string{"auto", "left", "right", "center", "justify", "start", "end", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TextCombineUprightHandler(value string) bool {
values := []string{"none", "all"}
splitVals := splitValues(value)
if in(splitVals, values) {
return true
}
reg := regexp.MustCompile(`digits [2-4]`)
reg.Longest()
return reg.FindString(value) == value && value != ""
}
func TextDecorationHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
usedFunctions := []func(string) bool{
TextDecorationStyleHandler,
ColorHandler,
TextDecorationLineHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func TextDecorationLineHandler(value string) bool {
values := []string{"none", "underline", "overline", "line-through", "initial", "inherit"}
splitVals := strings.Split(value, " ")
return in(splitVals, values)
}
func TextDecorationStyleHandler(value string) bool {
values := []string{"solid", "double", "dotted", "dashed", "wavy", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TextIndentHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TextJustifyHandler(value string) bool {
values := []string{"auto", "inter-word", "inter-character", "none", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TextOverflowHandler(value string) bool {
reg := regexp.MustCompile("[\"'][a-z]+[\"']")
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
values := []string{"clip", "ellipsis", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TextOrientationHandler(value string) bool {
values := []string{"mixed", "upright", "sideways", "sideways-right"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TextShadowHandler(value string) bool {
values := []string{"none", "initial", "inherit"}
if in([]string{value}, values) {
return true
}
commaSplitVals := strings.Split(value, ",")
for _, val := range commaSplitVals {
splitVals := strings.Split(val, " ")
if len(splitVals) > 6 || len(splitVals) < 2 {
return false
}
if !LengthHandler(splitVals[0]) {
return false
}
if !LengthHandler(splitVals[1]) {
return false
}
usedFunctions := []func(string) bool{
LengthHandler,
ColorHandler,
}
if len(splitVals) > 2 && !recursiveCheck(splitVals[2:], usedFunctions) {
return false
}
}
return true
}
func TextTransformHandler(value string) bool {
values := []string{"none", "capitalize", "uppercase", "lowercase", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TransformHandler(value string) bool {
values := []string{"none", "initial", "inherit"}
if in([]string{value}, values) {
return true
}
reg := regexp.MustCompile(`matrix\(([ ]*[0-9]+[\.]?[0-9]*,){5}([ ]*[0-9]+[\.]?[0-9]*)\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
reg = regexp.MustCompile(`matrix3d\(([ ]*[0-9]+[\.]?[0-9]*,){15}([ ]*[0-9]+[\.]?[0-9]*)\)`)
if reg.FindString(value) == value && value != "" {
return true
}
reg = regexp.MustCompile(`(translate|translate3d|translatex|translatey|translatez|scale|scale3d|scalex|scaley|scalez)\(`)
reg.Longest()
subValue := string(reg.ReplaceAll([]byte(value), []byte{}))
trimValue := strings.Split(strings.TrimSuffix(subValue, ")"), ",")
valid := true
for _, i := range trimValue {
if !LengthHandler(strings.TrimSpace(i)) {
valid = false
break
}
}
if valid && trimValue != nil {
return true
}
reg = regexp.MustCompile(`rotate(x|y|z)?\(([12]?|3[0-5][0-9]|360)\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
reg = regexp.MustCompile(`rotate3d\(([ ]?(1(\.0)?|0\.[0-9]+),){3}([12]?|3[0-5][0-9]|360)\)`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
reg = regexp.MustCompile(`skew(x|y)?\(`)
reg.Longest()
subValue = string(reg.ReplaceAll([]byte(value), []byte{}))
subValue = strings.TrimSuffix(subValue, ")")
trimValue = strings.Split(subValue, ",")
valid = true
for _, i := range trimValue {
if !LengthHandler(strings.TrimSpace(i)) {
valid = false
break
}
}
if valid {
return true
}
reg = regexp.MustCompile(`perspective\(`)
reg.Longest()
subValue = string(reg.ReplaceAll([]byte(value), []byte{}))
subValue = strings.TrimSuffix(subValue, ")")
return LengthHandler(subValue)
}
func TransformOriginHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
xValues := []string{"left", "center", "right"}
yValues := []string{"top", "center", "bottom"}
if len(splitVals) > 2 {
if !in([]string{splitVals[0]}, xValues) && !LengthHandler(splitVals[0]) {
return false
}
if !in([]string{splitVals[1]}, yValues) && !LengthHandler(splitVals[1]) {
return false
}
return LengthHandler(splitVals[2])
} else if len(splitVals) > 1 {
if !in([]string{splitVals[0]}, xValues) && !LengthHandler(splitVals[0]) {
return false
}
return in([]string{splitVals[1]}, yValues) || LengthHandler(splitVals[1])
} else if len(splitVals) == 1 {
return in(splitVals, xValues) || in(splitVals, yValues) || LengthHandler(splitVals[0])
}
return false
}
func TransformStyleHandler(value string) bool {
values := []string{"flat", "preserve-3d", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TransitionHandler(value string) bool {
values := []string{"initial", "inherit"}
if in([]string{value}, values) {
return true
}
splitVals := strings.Split(value, " ")
usedFunctions := []func(string) bool{
TransitionPropertyHandler,
TransitionDurationHandler,
TimingFunctionHandler,
TransitionDelayHandler,
ColorHandler,
}
return recursiveCheck(splitVals, usedFunctions)
}
func TransitionDelayHandler(value string) bool {
reg := regexp.MustCompile("[0-9]+[.]?[0-9]*(s|ms)?")
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
values := []string{"initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TransitionDurationHandler(value string) bool {
reg := regexp.MustCompile("[0-9]+[.]?[0-9]*(s|ms)?")
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
values := []string{"initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func TransitionPropertyHandler(value string) bool {
reg := regexp.MustCompile("([a-zA-Z]+,[ ]?)*[a-zA-Z]+")
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
values := []string{"none", "all", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func UnicodeBidiHandler(value string) bool {
values := []string{"normal", "embed", "bidi-override", "isolate", "isolate-override", "plaintext", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func UserSelectHandler(value string) bool {
values := []string{"auto", "none", "text", "all"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func VerticalAlignHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"baseline", "sub", "super", "top", "text-top", "middle", "bottom", "text-bottom", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func VisiblityHandler(value string) bool {
values := []string{"visible", "hidden", "collapse", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func WhiteSpaceHandler(value string) bool {
values := []string{"normal", "nowrap", "pre", "pre-line", "pre-wrap", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func WidthHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"auto", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func WordSpacingHandler(value string) bool {
if LengthHandler(value) {
return true
}
values := []string{"normal", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func WordBreakHandler(value string) bool {
values := []string{"normal", "break-all", "keep-all", "break-word", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func WordWrapHandler(value string) bool {
values := []string{"normal", "break-word", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func WritingModeHandler(value string) bool {
values := []string{"horizontal-tb", "vertical-rl", "vertical-lr"}
splitVals := splitValues(value)
return in(splitVals, values)
}
func ZIndexHandler(value string) bool {
reg := regexp.MustCompile(`[\-]?[0-9]+`)
reg.Longest()
if reg.FindString(value) == value && value != "" {
return true
}
values := []string{"auto", "initial", "inherit"}
splitVals := splitValues(value)
return in(splitVals, values)
}

View File

@ -135,7 +135,7 @@ func (p *Policy) AllowStandardURLs() {
// Most common URL schemes only
p.AllowURLSchemes("mailto", "http", "https")
// For all anchors we will add rel="nofollow" if it does not already exist
// For linking elements we will add rel="nofollow" if it does not already exist
// This applies to "a" "area" "link"
p.RequireNoFollowOnLinks(true)
}

View File

@ -29,6 +29,8 @@
package bluemonday
//TODO sgutzwiller create map of styles to default handlers
//TODO sgutzwiller create handlers for various attributes
import (
"net/url"
"regexp"
@ -51,14 +53,22 @@ type Policy struct {
// tag is replaced by a space character.
addSpaces bool
// When true, add rel="nofollow" to HTML anchors
// When true, add rel="nofollow" to HTML a, area, and link tags
requireNoFollow bool
// When true, add rel="nofollow" to HTML anchors
// When true, add rel="nofollow" to HTML a, area, and link tags
// Will add for href="http://foo"
// Will skip for href="/foo" or href="foo"
requireNoFollowFullyQualifiedLinks bool
// When true, add rel="noreferrer" to HTML a, area, and link tags
requireNoReferrer bool
// When true, add rel="noreferrer" to HTML a, area, and link tags
// Will add for href="http://foo"
// Will skip for href="/foo" or href="foo"
requireNoReferrerFullyQualifiedLinks bool
// When true add target="_blank" to fully qualified links
// Will add for href="http://foo"
// Will skip for href="/foo" or href="foo"
@ -76,9 +86,21 @@ type Policy struct {
// map[htmlElementName]map[htmlAttributeName]attrPolicy
elsAndAttrs map[string]map[string]attrPolicy
// elsMatchingAndAttrs stores regex based element matches along with attributes
elsMatchingAndAttrs map[*regexp.Regexp]map[string]attrPolicy
// map[htmlAttributeName]attrPolicy
globalAttrs map[string]attrPolicy
// map[htmlElementName]map[cssPropertyName]stylePolicy
elsAndStyles map[string]map[string]stylePolicy
// map[regex]map[cssPropertyName]stylePolicy
elsMatchingAndStyles map[*regexp.Regexp]map[string]stylePolicy
// map[cssPropertyName]stylePolicy
globalStyles map[string]stylePolicy
// If urlPolicy is nil, all URLs with matching schema are allowed.
// Otherwise, only the URLs with matching schema and urlPolicy(url)
// returning true are allowed.
@ -93,6 +115,16 @@ type Policy struct {
// be maintained in the output HTML.
setOfElementsAllowedWithoutAttrs map[string]struct{}
// If an element has had all attributes removed as a result of a policy
// being applied, then the element would be removed from the output.
//
// However some elements are valid and have strong layout meaning without
// any attributes, i.e. <table>.
//
// In this case, any element matching a regular expression will be accepted without
// attributes added.
setOfElementsMatchingAllowedWithoutAttrs []*regexp.Regexp
setOfElementsToSkipContent map[string]struct{}
}
@ -103,6 +135,20 @@ type attrPolicy struct {
regexp *regexp.Regexp
}
type stylePolicy struct {
// handler to validate
handler func(string) bool
// optional pattern to match, when not nil the regexp needs to match
// otherwise the property is removed
regexp *regexp.Regexp
// optional list of allowed property values, for properties which
// have a defined list of allowed values; property will be removed
// if the value is not allowed
enum []string
}
type attrPolicyBuilder struct {
p *Policy
@ -111,13 +157,26 @@ type attrPolicyBuilder struct {
allowEmpty bool
}
type stylePolicyBuilder struct {
p *Policy
propertyNames []string
regexp *regexp.Regexp
enum []string
handler func(string) bool
}
type urlPolicy func(url *url.URL) (allowUrl bool)
// init initializes the maps if this has not been done already
func (p *Policy) init() {
if !p.initialized {
p.elsAndAttrs = make(map[string]map[string]attrPolicy)
p.elsMatchingAndAttrs = make(map[*regexp.Regexp]map[string]attrPolicy)
p.globalAttrs = make(map[string]attrPolicy)
p.elsAndStyles = make(map[string]map[string]stylePolicy)
p.elsMatchingAndStyles = make(map[*regexp.Regexp]map[string]stylePolicy)
p.globalStyles = make(map[string]stylePolicy)
p.allowURLSchemes = make(map[string]urlPolicy)
p.setOfElementsAllowedWithoutAttrs = make(map[string]struct{})
p.setOfElementsToSkipContent = make(map[string]struct{})
@ -245,6 +304,30 @@ func (abp *attrPolicyBuilder) OnElements(elements ...string) *Policy {
return abp.p
}
// OnElementsMatching will bind an attribute policy to all elements matching a given regex
// and return the updated policy
func (abp *attrPolicyBuilder) OnElementsMatching(regex *regexp.Regexp) *Policy {
for _, attr := range abp.attrNames {
if _, ok := abp.p.elsMatchingAndAttrs[regex]; !ok {
abp.p.elsMatchingAndAttrs[regex] = make(map[string]attrPolicy)
}
ap := attrPolicy{}
if abp.regexp != nil {
ap.regexp = abp.regexp
}
abp.p.elsMatchingAndAttrs[regex][attr] = ap
}
if abp.allowEmpty {
abp.p.setOfElementsMatchingAllowedWithoutAttrs = append(abp.p.setOfElementsMatchingAllowedWithoutAttrs, regex)
if _, ok := abp.p.elsMatchingAndAttrs[regex]; !ok {
abp.p.elsMatchingAndAttrs[regex] = make(map[string]attrPolicy)
}
}
return abp.p
}
// Globally will bind an attribute policy to all HTML elements and return the
// updated policy
func (abp *attrPolicyBuilder) Globally() *Policy {
@ -265,6 +348,139 @@ func (abp *attrPolicyBuilder) Globally() *Policy {
return abp.p
}
// AllowStyles takes a range of CSS property names and returns a
// style policy builder that allows you to specify the pattern and scope of
// the whitelisted property.
//
// The style policy is only added to the core policy when either Globally()
// or OnElements(...) are called.
func (p *Policy) AllowStyles(propertyNames ...string) *stylePolicyBuilder {
p.init()
abp := stylePolicyBuilder{
p: p,
}
for _, propertyName := range propertyNames {
abp.propertyNames = append(abp.propertyNames, strings.ToLower(propertyName))
}
return &abp
}
// Matching allows a regular expression to be applied to a nascent style
// policy, and returns the style policy. Calling this more than once will
// replace the existing regexp.
func (spb *stylePolicyBuilder) Matching(regex *regexp.Regexp) *stylePolicyBuilder {
spb.regexp = regex
return spb
}
// MatchingEnum allows a list of allowed values to be applied to a nascent style
// policy, and returns the style policy. Calling this more than once will
// replace the existing list of allowed values.
func (spb *stylePolicyBuilder) MatchingEnum(enum ...string) *stylePolicyBuilder {
spb.enum = enum
return spb
}
// MatchingHandler allows a handler to be applied to a nascent style
// policy, and returns the style policy. Calling this more than once will
// replace the existing handler.
func (spb *stylePolicyBuilder) MatchingHandler(handler func(string) bool) *stylePolicyBuilder {
spb.handler = handler
return spb
}
// OnElements will bind a style policy to a given range of HTML elements
// and return the updated policy
func (spb *stylePolicyBuilder) OnElements(elements ...string) *Policy {
for _, element := range elements {
element = strings.ToLower(element)
for _, attr := range spb.propertyNames {
if _, ok := spb.p.elsAndStyles[element]; !ok {
spb.p.elsAndStyles[element] = make(map[string]stylePolicy)
}
sp := stylePolicy{}
if spb.handler != nil {
sp.handler = spb.handler
} else if len(spb.enum) > 0 {
sp.enum = spb.enum
} else if spb.regexp != nil {
sp.regexp = spb.regexp
} else {
sp.handler = getDefaultHandler(attr)
}
spb.p.elsAndStyles[element][attr] = sp
}
}
return spb.p
}
// OnElementsMatching will bind a style policy to any HTML elements matching the pattern
// and return the updated policy
func (spb *stylePolicyBuilder) OnElementsMatching(regex *regexp.Regexp) *Policy {
for _, attr := range spb.propertyNames {
if _, ok := spb.p.elsMatchingAndStyles[regex]; !ok {
spb.p.elsMatchingAndStyles[regex] = make(map[string]stylePolicy)
}
sp := stylePolicy{}
if spb.handler != nil {
sp.handler = spb.handler
} else if len(spb.enum) > 0 {
sp.enum = spb.enum
} else if spb.regexp != nil {
sp.regexp = spb.regexp
} else {
sp.handler = getDefaultHandler(attr)
}
spb.p.elsMatchingAndStyles[regex][attr] = sp
}
return spb.p
}
// Globally will bind a style policy to all HTML elements and return the
// updated policy
func (spb *stylePolicyBuilder) Globally() *Policy {
for _, attr := range spb.propertyNames {
if _, ok := spb.p.globalStyles[attr]; !ok {
spb.p.globalStyles[attr] = stylePolicy{}
}
// Use only one strategy for validating styles, fallback to default
sp := stylePolicy{}
if spb.handler != nil {
sp.handler = spb.handler
} else if len(spb.enum) > 0 {
sp.enum = spb.enum
} else if spb.regexp != nil {
sp.regexp = spb.regexp
} else {
sp.handler = getDefaultHandler(attr)
}
spb.p.globalStyles[attr] = sp
}
return spb.p
}
// AllowElements will append HTML elements to the whitelist without applying an
// attribute policy to those elements (the elements are permitted
// sans-attributes)
@ -282,8 +498,16 @@ func (p *Policy) AllowElements(names ...string) *Policy {
return p
}
// RequireNoFollowOnLinks will result in all <a> tags having a rel="nofollow"
// added to them if one does not already exist
func (p *Policy) AllowElementsMatching(regex *regexp.Regexp) *Policy {
p.init()
if _, ok := p.elsMatchingAndAttrs[regex]; !ok {
p.elsMatchingAndAttrs[regex] = make(map[string]attrPolicy)
}
return p
}
// RequireNoFollowOnLinks will result in all a, area, link tags having a
// rel="nofollow"added to them if one does not already exist
//
// Note: This requires p.RequireParseableURLs(true) and will enable it.
func (p *Policy) RequireNoFollowOnLinks(require bool) *Policy {
@ -294,9 +518,10 @@ func (p *Policy) RequireNoFollowOnLinks(require bool) *Policy {
return p
}
// RequireNoFollowOnFullyQualifiedLinks will result in all <a> tags that point
// to a non-local destination (i.e. starts with a protocol and has a host)
// having a rel="nofollow" added to them if one does not already exist
// RequireNoFollowOnFullyQualifiedLinks will result in all a, area, and link
// tags that point to a non-local destination (i.e. starts with a protocol and
// has a host) having a rel="nofollow" added to them if one does not already
// exist
//
// Note: This requires p.RequireParseableURLs(true) and will enable it.
func (p *Policy) RequireNoFollowOnFullyQualifiedLinks(require bool) *Policy {
@ -307,9 +532,35 @@ func (p *Policy) RequireNoFollowOnFullyQualifiedLinks(require bool) *Policy {
return p
}
// AddTargetBlankToFullyQualifiedLinks will result in all <a> tags that point
// to a non-local destination (i.e. starts with a protocol and has a host)
// having a target="_blank" added to them if one does not already exist
// RequireNoReferrerOnLinks will result in all a, area, and link tags having a
// rel="noreferrrer" added to them if one does not already exist
//
// Note: This requires p.RequireParseableURLs(true) and will enable it.
func (p *Policy) RequireNoReferrerOnLinks(require bool) *Policy {
p.requireNoReferrer = require
p.requireParseableURLs = true
return p
}
// RequireNoReferrerOnFullyQualifiedLinks will result in all a, area, and link
// tags that point to a non-local destination (i.e. starts with a protocol and
// has a host) having a rel="noreferrer" added to them if one does not already
// exist
//
// Note: This requires p.RequireParseableURLs(true) and will enable it.
func (p *Policy) RequireNoReferrerOnFullyQualifiedLinks(require bool) *Policy {
p.requireNoReferrerFullyQualifiedLinks = require
p.requireParseableURLs = true
return p
}
// AddTargetBlankToFullyQualifiedLinks will result in all a, area and link tags
// that point to a non-local destination (i.e. starts with a protocol and has a
// host) having a target="_blank" added to them if one does not already exist
//
// Note: This requires p.RequireParseableURLs(true) and will enable it.
func (p *Policy) AddTargetBlankToFullyQualifiedLinks(require bool) *Policy {

View File

@ -34,15 +34,19 @@ import (
"io"
"net/url"
"regexp"
"strconv"
"strings"
"golang.org/x/net/html"
cssparser "github.com/chris-ramon/douceur/parser"
)
var (
dataAttribute = regexp.MustCompile("^data-.+")
dataAttributeXMLPrefix = regexp.MustCompile("^xml.+")
dataAttributeInvalidChars = regexp.MustCompile("[A-Z;]+")
cssUnicodeChar = regexp.MustCompile(`\\[0-9a-f]{1,6} ?`)
)
// Sanitize takes a string that contains a HTML fragment or document and applies
@ -82,6 +86,98 @@ func (p *Policy) SanitizeReader(r io.Reader) *bytes.Buffer {
return p.sanitize(r)
}
const escapedURLChars = "'<>\"\r"
func escapeUrlComponent(val string) string {
w := bytes.NewBufferString("")
i := strings.IndexAny(val, escapedURLChars)
for i != -1 {
if _, err := w.WriteString(val[:i]); err != nil {
return w.String()
}
var esc string
switch val[i] {
case '\'':
// "&#39;" is shorter than "&apos;" and apos was not in HTML until HTML5.
esc = "&#39;"
case '<':
esc = "&lt;"
case '>':
esc = "&gt;"
case '"':
// "&#34;" is shorter than "&quot;".
esc = "&#34;"
case '\r':
esc = "&#13;"
default:
panic("unrecognized escape character")
}
val = val[i+1:]
if _, err := w.WriteString(esc); err != nil {
return w.String()
}
i = strings.IndexAny(val, escapedURLChars)
}
w.WriteString(val)
return w.String()
}
func sanitizedUrl(val string) (string, error) {
u, err := url.Parse(val)
if err != nil {
return "", err
}
// sanitize the url query params
sanitizedQueryValues := make(url.Values, 0)
queryValues := u.Query()
for k, vals := range queryValues {
sk := html.EscapeString(k)
for _, v := range vals {
sv := v
sanitizedQueryValues.Add(sk, sv)
}
}
u.RawQuery = sanitizedQueryValues.Encode()
// u.String() will also sanitize host/scheme/user/pass
return u.String(), nil
}
func (p *Policy) writeLinkableBuf(buff *bytes.Buffer, token *html.Token) {
// do not escape multiple query parameters
tokenBuff := bytes.NewBufferString("")
tokenBuff.WriteString("<")
tokenBuff.WriteString(token.Data)
for _, attr := range token.Attr {
tokenBuff.WriteByte(' ')
tokenBuff.WriteString(attr.Key)
tokenBuff.WriteString(`="`)
switch attr.Key {
case "href", "src":
u, ok := p.validURL(attr.Val)
if !ok {
tokenBuff.WriteString(html.EscapeString(attr.Val))
continue
}
u, err := sanitizedUrl(u)
if err == nil {
tokenBuff.WriteString(u)
} else {
// fallthrough
tokenBuff.WriteString(html.EscapeString(attr.Val))
}
default:
// re-apply
tokenBuff.WriteString(html.EscapeString(attr.Val))
}
tokenBuff.WriteByte('"')
}
if token.Type == html.SelfClosingTagToken {
tokenBuff.WriteString("/")
}
tokenBuff.WriteString(">")
buff.WriteString(tokenBuff.String())
}
// Performs the actual sanitization process.
func (p *Policy) sanitize(r io.Reader) *bytes.Buffer {
@ -133,20 +229,23 @@ func (p *Policy) sanitize(r io.Reader) *bytes.Buffer {
case html.StartTagToken:
mostRecentlyStartedToken = token.Data
mostRecentlyStartedToken = strings.ToLower(token.Data)
aps, ok := p.elsAndAttrs[token.Data]
if !ok {
if _, ok := p.setOfElementsToSkipContent[token.Data]; ok {
skipElementContent = true
skippingElementsCount++
aa, matched := p.matchRegex(token.Data)
if !matched {
if _, ok := p.setOfElementsToSkipContent[token.Data]; ok {
skipElementContent = true
skippingElementsCount++
}
if p.addSpaces {
buff.WriteString(" ")
}
break
}
if p.addSpaces {
buff.WriteString(" ")
}
break
aps = aa
}
if len(token.Attr) != 0 {
token.Attr = p.sanitizeAttrs(token.Data, token.Attr, aps)
}
@ -163,12 +262,17 @@ func (p *Policy) sanitize(r io.Reader) *bytes.Buffer {
}
if !skipElementContent {
buff.WriteString(token.String())
// do not escape multiple query parameters
if linkable(token.Data) {
p.writeLinkableBuf(&buff, &token)
} else {
buff.WriteString(token.String())
}
}
case html.EndTagToken:
if mostRecentlyStartedToken == token.Data {
if mostRecentlyStartedToken == strings.ToLower(token.Data) {
mostRecentlyStartedToken = ""
}
@ -182,18 +286,27 @@ func (p *Policy) sanitize(r io.Reader) *bytes.Buffer {
}
break
}
if _, ok := p.elsAndAttrs[token.Data]; !ok {
if _, ok := p.setOfElementsToSkipContent[token.Data]; ok {
match := false
for regex := range p.elsMatchingAndAttrs {
if regex.MatchString(token.Data) {
skipElementContent = false
match = true
break
}
}
if _, ok := p.setOfElementsToSkipContent[token.Data]; ok && !match {
skippingElementsCount--
if skippingElementsCount == 0 {
skipElementContent = false
}
}
if p.addSpaces {
buff.WriteString(" ")
if !match {
if p.addSpaces {
buff.WriteString(" ")
}
break
}
break
}
if !skipElementContent {
@ -204,10 +317,14 @@ func (p *Policy) sanitize(r io.Reader) *bytes.Buffer {
aps, ok := p.elsAndAttrs[token.Data]
if !ok {
if p.addSpaces {
buff.WriteString(" ")
aa, matched := p.matchRegex(token.Data)
if !matched {
if p.addSpaces && !matched {
buff.WriteString(" ")
}
break
}
break
aps = aa
}
if len(token.Attr) != 0 {
@ -217,12 +334,16 @@ func (p *Policy) sanitize(r io.Reader) *bytes.Buffer {
if len(token.Attr) == 0 && !p.allowNoAttrs(token.Data) {
if p.addSpaces {
buff.WriteString(" ")
break
}
break
}
if !skipElementContent {
buff.WriteString(token.String())
// do not escape multiple query parameters
if linkable(token.Data) {
p.writeLinkableBuf(&buff, &token)
} else {
buff.WriteString(token.String())
}
}
case html.TextToken:
@ -242,6 +363,7 @@ func (p *Policy) sanitize(r io.Reader) *bytes.Buffer {
buff.WriteString(token.String())
}
}
default:
// A token that didn't exist in the html package when we wrote this
return &bytes.Buffer{}
@ -262,6 +384,23 @@ func (p *Policy) sanitizeAttrs(
return attrs
}
hasStylePolicies := false
sps, elementHasStylePolicies := p.elsAndStyles[elementName]
if len(p.globalStyles) > 0 || (elementHasStylePolicies && len(sps) > 0) {
hasStylePolicies = true
}
// no specific element policy found, look for a pattern match
if !hasStylePolicies {
for k, v := range p.elsMatchingAndStyles {
if k.MatchString(elementName) {
if len(v) > 0 {
hasStylePolicies = true
break
}
}
}
}
// Builds a new attribute slice based on the whether the attribute has been
// whitelisted explicitly or globally.
cleanAttrs := []html.Attribute{}
@ -273,6 +412,19 @@ func (p *Policy) sanitizeAttrs(
continue
}
}
// Is this a "style" attribute, and if so, do we need to sanitize it?
if htmlAttr.Key == "style" && hasStylePolicies {
htmlAttr = p.sanitizeStyles(htmlAttr, elementName)
if htmlAttr.Val == "" {
// We've sanitized away any and all styles; don't bother to
// output the style attribute (even if it's allowed)
continue
} else {
cleanAttrs = append(cleanAttrs, htmlAttr)
continue
}
}
// Is there an element specific attribute policy that applies?
if ap, ok := aps[htmlAttr.Key]; ok {
if ap.regexp != nil {
@ -354,6 +506,8 @@ func (p *Policy) sanitizeAttrs(
if (p.requireNoFollow ||
p.requireNoFollowFullyQualifiedLinks ||
p.requireNoReferrer ||
p.requireNoReferrerFullyQualifiedLinks ||
p.addTargetBlankToFullyQualifiedLinks) &&
len(cleanAttrs) > 0 {
@ -381,12 +535,16 @@ func (p *Policy) sanitizeAttrs(
if hrefFound {
var (
noFollowFound bool
noReferrerFound bool
targetBlankFound bool
)
addNoFollow := (p.requireNoFollow ||
externalLink && p.requireNoFollowFullyQualifiedLinks)
addNoReferrer := (p.requireNoReferrer ||
externalLink && p.requireNoReferrerFullyQualifiedLinks)
addTargetBlank := (externalLink &&
p.addTargetBlankToFullyQualifiedLinks)
@ -394,18 +552,18 @@ func (p *Policy) sanitizeAttrs(
for _, htmlAttr := range cleanAttrs {
var appended bool
if htmlAttr.Key == "rel" && addNoFollow {
if htmlAttr.Key == "rel" && (addNoFollow || addNoReferrer) {
if strings.Contains(htmlAttr.Val, "nofollow") {
noFollowFound = true
tmpAttrs = append(tmpAttrs, htmlAttr)
appended = true
} else {
if addNoFollow && !strings.Contains(htmlAttr.Val, "nofollow") {
htmlAttr.Val += " nofollow"
noFollowFound = true
tmpAttrs = append(tmpAttrs, htmlAttr)
appended = true
}
if addNoReferrer && !strings.Contains(htmlAttr.Val, "noreferrer") {
htmlAttr.Val += " noreferrer"
}
noFollowFound = addNoFollow
noReferrerFound = addNoReferrer
tmpAttrs = append(tmpAttrs, htmlAttr)
appended = true
}
if elementName == "a" && htmlAttr.Key == "target" {
@ -424,14 +582,22 @@ func (p *Policy) sanitizeAttrs(
tmpAttrs = append(tmpAttrs, htmlAttr)
}
}
if noFollowFound || targetBlankFound {
if noFollowFound || noReferrerFound || targetBlankFound {
cleanAttrs = tmpAttrs
}
if addNoFollow && !noFollowFound {
if (addNoFollow && !noFollowFound) || (addNoReferrer && !noReferrerFound) {
rel := html.Attribute{}
rel.Key = "rel"
rel.Val = "nofollow"
if addNoFollow {
rel.Val = "nofollow"
}
if addNoReferrer {
if rel.Val != "" {
rel.Val += " "
}
rel.Val += "noreferrer"
}
cleanAttrs = append(cleanAttrs, rel)
}
@ -501,8 +667,95 @@ func (p *Policy) sanitizeAttrs(
return cleanAttrs
}
func (p *Policy) sanitizeStyles(attr html.Attribute, elementName string) html.Attribute {
sps := p.elsAndStyles[elementName]
if len(sps) == 0 {
sps = map[string]stylePolicy{}
// check for any matching elements, if we don't already have a policy found
// if multiple matches are found they will be overwritten, it's best
// to not have overlapping matchers
for regex, policies := range p.elsMatchingAndStyles {
if regex.MatchString(elementName) {
for k, v := range policies {
sps[k] = v
}
}
}
}
//Add semi-colon to end to fix parsing issue
if len(attr.Val) > 0 && attr.Val[len(attr.Val)-1] != ';' {
attr.Val = attr.Val + ";"
}
decs, err := cssparser.ParseDeclarations(attr.Val)
if err != nil {
attr.Val = ""
return attr
}
clean := []string{}
prefixes := []string{"-webkit-", "-moz-", "-ms-", "-o-", "mso-", "-xv-", "-atsc-", "-wap-", "-khtml-", "prince-", "-ah-", "-hp-", "-ro-", "-rim-", "-tc-"}
for _, dec := range decs {
addedProperty := false
tempProperty := strings.ToLower(dec.Property)
tempValue := removeUnicode(strings.ToLower(dec.Value))
for _, i := range prefixes {
tempProperty = strings.TrimPrefix(tempProperty, i)
}
if sp, ok := sps[tempProperty]; ok {
if sp.handler != nil {
if sp.handler(tempValue) {
clean = append(clean, dec.Property+": "+dec.Value)
addedProperty = true
}
} else if len(sp.enum) > 0 {
if stringInSlice(tempValue, sp.enum) {
clean = append(clean, dec.Property+": "+dec.Value)
addedProperty = true
}
} else if sp.regexp != nil {
if sp.regexp.MatchString(tempValue) {
clean = append(clean, dec.Property+": "+dec.Value)
addedProperty = true
}
continue
}
}
if sp, ok := p.globalStyles[tempProperty]; ok && !addedProperty {
if sp.handler != nil {
if sp.handler(tempValue) {
clean = append(clean, dec.Property+": "+dec.Value)
}
} else if len(sp.enum) > 0 {
if stringInSlice(tempValue, sp.enum) {
clean = append(clean, dec.Property+": "+dec.Value)
}
} else if sp.regexp != nil {
if sp.regexp.MatchString(tempValue) {
clean = append(clean, dec.Property+": "+dec.Value)
}
continue
}
}
}
if len(clean) > 0 {
attr.Val = strings.Join(clean, "; ")
} else {
attr.Val = ""
}
return attr
}
func (p *Policy) allowNoAttrs(elementName string) bool {
_, ok := p.setOfElementsAllowedWithoutAttrs[elementName]
if !ok {
for _, r := range p.setOfElementsMatchingAllowedWithoutAttrs {
if r.MatchString(elementName) {
ok = true
break
}
}
}
return ok
}
@ -561,6 +814,16 @@ func linkable(elementName string) bool {
}
}
// stringInSlice returns true if needle exists in haystack
func stringInSlice(needle string, haystack []string) bool {
for _, straw := range haystack {
if strings.ToLower(straw) == strings.ToLower(needle) {
return true
}
}
return false
}
func isDataAttribute(val string) bool {
if !dataAttribute.MatchString(val) {
return false
@ -579,3 +842,48 @@ func isDataAttribute(val string) bool {
}
return true
}
func removeUnicode(value string) string {
substitutedValue := value
currentLoc := cssUnicodeChar.FindStringIndex(substitutedValue)
for currentLoc != nil {
character := substitutedValue[currentLoc[0]+1 : currentLoc[1]]
character = strings.TrimSpace(character)
if len(character) < 4 {
character = strings.Repeat("0", 4-len(character)) + character
} else {
for len(character) > 4 {
if character[0] != '0' {
character = ""
break
} else {
character = character[1:]
}
}
}
character = "\\u" + character
translatedChar, err := strconv.Unquote(`"` + character + `"`)
translatedChar = strings.TrimSpace(translatedChar)
if err != nil {
return ""
}
substitutedValue = substitutedValue[0:currentLoc[0]] + translatedChar + substitutedValue[currentLoc[1]:]
currentLoc = cssUnicodeChar.FindStringIndex(substitutedValue)
}
return substitutedValue
}
func (p *Policy) matchRegex(elementName string) (map[string]attrPolicy, bool) {
aps := make(map[string]attrPolicy, 0)
matched := false
for regex, attrs := range p.elsMatchingAndAttrs {
if regex.MatchString(elementName) {
matched = true
for k, v := range attrs {
aps[k] = v
}
}
}
return aps, matched
}

View File

@ -11,11 +11,16 @@ type Buffer struct {
bytes.Buffer
}
// PrintableRuneCount returns the amount of printable runes in the buffer.
func (w Buffer) PrintableRuneCount() int {
// PrintableRuneWidth returns the width of all printable runes in the buffer.
func (w Buffer) PrintableRuneWidth() int {
return PrintableRuneWidth(w.String())
}
func PrintableRuneWidth(s string) int {
var n int
var ansi bool
for _, c := range w.String() {
for _, c := range s {
if c == '\x1B' {
// ANSI escape sequence
ansi = true

View File

@ -66,7 +66,7 @@ func (w *WordWrap) addSpace() {
func (w *WordWrap) addWord() {
if w.word.Len() > 0 {
w.addSpace()
w.lineLen += w.word.PrintableRuneCount()
w.lineLen += w.word.PrintableRuneWidth()
w.buf.Write(w.word.Bytes())
w.word.Reset()
}
@ -139,8 +139,8 @@ func (w *WordWrap) Write(b []byte) (int, error) {
// add a line break if the current word would exceed the line's
// character limit
if w.lineLen+w.space.Len()+w.word.PrintableRuneCount() > w.Limit &&
w.word.PrintableRuneCount() < w.Limit {
if w.lineLen+w.space.Len()+w.word.PrintableRuneWidth() > w.Limit &&
w.word.PrintableRuneWidth() < w.Limit {
w.addNewLine()
}
}

15
vendor/github.com/yuin/goldmark-emoji/.gitignore generated vendored Normal file
View File

@ -0,0 +1,15 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, build with `go test -c`
*.test
*.pprof
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
.DS_Store

21
vendor/github.com/yuin/goldmark-emoji/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Yusuke Inuzuka
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.

71
vendor/github.com/yuin/goldmark-emoji/README.md generated vendored Normal file
View File

@ -0,0 +1,71 @@
goldmark-emoji
=========================
[![GoDev][godev-image]][godev-url]
[godev-image]: https://pkg.go.dev/badge/github.com/yuin/goldmark-emoji
[godev-url]: https://pkg.go.dev/github.com/yuin/goldmark-emoji
goldmark-emoji is an extension for the [goldmark](http://github.com/yuin/goldmark)
that parses `:joy:` style emojis.
Installation
--------------------
```
go get github.com/yuin/goldmark-emoji
```
Usage
--------------------
```go
import (
"bytes"
"fmt"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark-emoji"
"github.com/yuin/goldmark-emoji/definition"
)
func main() {
markdown := goldmark.New(
goldmark.WithExtensions(
emoji.Emoji,
),
)
source := `
Joy :joy:
`
var buf bytes.Buffer
if err := markdown.Convert([]byte(source), &buf); err != nil {
panic(err)
}
fmt.Print(buf.String())
}
```
See `emoji_test.go` for detailed usage.
### Options
Options for the extension
| Option | Description |
| ------ | ----------- |
| `WithEmojis` | Definition of emojis. This defaults to github emoji set |
| `WithRenderingMethod` | `Entity` : renders as HTML entities, `Twemoji` : renders as an img tag that uses [twemoji](https://github.com/twitter/twemoji), `Func` : renders using a go function |
| `WithTwemojiTemplate` | Twemoji img tag printf template |
| `WithRendererFunc` | renders by a go function |
License
--------------------
MIT
Author
--------------------
Yusuke Inuzuka

42
vendor/github.com/yuin/goldmark-emoji/ast/emoji.go generated vendored Normal file
View File

@ -0,0 +1,42 @@
// Package ast defines AST nodes that represetns emoji extension's elements.
package ast
import (
"fmt"
"github.com/yuin/goldmark-emoji/definition"
gast "github.com/yuin/goldmark/ast"
)
// Emoji represents an inline emoji.
type Emoji struct {
gast.BaseInline
ShortName []byte
Value *definition.Emoji
}
// Dump implements Node.Dump.
func (n *Emoji) Dump(source []byte, level int) {
m := map[string]string{
"ShortName": string(n.ShortName),
"Value": fmt.Sprintf("%#v", n.Value),
}
gast.DumpHelper(n, source, level, m, nil)
}
// KindEmoji is a NodeKind of the emoji node.
var KindEmoji = gast.NewNodeKind("Emoji")
// Kind implements Node.Kind.
func (n *Emoji) Kind() gast.NodeKind {
return KindEmoji
}
// NewEmoji returns a new Emoji node.
func NewEmoji(shortName []byte, value *definition.Emoji) *Emoji {
return &Emoji{
ShortName: shortName,
Value: value,
}
}

View File

@ -0,0 +1,106 @@
package definition
// Emoji is a data structure that holds a single emoji.
type Emoji struct {
// Name is a name of this emoji.
Name string
// ShortNames is a shorter representation of this emoji.
ShortNames []string
// Unicode is an unicode representation of this emoji.
Unicode []rune
}
// NewEmoji returns a new Emoji.
func NewEmoji(name string, unicode []rune, shortNames ...string) Emoji {
if len(shortNames) == 0 {
panic("Emoji must have at leat 1 short name.")
}
if unicode == nil || len(unicode) == 0 {
unicode = []rune{0xFFFD}
}
return Emoji{
Name: name,
ShortNames: shortNames,
Unicode: unicode,
}
}
// IsUnicode returns true if this emoji is defined in unicode, otherwise false.
func (em *Emoji) IsUnicode() bool {
return !(len(em.Unicode) == 1 && em.Unicode[0] == 0xFFFD)
}
// Emojis is a collection of emojis.
type Emojis interface {
// Get returns (*Emoji, true) if found mapping associated with given short name, otherwise (nil, false).
Get(shortName string) (*Emoji, bool)
// Add adds new emojis to this collection.
Add(Emojis)
// Clone clones this collection.
Clone() Emojis
}
type emojis struct {
list []Emoji
m map[string]*Emoji
children []Emojis
}
// NewEmojis returns a new Emojis.
func NewEmojis(es ...Emoji) Emojis {
m := &emojis{
list: es,
m: map[string]*Emoji{},
children: []Emojis{},
}
for i, _ := range es {
emoji := &m.list[i]
for _, s := range emoji.ShortNames {
m.m[s] = emoji
}
}
return m
}
func (m *emojis) Add(emojis Emojis) {
m.children = append(m.children, emojis)
}
func (m *emojis) Clone() Emojis {
es := &emojis{
list: m.list,
m: m.m,
children: make([]Emojis, len(m.children)),
}
copy(es.children, m.children)
return es
}
func (m *emojis) Get(shortName string) (*Emoji, bool) {
v, ok := m.m[shortName]
if ok {
return v, ok
}
for _, es := range m.children {
v, ok := es.Get(shortName)
if ok {
return v, ok
}
}
return nil, false
}
// EmojisOption sets options for Emojis.
type EmojisOption func(Emojis)
// WithEmojis is an EmojisOption that adds emojis to the Emojis.
func WithEmojis(emojis ...Emoji) EmojisOption {
return func(m Emojis) {
m.Add(NewEmojis(emojis...))
}
}

View File

@ -0,0 +1,1757 @@
// This file was generated by _tools/gen-deifinition.go. DO NOT EDIT.
package definition
import "sync"
var github Emojis
var githubOnce sync.Once
func Github(opts ...EmojisOption) Emojis {
githubOnce.Do(func() {
github = NewEmojis(
NewEmoji("grinning face", []int32{128512}, "grinning"),
NewEmoji("grinning face with big eyes", []int32{128515}, "smiley"),
NewEmoji("grinning face with smiling eyes", []int32{128516}, "smile"),
NewEmoji("beaming face with smiling eyes", []int32{128513}, "grin"),
NewEmoji("grinning squinting face", []int32{128518}, "laughing"),
NewEmoji("grinning face with sweat", []int32{128517}, "sweat_smile"),
NewEmoji("rolling on the floor laughing", []int32{129315}, "rofl"),
NewEmoji("face with tears of joy", []int32{128514}, "joy"),
NewEmoji("slightly smiling face", []int32{128578}, "slightly_smiling_face"),
NewEmoji("upside-down face", []int32{128579}, "upside_down_face"),
NewEmoji("winking face", []int32{128521}, "wink"),
NewEmoji("smiling face with smiling eyes", []int32{128522}, "blush"),
NewEmoji("smiling face with halo", []int32{128519}, "innocent"),
NewEmoji("smiling face with hearts", []int32{129392}, "smiling_face_with_three_hearts"),
NewEmoji("smiling face with heart-eyes", []int32{128525}, "heart_eyes"),
NewEmoji("star-struck", []int32{129321}, "star_struck"),
NewEmoji("face blowing a kiss", []int32{128536}, "kissing_heart"),
NewEmoji("kissing face", []int32{128535}, "kissing"),
NewEmoji("smiling face", []int32{9786, 65039}, "relaxed"),
NewEmoji("kissing face with closed eyes", []int32{128538}, "kissing_closed_eyes"),
NewEmoji("kissing face with smiling eyes", []int32{128537}, "kissing_smiling_eyes"),
NewEmoji("face savoring food", []int32{128523}, "yum"),
NewEmoji("face with tongue", []int32{128539}, "stuck_out_tongue"),
NewEmoji("winking face with tongue", []int32{128540}, "stuck_out_tongue_winking_eye"),
NewEmoji("zany face", []int32{129322}, "zany_face"),
NewEmoji("squinting face with tongue", []int32{128541}, "stuck_out_tongue_closed_eyes"),
NewEmoji("money-mouth face", []int32{129297}, "money_mouth_face"),
NewEmoji("hugging face", []int32{129303}, "hugs"),
NewEmoji("face with hand over mouth", []int32{129325}, "hand_over_mouth"),
NewEmoji("shushing face", []int32{129323}, "shushing_face"),
NewEmoji("thinking face", []int32{129300}, "thinking"),
NewEmoji("zipper-mouth face", []int32{129296}, "zipper_mouth_face"),
NewEmoji("face with raised eyebrow", []int32{129320}, "raised_eyebrow"),
NewEmoji("neutral face", []int32{128528}, "neutral_face"),
NewEmoji("expressionless face", []int32{128529}, "expressionless"),
NewEmoji("face without mouth", []int32{128566}, "no_mouth"),
NewEmoji("smirking face", []int32{128527}, "smirk"),
NewEmoji("unamused face", []int32{128530}, "unamused"),
NewEmoji("face with rolling eyes", []int32{128580}, "roll_eyes"),
NewEmoji("grimacing face", []int32{128556}, "grimacing"),
NewEmoji("lying face", []int32{129317}, "lying_face"),
NewEmoji("relieved face", []int32{128524}, "relieved"),
NewEmoji("pensive face", []int32{128532}, "pensive"),
NewEmoji("sleepy face", []int32{128554}, "sleepy"),
NewEmoji("drooling face", []int32{129316}, "drooling_face"),
NewEmoji("sleeping face", []int32{128564}, "sleeping"),
NewEmoji("face with medical mask", []int32{128567}, "mask"),
NewEmoji("face with thermometer", []int32{129298}, "face_with_thermometer"),
NewEmoji("face with head-bandage", []int32{129301}, "face_with_head_bandage"),
NewEmoji("nauseated face", []int32{129314}, "nauseated_face"),
NewEmoji("face vomiting", []int32{129326}, "vomiting_face"),
NewEmoji("sneezing face", []int32{129319}, "sneezing_face"),
NewEmoji("hot face", []int32{129397}, "hot_face"),
NewEmoji("cold face", []int32{129398}, "cold_face"),
NewEmoji("woozy face", []int32{129396}, "woozy_face"),
NewEmoji("dizzy face", []int32{128565}, "dizzy_face"),
NewEmoji("exploding head", []int32{129327}, "exploding_head"),
NewEmoji("cowboy hat face", []int32{129312}, "cowboy_hat_face"),
NewEmoji("partying face", []int32{129395}, "partying_face"),
NewEmoji("smiling face with sunglasses", []int32{128526}, "sunglasses"),
NewEmoji("nerd face", []int32{129299}, "nerd_face"),
NewEmoji("face with monocle", []int32{129488}, "monocle_face"),
NewEmoji("confused face", []int32{128533}, "confused"),
NewEmoji("worried face", []int32{128543}, "worried"),
NewEmoji("slightly frowning face", []int32{128577}, "slightly_frowning_face"),
NewEmoji("frowning face", []int32{9785, 65039}, "frowning_face"),
NewEmoji("face with open mouth", []int32{128558}, "open_mouth"),
NewEmoji("hushed face", []int32{128559}, "hushed"),
NewEmoji("astonished face", []int32{128562}, "astonished"),
NewEmoji("flushed face", []int32{128563}, "flushed"),
NewEmoji("pleading face", []int32{129402}, "pleading_face"),
NewEmoji("frowning face with open mouth", []int32{128550}, "frowning"),
NewEmoji("anguished face", []int32{128551}, "anguished"),
NewEmoji("fearful face", []int32{128552}, "fearful"),
NewEmoji("anxious face with sweat", []int32{128560}, "cold_sweat"),
NewEmoji("sad but relieved face", []int32{128549}, "disappointed_relieved"),
NewEmoji("crying face", []int32{128546}, "cry"),
NewEmoji("loudly crying face", []int32{128557}, "sob"),
NewEmoji("face screaming in fear", []int32{128561}, "scream"),
NewEmoji("confounded face", []int32{128534}, "confounded"),
NewEmoji("persevering face", []int32{128547}, "persevere"),
NewEmoji("disappointed face", []int32{128542}, "disappointed"),
NewEmoji("downcast face with sweat", []int32{128531}, "sweat"),
NewEmoji("weary face", []int32{128553}, "weary"),
NewEmoji("tired face", []int32{128555}, "tired_face"),
NewEmoji("yawning face", []int32{129393}, "yawning_face"),
NewEmoji("face with steam from nose", []int32{128548}, "triumph"),
NewEmoji("pouting face", []int32{128545}, "rage"),
NewEmoji("angry face", []int32{128544}, "angry"),
NewEmoji("face with symbols on mouth", []int32{129324}, "cursing_face"),
NewEmoji("smiling face with horns", []int32{128520}, "smiling_imp"),
NewEmoji("angry face with horns", []int32{128127}, "imp"),
NewEmoji("skull", []int32{128128}, "skull"),
NewEmoji("skull and crossbones", []int32{9760, 65039}, "skull_and_crossbones"),
NewEmoji("pile of poo", []int32{128169}, "hankey"),
NewEmoji("clown face", []int32{129313}, "clown_face"),
NewEmoji("ogre", []int32{128121}, "japanese_ogre"),
NewEmoji("goblin", []int32{128122}, "japanese_goblin"),
NewEmoji("ghost", []int32{128123}, "ghost"),
NewEmoji("alien", []int32{128125}, "alien"),
NewEmoji("alien monster", []int32{128126}, "space_invader"),
NewEmoji("robot", []int32{129302}, "robot"),
NewEmoji("grinning cat", []int32{128570}, "smiley_cat"),
NewEmoji("grinning cat with smiling eyes", []int32{128568}, "smile_cat"),
NewEmoji("cat with tears of joy", []int32{128569}, "joy_cat"),
NewEmoji("smiling cat with heart-eyes", []int32{128571}, "heart_eyes_cat"),
NewEmoji("cat with wry smile", []int32{128572}, "smirk_cat"),
NewEmoji("kissing cat", []int32{128573}, "kissing_cat"),
NewEmoji("weary cat", []int32{128576}, "scream_cat"),
NewEmoji("crying cat", []int32{128575}, "crying_cat_face"),
NewEmoji("pouting cat", []int32{128574}, "pouting_cat"),
NewEmoji("see-no-evil monkey", []int32{128584}, "see_no_evil"),
NewEmoji("hear-no-evil monkey", []int32{128585}, "hear_no_evil"),
NewEmoji("speak-no-evil monkey", []int32{128586}, "speak_no_evil"),
NewEmoji("kiss mark", []int32{128139}, "kiss"),
NewEmoji("love letter", []int32{128140}, "love_letter"),
NewEmoji("heart with arrow", []int32{128152}, "cupid"),
NewEmoji("heart with ribbon", []int32{128157}, "gift_heart"),
NewEmoji("sparkling heart", []int32{128150}, "sparkling_heart"),
NewEmoji("growing heart", []int32{128151}, "heartpulse"),
NewEmoji("beating heart", []int32{128147}, "heartbeat"),
NewEmoji("revolving hearts", []int32{128158}, "revolving_hearts"),
NewEmoji("two hearts", []int32{128149}, "two_hearts"),
NewEmoji("heart decoration", []int32{128159}, "heart_decoration"),
NewEmoji("heart exclamation", []int32{10083, 65039}, "heavy_heart_exclamation"),
NewEmoji("broken heart", []int32{128148}, "broken_heart"),
NewEmoji("red heart", []int32{10084, 65039}, "heart"),
NewEmoji("orange heart", []int32{129505}, "orange_heart"),
NewEmoji("yellow heart", []int32{128155}, "yellow_heart"),
NewEmoji("green heart", []int32{128154}, "green_heart"),
NewEmoji("blue heart", []int32{128153}, "blue_heart"),
NewEmoji("purple heart", []int32{128156}, "purple_heart"),
NewEmoji("brown heart", []int32{129294}, "brown_heart"),
NewEmoji("black heart", []int32{128420}, "black_heart"),
NewEmoji("white heart", []int32{129293}, "white_heart"),
NewEmoji("hundred points", []int32{128175}, "100"),
NewEmoji("anger symbol", []int32{128162}, "anger"),
NewEmoji("collision", []int32{128165}, "boom"),
NewEmoji("dizzy", []int32{128171}, "dizzy"),
NewEmoji("sweat droplets", []int32{128166}, "sweat_drops"),
NewEmoji("dashing away", []int32{128168}, "dash"),
NewEmoji("hole", []int32{128371, 65039}, "hole"),
NewEmoji("bomb", []int32{128163}, "bomb"),
NewEmoji("speech balloon", []int32{128172}, "speech_balloon"),
NewEmoji("eye in speech bubble", []int32{128065, 65039, 8205, 128488, 65039}, "eye_speech_bubble"),
NewEmoji("left speech bubble", []int32{128488, 65039}, "left_speech_bubble"),
NewEmoji("right anger bubble", []int32{128495, 65039}, "right_anger_bubble"),
NewEmoji("thought balloon", []int32{128173}, "thought_balloon"),
NewEmoji("zzz", []int32{128164}, "zzz"),
NewEmoji("waving hand", []int32{128075}, "wave"),
NewEmoji("raised back of hand", []int32{129306}, "raised_back_of_hand"),
NewEmoji("hand with fingers splayed", []int32{128400, 65039}, "raised_hand_with_fingers_splayed"),
NewEmoji("raised hand", []int32{9995}, "hand"),
NewEmoji("vulcan salute", []int32{128406}, "vulcan_salute"),
NewEmoji("OK hand", []int32{128076}, "ok_hand"),
NewEmoji("pinching hand", []int32{129295}, "pinching_hand"),
NewEmoji("victory hand", []int32{9996, 65039}, "v"),
NewEmoji("crossed fingers", []int32{129310}, "crossed_fingers"),
NewEmoji("love-you gesture", []int32{129311}, "love_you_gesture"),
NewEmoji("sign of the horns", []int32{129304}, "metal"),
NewEmoji("call me hand", []int32{129305}, "call_me_hand"),
NewEmoji("backhand index pointing left", []int32{128072}, "point_left"),
NewEmoji("backhand index pointing right", []int32{128073}, "point_right"),
NewEmoji("backhand index pointing up", []int32{128070}, "point_up_2"),
NewEmoji("middle finger", []int32{128405}, "middle_finger"),
NewEmoji("backhand index pointing down", []int32{128071}, "point_down"),
NewEmoji("index pointing up", []int32{9757, 65039}, "point_up"),
NewEmoji("thumbs up", []int32{128077}, "+1"),
NewEmoji("thumbs down", []int32{128078}, "-1"),
NewEmoji("raised fist", []int32{9994}, "fist_raised"),
NewEmoji("oncoming fist", []int32{128074}, "fist_oncoming"),
NewEmoji("left-facing fist", []int32{129307}, "fist_left"),
NewEmoji("right-facing fist", []int32{129308}, "fist_right"),
NewEmoji("clapping hands", []int32{128079}, "clap"),
NewEmoji("raising hands", []int32{128588}, "raised_hands"),
NewEmoji("open hands", []int32{128080}, "open_hands"),
NewEmoji("palms up together", []int32{129330}, "palms_up_together"),
NewEmoji("handshake", []int32{129309}, "handshake"),
NewEmoji("folded hands", []int32{128591}, "pray"),
NewEmoji("writing hand", []int32{9997, 65039}, "writing_hand"),
NewEmoji("nail polish", []int32{128133}, "nail_care"),
NewEmoji("selfie", []int32{129331}, "selfie"),
NewEmoji("flexed biceps", []int32{128170}, "muscle"),
NewEmoji("mechanical arm", []int32{129470}, "mechanical_arm"),
NewEmoji("mechanical leg", []int32{129471}, "mechanical_leg"),
NewEmoji("leg", []int32{129461}, "leg"),
NewEmoji("foot", []int32{129462}, "foot"),
NewEmoji("ear", []int32{128066}, "ear"),
NewEmoji("ear with hearing aid", []int32{129467}, "ear_with_hearing_aid"),
NewEmoji("nose", []int32{128067}, "nose"),
NewEmoji("brain", []int32{129504}, "brain"),
NewEmoji("tooth", []int32{129463}, "tooth"),
NewEmoji("bone", []int32{129460}, "bone"),
NewEmoji("eyes", []int32{128064}, "eyes"),
NewEmoji("eye", []int32{128065, 65039}, "eye"),
NewEmoji("tongue", []int32{128069}, "tongue"),
NewEmoji("mouth", []int32{128068}, "lips"),
NewEmoji("baby", []int32{128118}, "baby"),
NewEmoji("child", []int32{129490}, "child"),
NewEmoji("boy", []int32{128102}, "boy"),
NewEmoji("girl", []int32{128103}, "girl"),
NewEmoji("person", []int32{129489}, "adult"),
NewEmoji("person: blond hair", []int32{128113}, "blond_haired_person"),
NewEmoji("man", []int32{128104}, "man"),
NewEmoji("man: beard", []int32{129492}, "bearded_person"),
NewEmoji("man: red hair", []int32{128104, 8205, 129456}, "red_haired_man"),
NewEmoji("man: curly hair", []int32{128104, 8205, 129457}, "curly_haired_man"),
NewEmoji("man: white hair", []int32{128104, 8205, 129459}, "white_haired_man"),
NewEmoji("man: bald", []int32{128104, 8205, 129458}, "bald_man"),
NewEmoji("woman", []int32{128105}, "woman"),
NewEmoji("woman: red hair", []int32{128105, 8205, 129456}, "red_haired_woman"),
NewEmoji("person: red hair", []int32{129489, 8205, 129456}, "person_red_hair"),
NewEmoji("woman: curly hair", []int32{128105, 8205, 129457}, "curly_haired_woman"),
NewEmoji("person: curly hair", []int32{129489, 8205, 129457}, "person_curly_hair"),
NewEmoji("woman: white hair", []int32{128105, 8205, 129459}, "white_haired_woman"),
NewEmoji("person: white hair", []int32{129489, 8205, 129459}, "person_white_hair"),
NewEmoji("woman: bald", []int32{128105, 8205, 129458}, "bald_woman"),
NewEmoji("person: bald", []int32{129489, 8205, 129458}, "person_bald"),
NewEmoji("woman: blond hair", []int32{128113, 8205, 9792, 65039}, "blond_haired_woman"),
NewEmoji("man: blond hair", []int32{128113, 8205, 9794, 65039}, "blond_haired_man"),
NewEmoji("older person", []int32{129491}, "older_adult"),
NewEmoji("old man", []int32{128116}, "older_man"),
NewEmoji("old woman", []int32{128117}, "older_woman"),
NewEmoji("person frowning", []int32{128589}, "frowning_person"),
NewEmoji("man frowning", []int32{128589, 8205, 9794, 65039}, "frowning_man"),
NewEmoji("woman frowning", []int32{128589, 8205, 9792, 65039}, "frowning_woman"),
NewEmoji("person pouting", []int32{128590}, "pouting_face"),
NewEmoji("man pouting", []int32{128590, 8205, 9794, 65039}, "pouting_man"),
NewEmoji("woman pouting", []int32{128590, 8205, 9792, 65039}, "pouting_woman"),
NewEmoji("person gesturing NO", []int32{128581}, "no_good"),
NewEmoji("man gesturing NO", []int32{128581, 8205, 9794, 65039}, "no_good_man"),
NewEmoji("woman gesturing NO", []int32{128581, 8205, 9792, 65039}, "no_good_woman"),
NewEmoji("person gesturing OK", []int32{128582}, "ok_person"),
NewEmoji("man gesturing OK", []int32{128582, 8205, 9794, 65039}, "ok_man"),
NewEmoji("woman gesturing OK", []int32{128582, 8205, 9792, 65039}, "ok_woman"),
NewEmoji("person tipping hand", []int32{128129}, "tipping_hand_person"),
NewEmoji("man tipping hand", []int32{128129, 8205, 9794, 65039}, "tipping_hand_man"),
NewEmoji("woman tipping hand", []int32{128129, 8205, 9792, 65039}, "tipping_hand_woman"),
NewEmoji("person raising hand", []int32{128587}, "raising_hand"),
NewEmoji("man raising hand", []int32{128587, 8205, 9794, 65039}, "raising_hand_man"),
NewEmoji("woman raising hand", []int32{128587, 8205, 9792, 65039}, "raising_hand_woman"),
NewEmoji("deaf person", []int32{129487}, "deaf_person"),
NewEmoji("deaf man", []int32{129487, 8205, 9794, 65039}, "deaf_man"),
NewEmoji("deaf woman", []int32{129487, 8205, 9792, 65039}, "deaf_woman"),
NewEmoji("person bowing", []int32{128583}, "bow"),
NewEmoji("man bowing", []int32{128583, 8205, 9794, 65039}, "bowing_man"),
NewEmoji("woman bowing", []int32{128583, 8205, 9792, 65039}, "bowing_woman"),
NewEmoji("person facepalming", []int32{129318}, "facepalm"),
NewEmoji("man facepalming", []int32{129318, 8205, 9794, 65039}, "man_facepalming"),
NewEmoji("woman facepalming", []int32{129318, 8205, 9792, 65039}, "woman_facepalming"),
NewEmoji("person shrugging", []int32{129335}, "shrug"),
NewEmoji("man shrugging", []int32{129335, 8205, 9794, 65039}, "man_shrugging"),
NewEmoji("woman shrugging", []int32{129335, 8205, 9792, 65039}, "woman_shrugging"),
NewEmoji("health worker", []int32{129489, 8205, 9877, 65039}, "health_worker"),
NewEmoji("man health worker", []int32{128104, 8205, 9877, 65039}, "man_health_worker"),
NewEmoji("woman health worker", []int32{128105, 8205, 9877, 65039}, "woman_health_worker"),
NewEmoji("student", []int32{129489, 8205, 127891}, "student"),
NewEmoji("man student", []int32{128104, 8205, 127891}, "man_student"),
NewEmoji("woman student", []int32{128105, 8205, 127891}, "woman_student"),
NewEmoji("teacher", []int32{129489, 8205, 127979}, "teacher"),
NewEmoji("man teacher", []int32{128104, 8205, 127979}, "man_teacher"),
NewEmoji("woman teacher", []int32{128105, 8205, 127979}, "woman_teacher"),
NewEmoji("judge", []int32{129489, 8205, 9878, 65039}, "judge"),
NewEmoji("man judge", []int32{128104, 8205, 9878, 65039}, "man_judge"),
NewEmoji("woman judge", []int32{128105, 8205, 9878, 65039}, "woman_judge"),
NewEmoji("farmer", []int32{129489, 8205, 127806}, "farmer"),
NewEmoji("man farmer", []int32{128104, 8205, 127806}, "man_farmer"),
NewEmoji("woman farmer", []int32{128105, 8205, 127806}, "woman_farmer"),
NewEmoji("cook", []int32{129489, 8205, 127859}, "cook"),
NewEmoji("man cook", []int32{128104, 8205, 127859}, "man_cook"),
NewEmoji("woman cook", []int32{128105, 8205, 127859}, "woman_cook"),
NewEmoji("mechanic", []int32{129489, 8205, 128295}, "mechanic"),
NewEmoji("man mechanic", []int32{128104, 8205, 128295}, "man_mechanic"),
NewEmoji("woman mechanic", []int32{128105, 8205, 128295}, "woman_mechanic"),
NewEmoji("factory worker", []int32{129489, 8205, 127981}, "factory_worker"),
NewEmoji("man factory worker", []int32{128104, 8205, 127981}, "man_factory_worker"),
NewEmoji("woman factory worker", []int32{128105, 8205, 127981}, "woman_factory_worker"),
NewEmoji("office worker", []int32{129489, 8205, 128188}, "office_worker"),
NewEmoji("man office worker", []int32{128104, 8205, 128188}, "man_office_worker"),
NewEmoji("woman office worker", []int32{128105, 8205, 128188}, "woman_office_worker"),
NewEmoji("scientist", []int32{129489, 8205, 128300}, "scientist"),
NewEmoji("man scientist", []int32{128104, 8205, 128300}, "man_scientist"),
NewEmoji("woman scientist", []int32{128105, 8205, 128300}, "woman_scientist"),
NewEmoji("technologist", []int32{129489, 8205, 128187}, "technologist"),
NewEmoji("man technologist", []int32{128104, 8205, 128187}, "man_technologist"),
NewEmoji("woman technologist", []int32{128105, 8205, 128187}, "woman_technologist"),
NewEmoji("singer", []int32{129489, 8205, 127908}, "singer"),
NewEmoji("man singer", []int32{128104, 8205, 127908}, "man_singer"),
NewEmoji("woman singer", []int32{128105, 8205, 127908}, "woman_singer"),
NewEmoji("artist", []int32{129489, 8205, 127912}, "artist"),
NewEmoji("man artist", []int32{128104, 8205, 127912}, "man_artist"),
NewEmoji("woman artist", []int32{128105, 8205, 127912}, "woman_artist"),
NewEmoji("pilot", []int32{129489, 8205, 9992, 65039}, "pilot"),
NewEmoji("man pilot", []int32{128104, 8205, 9992, 65039}, "man_pilot"),
NewEmoji("woman pilot", []int32{128105, 8205, 9992, 65039}, "woman_pilot"),
NewEmoji("astronaut", []int32{129489, 8205, 128640}, "astronaut"),
NewEmoji("man astronaut", []int32{128104, 8205, 128640}, "man_astronaut"),
NewEmoji("woman astronaut", []int32{128105, 8205, 128640}, "woman_astronaut"),
NewEmoji("firefighter", []int32{129489, 8205, 128658}, "firefighter"),
NewEmoji("man firefighter", []int32{128104, 8205, 128658}, "man_firefighter"),
NewEmoji("woman firefighter", []int32{128105, 8205, 128658}, "woman_firefighter"),
NewEmoji("police officer", []int32{128110}, "police_officer"),
NewEmoji("man police officer", []int32{128110, 8205, 9794, 65039}, "policeman"),
NewEmoji("woman police officer", []int32{128110, 8205, 9792, 65039}, "policewoman"),
NewEmoji("detective", []int32{128373, 65039}, "detective"),
NewEmoji("man detective", []int32{128373, 65039, 8205, 9794, 65039}, "male_detective"),
NewEmoji("woman detective", []int32{128373, 65039, 8205, 9792, 65039}, "female_detective"),
NewEmoji("guard", []int32{128130}, "guard"),
NewEmoji("man guard", []int32{128130, 8205, 9794, 65039}, "guardsman"),
NewEmoji("woman guard", []int32{128130, 8205, 9792, 65039}, "guardswoman"),
NewEmoji("construction worker", []int32{128119}, "construction_worker"),
NewEmoji("man construction worker", []int32{128119, 8205, 9794, 65039}, "construction_worker_man"),
NewEmoji("woman construction worker", []int32{128119, 8205, 9792, 65039}, "construction_worker_woman"),
NewEmoji("prince", []int32{129332}, "prince"),
NewEmoji("princess", []int32{128120}, "princess"),
NewEmoji("person wearing turban", []int32{128115}, "person_with_turban"),
NewEmoji("man wearing turban", []int32{128115, 8205, 9794, 65039}, "man_with_turban"),
NewEmoji("woman wearing turban", []int32{128115, 8205, 9792, 65039}, "woman_with_turban"),
NewEmoji("person with skullcap", []int32{128114}, "man_with_gua_pi_mao"),
NewEmoji("woman with headscarf", []int32{129493}, "woman_with_headscarf"),
NewEmoji("man in tuxedo", []int32{129333, 8205, 9794, 65039}, "man_in_tuxedo"),
NewEmoji("woman with veil", []int32{128112, 8205, 9792, 65039}, "bride_with_veil"),
NewEmoji("pregnant woman", []int32{129328}, "pregnant_woman"),
NewEmoji("breast-feeding", []int32{129329}, "breast_feeding"),
NewEmoji("baby angel", []int32{128124}, "angel"),
NewEmoji("Santa Claus", []int32{127877}, "santa"),
NewEmoji("Mrs. Claus", []int32{129334}, "mrs_claus"),
NewEmoji("superhero", []int32{129464}, "superhero"),
NewEmoji("man superhero", []int32{129464, 8205, 9794, 65039}, "superhero_man"),
NewEmoji("woman superhero", []int32{129464, 8205, 9792, 65039}, "superhero_woman"),
NewEmoji("supervillain", []int32{129465}, "supervillain"),
NewEmoji("man supervillain", []int32{129465, 8205, 9794, 65039}, "supervillain_man"),
NewEmoji("woman supervillain", []int32{129465, 8205, 9792, 65039}, "supervillain_woman"),
NewEmoji("mage", []int32{129497}, "mage"),
NewEmoji("man mage", []int32{129497, 8205, 9794, 65039}, "mage_man"),
NewEmoji("woman mage", []int32{129497, 8205, 9792, 65039}, "mage_woman"),
NewEmoji("fairy", []int32{129498}, "fairy"),
NewEmoji("man fairy", []int32{129498, 8205, 9794, 65039}, "fairy_man"),
NewEmoji("woman fairy", []int32{129498, 8205, 9792, 65039}, "fairy_woman"),
NewEmoji("vampire", []int32{129499}, "vampire"),
NewEmoji("man vampire", []int32{129499, 8205, 9794, 65039}, "vampire_man"),
NewEmoji("woman vampire", []int32{129499, 8205, 9792, 65039}, "vampire_woman"),
NewEmoji("merperson", []int32{129500}, "merperson"),
NewEmoji("merman", []int32{129500, 8205, 9794, 65039}, "merman"),
NewEmoji("mermaid", []int32{129500, 8205, 9792, 65039}, "mermaid"),
NewEmoji("elf", []int32{129501}, "elf"),
NewEmoji("man elf", []int32{129501, 8205, 9794, 65039}, "elf_man"),
NewEmoji("woman elf", []int32{129501, 8205, 9792, 65039}, "elf_woman"),
NewEmoji("genie", []int32{129502}, "genie"),
NewEmoji("man genie", []int32{129502, 8205, 9794, 65039}, "genie_man"),
NewEmoji("woman genie", []int32{129502, 8205, 9792, 65039}, "genie_woman"),
NewEmoji("zombie", []int32{129503}, "zombie"),
NewEmoji("man zombie", []int32{129503, 8205, 9794, 65039}, "zombie_man"),
NewEmoji("woman zombie", []int32{129503, 8205, 9792, 65039}, "zombie_woman"),
NewEmoji("person getting massage", []int32{128134}, "massage"),
NewEmoji("man getting massage", []int32{128134, 8205, 9794, 65039}, "massage_man"),
NewEmoji("woman getting massage", []int32{128134, 8205, 9792, 65039}, "massage_woman"),
NewEmoji("person getting haircut", []int32{128135}, "haircut"),
NewEmoji("man getting haircut", []int32{128135, 8205, 9794, 65039}, "haircut_man"),
NewEmoji("woman getting haircut", []int32{128135, 8205, 9792, 65039}, "haircut_woman"),
NewEmoji("person walking", []int32{128694}, "walking"),
NewEmoji("man walking", []int32{128694, 8205, 9794, 65039}, "walking_man"),
NewEmoji("woman walking", []int32{128694, 8205, 9792, 65039}, "walking_woman"),
NewEmoji("person standing", []int32{129485}, "standing_person"),
NewEmoji("man standing", []int32{129485, 8205, 9794, 65039}, "standing_man"),
NewEmoji("woman standing", []int32{129485, 8205, 9792, 65039}, "standing_woman"),
NewEmoji("person kneeling", []int32{129486}, "kneeling_person"),
NewEmoji("man kneeling", []int32{129486, 8205, 9794, 65039}, "kneeling_man"),
NewEmoji("woman kneeling", []int32{129486, 8205, 9792, 65039}, "kneeling_woman"),
NewEmoji("person with white cane", []int32{129489, 8205, 129455}, "person_with_probing_cane"),
NewEmoji("man with white cane", []int32{128104, 8205, 129455}, "man_with_probing_cane"),
NewEmoji("woman with white cane", []int32{128105, 8205, 129455}, "woman_with_probing_cane"),
NewEmoji("person in motorized wheelchair", []int32{129489, 8205, 129468}, "person_in_motorized_wheelchair"),
NewEmoji("man in motorized wheelchair", []int32{128104, 8205, 129468}, "man_in_motorized_wheelchair"),
NewEmoji("woman in motorized wheelchair", []int32{128105, 8205, 129468}, "woman_in_motorized_wheelchair"),
NewEmoji("person in manual wheelchair", []int32{129489, 8205, 129469}, "person_in_manual_wheelchair"),
NewEmoji("man in manual wheelchair", []int32{128104, 8205, 129469}, "man_in_manual_wheelchair"),
NewEmoji("woman in manual wheelchair", []int32{128105, 8205, 129469}, "woman_in_manual_wheelchair"),
NewEmoji("person running", []int32{127939}, "runner"),
NewEmoji("man running", []int32{127939, 8205, 9794, 65039}, "running_man"),
NewEmoji("woman running", []int32{127939, 8205, 9792, 65039}, "running_woman"),
NewEmoji("woman dancing", []int32{128131}, "woman_dancing"),
NewEmoji("man dancing", []int32{128378}, "man_dancing"),
NewEmoji("person in suit levitating", []int32{128372, 65039}, "business_suit_levitating"),
NewEmoji("people with bunny ears", []int32{128111}, "dancers"),
NewEmoji("men with bunny ears", []int32{128111, 8205, 9794, 65039}, "dancing_men"),
NewEmoji("women with bunny ears", []int32{128111, 8205, 9792, 65039}, "dancing_women"),
NewEmoji("person in steamy room", []int32{129494}, "sauna_person"),
NewEmoji("man in steamy room", []int32{129494, 8205, 9794, 65039}, "sauna_man"),
NewEmoji("woman in steamy room", []int32{129494, 8205, 9792, 65039}, "sauna_woman"),
NewEmoji("person climbing", []int32{129495}, "climbing"),
NewEmoji("man climbing", []int32{129495, 8205, 9794, 65039}, "climbing_man"),
NewEmoji("woman climbing", []int32{129495, 8205, 9792, 65039}, "climbing_woman"),
NewEmoji("person fencing", []int32{129338}, "person_fencing"),
NewEmoji("horse racing", []int32{127943}, "horse_racing"),
NewEmoji("skier", []int32{9975, 65039}, "skier"),
NewEmoji("snowboarder", []int32{127938}, "snowboarder"),
NewEmoji("person golfing", []int32{127948, 65039}, "golfing"),
NewEmoji("man golfing", []int32{127948, 65039, 8205, 9794, 65039}, "golfing_man"),
NewEmoji("woman golfing", []int32{127948, 65039, 8205, 9792, 65039}, "golfing_woman"),
NewEmoji("person surfing", []int32{127940}, "surfer"),
NewEmoji("man surfing", []int32{127940, 8205, 9794, 65039}, "surfing_man"),
NewEmoji("woman surfing", []int32{127940, 8205, 9792, 65039}, "surfing_woman"),
NewEmoji("person rowing boat", []int32{128675}, "rowboat"),
NewEmoji("man rowing boat", []int32{128675, 8205, 9794, 65039}, "rowing_man"),
NewEmoji("woman rowing boat", []int32{128675, 8205, 9792, 65039}, "rowing_woman"),
NewEmoji("person swimming", []int32{127946}, "swimmer"),
NewEmoji("man swimming", []int32{127946, 8205, 9794, 65039}, "swimming_man"),
NewEmoji("woman swimming", []int32{127946, 8205, 9792, 65039}, "swimming_woman"),
NewEmoji("person bouncing ball", []int32{9977, 65039}, "bouncing_ball_person"),
NewEmoji("man bouncing ball", []int32{9977, 65039, 8205, 9794, 65039}, "bouncing_ball_man"),
NewEmoji("woman bouncing ball", []int32{9977, 65039, 8205, 9792, 65039}, "bouncing_ball_woman"),
NewEmoji("person lifting weights", []int32{127947, 65039}, "weight_lifting"),
NewEmoji("man lifting weights", []int32{127947, 65039, 8205, 9794, 65039}, "weight_lifting_man"),
NewEmoji("woman lifting weights", []int32{127947, 65039, 8205, 9792, 65039}, "weight_lifting_woman"),
NewEmoji("person biking", []int32{128692}, "bicyclist"),
NewEmoji("man biking", []int32{128692, 8205, 9794, 65039}, "biking_man"),
NewEmoji("woman biking", []int32{128692, 8205, 9792, 65039}, "biking_woman"),
NewEmoji("person mountain biking", []int32{128693}, "mountain_bicyclist"),
NewEmoji("man mountain biking", []int32{128693, 8205, 9794, 65039}, "mountain_biking_man"),
NewEmoji("woman mountain biking", []int32{128693, 8205, 9792, 65039}, "mountain_biking_woman"),
NewEmoji("person cartwheeling", []int32{129336}, "cartwheeling"),
NewEmoji("man cartwheeling", []int32{129336, 8205, 9794, 65039}, "man_cartwheeling"),
NewEmoji("woman cartwheeling", []int32{129336, 8205, 9792, 65039}, "woman_cartwheeling"),
NewEmoji("people wrestling", []int32{129340}, "wrestling"),
NewEmoji("men wrestling", []int32{129340, 8205, 9794, 65039}, "men_wrestling"),
NewEmoji("women wrestling", []int32{129340, 8205, 9792, 65039}, "women_wrestling"),
NewEmoji("person playing water polo", []int32{129341}, "water_polo"),
NewEmoji("man playing water polo", []int32{129341, 8205, 9794, 65039}, "man_playing_water_polo"),
NewEmoji("woman playing water polo", []int32{129341, 8205, 9792, 65039}, "woman_playing_water_polo"),
NewEmoji("person playing handball", []int32{129342}, "handball_person"),
NewEmoji("man playing handball", []int32{129342, 8205, 9794, 65039}, "man_playing_handball"),
NewEmoji("woman playing handball", []int32{129342, 8205, 9792, 65039}, "woman_playing_handball"),
NewEmoji("person juggling", []int32{129337}, "juggling_person"),
NewEmoji("man juggling", []int32{129337, 8205, 9794, 65039}, "man_juggling"),
NewEmoji("woman juggling", []int32{129337, 8205, 9792, 65039}, "woman_juggling"),
NewEmoji("person in lotus position", []int32{129496}, "lotus_position"),
NewEmoji("man in lotus position", []int32{129496, 8205, 9794, 65039}, "lotus_position_man"),
NewEmoji("woman in lotus position", []int32{129496, 8205, 9792, 65039}, "lotus_position_woman"),
NewEmoji("person taking bath", []int32{128704}, "bath"),
NewEmoji("person in bed", []int32{128716}, "sleeping_bed"),
NewEmoji("people holding hands", []int32{129489, 8205, 129309, 8205, 129489}, "people_holding_hands"),
NewEmoji("women holding hands", []int32{128109}, "two_women_holding_hands"),
NewEmoji("woman and man holding hands", []int32{128107}, "couple"),
NewEmoji("men holding hands", []int32{128108}, "two_men_holding_hands"),
NewEmoji("kiss", []int32{128143}, "couplekiss"),
NewEmoji("kiss: woman, man", []int32{128105, 8205, 10084, 65039, 8205, 128139, 8205, 128104}, "couplekiss_man_woman"),
NewEmoji("kiss: man, man", []int32{128104, 8205, 10084, 65039, 8205, 128139, 8205, 128104}, "couplekiss_man_man"),
NewEmoji("kiss: woman, woman", []int32{128105, 8205, 10084, 65039, 8205, 128139, 8205, 128105}, "couplekiss_woman_woman"),
NewEmoji("couple with heart", []int32{128145}, "couple_with_heart"),
NewEmoji("couple with heart: woman, man", []int32{128105, 8205, 10084, 65039, 8205, 128104}, "couple_with_heart_woman_man"),
NewEmoji("couple with heart: man, man", []int32{128104, 8205, 10084, 65039, 8205, 128104}, "couple_with_heart_man_man"),
NewEmoji("couple with heart: woman, woman", []int32{128105, 8205, 10084, 65039, 8205, 128105}, "couple_with_heart_woman_woman"),
NewEmoji("family", []int32{128106}, "family"),
NewEmoji("family: man, woman, boy", []int32{128104, 8205, 128105, 8205, 128102}, "family_man_woman_boy"),
NewEmoji("family: man, woman, girl", []int32{128104, 8205, 128105, 8205, 128103}, "family_man_woman_girl"),
NewEmoji("family: man, woman, girl, boy", []int32{128104, 8205, 128105, 8205, 128103, 8205, 128102}, "family_man_woman_girl_boy"),
NewEmoji("family: man, woman, boy, boy", []int32{128104, 8205, 128105, 8205, 128102, 8205, 128102}, "family_man_woman_boy_boy"),
NewEmoji("family: man, woman, girl, girl", []int32{128104, 8205, 128105, 8205, 128103, 8205, 128103}, "family_man_woman_girl_girl"),
NewEmoji("family: man, man, boy", []int32{128104, 8205, 128104, 8205, 128102}, "family_man_man_boy"),
NewEmoji("family: man, man, girl", []int32{128104, 8205, 128104, 8205, 128103}, "family_man_man_girl"),
NewEmoji("family: man, man, girl, boy", []int32{128104, 8205, 128104, 8205, 128103, 8205, 128102}, "family_man_man_girl_boy"),
NewEmoji("family: man, man, boy, boy", []int32{128104, 8205, 128104, 8205, 128102, 8205, 128102}, "family_man_man_boy_boy"),
NewEmoji("family: man, man, girl, girl", []int32{128104, 8205, 128104, 8205, 128103, 8205, 128103}, "family_man_man_girl_girl"),
NewEmoji("family: woman, woman, boy", []int32{128105, 8205, 128105, 8205, 128102}, "family_woman_woman_boy"),
NewEmoji("family: woman, woman, girl", []int32{128105, 8205, 128105, 8205, 128103}, "family_woman_woman_girl"),
NewEmoji("family: woman, woman, girl, boy", []int32{128105, 8205, 128105, 8205, 128103, 8205, 128102}, "family_woman_woman_girl_boy"),
NewEmoji("family: woman, woman, boy, boy", []int32{128105, 8205, 128105, 8205, 128102, 8205, 128102}, "family_woman_woman_boy_boy"),
NewEmoji("family: woman, woman, girl, girl", []int32{128105, 8205, 128105, 8205, 128103, 8205, 128103}, "family_woman_woman_girl_girl"),
NewEmoji("family: man, boy", []int32{128104, 8205, 128102}, "family_man_boy"),
NewEmoji("family: man, boy, boy", []int32{128104, 8205, 128102, 8205, 128102}, "family_man_boy_boy"),
NewEmoji("family: man, girl", []int32{128104, 8205, 128103}, "family_man_girl"),
NewEmoji("family: man, girl, boy", []int32{128104, 8205, 128103, 8205, 128102}, "family_man_girl_boy"),
NewEmoji("family: man, girl, girl", []int32{128104, 8205, 128103, 8205, 128103}, "family_man_girl_girl"),
NewEmoji("family: woman, boy", []int32{128105, 8205, 128102}, "family_woman_boy"),
NewEmoji("family: woman, boy, boy", []int32{128105, 8205, 128102, 8205, 128102}, "family_woman_boy_boy"),
NewEmoji("family: woman, girl", []int32{128105, 8205, 128103}, "family_woman_girl"),
NewEmoji("family: woman, girl, boy", []int32{128105, 8205, 128103, 8205, 128102}, "family_woman_girl_boy"),
NewEmoji("family: woman, girl, girl", []int32{128105, 8205, 128103, 8205, 128103}, "family_woman_girl_girl"),
NewEmoji("speaking head", []int32{128483, 65039}, "speaking_head"),
NewEmoji("bust in silhouette", []int32{128100}, "bust_in_silhouette"),
NewEmoji("busts in silhouette", []int32{128101}, "busts_in_silhouette"),
NewEmoji("footprints", []int32{128099}, "footprints"),
NewEmoji("monkey face", []int32{128053}, "monkey_face"),
NewEmoji("monkey", []int32{128018}, "monkey"),
NewEmoji("gorilla", []int32{129421}, "gorilla"),
NewEmoji("orangutan", []int32{129447}, "orangutan"),
NewEmoji("dog face", []int32{128054}, "dog"),
NewEmoji("dog", []int32{128021}, "dog2"),
NewEmoji("guide dog", []int32{129454}, "guide_dog"),
NewEmoji("service dog", []int32{128021, 8205, 129466}, "service_dog"),
NewEmoji("poodle", []int32{128041}, "poodle"),
NewEmoji("wolf", []int32{128058}, "wolf"),
NewEmoji("fox", []int32{129418}, "fox_face"),
NewEmoji("raccoon", []int32{129437}, "raccoon"),
NewEmoji("cat face", []int32{128049}, "cat"),
NewEmoji("cat", []int32{128008}, "cat2"),
NewEmoji("lion", []int32{129409}, "lion"),
NewEmoji("tiger face", []int32{128047}, "tiger"),
NewEmoji("tiger", []int32{128005}, "tiger2"),
NewEmoji("leopard", []int32{128006}, "leopard"),
NewEmoji("horse face", []int32{128052}, "horse"),
NewEmoji("horse", []int32{128014}, "racehorse"),
NewEmoji("unicorn", []int32{129412}, "unicorn"),
NewEmoji("zebra", []int32{129427}, "zebra"),
NewEmoji("deer", []int32{129420}, "deer"),
NewEmoji("cow face", []int32{128046}, "cow"),
NewEmoji("ox", []int32{128002}, "ox"),
NewEmoji("water buffalo", []int32{128003}, "water_buffalo"),
NewEmoji("cow", []int32{128004}, "cow2"),
NewEmoji("pig face", []int32{128055}, "pig"),
NewEmoji("pig", []int32{128022}, "pig2"),
NewEmoji("boar", []int32{128023}, "boar"),
NewEmoji("pig nose", []int32{128061}, "pig_nose"),
NewEmoji("ram", []int32{128015}, "ram"),
NewEmoji("ewe", []int32{128017}, "sheep"),
NewEmoji("goat", []int32{128016}, "goat"),
NewEmoji("camel", []int32{128042}, "dromedary_camel"),
NewEmoji("two-hump camel", []int32{128043}, "camel"),
NewEmoji("llama", []int32{129433}, "llama"),
NewEmoji("giraffe", []int32{129426}, "giraffe"),
NewEmoji("elephant", []int32{128024}, "elephant"),
NewEmoji("rhinoceros", []int32{129423}, "rhinoceros"),
NewEmoji("hippopotamus", []int32{129435}, "hippopotamus"),
NewEmoji("mouse face", []int32{128045}, "mouse"),
NewEmoji("mouse", []int32{128001}, "mouse2"),
NewEmoji("rat", []int32{128000}, "rat"),
NewEmoji("hamster", []int32{128057}, "hamster"),
NewEmoji("rabbit face", []int32{128048}, "rabbit"),
NewEmoji("rabbit", []int32{128007}, "rabbit2"),
NewEmoji("chipmunk", []int32{128063, 65039}, "chipmunk"),
NewEmoji("hedgehog", []int32{129428}, "hedgehog"),
NewEmoji("bat", []int32{129415}, "bat"),
NewEmoji("bear", []int32{128059}, "bear"),
NewEmoji("koala", []int32{128040}, "koala"),
NewEmoji("panda", []int32{128060}, "panda_face"),
NewEmoji("sloth", []int32{129445}, "sloth"),
NewEmoji("otter", []int32{129446}, "otter"),
NewEmoji("skunk", []int32{129448}, "skunk"),
NewEmoji("kangaroo", []int32{129432}, "kangaroo"),
NewEmoji("badger", []int32{129441}, "badger"),
NewEmoji("paw prints", []int32{128062}, "feet"),
NewEmoji("turkey", []int32{129411}, "turkey"),
NewEmoji("chicken", []int32{128020}, "chicken"),
NewEmoji("rooster", []int32{128019}, "rooster"),
NewEmoji("hatching chick", []int32{128035}, "hatching_chick"),
NewEmoji("baby chick", []int32{128036}, "baby_chick"),
NewEmoji("front-facing baby chick", []int32{128037}, "hatched_chick"),
NewEmoji("bird", []int32{128038}, "bird"),
NewEmoji("penguin", []int32{128039}, "penguin"),
NewEmoji("dove", []int32{128330, 65039}, "dove"),
NewEmoji("eagle", []int32{129413}, "eagle"),
NewEmoji("duck", []int32{129414}, "duck"),
NewEmoji("swan", []int32{129442}, "swan"),
NewEmoji("owl", []int32{129417}, "owl"),
NewEmoji("flamingo", []int32{129449}, "flamingo"),
NewEmoji("peacock", []int32{129434}, "peacock"),
NewEmoji("parrot", []int32{129436}, "parrot"),
NewEmoji("frog", []int32{128056}, "frog"),
NewEmoji("crocodile", []int32{128010}, "crocodile"),
NewEmoji("turtle", []int32{128034}, "turtle"),
NewEmoji("lizard", []int32{129422}, "lizard"),
NewEmoji("snake", []int32{128013}, "snake"),
NewEmoji("dragon face", []int32{128050}, "dragon_face"),
NewEmoji("dragon", []int32{128009}, "dragon"),
NewEmoji("sauropod", []int32{129429}, "sauropod"),
NewEmoji("T-Rex", []int32{129430}, "t-rex"),
NewEmoji("spouting whale", []int32{128051}, "whale"),
NewEmoji("whale", []int32{128011}, "whale2"),
NewEmoji("dolphin", []int32{128044}, "dolphin"),
NewEmoji("fish", []int32{128031}, "fish"),
NewEmoji("tropical fish", []int32{128032}, "tropical_fish"),
NewEmoji("blowfish", []int32{128033}, "blowfish"),
NewEmoji("shark", []int32{129416}, "shark"),
NewEmoji("octopus", []int32{128025}, "octopus"),
NewEmoji("spiral shell", []int32{128026}, "shell"),
NewEmoji("snail", []int32{128012}, "snail"),
NewEmoji("butterfly", []int32{129419}, "butterfly"),
NewEmoji("bug", []int32{128027}, "bug"),
NewEmoji("ant", []int32{128028}, "ant"),
NewEmoji("honeybee", []int32{128029}, "bee"),
NewEmoji("beetle", []int32{129714}, "beetle"),
NewEmoji("cricket", []int32{129431}, "cricket"),
NewEmoji("spider", []int32{128375, 65039}, "spider"),
NewEmoji("spider web", []int32{128376, 65039}, "spider_web"),
NewEmoji("scorpion", []int32{129410}, "scorpion"),
NewEmoji("mosquito", []int32{129439}, "mosquito"),
NewEmoji("microbe", []int32{129440}, "microbe"),
NewEmoji("bouquet", []int32{128144}, "bouquet"),
NewEmoji("cherry blossom", []int32{127800}, "cherry_blossom"),
NewEmoji("white flower", []int32{128174}, "white_flower"),
NewEmoji("rosette", []int32{127989, 65039}, "rosette"),
NewEmoji("rose", []int32{127801}, "rose"),
NewEmoji("wilted flower", []int32{129344}, "wilted_flower"),
NewEmoji("hibiscus", []int32{127802}, "hibiscus"),
NewEmoji("sunflower", []int32{127803}, "sunflower"),
NewEmoji("blossom", []int32{127804}, "blossom"),
NewEmoji("tulip", []int32{127799}, "tulip"),
NewEmoji("seedling", []int32{127793}, "seedling"),
NewEmoji("evergreen tree", []int32{127794}, "evergreen_tree"),
NewEmoji("deciduous tree", []int32{127795}, "deciduous_tree"),
NewEmoji("palm tree", []int32{127796}, "palm_tree"),
NewEmoji("cactus", []int32{127797}, "cactus"),
NewEmoji("sheaf of rice", []int32{127806}, "ear_of_rice"),
NewEmoji("herb", []int32{127807}, "herb"),
NewEmoji("shamrock", []int32{9752, 65039}, "shamrock"),
NewEmoji("four leaf clover", []int32{127808}, "four_leaf_clover"),
NewEmoji("maple leaf", []int32{127809}, "maple_leaf"),
NewEmoji("fallen leaf", []int32{127810}, "fallen_leaf"),
NewEmoji("leaf fluttering in wind", []int32{127811}, "leaves"),
NewEmoji("grapes", []int32{127815}, "grapes"),
NewEmoji("melon", []int32{127816}, "melon"),
NewEmoji("watermelon", []int32{127817}, "watermelon"),
NewEmoji("tangerine", []int32{127818}, "tangerine"),
NewEmoji("lemon", []int32{127819}, "lemon"),
NewEmoji("banana", []int32{127820}, "banana"),
NewEmoji("pineapple", []int32{127821}, "pineapple"),
NewEmoji("mango", []int32{129389}, "mango"),
NewEmoji("red apple", []int32{127822}, "apple"),
NewEmoji("green apple", []int32{127823}, "green_apple"),
NewEmoji("pear", []int32{127824}, "pear"),
NewEmoji("peach", []int32{127825}, "peach"),
NewEmoji("cherries", []int32{127826}, "cherries"),
NewEmoji("strawberry", []int32{127827}, "strawberry"),
NewEmoji("kiwi fruit", []int32{129373}, "kiwi_fruit"),
NewEmoji("tomato", []int32{127813}, "tomato"),
NewEmoji("coconut", []int32{129381}, "coconut"),
NewEmoji("avocado", []int32{129361}, "avocado"),
NewEmoji("eggplant", []int32{127814}, "eggplant"),
NewEmoji("potato", []int32{129364}, "potato"),
NewEmoji("carrot", []int32{129365}, "carrot"),
NewEmoji("ear of corn", []int32{127805}, "corn"),
NewEmoji("hot pepper", []int32{127798, 65039}, "hot_pepper"),
NewEmoji("cucumber", []int32{129362}, "cucumber"),
NewEmoji("leafy green", []int32{129388}, "leafy_green"),
NewEmoji("broccoli", []int32{129382}, "broccoli"),
NewEmoji("garlic", []int32{129476}, "garlic"),
NewEmoji("onion", []int32{129477}, "onion"),
NewEmoji("mushroom", []int32{127812}, "mushroom"),
NewEmoji("peanuts", []int32{129372}, "peanuts"),
NewEmoji("chestnut", []int32{127792}, "chestnut"),
NewEmoji("bread", []int32{127838}, "bread"),
NewEmoji("croissant", []int32{129360}, "croissant"),
NewEmoji("baguette bread", []int32{129366}, "baguette_bread"),
NewEmoji("pretzel", []int32{129384}, "pretzel"),
NewEmoji("bagel", []int32{129391}, "bagel"),
NewEmoji("pancakes", []int32{129374}, "pancakes"),
NewEmoji("waffle", []int32{129479}, "waffle"),
NewEmoji("cheese wedge", []int32{129472}, "cheese"),
NewEmoji("meat on bone", []int32{127830}, "meat_on_bone"),
NewEmoji("poultry leg", []int32{127831}, "poultry_leg"),
NewEmoji("cut of meat", []int32{129385}, "cut_of_meat"),
NewEmoji("bacon", []int32{129363}, "bacon"),
NewEmoji("hamburger", []int32{127828}, "hamburger"),
NewEmoji("french fries", []int32{127839}, "fries"),
NewEmoji("pizza", []int32{127829}, "pizza"),
NewEmoji("hot dog", []int32{127789}, "hotdog"),
NewEmoji("sandwich", []int32{129386}, "sandwich"),
NewEmoji("taco", []int32{127790}, "taco"),
NewEmoji("burrito", []int32{127791}, "burrito"),
NewEmoji("stuffed flatbread", []int32{129369}, "stuffed_flatbread"),
NewEmoji("falafel", []int32{129478}, "falafel"),
NewEmoji("egg", []int32{129370}, "egg"),
NewEmoji("cooking", []int32{127859}, "fried_egg"),
NewEmoji("shallow pan of food", []int32{129368}, "shallow_pan_of_food"),
NewEmoji("pot of food", []int32{127858}, "stew"),
NewEmoji("bowl with spoon", []int32{129379}, "bowl_with_spoon"),
NewEmoji("green salad", []int32{129367}, "green_salad"),
NewEmoji("popcorn", []int32{127871}, "popcorn"),
NewEmoji("butter", []int32{129480}, "butter"),
NewEmoji("salt", []int32{129474}, "salt"),
NewEmoji("canned food", []int32{129387}, "canned_food"),
NewEmoji("bento box", []int32{127857}, "bento"),
NewEmoji("rice cracker", []int32{127832}, "rice_cracker"),
NewEmoji("rice ball", []int32{127833}, "rice_ball"),
NewEmoji("cooked rice", []int32{127834}, "rice"),
NewEmoji("curry rice", []int32{127835}, "curry"),
NewEmoji("steaming bowl", []int32{127836}, "ramen"),
NewEmoji("spaghetti", []int32{127837}, "spaghetti"),
NewEmoji("roasted sweet potato", []int32{127840}, "sweet_potato"),
NewEmoji("oden", []int32{127842}, "oden"),
NewEmoji("sushi", []int32{127843}, "sushi"),
NewEmoji("fried shrimp", []int32{127844}, "fried_shrimp"),
NewEmoji("fish cake with swirl", []int32{127845}, "fish_cake"),
NewEmoji("moon cake", []int32{129390}, "moon_cake"),
NewEmoji("dango", []int32{127841}, "dango"),
NewEmoji("dumpling", []int32{129375}, "dumpling"),
NewEmoji("fortune cookie", []int32{129376}, "fortune_cookie"),
NewEmoji("takeout box", []int32{129377}, "takeout_box"),
NewEmoji("crab", []int32{129408}, "crab"),
NewEmoji("lobster", []int32{129438}, "lobster"),
NewEmoji("shrimp", []int32{129424}, "shrimp"),
NewEmoji("squid", []int32{129425}, "squid"),
NewEmoji("oyster", []int32{129450}, "oyster"),
NewEmoji("soft ice cream", []int32{127846}, "icecream"),
NewEmoji("shaved ice", []int32{127847}, "shaved_ice"),
NewEmoji("ice cream", []int32{127848}, "ice_cream"),
NewEmoji("doughnut", []int32{127849}, "doughnut"),
NewEmoji("cookie", []int32{127850}, "cookie"),
NewEmoji("birthday cake", []int32{127874}, "birthday"),
NewEmoji("shortcake", []int32{127856}, "cake"),
NewEmoji("cupcake", []int32{129473}, "cupcake"),
NewEmoji("pie", []int32{129383}, "pie"),
NewEmoji("chocolate bar", []int32{127851}, "chocolate_bar"),
NewEmoji("candy", []int32{127852}, "candy"),
NewEmoji("lollipop", []int32{127853}, "lollipop"),
NewEmoji("custard", []int32{127854}, "custard"),
NewEmoji("honey pot", []int32{127855}, "honey_pot"),
NewEmoji("baby bottle", []int32{127868}, "baby_bottle"),
NewEmoji("glass of milk", []int32{129371}, "milk_glass"),
NewEmoji("hot beverage", []int32{9749}, "coffee"),
NewEmoji("teacup without handle", []int32{127861}, "tea"),
NewEmoji("sake", []int32{127862}, "sake"),
NewEmoji("bottle with popping cork", []int32{127870}, "champagne"),
NewEmoji("wine glass", []int32{127863}, "wine_glass"),
NewEmoji("cocktail glass", []int32{127864}, "cocktail"),
NewEmoji("tropical drink", []int32{127865}, "tropical_drink"),
NewEmoji("beer mug", []int32{127866}, "beer"),
NewEmoji("clinking beer mugs", []int32{127867}, "beers"),
NewEmoji("clinking glasses", []int32{129346}, "clinking_glasses"),
NewEmoji("tumbler glass", []int32{129347}, "tumbler_glass"),
NewEmoji("cup with straw", []int32{129380}, "cup_with_straw"),
NewEmoji("beverage box", []int32{129475}, "beverage_box"),
NewEmoji("mate", []int32{129481}, "mate"),
NewEmoji("ice", []int32{129482}, "ice_cube"),
NewEmoji("chopsticks", []int32{129378}, "chopsticks"),
NewEmoji("fork and knife with plate", []int32{127869, 65039}, "plate_with_cutlery"),
NewEmoji("fork and knife", []int32{127860}, "fork_and_knife"),
NewEmoji("spoon", []int32{129348}, "spoon"),
NewEmoji("kitchen knife", []int32{128298}, "hocho"),
NewEmoji("amphora", []int32{127994}, "amphora"),
NewEmoji("globe showing Europe-Africa", []int32{127757}, "earth_africa"),
NewEmoji("globe showing Americas", []int32{127758}, "earth_americas"),
NewEmoji("globe showing Asia-Australia", []int32{127759}, "earth_asia"),
NewEmoji("globe with meridians", []int32{127760}, "globe_with_meridians"),
NewEmoji("world map", []int32{128506, 65039}, "world_map"),
NewEmoji("map of Japan", []int32{128510}, "japan"),
NewEmoji("compass", []int32{129517}, "compass"),
NewEmoji("snow-capped mountain", []int32{127956, 65039}, "mountain_snow"),
NewEmoji("mountain", []int32{9968, 65039}, "mountain"),
NewEmoji("volcano", []int32{127755}, "volcano"),
NewEmoji("mount fuji", []int32{128507}, "mount_fuji"),
NewEmoji("camping", []int32{127957, 65039}, "camping"),
NewEmoji("beach with umbrella", []int32{127958, 65039}, "beach_umbrella"),
NewEmoji("desert", []int32{127964, 65039}, "desert"),
NewEmoji("desert island", []int32{127965, 65039}, "desert_island"),
NewEmoji("national park", []int32{127966, 65039}, "national_park"),
NewEmoji("stadium", []int32{127967, 65039}, "stadium"),
NewEmoji("classical building", []int32{127963, 65039}, "classical_building"),
NewEmoji("building construction", []int32{127959, 65039}, "building_construction"),
NewEmoji("brick", []int32{129521}, "bricks"),
NewEmoji("houses", []int32{127960, 65039}, "houses"),
NewEmoji("derelict house", []int32{127962, 65039}, "derelict_house"),
NewEmoji("house", []int32{127968}, "house"),
NewEmoji("house with garden", []int32{127969}, "house_with_garden"),
NewEmoji("office building", []int32{127970}, "office"),
NewEmoji("Japanese post office", []int32{127971}, "post_office"),
NewEmoji("post office", []int32{127972}, "european_post_office"),
NewEmoji("hospital", []int32{127973}, "hospital"),
NewEmoji("bank", []int32{127974}, "bank"),
NewEmoji("hotel", []int32{127976}, "hotel"),
NewEmoji("love hotel", []int32{127977}, "love_hotel"),
NewEmoji("convenience store", []int32{127978}, "convenience_store"),
NewEmoji("school", []int32{127979}, "school"),
NewEmoji("department store", []int32{127980}, "department_store"),
NewEmoji("factory", []int32{127981}, "factory"),
NewEmoji("Japanese castle", []int32{127983}, "japanese_castle"),
NewEmoji("castle", []int32{127984}, "european_castle"),
NewEmoji("wedding", []int32{128146}, "wedding"),
NewEmoji("Tokyo tower", []int32{128508}, "tokyo_tower"),
NewEmoji("Statue of Liberty", []int32{128509}, "statue_of_liberty"),
NewEmoji("church", []int32{9962}, "church"),
NewEmoji("mosque", []int32{128332}, "mosque"),
NewEmoji("hindu temple", []int32{128725}, "hindu_temple"),
NewEmoji("synagogue", []int32{128333}, "synagogue"),
NewEmoji("shinto shrine", []int32{9961, 65039}, "shinto_shrine"),
NewEmoji("kaaba", []int32{128331}, "kaaba"),
NewEmoji("fountain", []int32{9970}, "fountain"),
NewEmoji("tent", []int32{9978}, "tent"),
NewEmoji("foggy", []int32{127745}, "foggy"),
NewEmoji("night with stars", []int32{127747}, "night_with_stars"),
NewEmoji("cityscape", []int32{127961, 65039}, "cityscape"),
NewEmoji("sunrise over mountains", []int32{127748}, "sunrise_over_mountains"),
NewEmoji("sunrise", []int32{127749}, "sunrise"),
NewEmoji("cityscape at dusk", []int32{127750}, "city_sunset"),
NewEmoji("sunset", []int32{127751}, "city_sunrise"),
NewEmoji("bridge at night", []int32{127753}, "bridge_at_night"),
NewEmoji("hot springs", []int32{9832, 65039}, "hotsprings"),
NewEmoji("carousel horse", []int32{127904}, "carousel_horse"),
NewEmoji("ferris wheel", []int32{127905}, "ferris_wheel"),
NewEmoji("roller coaster", []int32{127906}, "roller_coaster"),
NewEmoji("barber pole", []int32{128136}, "barber"),
NewEmoji("circus tent", []int32{127914}, "circus_tent"),
NewEmoji("locomotive", []int32{128642}, "steam_locomotive"),
NewEmoji("railway car", []int32{128643}, "railway_car"),
NewEmoji("high-speed train", []int32{128644}, "bullettrain_side"),
NewEmoji("bullet train", []int32{128645}, "bullettrain_front"),
NewEmoji("train", []int32{128646}, "train2"),
NewEmoji("metro", []int32{128647}, "metro"),
NewEmoji("light rail", []int32{128648}, "light_rail"),
NewEmoji("station", []int32{128649}, "station"),
NewEmoji("tram", []int32{128650}, "tram"),
NewEmoji("monorail", []int32{128669}, "monorail"),
NewEmoji("mountain railway", []int32{128670}, "mountain_railway"),
NewEmoji("tram car", []int32{128651}, "train"),
NewEmoji("bus", []int32{128652}, "bus"),
NewEmoji("oncoming bus", []int32{128653}, "oncoming_bus"),
NewEmoji("trolleybus", []int32{128654}, "trolleybus"),
NewEmoji("minibus", []int32{128656}, "minibus"),
NewEmoji("ambulance", []int32{128657}, "ambulance"),
NewEmoji("fire engine", []int32{128658}, "fire_engine"),
NewEmoji("police car", []int32{128659}, "police_car"),
NewEmoji("oncoming police car", []int32{128660}, "oncoming_police_car"),
NewEmoji("taxi", []int32{128661}, "taxi"),
NewEmoji("oncoming taxi", []int32{128662}, "oncoming_taxi"),
NewEmoji("automobile", []int32{128663}, "car"),
NewEmoji("oncoming automobile", []int32{128664}, "oncoming_automobile"),
NewEmoji("sport utility vehicle", []int32{128665}, "blue_car"),
NewEmoji("delivery truck", []int32{128666}, "truck"),
NewEmoji("articulated lorry", []int32{128667}, "articulated_lorry"),
NewEmoji("tractor", []int32{128668}, "tractor"),
NewEmoji("racing car", []int32{127950, 65039}, "racing_car"),
NewEmoji("motorcycle", []int32{127949, 65039}, "motorcycle"),
NewEmoji("motor scooter", []int32{128757}, "motor_scooter"),
NewEmoji("manual wheelchair", []int32{129469}, "manual_wheelchair"),
NewEmoji("motorized wheelchair", []int32{129468}, "motorized_wheelchair"),
NewEmoji("auto rickshaw", []int32{128762}, "auto_rickshaw"),
NewEmoji("bicycle", []int32{128690}, "bike"),
NewEmoji("kick scooter", []int32{128756}, "kick_scooter"),
NewEmoji("skateboard", []int32{128761}, "skateboard"),
NewEmoji("bus stop", []int32{128655}, "busstop"),
NewEmoji("motorway", []int32{128739, 65039}, "motorway"),
NewEmoji("railway track", []int32{128740, 65039}, "railway_track"),
NewEmoji("oil drum", []int32{128738, 65039}, "oil_drum"),
NewEmoji("fuel pump", []int32{9981}, "fuelpump"),
NewEmoji("police car light", []int32{128680}, "rotating_light"),
NewEmoji("horizontal traffic light", []int32{128677}, "traffic_light"),
NewEmoji("vertical traffic light", []int32{128678}, "vertical_traffic_light"),
NewEmoji("stop sign", []int32{128721}, "stop_sign"),
NewEmoji("construction", []int32{128679}, "construction"),
NewEmoji("anchor", []int32{9875}, "anchor"),
NewEmoji("sailboat", []int32{9973}, "boat"),
NewEmoji("canoe", []int32{128758}, "canoe"),
NewEmoji("speedboat", []int32{128676}, "speedboat"),
NewEmoji("passenger ship", []int32{128755, 65039}, "passenger_ship"),
NewEmoji("ferry", []int32{9972, 65039}, "ferry"),
NewEmoji("motor boat", []int32{128741, 65039}, "motor_boat"),
NewEmoji("ship", []int32{128674}, "ship"),
NewEmoji("airplane", []int32{9992, 65039}, "airplane"),
NewEmoji("small airplane", []int32{128745, 65039}, "small_airplane"),
NewEmoji("airplane departure", []int32{128747}, "flight_departure"),
NewEmoji("airplane arrival", []int32{128748}, "flight_arrival"),
NewEmoji("parachute", []int32{129666}, "parachute"),
NewEmoji("seat", []int32{128186}, "seat"),
NewEmoji("helicopter", []int32{128641}, "helicopter"),
NewEmoji("suspension railway", []int32{128671}, "suspension_railway"),
NewEmoji("mountain cableway", []int32{128672}, "mountain_cableway"),
NewEmoji("aerial tramway", []int32{128673}, "aerial_tramway"),
NewEmoji("satellite", []int32{128752, 65039}, "artificial_satellite"),
NewEmoji("rocket", []int32{128640}, "rocket"),
NewEmoji("flying saucer", []int32{128760}, "flying_saucer"),
NewEmoji("bellhop bell", []int32{128718, 65039}, "bellhop_bell"),
NewEmoji("luggage", []int32{129523}, "luggage"),
NewEmoji("hourglass done", []int32{8987}, "hourglass"),
NewEmoji("hourglass not done", []int32{9203}, "hourglass_flowing_sand"),
NewEmoji("watch", []int32{8986}, "watch"),
NewEmoji("alarm clock", []int32{9200}, "alarm_clock"),
NewEmoji("stopwatch", []int32{9201, 65039}, "stopwatch"),
NewEmoji("timer clock", []int32{9202, 65039}, "timer_clock"),
NewEmoji("mantelpiece clock", []int32{128368, 65039}, "mantelpiece_clock"),
NewEmoji("twelve oclock", []int32{128347}, "clock12"),
NewEmoji("twelve-thirty", []int32{128359}, "clock1230"),
NewEmoji("one oclock", []int32{128336}, "clock1"),
NewEmoji("one-thirty", []int32{128348}, "clock130"),
NewEmoji("two oclock", []int32{128337}, "clock2"),
NewEmoji("two-thirty", []int32{128349}, "clock230"),
NewEmoji("three oclock", []int32{128338}, "clock3"),
NewEmoji("three-thirty", []int32{128350}, "clock330"),
NewEmoji("four oclock", []int32{128339}, "clock4"),
NewEmoji("four-thirty", []int32{128351}, "clock430"),
NewEmoji("five oclock", []int32{128340}, "clock5"),
NewEmoji("five-thirty", []int32{128352}, "clock530"),
NewEmoji("six oclock", []int32{128341}, "clock6"),
NewEmoji("six-thirty", []int32{128353}, "clock630"),
NewEmoji("seven oclock", []int32{128342}, "clock7"),
NewEmoji("seven-thirty", []int32{128354}, "clock730"),
NewEmoji("eight oclock", []int32{128343}, "clock8"),
NewEmoji("eight-thirty", []int32{128355}, "clock830"),
NewEmoji("nine oclock", []int32{128344}, "clock9"),
NewEmoji("nine-thirty", []int32{128356}, "clock930"),
NewEmoji("ten oclock", []int32{128345}, "clock10"),
NewEmoji("ten-thirty", []int32{128357}, "clock1030"),
NewEmoji("eleven oclock", []int32{128346}, "clock11"),
NewEmoji("eleven-thirty", []int32{128358}, "clock1130"),
NewEmoji("new moon", []int32{127761}, "new_moon"),
NewEmoji("waxing crescent moon", []int32{127762}, "waxing_crescent_moon"),
NewEmoji("first quarter moon", []int32{127763}, "first_quarter_moon"),
NewEmoji("waxing gibbous moon", []int32{127764}, "moon"),
NewEmoji("full moon", []int32{127765}, "full_moon"),
NewEmoji("waning gibbous moon", []int32{127766}, "waning_gibbous_moon"),
NewEmoji("last quarter moon", []int32{127767}, "last_quarter_moon"),
NewEmoji("waning crescent moon", []int32{127768}, "waning_crescent_moon"),
NewEmoji("crescent moon", []int32{127769}, "crescent_moon"),
NewEmoji("new moon face", []int32{127770}, "new_moon_with_face"),
NewEmoji("first quarter moon face", []int32{127771}, "first_quarter_moon_with_face"),
NewEmoji("last quarter moon face", []int32{127772}, "last_quarter_moon_with_face"),
NewEmoji("thermometer", []int32{127777, 65039}, "thermometer"),
NewEmoji("sun", []int32{9728, 65039}, "sunny"),
NewEmoji("full moon face", []int32{127773}, "full_moon_with_face"),
NewEmoji("sun with face", []int32{127774}, "sun_with_face"),
NewEmoji("ringed planet", []int32{129680}, "ringed_planet"),
NewEmoji("star", []int32{11088}, "star"),
NewEmoji("glowing star", []int32{127775}, "star2"),
NewEmoji("shooting star", []int32{127776}, "stars"),
NewEmoji("milky way", []int32{127756}, "milky_way"),
NewEmoji("cloud", []int32{9729, 65039}, "cloud"),
NewEmoji("sun behind cloud", []int32{9925}, "partly_sunny"),
NewEmoji("cloud with lightning and rain", []int32{9928, 65039}, "cloud_with_lightning_and_rain"),
NewEmoji("sun behind small cloud", []int32{127780, 65039}, "sun_behind_small_cloud"),
NewEmoji("sun behind large cloud", []int32{127781, 65039}, "sun_behind_large_cloud"),
NewEmoji("sun behind rain cloud", []int32{127782, 65039}, "sun_behind_rain_cloud"),
NewEmoji("cloud with rain", []int32{127783, 65039}, "cloud_with_rain"),
NewEmoji("cloud with snow", []int32{127784, 65039}, "cloud_with_snow"),
NewEmoji("cloud with lightning", []int32{127785, 65039}, "cloud_with_lightning"),
NewEmoji("tornado", []int32{127786, 65039}, "tornado"),
NewEmoji("fog", []int32{127787, 65039}, "fog"),
NewEmoji("wind face", []int32{127788, 65039}, "wind_face"),
NewEmoji("cyclone", []int32{127744}, "cyclone"),
NewEmoji("rainbow", []int32{127752}, "rainbow"),
NewEmoji("closed umbrella", []int32{127746}, "closed_umbrella"),
NewEmoji("umbrella", []int32{9730, 65039}, "open_umbrella"),
NewEmoji("umbrella with rain drops", []int32{9748}, "umbrella"),
NewEmoji("umbrella on ground", []int32{9969, 65039}, "parasol_on_ground"),
NewEmoji("high voltage", []int32{9889}, "zap"),
NewEmoji("snowflake", []int32{10052, 65039}, "snowflake"),
NewEmoji("snowman", []int32{9731, 65039}, "snowman_with_snow"),
NewEmoji("snowman without snow", []int32{9924}, "snowman"),
NewEmoji("comet", []int32{9732, 65039}, "comet"),
NewEmoji("fire", []int32{128293}, "fire"),
NewEmoji("droplet", []int32{128167}, "droplet"),
NewEmoji("water wave", []int32{127754}, "ocean"),
NewEmoji("jack-o-lantern", []int32{127875}, "jack_o_lantern"),
NewEmoji("Christmas tree", []int32{127876}, "christmas_tree"),
NewEmoji("fireworks", []int32{127878}, "fireworks"),
NewEmoji("sparkler", []int32{127879}, "sparkler"),
NewEmoji("firecracker", []int32{129512}, "firecracker"),
NewEmoji("sparkles", []int32{10024}, "sparkles"),
NewEmoji("balloon", []int32{127880}, "balloon"),
NewEmoji("party popper", []int32{127881}, "tada"),
NewEmoji("confetti ball", []int32{127882}, "confetti_ball"),
NewEmoji("tanabata tree", []int32{127883}, "tanabata_tree"),
NewEmoji("pine decoration", []int32{127885}, "bamboo"),
NewEmoji("Japanese dolls", []int32{127886}, "dolls"),
NewEmoji("carp streamer", []int32{127887}, "flags"),
NewEmoji("wind chime", []int32{127888}, "wind_chime"),
NewEmoji("moon viewing ceremony", []int32{127889}, "rice_scene"),
NewEmoji("red envelope", []int32{129511}, "red_envelope"),
NewEmoji("ribbon", []int32{127872}, "ribbon"),
NewEmoji("wrapped gift", []int32{127873}, "gift"),
NewEmoji("reminder ribbon", []int32{127895, 65039}, "reminder_ribbon"),
NewEmoji("admission tickets", []int32{127903, 65039}, "tickets"),
NewEmoji("ticket", []int32{127915}, "ticket"),
NewEmoji("military medal", []int32{127894, 65039}, "medal_military"),
NewEmoji("trophy", []int32{127942}, "trophy"),
NewEmoji("sports medal", []int32{127941}, "medal_sports"),
NewEmoji("1st place medal", []int32{129351}, "1st_place_medal"),
NewEmoji("2nd place medal", []int32{129352}, "2nd_place_medal"),
NewEmoji("3rd place medal", []int32{129353}, "3rd_place_medal"),
NewEmoji("soccer ball", []int32{9917}, "soccer"),
NewEmoji("baseball", []int32{9918}, "baseball"),
NewEmoji("softball", []int32{129358}, "softball"),
NewEmoji("basketball", []int32{127936}, "basketball"),
NewEmoji("volleyball", []int32{127952}, "volleyball"),
NewEmoji("american football", []int32{127944}, "football"),
NewEmoji("rugby football", []int32{127945}, "rugby_football"),
NewEmoji("tennis", []int32{127934}, "tennis"),
NewEmoji("flying disc", []int32{129359}, "flying_disc"),
NewEmoji("bowling", []int32{127923}, "bowling"),
NewEmoji("cricket game", []int32{127951}, "cricket_game"),
NewEmoji("field hockey", []int32{127953}, "field_hockey"),
NewEmoji("ice hockey", []int32{127954}, "ice_hockey"),
NewEmoji("lacrosse", []int32{129357}, "lacrosse"),
NewEmoji("ping pong", []int32{127955}, "ping_pong"),
NewEmoji("badminton", []int32{127992}, "badminton"),
NewEmoji("boxing glove", []int32{129354}, "boxing_glove"),
NewEmoji("martial arts uniform", []int32{129355}, "martial_arts_uniform"),
NewEmoji("goal net", []int32{129349}, "goal_net"),
NewEmoji("flag in hole", []int32{9971}, "golf"),
NewEmoji("ice skate", []int32{9976, 65039}, "ice_skate"),
NewEmoji("fishing pole", []int32{127907}, "fishing_pole_and_fish"),
NewEmoji("diving mask", []int32{129343}, "diving_mask"),
NewEmoji("running shirt", []int32{127933}, "running_shirt_with_sash"),
NewEmoji("skis", []int32{127935}, "ski"),
NewEmoji("sled", []int32{128759}, "sled"),
NewEmoji("curling stone", []int32{129356}, "curling_stone"),
NewEmoji("direct hit", []int32{127919}, "dart"),
NewEmoji("yo-yo", []int32{129664}, "yo_yo"),
NewEmoji("kite", []int32{129665}, "kite"),
NewEmoji("pool 8 ball", []int32{127921}, "8ball"),
NewEmoji("crystal ball", []int32{128302}, "crystal_ball"),
NewEmoji("nazar amulet", []int32{129535}, "nazar_amulet"),
NewEmoji("video game", []int32{127918}, "video_game"),
NewEmoji("joystick", []int32{128377, 65039}, "joystick"),
NewEmoji("slot machine", []int32{127920}, "slot_machine"),
NewEmoji("game die", []int32{127922}, "game_die"),
NewEmoji("puzzle piece", []int32{129513}, "jigsaw"),
NewEmoji("teddy bear", []int32{129528}, "teddy_bear"),
NewEmoji("spade suit", []int32{9824, 65039}, "spades"),
NewEmoji("heart suit", []int32{9829, 65039}, "hearts"),
NewEmoji("diamond suit", []int32{9830, 65039}, "diamonds"),
NewEmoji("club suit", []int32{9827, 65039}, "clubs"),
NewEmoji("chess pawn", []int32{9823, 65039}, "chess_pawn"),
NewEmoji("joker", []int32{127183}, "black_joker"),
NewEmoji("mahjong red dragon", []int32{126980}, "mahjong"),
NewEmoji("flower playing cards", []int32{127924}, "flower_playing_cards"),
NewEmoji("performing arts", []int32{127917}, "performing_arts"),
NewEmoji("framed picture", []int32{128444, 65039}, "framed_picture"),
NewEmoji("artist palette", []int32{127912}, "art"),
NewEmoji("thread", []int32{129525}, "thread"),
NewEmoji("yarn", []int32{129526}, "yarn"),
NewEmoji("glasses", []int32{128083}, "eyeglasses"),
NewEmoji("sunglasses", []int32{128374, 65039}, "dark_sunglasses"),
NewEmoji("goggles", []int32{129405}, "goggles"),
NewEmoji("lab coat", []int32{129404}, "lab_coat"),
NewEmoji("safety vest", []int32{129466}, "safety_vest"),
NewEmoji("necktie", []int32{128084}, "necktie"),
NewEmoji("t-shirt", []int32{128085}, "shirt"),
NewEmoji("jeans", []int32{128086}, "jeans"),
NewEmoji("scarf", []int32{129507}, "scarf"),
NewEmoji("gloves", []int32{129508}, "gloves"),
NewEmoji("coat", []int32{129509}, "coat"),
NewEmoji("socks", []int32{129510}, "socks"),
NewEmoji("dress", []int32{128087}, "dress"),
NewEmoji("kimono", []int32{128088}, "kimono"),
NewEmoji("sari", []int32{129403}, "sari"),
NewEmoji("one-piece swimsuit", []int32{129649}, "one_piece_swimsuit"),
NewEmoji("briefs", []int32{129650}, "swim_brief"),
NewEmoji("shorts", []int32{129651}, "shorts"),
NewEmoji("bikini", []int32{128089}, "bikini"),
NewEmoji("womans clothes", []int32{128090}, "womans_clothes"),
NewEmoji("purse", []int32{128091}, "purse"),
NewEmoji("handbag", []int32{128092}, "handbag"),
NewEmoji("clutch bag", []int32{128093}, "pouch"),
NewEmoji("shopping bags", []int32{128717, 65039}, "shopping"),
NewEmoji("backpack", []int32{127890}, "school_satchel"),
NewEmoji("mans shoe", []int32{128094}, "mans_shoe"),
NewEmoji("running shoe", []int32{128095}, "athletic_shoe"),
NewEmoji("hiking boot", []int32{129406}, "hiking_boot"),
NewEmoji("flat shoe", []int32{129407}, "flat_shoe"),
NewEmoji("high-heeled shoe", []int32{128096}, "high_heel"),
NewEmoji("womans sandal", []int32{128097}, "sandal"),
NewEmoji("ballet shoes", []int32{129648}, "ballet_shoes"),
NewEmoji("womans boot", []int32{128098}, "boot"),
NewEmoji("crown", []int32{128081}, "crown"),
NewEmoji("womans hat", []int32{128082}, "womans_hat"),
NewEmoji("top hat", []int32{127913}, "tophat"),
NewEmoji("graduation cap", []int32{127891}, "mortar_board"),
NewEmoji("billed cap", []int32{129506}, "billed_cap"),
NewEmoji("rescue workers helmet", []int32{9937, 65039}, "rescue_worker_helmet"),
NewEmoji("prayer beads", []int32{128255}, "prayer_beads"),
NewEmoji("lipstick", []int32{128132}, "lipstick"),
NewEmoji("ring", []int32{128141}, "ring"),
NewEmoji("gem stone", []int32{128142}, "gem"),
NewEmoji("muted speaker", []int32{128263}, "mute"),
NewEmoji("speaker low volume", []int32{128264}, "speaker"),
NewEmoji("speaker medium volume", []int32{128265}, "sound"),
NewEmoji("speaker high volume", []int32{128266}, "loud_sound"),
NewEmoji("loudspeaker", []int32{128226}, "loudspeaker"),
NewEmoji("megaphone", []int32{128227}, "mega"),
NewEmoji("postal horn", []int32{128239}, "postal_horn"),
NewEmoji("bell", []int32{128276}, "bell"),
NewEmoji("bell with slash", []int32{128277}, "no_bell"),
NewEmoji("musical score", []int32{127932}, "musical_score"),
NewEmoji("musical note", []int32{127925}, "musical_note"),
NewEmoji("musical notes", []int32{127926}, "notes"),
NewEmoji("studio microphone", []int32{127897, 65039}, "studio_microphone"),
NewEmoji("level slider", []int32{127898, 65039}, "level_slider"),
NewEmoji("control knobs", []int32{127899, 65039}, "control_knobs"),
NewEmoji("microphone", []int32{127908}, "microphone"),
NewEmoji("headphone", []int32{127911}, "headphones"),
NewEmoji("radio", []int32{128251}, "radio"),
NewEmoji("saxophone", []int32{127927}, "saxophone"),
NewEmoji("guitar", []int32{127928}, "guitar"),
NewEmoji("musical keyboard", []int32{127929}, "musical_keyboard"),
NewEmoji("trumpet", []int32{127930}, "trumpet"),
NewEmoji("violin", []int32{127931}, "violin"),
NewEmoji("banjo", []int32{129685}, "banjo"),
NewEmoji("drum", []int32{129345}, "drum"),
NewEmoji("mobile phone", []int32{128241}, "iphone"),
NewEmoji("mobile phone with arrow", []int32{128242}, "calling"),
NewEmoji("telephone", []int32{9742, 65039}, "phone"),
NewEmoji("telephone receiver", []int32{128222}, "telephone_receiver"),
NewEmoji("pager", []int32{128223}, "pager"),
NewEmoji("fax machine", []int32{128224}, "fax"),
NewEmoji("battery", []int32{128267}, "battery"),
NewEmoji("electric plug", []int32{128268}, "electric_plug"),
NewEmoji("laptop", []int32{128187}, "computer"),
NewEmoji("desktop computer", []int32{128421, 65039}, "desktop_computer"),
NewEmoji("printer", []int32{128424, 65039}, "printer"),
NewEmoji("keyboard", []int32{9000, 65039}, "keyboard"),
NewEmoji("computer mouse", []int32{128433, 65039}, "computer_mouse"),
NewEmoji("trackball", []int32{128434, 65039}, "trackball"),
NewEmoji("computer disk", []int32{128189}, "minidisc"),
NewEmoji("floppy disk", []int32{128190}, "floppy_disk"),
NewEmoji("optical disk", []int32{128191}, "cd"),
NewEmoji("dvd", []int32{128192}, "dvd"),
NewEmoji("abacus", []int32{129518}, "abacus"),
NewEmoji("movie camera", []int32{127909}, "movie_camera"),
NewEmoji("film frames", []int32{127902, 65039}, "film_strip"),
NewEmoji("film projector", []int32{128253, 65039}, "film_projector"),
NewEmoji("clapper board", []int32{127916}, "clapper"),
NewEmoji("television", []int32{128250}, "tv"),
NewEmoji("camera", []int32{128247}, "camera"),
NewEmoji("camera with flash", []int32{128248}, "camera_flash"),
NewEmoji("video camera", []int32{128249}, "video_camera"),
NewEmoji("videocassette", []int32{128252}, "vhs"),
NewEmoji("magnifying glass tilted left", []int32{128269}, "mag"),
NewEmoji("magnifying glass tilted right", []int32{128270}, "mag_right"),
NewEmoji("candle", []int32{128367, 65039}, "candle"),
NewEmoji("light bulb", []int32{128161}, "bulb"),
NewEmoji("flashlight", []int32{128294}, "flashlight"),
NewEmoji("red paper lantern", []int32{127982}, "izakaya_lantern"),
NewEmoji("diya lamp", []int32{129684}, "diya_lamp"),
NewEmoji("notebook with decorative cover", []int32{128212}, "notebook_with_decorative_cover"),
NewEmoji("closed book", []int32{128213}, "closed_book"),
NewEmoji("open book", []int32{128214}, "book"),
NewEmoji("green book", []int32{128215}, "green_book"),
NewEmoji("blue book", []int32{128216}, "blue_book"),
NewEmoji("orange book", []int32{128217}, "orange_book"),
NewEmoji("books", []int32{128218}, "books"),
NewEmoji("notebook", []int32{128211}, "notebook"),
NewEmoji("ledger", []int32{128210}, "ledger"),
NewEmoji("page with curl", []int32{128195}, "page_with_curl"),
NewEmoji("scroll", []int32{128220}, "scroll"),
NewEmoji("page facing up", []int32{128196}, "page_facing_up"),
NewEmoji("newspaper", []int32{128240}, "newspaper"),
NewEmoji("rolled-up newspaper", []int32{128478, 65039}, "newspaper_roll"),
NewEmoji("bookmark tabs", []int32{128209}, "bookmark_tabs"),
NewEmoji("bookmark", []int32{128278}, "bookmark"),
NewEmoji("label", []int32{127991, 65039}, "label"),
NewEmoji("money bag", []int32{128176}, "moneybag"),
NewEmoji("yen banknote", []int32{128180}, "yen"),
NewEmoji("dollar banknote", []int32{128181}, "dollar"),
NewEmoji("euro banknote", []int32{128182}, "euro"),
NewEmoji("pound banknote", []int32{128183}, "pound"),
NewEmoji("money with wings", []int32{128184}, "money_with_wings"),
NewEmoji("credit card", []int32{128179}, "credit_card"),
NewEmoji("receipt", []int32{129534}, "receipt"),
NewEmoji("chart increasing with yen", []int32{128185}, "chart"),
NewEmoji("envelope", []int32{9993, 65039}, "email"),
NewEmoji("e-mail", []int32{128231}, "e-mail"),
NewEmoji("incoming envelope", []int32{128232}, "incoming_envelope"),
NewEmoji("envelope with arrow", []int32{128233}, "envelope_with_arrow"),
NewEmoji("outbox tray", []int32{128228}, "outbox_tray"),
NewEmoji("inbox tray", []int32{128229}, "inbox_tray"),
NewEmoji("package", []int32{128230}, "package"),
NewEmoji("closed mailbox with raised flag", []int32{128235}, "mailbox"),
NewEmoji("closed mailbox with lowered flag", []int32{128234}, "mailbox_closed"),
NewEmoji("open mailbox with raised flag", []int32{128236}, "mailbox_with_mail"),
NewEmoji("open mailbox with lowered flag", []int32{128237}, "mailbox_with_no_mail"),
NewEmoji("postbox", []int32{128238}, "postbox"),
NewEmoji("ballot box with ballot", []int32{128499, 65039}, "ballot_box"),
NewEmoji("pencil", []int32{9999, 65039}, "pencil2"),
NewEmoji("black nib", []int32{10002, 65039}, "black_nib"),
NewEmoji("fountain pen", []int32{128395, 65039}, "fountain_pen"),
NewEmoji("pen", []int32{128394, 65039}, "pen"),
NewEmoji("paintbrush", []int32{128396, 65039}, "paintbrush"),
NewEmoji("crayon", []int32{128397, 65039}, "crayon"),
NewEmoji("memo", []int32{128221}, "memo"),
NewEmoji("briefcase", []int32{128188}, "briefcase"),
NewEmoji("file folder", []int32{128193}, "file_folder"),
NewEmoji("open file folder", []int32{128194}, "open_file_folder"),
NewEmoji("card index dividers", []int32{128450, 65039}, "card_index_dividers"),
NewEmoji("calendar", []int32{128197}, "date"),
NewEmoji("tear-off calendar", []int32{128198}, "calendar"),
NewEmoji("spiral notepad", []int32{128466, 65039}, "spiral_notepad"),
NewEmoji("spiral calendar", []int32{128467, 65039}, "spiral_calendar"),
NewEmoji("card index", []int32{128199}, "card_index"),
NewEmoji("chart increasing", []int32{128200}, "chart_with_upwards_trend"),
NewEmoji("chart decreasing", []int32{128201}, "chart_with_downwards_trend"),
NewEmoji("bar chart", []int32{128202}, "bar_chart"),
NewEmoji("clipboard", []int32{128203}, "clipboard"),
NewEmoji("pushpin", []int32{128204}, "pushpin"),
NewEmoji("round pushpin", []int32{128205}, "round_pushpin"),
NewEmoji("paperclip", []int32{128206}, "paperclip"),
NewEmoji("linked paperclips", []int32{128391, 65039}, "paperclips"),
NewEmoji("straight ruler", []int32{128207}, "straight_ruler"),
NewEmoji("triangular ruler", []int32{128208}, "triangular_ruler"),
NewEmoji("scissors", []int32{9986, 65039}, "scissors"),
NewEmoji("card file box", []int32{128451, 65039}, "card_file_box"),
NewEmoji("file cabinet", []int32{128452, 65039}, "file_cabinet"),
NewEmoji("wastebasket", []int32{128465, 65039}, "wastebasket"),
NewEmoji("locked", []int32{128274}, "lock"),
NewEmoji("unlocked", []int32{128275}, "unlock"),
NewEmoji("locked with pen", []int32{128271}, "lock_with_ink_pen"),
NewEmoji("locked with key", []int32{128272}, "closed_lock_with_key"),
NewEmoji("key", []int32{128273}, "key"),
NewEmoji("old key", []int32{128477, 65039}, "old_key"),
NewEmoji("hammer", []int32{128296}, "hammer"),
NewEmoji("axe", []int32{129683}, "axe"),
NewEmoji("pick", []int32{9935, 65039}, "pick"),
NewEmoji("hammer and pick", []int32{9874, 65039}, "hammer_and_pick"),
NewEmoji("hammer and wrench", []int32{128736, 65039}, "hammer_and_wrench"),
NewEmoji("dagger", []int32{128481, 65039}, "dagger"),
NewEmoji("crossed swords", []int32{9876, 65039}, "crossed_swords"),
NewEmoji("pistol", []int32{128299}, "gun"),
NewEmoji("bow and arrow", []int32{127993}, "bow_and_arrow"),
NewEmoji("shield", []int32{128737, 65039}, "shield"),
NewEmoji("wrench", []int32{128295}, "wrench"),
NewEmoji("nut and bolt", []int32{128297}, "nut_and_bolt"),
NewEmoji("gear", []int32{9881, 65039}, "gear"),
NewEmoji("clamp", []int32{128476, 65039}, "clamp"),
NewEmoji("balance scale", []int32{9878, 65039}, "balance_scale"),
NewEmoji("white cane", []int32{129455}, "probing_cane"),
NewEmoji("link", []int32{128279}, "link"),
NewEmoji("chains", []int32{9939, 65039}, "chains"),
NewEmoji("toolbox", []int32{129520}, "toolbox"),
NewEmoji("magnet", []int32{129522}, "magnet"),
NewEmoji("alembic", []int32{9879, 65039}, "alembic"),
NewEmoji("test tube", []int32{129514}, "test_tube"),
NewEmoji("petri dish", []int32{129515}, "petri_dish"),
NewEmoji("dna", []int32{129516}, "dna"),
NewEmoji("microscope", []int32{128300}, "microscope"),
NewEmoji("telescope", []int32{128301}, "telescope"),
NewEmoji("satellite antenna", []int32{128225}, "satellite"),
NewEmoji("syringe", []int32{128137}, "syringe"),
NewEmoji("drop of blood", []int32{129656}, "drop_of_blood"),
NewEmoji("pill", []int32{128138}, "pill"),
NewEmoji("adhesive bandage", []int32{129657}, "adhesive_bandage"),
NewEmoji("stethoscope", []int32{129658}, "stethoscope"),
NewEmoji("door", []int32{128682}, "door"),
NewEmoji("bed", []int32{128719, 65039}, "bed"),
NewEmoji("couch and lamp", []int32{128715, 65039}, "couch_and_lamp"),
NewEmoji("chair", []int32{129681}, "chair"),
NewEmoji("toilet", []int32{128701}, "toilet"),
NewEmoji("shower", []int32{128703}, "shower"),
NewEmoji("bathtub", []int32{128705}, "bathtub"),
NewEmoji("razor", []int32{129682}, "razor"),
NewEmoji("lotion bottle", []int32{129524}, "lotion_bottle"),
NewEmoji("safety pin", []int32{129527}, "safety_pin"),
NewEmoji("broom", []int32{129529}, "broom"),
NewEmoji("basket", []int32{129530}, "basket"),
NewEmoji("roll of paper", []int32{129531}, "roll_of_paper"),
NewEmoji("soap", []int32{129532}, "soap"),
NewEmoji("sponge", []int32{129533}, "sponge"),
NewEmoji("fire extinguisher", []int32{129519}, "fire_extinguisher"),
NewEmoji("shopping cart", []int32{128722}, "shopping_cart"),
NewEmoji("cigarette", []int32{128684}, "smoking"),
NewEmoji("coffin", []int32{9904, 65039}, "coffin"),
NewEmoji("funeral urn", []int32{9905, 65039}, "funeral_urn"),
NewEmoji("moai", []int32{128511}, "moyai"),
NewEmoji("ATM sign", []int32{127975}, "atm"),
NewEmoji("litter in bin sign", []int32{128686}, "put_litter_in_its_place"),
NewEmoji("potable water", []int32{128688}, "potable_water"),
NewEmoji("wheelchair symbol", []int32{9855}, "wheelchair"),
NewEmoji("mens room", []int32{128697}, "mens"),
NewEmoji("womens room", []int32{128698}, "womens"),
NewEmoji("restroom", []int32{128699}, "restroom"),
NewEmoji("baby symbol", []int32{128700}, "baby_symbol"),
NewEmoji("water closet", []int32{128702}, "wc"),
NewEmoji("passport control", []int32{128706}, "passport_control"),
NewEmoji("customs", []int32{128707}, "customs"),
NewEmoji("baggage claim", []int32{128708}, "baggage_claim"),
NewEmoji("left luggage", []int32{128709}, "left_luggage"),
NewEmoji("warning", []int32{9888, 65039}, "warning"),
NewEmoji("children crossing", []int32{128696}, "children_crossing"),
NewEmoji("no entry", []int32{9940}, "no_entry"),
NewEmoji("prohibited", []int32{128683}, "no_entry_sign"),
NewEmoji("no bicycles", []int32{128691}, "no_bicycles"),
NewEmoji("no smoking", []int32{128685}, "no_smoking"),
NewEmoji("no littering", []int32{128687}, "do_not_litter"),
NewEmoji("non-potable water", []int32{128689}, "non-potable_water"),
NewEmoji("no pedestrians", []int32{128695}, "no_pedestrians"),
NewEmoji("no mobile phones", []int32{128245}, "no_mobile_phones"),
NewEmoji("no one under eighteen", []int32{128286}, "underage"),
NewEmoji("radioactive", []int32{9762, 65039}, "radioactive"),
NewEmoji("biohazard", []int32{9763, 65039}, "biohazard"),
NewEmoji("up arrow", []int32{11014, 65039}, "arrow_up"),
NewEmoji("up-right arrow", []int32{8599, 65039}, "arrow_upper_right"),
NewEmoji("right arrow", []int32{10145, 65039}, "arrow_right"),
NewEmoji("down-right arrow", []int32{8600, 65039}, "arrow_lower_right"),
NewEmoji("down arrow", []int32{11015, 65039}, "arrow_down"),
NewEmoji("down-left arrow", []int32{8601, 65039}, "arrow_lower_left"),
NewEmoji("left arrow", []int32{11013, 65039}, "arrow_left"),
NewEmoji("up-left arrow", []int32{8598, 65039}, "arrow_upper_left"),
NewEmoji("up-down arrow", []int32{8597, 65039}, "arrow_up_down"),
NewEmoji("left-right arrow", []int32{8596, 65039}, "left_right_arrow"),
NewEmoji("right arrow curving left", []int32{8617, 65039}, "leftwards_arrow_with_hook"),
NewEmoji("left arrow curving right", []int32{8618, 65039}, "arrow_right_hook"),
NewEmoji("right arrow curving up", []int32{10548, 65039}, "arrow_heading_up"),
NewEmoji("right arrow curving down", []int32{10549, 65039}, "arrow_heading_down"),
NewEmoji("clockwise vertical arrows", []int32{128259}, "arrows_clockwise"),
NewEmoji("counterclockwise arrows button", []int32{128260}, "arrows_counterclockwise"),
NewEmoji("BACK arrow", []int32{128281}, "back"),
NewEmoji("END arrow", []int32{128282}, "end"),
NewEmoji("ON! arrow", []int32{128283}, "on"),
NewEmoji("SOON arrow", []int32{128284}, "soon"),
NewEmoji("TOP arrow", []int32{128285}, "top"),
NewEmoji("place of worship", []int32{128720}, "place_of_worship"),
NewEmoji("atom symbol", []int32{9883, 65039}, "atom_symbol"),
NewEmoji("om", []int32{128329, 65039}, "om"),
NewEmoji("star of David", []int32{10017, 65039}, "star_of_david"),
NewEmoji("wheel of dharma", []int32{9784, 65039}, "wheel_of_dharma"),
NewEmoji("yin yang", []int32{9775, 65039}, "yin_yang"),
NewEmoji("latin cross", []int32{10013, 65039}, "latin_cross"),
NewEmoji("orthodox cross", []int32{9766, 65039}, "orthodox_cross"),
NewEmoji("star and crescent", []int32{9770, 65039}, "star_and_crescent"),
NewEmoji("peace symbol", []int32{9774, 65039}, "peace_symbol"),
NewEmoji("menorah", []int32{128334}, "menorah"),
NewEmoji("dotted six-pointed star", []int32{128303}, "six_pointed_star"),
NewEmoji("Aries", []int32{9800}, "aries"),
NewEmoji("Taurus", []int32{9801}, "taurus"),
NewEmoji("Gemini", []int32{9802}, "gemini"),
NewEmoji("Cancer", []int32{9803}, "cancer"),
NewEmoji("Leo", []int32{9804}, "leo"),
NewEmoji("Virgo", []int32{9805}, "virgo"),
NewEmoji("Libra", []int32{9806}, "libra"),
NewEmoji("Scorpio", []int32{9807}, "scorpius"),
NewEmoji("Sagittarius", []int32{9808}, "sagittarius"),
NewEmoji("Capricorn", []int32{9809}, "capricorn"),
NewEmoji("Aquarius", []int32{9810}, "aquarius"),
NewEmoji("Pisces", []int32{9811}, "pisces"),
NewEmoji("Ophiuchus", []int32{9934}, "ophiuchus"),
NewEmoji("shuffle tracks button", []int32{128256}, "twisted_rightwards_arrows"),
NewEmoji("repeat button", []int32{128257}, "repeat"),
NewEmoji("repeat single button", []int32{128258}, "repeat_one"),
NewEmoji("play button", []int32{9654, 65039}, "arrow_forward"),
NewEmoji("fast-forward button", []int32{9193}, "fast_forward"),
NewEmoji("next track button", []int32{9197, 65039}, "next_track_button"),
NewEmoji("play or pause button", []int32{9199, 65039}, "play_or_pause_button"),
NewEmoji("reverse button", []int32{9664, 65039}, "arrow_backward"),
NewEmoji("fast reverse button", []int32{9194}, "rewind"),
NewEmoji("last track button", []int32{9198, 65039}, "previous_track_button"),
NewEmoji("upwards button", []int32{128316}, "arrow_up_small"),
NewEmoji("fast up button", []int32{9195}, "arrow_double_up"),
NewEmoji("downwards button", []int32{128317}, "arrow_down_small"),
NewEmoji("fast down button", []int32{9196}, "arrow_double_down"),
NewEmoji("pause button", []int32{9208, 65039}, "pause_button"),
NewEmoji("stop button", []int32{9209, 65039}, "stop_button"),
NewEmoji("record button", []int32{9210, 65039}, "record_button"),
NewEmoji("eject button", []int32{9167, 65039}, "eject_button"),
NewEmoji("cinema", []int32{127910}, "cinema"),
NewEmoji("dim button", []int32{128261}, "low_brightness"),
NewEmoji("bright button", []int32{128262}, "high_brightness"),
NewEmoji("antenna bars", []int32{128246}, "signal_strength"),
NewEmoji("vibration mode", []int32{128243}, "vibration_mode"),
NewEmoji("mobile phone off", []int32{128244}, "mobile_phone_off"),
NewEmoji("female sign", []int32{9792, 65039}, "female_sign"),
NewEmoji("male sign", []int32{9794, 65039}, "male_sign"),
NewEmoji("multiply", []int32{10006, 65039}, "heavy_multiplication_x"),
NewEmoji("plus", []int32{10133}, "heavy_plus_sign"),
NewEmoji("minus", []int32{10134}, "heavy_minus_sign"),
NewEmoji("divide", []int32{10135}, "heavy_division_sign"),
NewEmoji("infinity", []int32{9854, 65039}, "infinity"),
NewEmoji("double exclamation mark", []int32{8252, 65039}, "bangbang"),
NewEmoji("exclamation question mark", []int32{8265, 65039}, "interrobang"),
NewEmoji("question mark", []int32{10067}, "question"),
NewEmoji("white question mark", []int32{10068}, "grey_question"),
NewEmoji("white exclamation mark", []int32{10069}, "grey_exclamation"),
NewEmoji("exclamation mark", []int32{10071}, "exclamation"),
NewEmoji("wavy dash", []int32{12336, 65039}, "wavy_dash"),
NewEmoji("currency exchange", []int32{128177}, "currency_exchange"),
NewEmoji("heavy dollar sign", []int32{128178}, "heavy_dollar_sign"),
NewEmoji("medical symbol", []int32{9877, 65039}, "medical_symbol"),
NewEmoji("recycling symbol", []int32{9851, 65039}, "recycle"),
NewEmoji("fleur-de-lis", []int32{9884, 65039}, "fleur_de_lis"),
NewEmoji("trident emblem", []int32{128305}, "trident"),
NewEmoji("name badge", []int32{128219}, "name_badge"),
NewEmoji("Japanese symbol for beginner", []int32{128304}, "beginner"),
NewEmoji("hollow red circle", []int32{11093}, "o"),
NewEmoji("check mark button", []int32{9989}, "white_check_mark"),
NewEmoji("check box with check", []int32{9745, 65039}, "ballot_box_with_check"),
NewEmoji("check mark", []int32{10004, 65039}, "heavy_check_mark"),
NewEmoji("cross mark", []int32{10060}, "x"),
NewEmoji("cross mark button", []int32{10062}, "negative_squared_cross_mark"),
NewEmoji("curly loop", []int32{10160}, "curly_loop"),
NewEmoji("double curly loop", []int32{10175}, "loop"),
NewEmoji("part alternation mark", []int32{12349, 65039}, "part_alternation_mark"),
NewEmoji("eight-spoked asterisk", []int32{10035, 65039}, "eight_spoked_asterisk"),
NewEmoji("eight-pointed star", []int32{10036, 65039}, "eight_pointed_black_star"),
NewEmoji("sparkle", []int32{10055, 65039}, "sparkle"),
NewEmoji("copyright", []int32{169, 65039}, "copyright"),
NewEmoji("registered", []int32{174, 65039}, "registered"),
NewEmoji("trade mark", []int32{8482, 65039}, "tm"),
NewEmoji("keycap: #", []int32{35, 65039, 8419}, "hash"),
NewEmoji("keycap: *", []int32{42, 65039, 8419}, "asterisk"),
NewEmoji("keycap: 0", []int32{48, 65039, 8419}, "zero"),
NewEmoji("keycap: 1", []int32{49, 65039, 8419}, "one"),
NewEmoji("keycap: 2", []int32{50, 65039, 8419}, "two"),
NewEmoji("keycap: 3", []int32{51, 65039, 8419}, "three"),
NewEmoji("keycap: 4", []int32{52, 65039, 8419}, "four"),
NewEmoji("keycap: 5", []int32{53, 65039, 8419}, "five"),
NewEmoji("keycap: 6", []int32{54, 65039, 8419}, "six"),
NewEmoji("keycap: 7", []int32{55, 65039, 8419}, "seven"),
NewEmoji("keycap: 8", []int32{56, 65039, 8419}, "eight"),
NewEmoji("keycap: 9", []int32{57, 65039, 8419}, "nine"),
NewEmoji("keycap: 10", []int32{128287}, "keycap_ten"),
NewEmoji("input latin uppercase", []int32{128288}, "capital_abcd"),
NewEmoji("input latin lowercase", []int32{128289}, "abcd"),
NewEmoji("input numbers", []int32{128290}, "1234"),
NewEmoji("input symbols", []int32{128291}, "symbols"),
NewEmoji("input latin letters", []int32{128292}, "abc"),
NewEmoji("A button (blood type)", []int32{127344, 65039}, "a"),
NewEmoji("AB button (blood type)", []int32{127374}, "ab"),
NewEmoji("B button (blood type)", []int32{127345, 65039}, "b"),
NewEmoji("CL button", []int32{127377}, "cl"),
NewEmoji("COOL button", []int32{127378}, "cool"),
NewEmoji("FREE button", []int32{127379}, "free"),
NewEmoji("information", []int32{8505, 65039}, "information_source"),
NewEmoji("ID button", []int32{127380}, "id"),
NewEmoji("circled M", []int32{9410, 65039}, "m"),
NewEmoji("NEW button", []int32{127381}, "new"),
NewEmoji("NG button", []int32{127382}, "ng"),
NewEmoji("O button (blood type)", []int32{127358, 65039}, "o2"),
NewEmoji("OK button", []int32{127383}, "ok"),
NewEmoji("P button", []int32{127359, 65039}, "parking"),
NewEmoji("SOS button", []int32{127384}, "sos"),
NewEmoji("UP! button", []int32{127385}, "up"),
NewEmoji("VS button", []int32{127386}, "vs"),
NewEmoji("Japanese “here” button", []int32{127489}, "koko"),
NewEmoji("Japanese “service charge” button", []int32{127490, 65039}, "sa"),
NewEmoji("Japanese “monthly amount” button", []int32{127543, 65039}, "u6708"),
NewEmoji("Japanese “not free of charge” button", []int32{127542}, "u6709"),
NewEmoji("Japanese “reserved” button", []int32{127535}, "u6307"),
NewEmoji("Japanese “bargain” button", []int32{127568}, "ideograph_advantage"),
NewEmoji("Japanese “discount” button", []int32{127545}, "u5272"),
NewEmoji("Japanese “free of charge” button", []int32{127514}, "u7121"),
NewEmoji("Japanese “prohibited” button", []int32{127538}, "u7981"),
NewEmoji("Japanese “acceptable” button", []int32{127569}, "accept"),
NewEmoji("Japanese “application” button", []int32{127544}, "u7533"),
NewEmoji("Japanese “passing grade” button", []int32{127540}, "u5408"),
NewEmoji("Japanese “vacancy” button", []int32{127539}, "u7a7a"),
NewEmoji("Japanese “congratulations” button", []int32{12951, 65039}, "congratulations"),
NewEmoji("Japanese “secret” button", []int32{12953, 65039}, "secret"),
NewEmoji("Japanese “open for business” button", []int32{127546}, "u55b6"),
NewEmoji("Japanese “no vacancy” button", []int32{127541}, "u6e80"),
NewEmoji("red circle", []int32{128308}, "red_circle"),
NewEmoji("orange circle", []int32{128992}, "orange_circle"),
NewEmoji("yellow circle", []int32{128993}, "yellow_circle"),
NewEmoji("green circle", []int32{128994}, "green_circle"),
NewEmoji("blue circle", []int32{128309}, "large_blue_circle"),
NewEmoji("purple circle", []int32{128995}, "purple_circle"),
NewEmoji("brown circle", []int32{128996}, "brown_circle"),
NewEmoji("black circle", []int32{9899}, "black_circle"),
NewEmoji("white circle", []int32{9898}, "white_circle"),
NewEmoji("red square", []int32{128997}, "red_square"),
NewEmoji("orange square", []int32{128999}, "orange_square"),
NewEmoji("yellow square", []int32{129000}, "yellow_square"),
NewEmoji("green square", []int32{129001}, "green_square"),
NewEmoji("blue square", []int32{128998}, "blue_square"),
NewEmoji("purple square", []int32{129002}, "purple_square"),
NewEmoji("brown square", []int32{129003}, "brown_square"),
NewEmoji("black large square", []int32{11035}, "black_large_square"),
NewEmoji("white large square", []int32{11036}, "white_large_square"),
NewEmoji("black medium square", []int32{9724, 65039}, "black_medium_square"),
NewEmoji("white medium square", []int32{9723, 65039}, "white_medium_square"),
NewEmoji("black medium-small square", []int32{9726}, "black_medium_small_square"),
NewEmoji("white medium-small square", []int32{9725}, "white_medium_small_square"),
NewEmoji("black small square", []int32{9642, 65039}, "black_small_square"),
NewEmoji("white small square", []int32{9643, 65039}, "white_small_square"),
NewEmoji("large orange diamond", []int32{128310}, "large_orange_diamond"),
NewEmoji("large blue diamond", []int32{128311}, "large_blue_diamond"),
NewEmoji("small orange diamond", []int32{128312}, "small_orange_diamond"),
NewEmoji("small blue diamond", []int32{128313}, "small_blue_diamond"),
NewEmoji("red triangle pointed up", []int32{128314}, "small_red_triangle"),
NewEmoji("red triangle pointed down", []int32{128315}, "small_red_triangle_down"),
NewEmoji("diamond with a dot", []int32{128160}, "diamond_shape_with_a_dot_inside"),
NewEmoji("radio button", []int32{128280}, "radio_button"),
NewEmoji("white square button", []int32{128307}, "white_square_button"),
NewEmoji("black square button", []int32{128306}, "black_square_button"),
NewEmoji("chequered flag", []int32{127937}, "checkered_flag"),
NewEmoji("triangular flag", []int32{128681}, "triangular_flag_on_post"),
NewEmoji("crossed flags", []int32{127884}, "crossed_flags"),
NewEmoji("black flag", []int32{127988}, "black_flag"),
NewEmoji("white flag", []int32{127987, 65039}, "white_flag"),
NewEmoji("rainbow flag", []int32{127987, 65039, 8205, 127752}, "rainbow_flag"),
NewEmoji("pirate flag", []int32{127988, 8205, 9760, 65039}, "pirate_flag"),
NewEmoji("flag: Ascension Island", []int32{127462, 127464}, "ascension_island"),
NewEmoji("flag: Andorra", []int32{127462, 127465}, "andorra"),
NewEmoji("flag: United Arab Emirates", []int32{127462, 127466}, "united_arab_emirates"),
NewEmoji("flag: Afghanistan", []int32{127462, 127467}, "afghanistan"),
NewEmoji("flag: Antigua & Barbuda", []int32{127462, 127468}, "antigua_barbuda"),
NewEmoji("flag: Anguilla", []int32{127462, 127470}, "anguilla"),
NewEmoji("flag: Albania", []int32{127462, 127473}, "albania"),
NewEmoji("flag: Armenia", []int32{127462, 127474}, "armenia"),
NewEmoji("flag: Angola", []int32{127462, 127476}, "angola"),
NewEmoji("flag: Antarctica", []int32{127462, 127478}, "antarctica"),
NewEmoji("flag: Argentina", []int32{127462, 127479}, "argentina"),
NewEmoji("flag: American Samoa", []int32{127462, 127480}, "american_samoa"),
NewEmoji("flag: Austria", []int32{127462, 127481}, "austria"),
NewEmoji("flag: Australia", []int32{127462, 127482}, "australia"),
NewEmoji("flag: Aruba", []int32{127462, 127484}, "aruba"),
NewEmoji("flag: Åland Islands", []int32{127462, 127485}, "aland_islands"),
NewEmoji("flag: Azerbaijan", []int32{127462, 127487}, "azerbaijan"),
NewEmoji("flag: Bosnia & Herzegovina", []int32{127463, 127462}, "bosnia_herzegovina"),
NewEmoji("flag: Barbados", []int32{127463, 127463}, "barbados"),
NewEmoji("flag: Bangladesh", []int32{127463, 127465}, "bangladesh"),
NewEmoji("flag: Belgium", []int32{127463, 127466}, "belgium"),
NewEmoji("flag: Burkina Faso", []int32{127463, 127467}, "burkina_faso"),
NewEmoji("flag: Bulgaria", []int32{127463, 127468}, "bulgaria"),
NewEmoji("flag: Bahrain", []int32{127463, 127469}, "bahrain"),
NewEmoji("flag: Burundi", []int32{127463, 127470}, "burundi"),
NewEmoji("flag: Benin", []int32{127463, 127471}, "benin"),
NewEmoji("flag: St. Barthélemy", []int32{127463, 127473}, "st_barthelemy"),
NewEmoji("flag: Bermuda", []int32{127463, 127474}, "bermuda"),
NewEmoji("flag: Brunei", []int32{127463, 127475}, "brunei"),
NewEmoji("flag: Bolivia", []int32{127463, 127476}, "bolivia"),
NewEmoji("flag: Caribbean Netherlands", []int32{127463, 127478}, "caribbean_netherlands"),
NewEmoji("flag: Brazil", []int32{127463, 127479}, "brazil"),
NewEmoji("flag: Bahamas", []int32{127463, 127480}, "bahamas"),
NewEmoji("flag: Bhutan", []int32{127463, 127481}, "bhutan"),
NewEmoji("flag: Bouvet Island", []int32{127463, 127483}, "bouvet_island"),
NewEmoji("flag: Botswana", []int32{127463, 127484}, "botswana"),
NewEmoji("flag: Belarus", []int32{127463, 127486}, "belarus"),
NewEmoji("flag: Belize", []int32{127463, 127487}, "belize"),
NewEmoji("flag: Canada", []int32{127464, 127462}, "canada"),
NewEmoji("flag: Cocos (Keeling) Islands", []int32{127464, 127464}, "cocos_islands"),
NewEmoji("flag: Congo - Kinshasa", []int32{127464, 127465}, "congo_kinshasa"),
NewEmoji("flag: Central African Republic", []int32{127464, 127467}, "central_african_republic"),
NewEmoji("flag: Congo - Brazzaville", []int32{127464, 127468}, "congo_brazzaville"),
NewEmoji("flag: Switzerland", []int32{127464, 127469}, "switzerland"),
NewEmoji("flag: Côte dIvoire", []int32{127464, 127470}, "cote_divoire"),
NewEmoji("flag: Cook Islands", []int32{127464, 127472}, "cook_islands"),
NewEmoji("flag: Chile", []int32{127464, 127473}, "chile"),
NewEmoji("flag: Cameroon", []int32{127464, 127474}, "cameroon"),
NewEmoji("flag: China", []int32{127464, 127475}, "cn"),
NewEmoji("flag: Colombia", []int32{127464, 127476}, "colombia"),
NewEmoji("flag: Clipperton Island", []int32{127464, 127477}, "clipperton_island"),
NewEmoji("flag: Costa Rica", []int32{127464, 127479}, "costa_rica"),
NewEmoji("flag: Cuba", []int32{127464, 127482}, "cuba"),
NewEmoji("flag: Cape Verde", []int32{127464, 127483}, "cape_verde"),
NewEmoji("flag: Curaçao", []int32{127464, 127484}, "curacao"),
NewEmoji("flag: Christmas Island", []int32{127464, 127485}, "christmas_island"),
NewEmoji("flag: Cyprus", []int32{127464, 127486}, "cyprus"),
NewEmoji("flag: Czechia", []int32{127464, 127487}, "czech_republic"),
NewEmoji("flag: Germany", []int32{127465, 127466}, "de"),
NewEmoji("flag: Diego Garcia", []int32{127465, 127468}, "diego_garcia"),
NewEmoji("flag: Djibouti", []int32{127465, 127471}, "djibouti"),
NewEmoji("flag: Denmark", []int32{127465, 127472}, "denmark"),
NewEmoji("flag: Dominica", []int32{127465, 127474}, "dominica"),
NewEmoji("flag: Dominican Republic", []int32{127465, 127476}, "dominican_republic"),
NewEmoji("flag: Algeria", []int32{127465, 127487}, "algeria"),
NewEmoji("flag: Ceuta & Melilla", []int32{127466, 127462}, "ceuta_melilla"),
NewEmoji("flag: Ecuador", []int32{127466, 127464}, "ecuador"),
NewEmoji("flag: Estonia", []int32{127466, 127466}, "estonia"),
NewEmoji("flag: Egypt", []int32{127466, 127468}, "egypt"),
NewEmoji("flag: Western Sahara", []int32{127466, 127469}, "western_sahara"),
NewEmoji("flag: Eritrea", []int32{127466, 127479}, "eritrea"),
NewEmoji("flag: Spain", []int32{127466, 127480}, "es"),
NewEmoji("flag: Ethiopia", []int32{127466, 127481}, "ethiopia"),
NewEmoji("flag: European Union", []int32{127466, 127482}, "eu"),
NewEmoji("flag: Finland", []int32{127467, 127470}, "finland"),
NewEmoji("flag: Fiji", []int32{127467, 127471}, "fiji"),
NewEmoji("flag: Falkland Islands", []int32{127467, 127472}, "falkland_islands"),
NewEmoji("flag: Micronesia", []int32{127467, 127474}, "micronesia"),
NewEmoji("flag: Faroe Islands", []int32{127467, 127476}, "faroe_islands"),
NewEmoji("flag: France", []int32{127467, 127479}, "fr"),
NewEmoji("flag: Gabon", []int32{127468, 127462}, "gabon"),
NewEmoji("flag: United Kingdom", []int32{127468, 127463}, "gb"),
NewEmoji("flag: Grenada", []int32{127468, 127465}, "grenada"),
NewEmoji("flag: Georgia", []int32{127468, 127466}, "georgia"),
NewEmoji("flag: French Guiana", []int32{127468, 127467}, "french_guiana"),
NewEmoji("flag: Guernsey", []int32{127468, 127468}, "guernsey"),
NewEmoji("flag: Ghana", []int32{127468, 127469}, "ghana"),
NewEmoji("flag: Gibraltar", []int32{127468, 127470}, "gibraltar"),
NewEmoji("flag: Greenland", []int32{127468, 127473}, "greenland"),
NewEmoji("flag: Gambia", []int32{127468, 127474}, "gambia"),
NewEmoji("flag: Guinea", []int32{127468, 127475}, "guinea"),
NewEmoji("flag: Guadeloupe", []int32{127468, 127477}, "guadeloupe"),
NewEmoji("flag: Equatorial Guinea", []int32{127468, 127478}, "equatorial_guinea"),
NewEmoji("flag: Greece", []int32{127468, 127479}, "greece"),
NewEmoji("flag: South Georgia & South Sandwich Islands", []int32{127468, 127480}, "south_georgia_south_sandwich_islands"),
NewEmoji("flag: Guatemala", []int32{127468, 127481}, "guatemala"),
NewEmoji("flag: Guam", []int32{127468, 127482}, "guam"),
NewEmoji("flag: Guinea-Bissau", []int32{127468, 127484}, "guinea_bissau"),
NewEmoji("flag: Guyana", []int32{127468, 127486}, "guyana"),
NewEmoji("flag: Hong Kong SAR China", []int32{127469, 127472}, "hong_kong"),
NewEmoji("flag: Heard & McDonald Islands", []int32{127469, 127474}, "heard_mcdonald_islands"),
NewEmoji("flag: Honduras", []int32{127469, 127475}, "honduras"),
NewEmoji("flag: Croatia", []int32{127469, 127479}, "croatia"),
NewEmoji("flag: Haiti", []int32{127469, 127481}, "haiti"),
NewEmoji("flag: Hungary", []int32{127469, 127482}, "hungary"),
NewEmoji("flag: Canary Islands", []int32{127470, 127464}, "canary_islands"),
NewEmoji("flag: Indonesia", []int32{127470, 127465}, "indonesia"),
NewEmoji("flag: Ireland", []int32{127470, 127466}, "ireland"),
NewEmoji("flag: Israel", []int32{127470, 127473}, "israel"),
NewEmoji("flag: Isle of Man", []int32{127470, 127474}, "isle_of_man"),
NewEmoji("flag: India", []int32{127470, 127475}, "india"),
NewEmoji("flag: British Indian Ocean Territory", []int32{127470, 127476}, "british_indian_ocean_territory"),
NewEmoji("flag: Iraq", []int32{127470, 127478}, "iraq"),
NewEmoji("flag: Iran", []int32{127470, 127479}, "iran"),
NewEmoji("flag: Iceland", []int32{127470, 127480}, "iceland"),
NewEmoji("flag: Italy", []int32{127470, 127481}, "it"),
NewEmoji("flag: Jersey", []int32{127471, 127466}, "jersey"),
NewEmoji("flag: Jamaica", []int32{127471, 127474}, "jamaica"),
NewEmoji("flag: Jordan", []int32{127471, 127476}, "jordan"),
NewEmoji("flag: Japan", []int32{127471, 127477}, "jp"),
NewEmoji("flag: Kenya", []int32{127472, 127466}, "kenya"),
NewEmoji("flag: Kyrgyzstan", []int32{127472, 127468}, "kyrgyzstan"),
NewEmoji("flag: Cambodia", []int32{127472, 127469}, "cambodia"),
NewEmoji("flag: Kiribati", []int32{127472, 127470}, "kiribati"),
NewEmoji("flag: Comoros", []int32{127472, 127474}, "comoros"),
NewEmoji("flag: St. Kitts & Nevis", []int32{127472, 127475}, "st_kitts_nevis"),
NewEmoji("flag: North Korea", []int32{127472, 127477}, "north_korea"),
NewEmoji("flag: South Korea", []int32{127472, 127479}, "kr"),
NewEmoji("flag: Kuwait", []int32{127472, 127484}, "kuwait"),
NewEmoji("flag: Cayman Islands", []int32{127472, 127486}, "cayman_islands"),
NewEmoji("flag: Kazakhstan", []int32{127472, 127487}, "kazakhstan"),
NewEmoji("flag: Laos", []int32{127473, 127462}, "laos"),
NewEmoji("flag: Lebanon", []int32{127473, 127463}, "lebanon"),
NewEmoji("flag: St. Lucia", []int32{127473, 127464}, "st_lucia"),
NewEmoji("flag: Liechtenstein", []int32{127473, 127470}, "liechtenstein"),
NewEmoji("flag: Sri Lanka", []int32{127473, 127472}, "sri_lanka"),
NewEmoji("flag: Liberia", []int32{127473, 127479}, "liberia"),
NewEmoji("flag: Lesotho", []int32{127473, 127480}, "lesotho"),
NewEmoji("flag: Lithuania", []int32{127473, 127481}, "lithuania"),
NewEmoji("flag: Luxembourg", []int32{127473, 127482}, "luxembourg"),
NewEmoji("flag: Latvia", []int32{127473, 127483}, "latvia"),
NewEmoji("flag: Libya", []int32{127473, 127486}, "libya"),
NewEmoji("flag: Morocco", []int32{127474, 127462}, "morocco"),
NewEmoji("flag: Monaco", []int32{127474, 127464}, "monaco"),
NewEmoji("flag: Moldova", []int32{127474, 127465}, "moldova"),
NewEmoji("flag: Montenegro", []int32{127474, 127466}, "montenegro"),
NewEmoji("flag: St. Martin", []int32{127474, 127467}, "st_martin"),
NewEmoji("flag: Madagascar", []int32{127474, 127468}, "madagascar"),
NewEmoji("flag: Marshall Islands", []int32{127474, 127469}, "marshall_islands"),
NewEmoji("flag: North Macedonia", []int32{127474, 127472}, "macedonia"),
NewEmoji("flag: Mali", []int32{127474, 127473}, "mali"),
NewEmoji("flag: Myanmar (Burma)", []int32{127474, 127474}, "myanmar"),
NewEmoji("flag: Mongolia", []int32{127474, 127475}, "mongolia"),
NewEmoji("flag: Macao SAR China", []int32{127474, 127476}, "macau"),
NewEmoji("flag: Northern Mariana Islands", []int32{127474, 127477}, "northern_mariana_islands"),
NewEmoji("flag: Martinique", []int32{127474, 127478}, "martinique"),
NewEmoji("flag: Mauritania", []int32{127474, 127479}, "mauritania"),
NewEmoji("flag: Montserrat", []int32{127474, 127480}, "montserrat"),
NewEmoji("flag: Malta", []int32{127474, 127481}, "malta"),
NewEmoji("flag: Mauritius", []int32{127474, 127482}, "mauritius"),
NewEmoji("flag: Maldives", []int32{127474, 127483}, "maldives"),
NewEmoji("flag: Malawi", []int32{127474, 127484}, "malawi"),
NewEmoji("flag: Mexico", []int32{127474, 127485}, "mexico"),
NewEmoji("flag: Malaysia", []int32{127474, 127486}, "malaysia"),
NewEmoji("flag: Mozambique", []int32{127474, 127487}, "mozambique"),
NewEmoji("flag: Namibia", []int32{127475, 127462}, "namibia"),
NewEmoji("flag: New Caledonia", []int32{127475, 127464}, "new_caledonia"),
NewEmoji("flag: Niger", []int32{127475, 127466}, "niger"),
NewEmoji("flag: Norfolk Island", []int32{127475, 127467}, "norfolk_island"),
NewEmoji("flag: Nigeria", []int32{127475, 127468}, "nigeria"),
NewEmoji("flag: Nicaragua", []int32{127475, 127470}, "nicaragua"),
NewEmoji("flag: Netherlands", []int32{127475, 127473}, "netherlands"),
NewEmoji("flag: Norway", []int32{127475, 127476}, "norway"),
NewEmoji("flag: Nepal", []int32{127475, 127477}, "nepal"),
NewEmoji("flag: Nauru", []int32{127475, 127479}, "nauru"),
NewEmoji("flag: Niue", []int32{127475, 127482}, "niue"),
NewEmoji("flag: New Zealand", []int32{127475, 127487}, "new_zealand"),
NewEmoji("flag: Oman", []int32{127476, 127474}, "oman"),
NewEmoji("flag: Panama", []int32{127477, 127462}, "panama"),
NewEmoji("flag: Peru", []int32{127477, 127466}, "peru"),
NewEmoji("flag: French Polynesia", []int32{127477, 127467}, "french_polynesia"),
NewEmoji("flag: Papua New Guinea", []int32{127477, 127468}, "papua_new_guinea"),
NewEmoji("flag: Philippines", []int32{127477, 127469}, "philippines"),
NewEmoji("flag: Pakistan", []int32{127477, 127472}, "pakistan"),
NewEmoji("flag: Poland", []int32{127477, 127473}, "poland"),
NewEmoji("flag: St. Pierre & Miquelon", []int32{127477, 127474}, "st_pierre_miquelon"),
NewEmoji("flag: Pitcairn Islands", []int32{127477, 127475}, "pitcairn_islands"),
NewEmoji("flag: Puerto Rico", []int32{127477, 127479}, "puerto_rico"),
NewEmoji("flag: Palestinian Territories", []int32{127477, 127480}, "palestinian_territories"),
NewEmoji("flag: Portugal", []int32{127477, 127481}, "portugal"),
NewEmoji("flag: Palau", []int32{127477, 127484}, "palau"),
NewEmoji("flag: Paraguay", []int32{127477, 127486}, "paraguay"),
NewEmoji("flag: Qatar", []int32{127478, 127462}, "qatar"),
NewEmoji("flag: Réunion", []int32{127479, 127466}, "reunion"),
NewEmoji("flag: Romania", []int32{127479, 127476}, "romania"),
NewEmoji("flag: Serbia", []int32{127479, 127480}, "serbia"),
NewEmoji("flag: Russia", []int32{127479, 127482}, "ru"),
NewEmoji("flag: Rwanda", []int32{127479, 127484}, "rwanda"),
NewEmoji("flag: Saudi Arabia", []int32{127480, 127462}, "saudi_arabia"),
NewEmoji("flag: Solomon Islands", []int32{127480, 127463}, "solomon_islands"),
NewEmoji("flag: Seychelles", []int32{127480, 127464}, "seychelles"),
NewEmoji("flag: Sudan", []int32{127480, 127465}, "sudan"),
NewEmoji("flag: Sweden", []int32{127480, 127466}, "sweden"),
NewEmoji("flag: Singapore", []int32{127480, 127468}, "singapore"),
NewEmoji("flag: St. Helena", []int32{127480, 127469}, "st_helena"),
NewEmoji("flag: Slovenia", []int32{127480, 127470}, "slovenia"),
NewEmoji("flag: Svalbard & Jan Mayen", []int32{127480, 127471}, "svalbard_jan_mayen"),
NewEmoji("flag: Slovakia", []int32{127480, 127472}, "slovakia"),
NewEmoji("flag: Sierra Leone", []int32{127480, 127473}, "sierra_leone"),
NewEmoji("flag: San Marino", []int32{127480, 127474}, "san_marino"),
NewEmoji("flag: Senegal", []int32{127480, 127475}, "senegal"),
NewEmoji("flag: Somalia", []int32{127480, 127476}, "somalia"),
NewEmoji("flag: Suriname", []int32{127480, 127479}, "suriname"),
NewEmoji("flag: South Sudan", []int32{127480, 127480}, "south_sudan"),
NewEmoji("flag: São Tomé & Príncipe", []int32{127480, 127481}, "sao_tome_principe"),
NewEmoji("flag: El Salvador", []int32{127480, 127483}, "el_salvador"),
NewEmoji("flag: Sint Maarten", []int32{127480, 127485}, "sint_maarten"),
NewEmoji("flag: Syria", []int32{127480, 127486}, "syria"),
NewEmoji("flag: Eswatini", []int32{127480, 127487}, "swaziland"),
NewEmoji("flag: Tristan da Cunha", []int32{127481, 127462}, "tristan_da_cunha"),
NewEmoji("flag: Turks & Caicos Islands", []int32{127481, 127464}, "turks_caicos_islands"),
NewEmoji("flag: Chad", []int32{127481, 127465}, "chad"),
NewEmoji("flag: French Southern Territories", []int32{127481, 127467}, "french_southern_territories"),
NewEmoji("flag: Togo", []int32{127481, 127468}, "togo"),
NewEmoji("flag: Thailand", []int32{127481, 127469}, "thailand"),
NewEmoji("flag: Tajikistan", []int32{127481, 127471}, "tajikistan"),
NewEmoji("flag: Tokelau", []int32{127481, 127472}, "tokelau"),
NewEmoji("flag: Timor-Leste", []int32{127481, 127473}, "timor_leste"),
NewEmoji("flag: Turkmenistan", []int32{127481, 127474}, "turkmenistan"),
NewEmoji("flag: Tunisia", []int32{127481, 127475}, "tunisia"),
NewEmoji("flag: Tonga", []int32{127481, 127476}, "tonga"),
NewEmoji("flag: Turkey", []int32{127481, 127479}, "tr"),
NewEmoji("flag: Trinidad & Tobago", []int32{127481, 127481}, "trinidad_tobago"),
NewEmoji("flag: Tuvalu", []int32{127481, 127483}, "tuvalu"),
NewEmoji("flag: Taiwan", []int32{127481, 127484}, "taiwan"),
NewEmoji("flag: Tanzania", []int32{127481, 127487}, "tanzania"),
NewEmoji("flag: Ukraine", []int32{127482, 127462}, "ukraine"),
NewEmoji("flag: Uganda", []int32{127482, 127468}, "uganda"),
NewEmoji("flag: U.S. Outlying Islands", []int32{127482, 127474}, "us_outlying_islands"),
NewEmoji("flag: United Nations", []int32{127482, 127475}, "united_nations"),
NewEmoji("flag: United States", []int32{127482, 127480}, "us"),
NewEmoji("flag: Uruguay", []int32{127482, 127486}, "uruguay"),
NewEmoji("flag: Uzbekistan", []int32{127482, 127487}, "uzbekistan"),
NewEmoji("flag: Vatican City", []int32{127483, 127462}, "vatican_city"),
NewEmoji("flag: St. Vincent & Grenadines", []int32{127483, 127464}, "st_vincent_grenadines"),
NewEmoji("flag: Venezuela", []int32{127483, 127466}, "venezuela"),
NewEmoji("flag: British Virgin Islands", []int32{127483, 127468}, "british_virgin_islands"),
NewEmoji("flag: U.S. Virgin Islands", []int32{127483, 127470}, "us_virgin_islands"),
NewEmoji("flag: Vietnam", []int32{127483, 127475}, "vietnam"),
NewEmoji("flag: Vanuatu", []int32{127483, 127482}, "vanuatu"),
NewEmoji("flag: Wallis & Futuna", []int32{127484, 127467}, "wallis_futuna"),
NewEmoji("flag: Samoa", []int32{127484, 127480}, "samoa"),
NewEmoji("flag: Kosovo", []int32{127485, 127472}, "kosovo"),
NewEmoji("flag: Yemen", []int32{127486, 127466}, "yemen"),
NewEmoji("flag: Mayotte", []int32{127486, 127481}, "mayotte"),
NewEmoji("flag: South Africa", []int32{127487, 127462}, "south_africa"),
NewEmoji("flag: Zambia", []int32{127487, 127474}, "zambia"),
NewEmoji("flag: Zimbabwe", []int32{127487, 127484}, "zimbabwe"),
NewEmoji("flag: England", []int32{127988, 917607, 917602, 917605, 917614, 917607, 917631}, "england"),
NewEmoji("flag: Scotland", []int32{127988, 917607, 917602, 917619, 917603, 917620, 917631}, "scotland"),
NewEmoji("flag: Wales", []int32{127988, 917607, 917602, 917623, 917612, 917619, 917631}, "wales"),
)
})
m := github.Clone()
for _, opt := range opts {
opt(m)
}
return m
}

360
vendor/github.com/yuin/goldmark-emoji/emoji.go generated vendored Normal file
View File

@ -0,0 +1,360 @@
// package emoji is a extension for the goldmark(http://github.com/yuin/goldmark).
package emoji
import (
"fmt"
"strings"
"github.com/yuin/goldmark"
east "github.com/yuin/goldmark-emoji/ast"
"github.com/yuin/goldmark-emoji/definition"
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer"
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
)
// Option interface sets options for this extension.
type Option interface {
emojiOption()
}
// ParserConfig struct is a data structure that holds configuration of
// the Emoji extension.
type ParserConfig struct {
Emojis definition.Emojis
}
const optEmojis parser.OptionName = "EmojiEmojis"
// SetOption implements parser.SetOptioner
func (c *ParserConfig) SetOption(name parser.OptionName, value interface{}) {
switch name {
case optEmojis:
c.Emojis = value.(definition.Emojis)
}
}
// A ParserOption interface sets options for the emoji parser.
type ParserOption interface {
Option
parser.Option
SetEmojiOption(*ParserConfig)
}
var _ ParserOption = &withEmojis{}
type withEmojis struct {
value definition.Emojis
}
func (o *withEmojis) emojiOption() {}
func (o *withEmojis) SetParserOption(c *parser.Config) {
c.Options[optEmojis] = o.value
}
func (o *withEmojis) SetEmojiOption(c *ParserConfig) {
c.Emojis = o.value
}
// WithMaping is a functional option that defines links names to unicode emojis.
func WithEmojis(value definition.Emojis) Option {
return &withEmojis{
value: value,
}
}
// RenderingMethod indicates how emojis are rendered.
type RenderingMethod int
// RendererFunc will be used for rendering emojis.
type RendererFunc func(w util.BufWriter, source []byte, n *east.Emoji, config *RendererConfig)
const (
// Entity renders an emoji as an html entity.
Entity RenderingMethod = iota
// Unicode renders an emoji as unicode character.
Unicode
// Twemoji renders an emoji as an img tag with [twemoji](https://github.com/twitter/twemoji).
Twemoji
// Func renders an emoji using RendererFunc.
Func
)
// RendererConfig struct holds options for the emoji renderer.
type RendererConfig struct {
html.Config
// Method indicates how emojis are rendered.
Method RenderingMethod
// TwemojiTemplate is a printf template for twemoji. This value is valid only when Method is set to Twemoji.
// `printf` arguments are:
//
// 1: name (e.g. "face with tears of joy")
// 2: file name without an extension (e.g. 1f646-2642)
// 3: '/' if XHTML, otherwise ''
//
TwemojiTemplate string
// RendererFunc is a RendererFunc that renders emojis. This value is valid only when Method is set to Func.
RendererFunc RendererFunc
}
// DefaultTwemojiTemplate is a default value for RendererConfig.TwemojiTemplate.
const DefaultTwemojiTemplate = `<img class="emoji" draggable="false" alt="%[1]s" src="https://twemoji.maxcdn.com/v/latest/72x72/%[2]s.png"%[3]s>`
// SetOption implements renderer.SetOptioner.
func (c *RendererConfig) SetOption(name renderer.OptionName, value interface{}) {
switch name {
case optRenderingMethod:
c.Method = value.(RenderingMethod)
case optTwemojiTemplate:
c.TwemojiTemplate = value.(string)
case optRendererFunc:
c.RendererFunc = value.(RendererFunc)
default:
c.Config.SetOption(name, value)
}
}
// A RendererOption interface sets options for the emoji renderer.
type RendererOption interface {
Option
renderer.Option
SetEmojiOption(*RendererConfig)
}
var _ RendererOption = &withRenderingMethod{}
type withRenderingMethod struct {
value RenderingMethod
}
func (o *withRenderingMethod) emojiOption() {
}
// SetConfig implements renderer.Option#SetConfig.
func (o *withRenderingMethod) SetConfig(c *renderer.Config) {
c.Options[optRenderingMethod] = o.value
}
// SetEmojiOption implements RendererOption#SetEmojiOption
func (o *withRenderingMethod) SetEmojiOption(c *RendererConfig) {
c.Method = o.value
}
const optRenderingMethod renderer.OptionName = "EmojiRenderingMethod"
// WithRenderingMethod is a functional option that indicates how emojis are rendered.
func WithRenderingMethod(a RenderingMethod) Option {
return &withRenderingMethod{a}
}
type withTwemojiTemplate struct {
value string
}
func (o *withTwemojiTemplate) emojiOption() {
}
// SetConfig implements renderer.Option#SetConfig.
func (o *withTwemojiTemplate) SetConfig(c *renderer.Config) {
c.Options[optTwemojiTemplate] = o.value
}
// SetEmojiOption implements RendererOption#SetEmojiOption
func (o *withTwemojiTemplate) SetEmojiOption(c *RendererConfig) {
c.TwemojiTemplate = o.value
}
const optTwemojiTemplate renderer.OptionName = "EmojiTwemojiTemplate"
// WithTwemojiTemplate is a functional option that changes a twemoji img tag.
func WithTwemojiTemplate(s string) Option {
return &withTwemojiTemplate{s}
}
var _ RendererOption = &withRendererFunc{}
type withRendererFunc struct {
value RendererFunc
}
func (o *withRendererFunc) emojiOption() {
}
// SetConfig implements renderer.Option#SetConfig.
func (o *withRendererFunc) SetConfig(c *renderer.Config) {
c.Options[optRendererFunc] = o.value
}
// SetEmojiOption implements RendererOption#SetEmojiOption
func (o *withRendererFunc) SetEmojiOption(c *RendererConfig) {
c.RendererFunc = o.value
}
const optRendererFunc renderer.OptionName = "EmojiRendererFunc"
// WithRendererFunc is a functional option that changes a renderer func.
func WithRendererFunc(f RendererFunc) Option {
return &withRendererFunc{f}
}
type emojiParser struct {
ParserConfig
}
// NewParser returns a new parser.InlineParser that can parse emoji expressions.
func NewParser(opts ...ParserOption) parser.InlineParser {
p := &emojiParser{
ParserConfig: ParserConfig{
Emojis: definition.Github(),
},
}
for _, o := range opts {
o.SetEmojiOption(&p.ParserConfig)
}
return p
}
func (s *emojiParser) Trigger() []byte {
return []byte{':'}
}
func (s *emojiParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node {
line, _ := block.PeekLine()
if len(line) < 1 {
return nil
}
i := 1
for ; i < len(line); i++ {
c := line[i]
if !(util.IsAlphaNumeric(c) || c == '_' || c == '-' || c == '+') {
break
}
}
if i >= len(line) || line[i] != ':' {
return nil
}
block.Advance(i + 1)
shortName := line[1:i]
emoji, ok := s.Emojis.Get(util.BytesToReadOnlyString(shortName))
if !ok {
return nil
}
return east.NewEmoji(shortName, emoji)
}
type emojiHTMLRenderer struct {
RendererConfig
}
// NewHTMLRenderer returns a new HTMLRenderer.
func NewHTMLRenderer(opts ...RendererOption) renderer.NodeRenderer {
r := &emojiHTMLRenderer{
RendererConfig: RendererConfig{
Config: html.NewConfig(),
Method: Entity,
TwemojiTemplate: DefaultTwemojiTemplate,
RendererFunc: nil,
},
}
for _, opt := range opts {
opt.SetEmojiOption(&r.RendererConfig)
}
return r
}
// RegisterFuncs implements renderer.NodeRenderer.RegisterFuncs.
func (r *emojiHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
reg.Register(east.KindEmoji, r.renderEmoji)
}
const slash = " /"
const empty = ""
func (r *emojiHTMLRenderer) renderEmoji(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
if !entering {
return ast.WalkContinue, nil
}
node := n.(*east.Emoji)
if !node.Value.IsUnicode() && r.Method != Func {
fmt.Fprintf(w, `<span title="%s">:%s:</span>`, util.EscapeHTML(util.StringToReadOnlyBytes(node.Value.Name)), node.ShortName)
return ast.WalkContinue, nil
}
switch r.Method {
case Entity:
for _, r := range node.Value.Unicode {
if r == 0x200D {
_, _ = w.WriteString("&zwj;")
continue
}
fmt.Fprintf(w, "&#x%x;", r)
}
case Unicode:
fmt.Fprintf(w, "%s", string(node.Value.Unicode))
case Twemoji:
s := slash
if !r.XHTML {
s = empty
}
values := []string{}
for _, r := range node.Value.Unicode {
values = append(values, fmt.Sprintf("%x", r))
}
fmt.Fprintf(w, r.TwemojiTemplate, util.EscapeHTML(util.StringToReadOnlyBytes(node.Value.Name)), strings.Join(values, "-"), s)
case Func:
r.RendererFunc(w, source, node, &r.RendererConfig)
}
return ast.WalkContinue, nil
}
type emoji struct {
options []Option
}
// Emoji is a goldmark.Extender implementation.
var Emoji = &emoji{
options: []Option{},
}
// New returns a new extension with given options.
func New(opts ...Option) goldmark.Extender {
return &emoji{
options: opts,
}
}
// Extend implements goldmark.Extender.
func (e *emoji) Extend(m goldmark.Markdown) {
pOpts := []ParserOption{}
rOpts := []RendererOption{}
for _, o := range e.options {
if po, ok := o.(ParserOption); ok {
pOpts = append(pOpts, po)
continue
}
if ro, ok := o.(RendererOption); ok {
rOpts = append(rOpts, ro)
}
}
m.Renderer().AddOptions(renderer.WithNodeRenderers(
util.Prioritized(NewHTMLRenderer(rOpts...), 200),
))
m.Parser().AddOptions(parser.WithInlineParsers(
util.Prioritized(NewParser(pOpts...), 999),
))
}

5
vendor/github.com/yuin/goldmark-emoji/go.mod generated vendored Normal file
View File

@ -0,0 +1,5 @@
module github.com/yuin/goldmark-emoji
go 1.15
require github.com/yuin/goldmark v1.2.1

2
vendor/github.com/yuin/goldmark-emoji/go.sum generated vendored Normal file
View File

@ -0,0 +1,2 @@
github.com/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=

View File

@ -1,7 +1,7 @@
goldmark
==========================================
[![http://godoc.org/github.com/yuin/goldmark](https://godoc.org/github.com/yuin/goldmark?status.svg)](http://godoc.org/github.com/yuin/goldmark)
[![https://pkg.go.dev/github.com/yuin/goldmark](https://pkg.go.dev/badge/github.com/yuin/goldmark.svg)](https://pkg.go.dev/github.com/yuin/goldmark)
[![https://github.com/yuin/goldmark/actions?query=workflow:test](https://github.com/yuin/goldmark/workflows/test/badge.svg?branch=master&event=push)](https://github.com/yuin/goldmark/actions?query=workflow:test)
[![https://coveralls.io/github/yuin/goldmark](https://coveralls.io/repos/github/yuin/goldmark/badge.svg?branch=master)](https://coveralls.io/github/yuin/goldmark)
[![https://goreportcard.com/report/github.com/yuin/goldmark](https://goreportcard.com/badge/github.com/yuin/goldmark)](https://goreportcard.com/report/github.com/yuin/goldmark)
@ -173,6 +173,7 @@ Parser and Renderer options
- This extension enables Table, Strikethrough, Linkify and TaskList.
- This extension does not filter tags defined in [6.11: Disallowed Raw HTML (extension)](https://github.github.com/gfm/#disallowed-raw-html-extension-).
If you need to filter HTML tags, see [Security](#security).
- If you need to parse github emojis, you can use [goldmark-emoji](https://github.com/yuin/goldmark-emoji) extension.
- `extension.DefinitionList`
- [PHP Markdown Extra: Definition lists](https://michelf.ca/projects/php-markdown/extra/#def-list)
- `extension.Footnote`
@ -286,6 +287,89 @@ markdown := goldmark.New(
)
```
### Footnotes extension
The Footnote extension implements [PHP Markdown Extra: Footnotes](https://michelf.ca/projects/php-markdown/extra/#footnotes).
This extension has some options:
| Functional option | Type | Description |
| ----------------- | ---- | ----------- |
| `extension.WithFootnoteIDPrefix` | `[]byte` | a prefix for the id attributes.|
| `extension.WithFootnoteIDPrefixFunction` | `func(gast.Node) []byte` | a function that determines the id attribute for given Node.|
| `extension.WithFootnoteLinkTitle` | `[]byte` | an optional title attribute for footnote links.|
| `extension.WithFootnoteBacklinkTitle` | `[]byte` | an optional title attribute for footnote backlinks. |
| `extension.WithFootnoteLinkClass` | `[]byte` | a class for footnote links. This defaults to `footnote-ref`. |
| `extension.WithFootnoteBacklinkClass` | `[]byte` | a class for footnote backlinks. This defaults to `footnote-backref`. |
| `extension.WithFootnoteBacklinkHTML` | `[]byte` | a class for footnote backlinks. This defaults to `&#x21a9;&#xfe0e;`. |
Some options can have special substitutions. Occurances of “^^” in the string will be replaced by the corresponding footnote number in the HTML output. Occurances of “%%” will be replaced by a number for the reference (footnotes can have multiple references).
`extension.WithFootnoteIDPrefix` and `extension.WithFootnoteIDPrefixFunction` are useful if you have multiple Markdown documents displayed inside one HTML document to avoid footnote ids to clash each other.
`extension.WithFootnoteIDPrefix` sets fixed id prefix, so you may write codes like the following:
```go
for _, path := range files {
source := readAll(path)
prefix := getPrefix(path)
markdown := goldmark.New(
goldmark.WithExtensions(
NewFootnote(
WithFootnoteIDPrefix([]byte(path)),
),
),
)
var b bytes.Buffer
err := markdown.Convert(source, &b)
if err != nil {
t.Error(err.Error())
}
}
```
`extension.WithFootnoteIDPrefixFunction` determines an id prefix by calling given function, so you may write codes like the following:
```go
markdown := goldmark.New(
goldmark.WithExtensions(
NewFootnote(
WithFootnoteIDPrefixFunction(func(n gast.Node) []byte {
v, ok := n.OwnerDocument().Meta()["footnote-prefix"]
if ok {
return util.StringToReadOnlyBytes(v.(string))
}
return nil
}),
),
),
)
for _, path := range files {
source := readAll(path)
var b bytes.Buffer
doc := markdown.Parser().Parse(text.NewReader(source))
doc.Meta()["footnote-prefix"] = getPrefix(path)
err := markdown.Renderer().Render(&b, source, doc)
}
```
You can use [goldmark-meta](https://github.com/yuin/goldmark-meta) to define a id prefix in the markdown document:
```markdown
---
title: document title
slug: article1
footnote-prefix: article1
---
# My article
```
Security
--------------------
By default, goldmark does not render raw HTML or potentially-dangerous URLs.
@ -336,6 +420,8 @@ Extensions
extension for the goldmark Markdown parser.
- [goldmark-highlighting](https://github.com/yuin/goldmark-highlighting): A syntax-highlighting extension
for the goldmark markdown parser.
- [goldmark-emoji](https://github.com/yuin/goldmark-emoji): An emoji
extension for the goldmark Markdown parser.
- [goldmark-mathjax](https://github.com/litao91/goldmark-mathjax): Mathjax support for the goldmark markdown parser
goldmark internal(for extension developers)

View File

@ -45,11 +45,6 @@ type Attribute struct {
Value interface{}
}
var attrNameIDS = []byte("#")
var attrNameID = []byte("id")
var attrNameClassS = []byte(".")
var attrNameClass = []byte("class")
// A Node interface defines basic AST node functionalities.
type Node interface {
// Type returns a type of this node.
@ -116,6 +111,11 @@ type Node interface {
// tail of the children.
InsertAfter(self, v1, insertee Node)
// OwnerDocument returns this node's owner document.
// If this node is not a child of the Document node, OwnerDocument
// returns nil.
OwnerDocument() *Document
// Dump dumps an AST tree structure to stdout.
// This function completely aimed for debugging.
// level is a indent level. Implementer should indent informations with
@ -169,7 +169,7 @@ type Node interface {
RemoveAttributes()
}
// A BaseNode struct implements the Node interface.
// A BaseNode struct implements the Node interface partialliy.
type BaseNode struct {
firstChild Node
lastChild Node
@ -358,6 +358,22 @@ func (n *BaseNode) InsertBefore(self, v1, insertee Node) {
}
}
// OwnerDocument implements Node.OwnerDocument
func (n *BaseNode) OwnerDocument() *Document {
d := n.Parent()
for {
p := d.Parent()
if p == nil {
if v, ok := d.(*Document); ok {
return v
}
break
}
d = p
}
return nil
}
// Text implements Node.Text .
func (n *BaseNode) Text(source []byte) []byte {
var buf bytes.Buffer

View File

@ -7,7 +7,7 @@ import (
textm "github.com/yuin/goldmark/text"
)
// A BaseBlock struct implements the Node interface.
// A BaseBlock struct implements the Node interface partialliy.
type BaseBlock struct {
BaseNode
blankPreviousLines bool
@ -50,6 +50,8 @@ func (b *BaseBlock) SetLines(v *textm.Segments) {
// A Document struct is a root node of Markdown text.
type Document struct {
BaseBlock
meta map[string]interface{}
}
// KindDocument is a NodeKind of the Document node.
@ -70,10 +72,29 @@ func (n *Document) Kind() NodeKind {
return KindDocument
}
// OwnerDocument implements Node.OwnerDocument
func (n *Document) OwnerDocument() *Document {
return n
}
// Meta returns metadata of this document.
func (n *Document) Meta() map[string]interface{} {
if n.meta == nil {
n.meta = map[string]interface{}{}
}
return n.meta
}
// SetMeta sets given metadata to this document.
func (n *Document) SetMeta(meta map[string]interface{}) {
n.meta = meta
}
// NewDocument returns a new Document node.
func NewDocument() *Document {
return &Document{
BaseBlock: BaseBlock{},
meta: nil,
}
}

View File

@ -8,7 +8,7 @@ import (
"github.com/yuin/goldmark/util"
)
// A BaseInline struct implements the Node interface.
// A BaseInline struct implements the Node interface partialliy.
type BaseInline struct {
BaseNode
}

View File

@ -2,6 +2,7 @@ package ast
import (
"fmt"
gast "github.com/yuin/goldmark/ast"
)
@ -9,13 +10,15 @@ import (
// (PHP Markdown Extra) text.
type FootnoteLink struct {
gast.BaseInline
Index int
Index int
RefCount int
}
// Dump implements Node.Dump.
func (n *FootnoteLink) Dump(source []byte, level int) {
m := map[string]string{}
m["Index"] = fmt.Sprintf("%v", n.Index)
m["RefCount"] = fmt.Sprintf("%v", n.RefCount)
gast.DumpHelper(n, source, level, m, nil)
}
@ -30,36 +33,40 @@ func (n *FootnoteLink) Kind() gast.NodeKind {
// NewFootnoteLink returns a new FootnoteLink node.
func NewFootnoteLink(index int) *FootnoteLink {
return &FootnoteLink{
Index: index,
Index: index,
RefCount: 0,
}
}
// A FootnoteBackLink struct represents a link to a footnote of Markdown
// A FootnoteBacklink struct represents a link to a footnote of Markdown
// (PHP Markdown Extra) text.
type FootnoteBackLink struct {
type FootnoteBacklink struct {
gast.BaseInline
Index int
Index int
RefCount int
}
// Dump implements Node.Dump.
func (n *FootnoteBackLink) Dump(source []byte, level int) {
func (n *FootnoteBacklink) Dump(source []byte, level int) {
m := map[string]string{}
m["Index"] = fmt.Sprintf("%v", n.Index)
m["RefCount"] = fmt.Sprintf("%v", n.RefCount)
gast.DumpHelper(n, source, level, m, nil)
}
// KindFootnoteBackLink is a NodeKind of the FootnoteBackLink node.
var KindFootnoteBackLink = gast.NewNodeKind("FootnoteBackLink")
// KindFootnoteBacklink is a NodeKind of the FootnoteBacklink node.
var KindFootnoteBacklink = gast.NewNodeKind("FootnoteBacklink")
// Kind implements Node.Kind.
func (n *FootnoteBackLink) Kind() gast.NodeKind {
return KindFootnoteBackLink
func (n *FootnoteBacklink) Kind() gast.NodeKind {
return KindFootnoteBacklink
}
// NewFootnoteBackLink returns a new FootnoteBackLink node.
func NewFootnoteBackLink(index int) *FootnoteBackLink {
return &FootnoteBackLink{
Index: index,
// NewFootnoteBacklink returns a new FootnoteBacklink node.
func NewFootnoteBacklink(index int) *FootnoteBacklink {
return &FootnoteBacklink{
Index: index,
RefCount: 0,
}
}

View File

@ -2,6 +2,8 @@ package extension
import (
"bytes"
"strconv"
"github.com/yuin/goldmark"
gast "github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/extension/ast"
@ -10,10 +12,10 @@ import (
"github.com/yuin/goldmark/renderer/html"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
"strconv"
)
var footnoteListKey = parser.NewContextKey()
var footnoteLinkListKey = parser.NewContextKey()
type footnoteBlockParser struct {
}
@ -164,7 +166,20 @@ func (s *footnoteParser) Parse(parent gast.Node, block text.Reader, pc parser.Co
return nil
}
return ast.NewFootnoteLink(index)
fnlink := ast.NewFootnoteLink(index)
var fnlist []*ast.FootnoteLink
if tmp := pc.Get(footnoteLinkListKey); tmp != nil {
fnlist = tmp.([]*ast.FootnoteLink)
} else {
fnlist = []*ast.FootnoteLink{}
pc.Set(footnoteLinkListKey, fnlist)
}
pc.Set(footnoteLinkListKey, append(fnlist, fnlink))
if line[0] == '!' {
parent.AppendChild(parent, gast.NewTextSegment(text.NewSegment(segment.Start, segment.Start+1)))
}
return fnlink
}
type footnoteASTTransformer struct {
@ -180,23 +195,46 @@ func NewFootnoteASTTransformer() parser.ASTTransformer {
func (a *footnoteASTTransformer) Transform(node *gast.Document, reader text.Reader, pc parser.Context) {
var list *ast.FootnoteList
if tlist := pc.Get(footnoteListKey); tlist != nil {
list = tlist.(*ast.FootnoteList)
} else {
var fnlist []*ast.FootnoteLink
if tmp := pc.Get(footnoteListKey); tmp != nil {
list = tmp.(*ast.FootnoteList)
}
if tmp := pc.Get(footnoteLinkListKey); tmp != nil {
fnlist = tmp.([]*ast.FootnoteLink)
}
pc.Set(footnoteListKey, nil)
pc.Set(footnoteLinkListKey, nil)
if list == nil {
return
}
pc.Set(footnoteListKey, nil)
counter := map[int]int{}
if fnlist != nil {
for _, fnlink := range fnlist {
if fnlink.Index >= 0 {
counter[fnlink.Index]++
}
}
for _, fnlink := range fnlist {
fnlink.RefCount = counter[fnlink.Index]
}
}
for footnote := list.FirstChild(); footnote != nil; {
var container gast.Node = footnote
next := footnote.NextSibling()
if fc := container.LastChild(); fc != nil && gast.IsParagraph(fc) {
container = fc
}
index := footnote.(*ast.Footnote).Index
fn := footnote.(*ast.Footnote)
index := fn.Index
if index < 0 {
list.RemoveChild(list, footnote)
} else {
container.AppendChild(container, ast.NewFootnoteBackLink(index))
backLink := ast.NewFootnoteBacklink(index)
backLink.RefCount = counter[index]
container.AppendChild(container, backLink)
}
footnote = next
}
@ -214,19 +252,250 @@ func (a *footnoteASTTransformer) Transform(node *gast.Document, reader text.Read
node.AppendChild(node, list)
}
// FootnoteConfig holds configuration values for the footnote extension.
//
// Link* and Backlink* configurations have some variables:
// Occurrances of “^^” in the string will be replaced by the
// corresponding footnote number in the HTML output.
// Occurrances of “%%” will be replaced by a number for the
// reference (footnotes can have multiple references).
type FootnoteConfig struct {
html.Config
// IDPrefix is a prefix for the id attributes generated by footnotes.
IDPrefix []byte
// IDPrefix is a function that determines the id attribute for given Node.
IDPrefixFunction func(gast.Node) []byte
// LinkTitle is an optional title attribute for footnote links.
LinkTitle []byte
// BacklinkTitle is an optional title attribute for footnote backlinks.
BacklinkTitle []byte
// LinkClass is a class for footnote links.
LinkClass []byte
// BacklinkClass is a class for footnote backlinks.
BacklinkClass []byte
// BacklinkHTML is an HTML content for footnote backlinks.
BacklinkHTML []byte
}
// FootnoteOption interface is a functional option interface for the extension.
type FootnoteOption interface {
renderer.Option
// SetFootnoteOption sets given option to the extension.
SetFootnoteOption(*FootnoteConfig)
}
// NewFootnoteConfig returns a new Config with defaults.
func NewFootnoteConfig() FootnoteConfig {
return FootnoteConfig{
Config: html.NewConfig(),
LinkTitle: []byte(""),
BacklinkTitle: []byte(""),
LinkClass: []byte("footnote-ref"),
BacklinkClass: []byte("footnote-backref"),
BacklinkHTML: []byte("&#x21a9;&#xfe0e;"),
}
}
// SetOption implements renderer.SetOptioner.
func (c *FootnoteConfig) SetOption(name renderer.OptionName, value interface{}) {
switch name {
case optFootnoteIDPrefixFunction:
c.IDPrefixFunction = value.(func(gast.Node) []byte)
case optFootnoteIDPrefix:
c.IDPrefix = value.([]byte)
case optFootnoteLinkTitle:
c.LinkTitle = value.([]byte)
case optFootnoteBacklinkTitle:
c.BacklinkTitle = value.([]byte)
case optFootnoteLinkClass:
c.LinkClass = value.([]byte)
case optFootnoteBacklinkClass:
c.BacklinkClass = value.([]byte)
case optFootnoteBacklinkHTML:
c.BacklinkHTML = value.([]byte)
default:
c.Config.SetOption(name, value)
}
}
type withFootnoteHTMLOptions struct {
value []html.Option
}
func (o *withFootnoteHTMLOptions) SetConfig(c *renderer.Config) {
if o.value != nil {
for _, v := range o.value {
v.(renderer.Option).SetConfig(c)
}
}
}
func (o *withFootnoteHTMLOptions) SetFootnoteOption(c *FootnoteConfig) {
if o.value != nil {
for _, v := range o.value {
v.SetHTMLOption(&c.Config)
}
}
}
// WithFootnoteHTMLOptions is functional option that wraps goldmark HTMLRenderer options.
func WithFootnoteHTMLOptions(opts ...html.Option) FootnoteOption {
return &withFootnoteHTMLOptions{opts}
}
const optFootnoteIDPrefix renderer.OptionName = "FootnoteIDPrefix"
type withFootnoteIDPrefix struct {
value []byte
}
func (o *withFootnoteIDPrefix) SetConfig(c *renderer.Config) {
c.Options[optFootnoteIDPrefix] = o.value
}
func (o *withFootnoteIDPrefix) SetFootnoteOption(c *FootnoteConfig) {
c.IDPrefix = o.value
}
// WithFootnoteIDPrefix is a functional option that is a prefix for the id attributes generated by footnotes.
func WithFootnoteIDPrefix(a []byte) FootnoteOption {
return &withFootnoteIDPrefix{a}
}
const optFootnoteIDPrefixFunction renderer.OptionName = "FootnoteIDPrefixFunction"
type withFootnoteIDPrefixFunction struct {
value func(gast.Node) []byte
}
func (o *withFootnoteIDPrefixFunction) SetConfig(c *renderer.Config) {
c.Options[optFootnoteIDPrefixFunction] = o.value
}
func (o *withFootnoteIDPrefixFunction) SetFootnoteOption(c *FootnoteConfig) {
c.IDPrefixFunction = o.value
}
// WithFootnoteIDPrefixFunction is a functional option that is a prefix for the id attributes generated by footnotes.
func WithFootnoteIDPrefixFunction(a func(gast.Node) []byte) FootnoteOption {
return &withFootnoteIDPrefixFunction{a}
}
const optFootnoteLinkTitle renderer.OptionName = "FootnoteLinkTitle"
type withFootnoteLinkTitle struct {
value []byte
}
func (o *withFootnoteLinkTitle) SetConfig(c *renderer.Config) {
c.Options[optFootnoteLinkTitle] = o.value
}
func (o *withFootnoteLinkTitle) SetFootnoteOption(c *FootnoteConfig) {
c.LinkTitle = o.value
}
// WithFootnoteLinkTitle is a functional option that is an optional title attribute for footnote links.
func WithFootnoteLinkTitle(a []byte) FootnoteOption {
return &withFootnoteLinkTitle{a}
}
const optFootnoteBacklinkTitle renderer.OptionName = "FootnoteBacklinkTitle"
type withFootnoteBacklinkTitle struct {
value []byte
}
func (o *withFootnoteBacklinkTitle) SetConfig(c *renderer.Config) {
c.Options[optFootnoteBacklinkTitle] = o.value
}
func (o *withFootnoteBacklinkTitle) SetFootnoteOption(c *FootnoteConfig) {
c.BacklinkTitle = o.value
}
// WithFootnoteBacklinkTitle is a functional option that is an optional title attribute for footnote backlinks.
func WithFootnoteBacklinkTitle(a []byte) FootnoteOption {
return &withFootnoteBacklinkTitle{a}
}
const optFootnoteLinkClass renderer.OptionName = "FootnoteLinkClass"
type withFootnoteLinkClass struct {
value []byte
}
func (o *withFootnoteLinkClass) SetConfig(c *renderer.Config) {
c.Options[optFootnoteLinkClass] = o.value
}
func (o *withFootnoteLinkClass) SetFootnoteOption(c *FootnoteConfig) {
c.LinkClass = o.value
}
// WithFootnoteLinkClass is a functional option that is a class for footnote links.
func WithFootnoteLinkClass(a []byte) FootnoteOption {
return &withFootnoteLinkClass{a}
}
const optFootnoteBacklinkClass renderer.OptionName = "FootnoteBacklinkClass"
type withFootnoteBacklinkClass struct {
value []byte
}
func (o *withFootnoteBacklinkClass) SetConfig(c *renderer.Config) {
c.Options[optFootnoteBacklinkClass] = o.value
}
func (o *withFootnoteBacklinkClass) SetFootnoteOption(c *FootnoteConfig) {
c.BacklinkClass = o.value
}
// WithFootnoteBacklinkClass is a functional option that is a class for footnote backlinks.
func WithFootnoteBacklinkClass(a []byte) FootnoteOption {
return &withFootnoteBacklinkClass{a}
}
const optFootnoteBacklinkHTML renderer.OptionName = "FootnoteBacklinkHTML"
type withFootnoteBacklinkHTML struct {
value []byte
}
func (o *withFootnoteBacklinkHTML) SetConfig(c *renderer.Config) {
c.Options[optFootnoteBacklinkHTML] = o.value
}
func (o *withFootnoteBacklinkHTML) SetFootnoteOption(c *FootnoteConfig) {
c.BacklinkHTML = o.value
}
// WithFootnoteBacklinkHTML is an HTML content for footnote backlinks.
func WithFootnoteBacklinkHTML(a []byte) FootnoteOption {
return &withFootnoteBacklinkHTML{a}
}
// FootnoteHTMLRenderer is a renderer.NodeRenderer implementation that
// renders FootnoteLink nodes.
type FootnoteHTMLRenderer struct {
html.Config
FootnoteConfig
}
// NewFootnoteHTMLRenderer returns a new FootnoteHTMLRenderer.
func NewFootnoteHTMLRenderer(opts ...html.Option) renderer.NodeRenderer {
func NewFootnoteHTMLRenderer(opts ...FootnoteOption) renderer.NodeRenderer {
r := &FootnoteHTMLRenderer{
Config: html.NewConfig(),
FootnoteConfig: NewFootnoteConfig(),
}
for _, opt := range opts {
opt.SetHTMLOption(&r.Config)
opt.SetFootnoteOption(&r.FootnoteConfig)
}
return r
}
@ -234,7 +503,7 @@ func NewFootnoteHTMLRenderer(opts ...html.Option) renderer.NodeRenderer {
// RegisterFuncs implements renderer.NodeRenderer.RegisterFuncs.
func (r *FootnoteHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
reg.Register(ast.KindFootnoteLink, r.renderFootnoteLink)
reg.Register(ast.KindFootnoteBackLink, r.renderFootnoteBackLink)
reg.Register(ast.KindFootnoteBacklink, r.renderFootnoteBacklink)
reg.Register(ast.KindFootnote, r.renderFootnote)
reg.Register(ast.KindFootnoteList, r.renderFootnoteList)
}
@ -243,25 +512,45 @@ func (r *FootnoteHTMLRenderer) renderFootnoteLink(w util.BufWriter, source []byt
if entering {
n := node.(*ast.FootnoteLink)
is := strconv.Itoa(n.Index)
_, _ = w.WriteString(`<sup id="fnref:`)
_, _ = w.WriteString(`<sup id="`)
_, _ = w.Write(r.idPrefix(node))
_, _ = w.WriteString(`fnref:`)
_, _ = w.WriteString(is)
_, _ = w.WriteString(`"><a href="#fn:`)
_, _ = w.WriteString(`"><a href="#`)
_, _ = w.Write(r.idPrefix(node))
_, _ = w.WriteString(`fn:`)
_, _ = w.WriteString(is)
_, _ = w.WriteString(`" class="footnote-ref" role="doc-noteref">`)
_, _ = w.WriteString(`" class="`)
_, _ = w.Write(applyFootnoteTemplate(r.FootnoteConfig.LinkClass,
n.Index, n.RefCount))
if len(r.FootnoteConfig.LinkTitle) > 0 {
_, _ = w.WriteString(`" title="`)
_, _ = w.Write(util.EscapeHTML(applyFootnoteTemplate(r.FootnoteConfig.LinkTitle, n.Index, n.RefCount)))
}
_, _ = w.WriteString(`" role="doc-noteref">`)
_, _ = w.WriteString(is)
_, _ = w.WriteString(`</a></sup>`)
}
return gast.WalkContinue, nil
}
func (r *FootnoteHTMLRenderer) renderFootnoteBackLink(w util.BufWriter, source []byte, node gast.Node, entering bool) (gast.WalkStatus, error) {
func (r *FootnoteHTMLRenderer) renderFootnoteBacklink(w util.BufWriter, source []byte, node gast.Node, entering bool) (gast.WalkStatus, error) {
if entering {
n := node.(*ast.FootnoteBackLink)
n := node.(*ast.FootnoteBacklink)
is := strconv.Itoa(n.Index)
_, _ = w.WriteString(` <a href="#fnref:`)
_, _ = w.WriteString(` <a href="#`)
_, _ = w.Write(r.idPrefix(node))
_, _ = w.WriteString(`fnref:`)
_, _ = w.WriteString(is)
_, _ = w.WriteString(`" class="footnote-backref" role="doc-backlink">`)
_, _ = w.WriteString("&#x21a9;&#xfe0e;")
_, _ = w.WriteString(`" class="`)
_, _ = w.Write(applyFootnoteTemplate(r.FootnoteConfig.BacklinkClass, n.Index, n.RefCount))
if len(r.FootnoteConfig.BacklinkTitle) > 0 {
_, _ = w.WriteString(`" title="`)
_, _ = w.Write(util.EscapeHTML(applyFootnoteTemplate(r.FootnoteConfig.BacklinkTitle, n.Index, n.RefCount)))
}
_, _ = w.WriteString(`" role="doc-backlink">`)
_, _ = w.Write(applyFootnoteTemplate(r.FootnoteConfig.BacklinkHTML, n.Index, n.RefCount))
_, _ = w.WriteString(`</a>`)
}
return gast.WalkContinue, nil
@ -271,7 +560,9 @@ func (r *FootnoteHTMLRenderer) renderFootnote(w util.BufWriter, source []byte, n
n := node.(*ast.Footnote)
is := strconv.Itoa(n.Index)
if entering {
_, _ = w.WriteString(`<li id="fn:`)
_, _ = w.WriteString(`<li id="`)
_, _ = w.Write(r.idPrefix(node))
_, _ = w.WriteString(`fn:`)
_, _ = w.WriteString(is)
_, _ = w.WriteString(`" role="doc-endnote"`)
if node.Attributes() != nil {
@ -312,11 +603,54 @@ func (r *FootnoteHTMLRenderer) renderFootnoteList(w util.BufWriter, source []byt
return gast.WalkContinue, nil
}
func (r *FootnoteHTMLRenderer) idPrefix(node gast.Node) []byte {
if r.FootnoteConfig.IDPrefix != nil {
return r.FootnoteConfig.IDPrefix
}
if r.FootnoteConfig.IDPrefixFunction != nil {
return r.FootnoteConfig.IDPrefixFunction(node)
}
return []byte("")
}
func applyFootnoteTemplate(b []byte, index, refCount int) []byte {
fast := true
for i, c := range b {
if i != 0 {
if b[i-1] == '^' && c == '^' {
fast = false
break
}
if b[i-1] == '%' && c == '%' {
fast = false
break
}
}
}
if fast {
return b
}
is := []byte(strconv.Itoa(index))
rs := []byte(strconv.Itoa(refCount))
ret := bytes.Replace(b, []byte("^^"), is, -1)
return bytes.Replace(ret, []byte("%%"), rs, -1)
}
type footnote struct {
options []FootnoteOption
}
// Footnote is an extension that allow you to use PHP Markdown Extra Footnotes.
var Footnote = &footnote{}
var Footnote = &footnote{
options: []FootnoteOption{},
}
// NewFootnote returns a new extension with given options.
func NewFootnote(opts ...FootnoteOption) goldmark.Extender {
return &footnote{
options: opts,
}
}
func (e *footnote) Extend(m goldmark.Markdown) {
m.Parser().AddOptions(
@ -331,6 +665,6 @@ func (e *footnote) Extend(m goldmark.Markdown) {
),
)
m.Renderer().AddOptions(renderer.WithNodeRenderers(
util.Prioritized(NewFootnoteHTMLRenderer(), 500),
util.Prioritized(NewFootnoteHTMLRenderer(e.options...), 500),
))
}

View File

@ -11,9 +11,9 @@ import (
"github.com/yuin/goldmark/util"
)
var wwwURLRegxp = regexp.MustCompile(`^www\.[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]+(?:(?:/|[#?])[-a-zA-Z0-9@:%_\+.~#!?&//=\(\);,'">\^{}\[\]` + "`" + `]*)?`)
var wwwURLRegxp = regexp.MustCompile(`^www\.[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]+(?:[/#?][-a-zA-Z0-9@:%_\+.~#!?&/=\(\);,'">\^{}\[\]` + "`" + `]*)?`)
var urlRegexp = regexp.MustCompile(`^(?:http|https|ftp):\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]+(?:(?:/|[#?])[-a-zA-Z0-9@:%_+.~#$!?&//=\(\);,'">\^{}\[\]` + "`" + `]*)?`)
var urlRegexp = regexp.MustCompile(`^(?:http|https|ftp)://[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]+(?::\d+)?(?:[/#?][-a-zA-Z0-9@:%_+.~#$!?&/=\(\);,'">\^{}\[\]` + "`" + `]*)?`)
// An LinkifyConfig struct is a data structure that holds configuration of the
// Linkify extension.

View File

@ -15,6 +15,13 @@ import (
"github.com/yuin/goldmark/util"
)
var escapedPipeCellListKey = parser.NewContextKey()
type escapedPipeCell struct {
Cell *ast.TableCell
Pos []int
}
// TableCellAlignMethod indicates how are table cells aligned in HTML format.indicates how are table cells aligned in HTML format.
type TableCellAlignMethod int
@ -148,7 +155,7 @@ func (b *tableParagraphTransformer) Transform(node *gast.Paragraph, reader text.
if alignments == nil {
continue
}
header := b.parseRow(lines.At(i-1), alignments, true, reader)
header := b.parseRow(lines.At(i-1), alignments, true, reader, pc)
if header == nil || len(alignments) != header.ChildCount() {
return
}
@ -156,7 +163,7 @@ func (b *tableParagraphTransformer) Transform(node *gast.Paragraph, reader text.
table.Alignments = alignments
table.AppendChild(table, ast.NewTableHeader(header))
for j := i + 1; j < lines.Len(); j++ {
table.AppendChild(table, b.parseRow(lines.At(j), alignments, false, reader))
table.AppendChild(table, b.parseRow(lines.At(j), alignments, false, reader, pc))
}
node.Lines().SetSliced(0, i-1)
node.Parent().InsertAfter(node.Parent(), node, table)
@ -170,7 +177,7 @@ func (b *tableParagraphTransformer) Transform(node *gast.Paragraph, reader text.
}
}
func (b *tableParagraphTransformer) parseRow(segment text.Segment, alignments []ast.Alignment, isHeader bool, reader text.Reader) *ast.TableRow {
func (b *tableParagraphTransformer) parseRow(segment text.Segment, alignments []ast.Alignment, isHeader bool, reader text.Reader, pc parser.Context) *ast.TableRow {
source := reader.Source()
line := segment.Value(source)
pos := 0
@ -194,18 +201,39 @@ func (b *tableParagraphTransformer) parseRow(segment text.Segment, alignments []
} else {
alignment = alignments[i]
}
closure := util.FindClosure(line[pos:], byte(0), '|', true, false)
if closure < 0 {
closure = len(line[pos:])
}
var escapedCell *escapedPipeCell
node := ast.NewTableCell()
seg := text.NewSegment(segment.Start+pos, segment.Start+pos+closure)
node.Alignment = alignment
hasBacktick := false
closure := pos
for ; closure < limit; closure++ {
if line[closure] == '`' {
hasBacktick = true
}
if line[closure] == '|' {
if closure == 0 || line[closure-1] != '\\' {
break
} else if hasBacktick {
if escapedCell == nil {
escapedCell = &escapedPipeCell{node, []int{}}
escapedList := pc.ComputeIfAbsent(escapedPipeCellListKey,
func() interface{} {
return []*escapedPipeCell{}
}).([]*escapedPipeCell)
escapedList = append(escapedList, escapedCell)
pc.Set(escapedPipeCellListKey, escapedList)
}
escapedCell.Pos = append(escapedCell.Pos, segment.Start+closure-1)
}
}
}
seg := text.NewSegment(segment.Start+pos, segment.Start+closure)
seg = seg.TrimLeftSpace(source)
seg = seg.TrimRightSpace(source)
node.Lines().Append(seg)
node.Alignment = alignment
row.AppendChild(row, node)
pos += closure + 1
pos = closure + 1
}
for ; i < len(alignments); i++ {
row.AppendChild(row, ast.NewTableCell())
@ -243,6 +271,49 @@ func (b *tableParagraphTransformer) parseDelimiter(segment text.Segment, reader
return alignments
}
type tableASTTransformer struct {
}
var defaultTableASTTransformer = &tableASTTransformer{}
// NewTableASTTransformer returns a parser.ASTTransformer for tables.
func NewTableASTTransformer() parser.ASTTransformer {
return defaultTableASTTransformer
}
func (a *tableASTTransformer) Transform(node *gast.Document, reader text.Reader, pc parser.Context) {
lst := pc.Get(escapedPipeCellListKey)
if lst == nil {
return
}
pc.Set(escapedPipeCellListKey, nil)
for _, v := range lst.([]*escapedPipeCell) {
_ = gast.Walk(v.Cell, func(n gast.Node, entering bool) (gast.WalkStatus, error) {
if n.Kind() != gast.KindCodeSpan {
return gast.WalkContinue, nil
}
c := n.FirstChild()
for c != nil {
next := c.NextSibling()
if c.Kind() == gast.KindText {
t := c.(*gast.Text)
for _, pos := range v.Pos {
if t.Segment.Start <= pos && t.Segment.Stop > pos {
n1 := gast.NewRawTextSegment(t.Segment.WithStop(pos))
n2 := gast.NewRawTextSegment(t.Segment.WithStart(pos + 1))
n.InsertAfter(n, c, n1)
n.InsertAfter(n, n1, n2)
n.RemoveChild(n, c)
}
}
}
c = next
}
return gast.WalkContinue, nil
})
}
}
// TableHTMLRenderer is a renderer.NodeRenderer implementation that
// renders Table nodes.
type TableHTMLRenderer struct {
@ -419,7 +490,7 @@ func (r *TableHTMLRenderer) renderTableCell(w util.BufWriter, source []byte, nod
cob.AppendByte(';')
}
style := fmt.Sprintf("text-align:%s", n.Alignment.String())
cob.Append(util.StringToReadOnlyBytes(style))
cob.AppendString(style)
n.SetAttributeString("style", cob.Bytes())
}
}
@ -454,9 +525,14 @@ func NewTable(opts ...TableOption) goldmark.Extender {
}
func (e *table) Extend(m goldmark.Markdown) {
m.Parser().AddOptions(parser.WithParagraphTransformers(
util.Prioritized(NewTableParagraphTransformer(), 200),
))
m.Parser().AddOptions(
parser.WithParagraphTransformers(
util.Prioritized(NewTableParagraphTransformer(), 200),
),
parser.WithASTTransformers(
util.Prioritized(defaultTableASTTransformer, 0),
),
)
m.Renderer().AddOptions(renderer.WithNodeRenderers(
util.Prioritized(NewTableHTMLRenderer(e.options...), 500),
))

View File

@ -1,3 +1,3 @@
module github.com/yuin/goldmark
go 1.13
go 1.15

View File

@ -2,7 +2,6 @@ package parser
import (
"fmt"
"regexp"
"strings"
"github.com/yuin/goldmark/ast"
@ -113,8 +112,6 @@ func (s *linkParser) Trigger() []byte {
return []byte{'!', '[', ']'}
}
var linkDestinationRegexp = regexp.MustCompile(`\s*([^\s].+)`)
var linkTitleRegexp = regexp.MustCompile(`\s+(\)|["'\(].+)`)
var linkBottom = NewContextKey()
func (s *linkParser) Parse(parent ast.Node, block text.Reader, pc Context) ast.Node {
@ -293,20 +290,17 @@ func (s *linkParser) parseLink(parent ast.Node, last *linkLabelState, block text
func parseLinkDestination(block text.Reader) ([]byte, bool) {
block.SkipSpaces()
line, _ := block.PeekLine()
buf := []byte{}
if block.Peek() == '<' {
i := 1
for i < len(line) {
c := line[i]
if c == '\\' && i < len(line)-1 && util.IsPunct(line[i+1]) {
buf = append(buf, '\\', line[i+1])
i += 2
continue
} else if c == '>' {
block.Advance(i + 1)
return line[1:i], true
}
buf = append(buf, c)
i++
}
return nil, false
@ -316,7 +310,6 @@ func parseLinkDestination(block text.Reader) ([]byte, bool) {
for i < len(line) {
c := line[i]
if c == '\\' && i < len(line)-1 && util.IsPunct(line[i+1]) {
buf = append(buf, '\\', line[i+1])
i += 2
continue
} else if c == '(' {
@ -329,7 +322,6 @@ func parseLinkDestination(block text.Reader) ([]byte, bool) {
} else if util.IsSpace(c) {
break
}
buf = append(buf, c)
i++
}
block.Advance(i)

View File

@ -138,6 +138,9 @@ type Context interface {
// Get returns a value associated with the given key.
Get(ContextKey) interface{}
// ComputeIfAbsent computes a value if a value associated with the given key is absent and returns the value.
ComputeIfAbsent(ContextKey, func() interface{}) interface{}
// Set sets the given value to the context.
Set(ContextKey, interface{})
@ -252,6 +255,15 @@ func (p *parseContext) Get(key ContextKey) interface{} {
return p.store[key]
}
func (p *parseContext) ComputeIfAbsent(key ContextKey, f func() interface{}) interface{} {
v := p.store[key]
if v == nil {
v = f()
p.store[key] = v
}
return v
}
func (p *parseContext) Set(key ContextKey, value interface{}) {
p.store[key] = value
}

View File

@ -2,10 +2,11 @@ package parser
import (
"bytes"
"regexp"
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
"regexp"
)
type rawHTMLParser struct {
@ -67,8 +68,6 @@ func (s *rawHTMLParser) parseSingleLineRegexp(reg *regexp.Regexp, block text.Rea
return node
}
var dummyMatch = [][]byte{}
func (s *rawHTMLParser) parseMultiLineRegexp(reg *regexp.Regexp, block text.Reader, pc Context) ast.Node {
sline, ssegment := block.Position()
if block.Match(reg) {
@ -102,7 +101,3 @@ func (s *rawHTMLParser) parseMultiLineRegexp(reg *regexp.Regexp, block text.Read
}
return nil
}
func (s *rawHTMLParser) CloseBlock(parent ast.Node, pc Context) {
// nothing to do
}

View File

@ -37,6 +37,12 @@ func (b *CopyOnWriteBuffer) Write(value []byte) {
b.buffer = append(b.buffer, value...)
}
// WriteString writes given string to the buffer.
// WriteString allocate new buffer and clears it at the first time.
func (b *CopyOnWriteBuffer) WriteString(value string) {
b.Write(StringToReadOnlyBytes(value))
}
// Append appends given bytes to the buffer.
// Append copy buffer at the first time.
func (b *CopyOnWriteBuffer) Append(value []byte) {
@ -49,6 +55,12 @@ func (b *CopyOnWriteBuffer) Append(value []byte) {
b.buffer = append(b.buffer, value...)
}
// AppendString appends given string to the buffer.
// AppendString copy buffer at the first time.
func (b *CopyOnWriteBuffer) AppendString(value string) {
b.Append(StringToReadOnlyBytes(value))
}
// WriteByte writes the given byte to the buffer.
// WriteByte allocate new buffer and clears it at the first time.
func (b *CopyOnWriteBuffer) WriteByte(c byte) {
@ -804,7 +816,7 @@ func IsPunct(c byte) bool {
return punctTable[c] == 1
}
// IsPunct returns true if the given rune is a punctuation, otherwise false.
// IsPunctRune returns true if the given rune is a punctuation, otherwise false.
func IsPunctRune(r rune) bool {
return int32(r) <= 256 && IsPunct(byte(r)) || unicode.IsPunct(r)
}
@ -814,7 +826,7 @@ func IsSpace(c byte) bool {
return spaceTable[c] == 1
}
// IsSpace returns true if the given rune is a space, otherwise false.
// IsSpaceRune returns true if the given rune is a space, otherwise false.
func IsSpaceRune(r rune) bool {
return int32(r) <= 256 && IsSpace(byte(r)) || unicode.IsSpace(r)
}

21
vendor/modules.txt vendored
View File

@ -15,7 +15,7 @@ github.com/Microsoft/go-winio
github.com/Microsoft/go-winio/pkg/guid
# github.com/adrg/xdg v0.3.1
github.com/adrg/xdg
# github.com/alecthomas/chroma v0.7.3
# github.com/alecthomas/chroma v0.8.1
github.com/alecthomas/chroma
github.com/alecthomas/chroma/formatters
github.com/alecthomas/chroma/formatters/html
@ -47,13 +47,18 @@ github.com/alecthomas/chroma/lexers/v
github.com/alecthomas/chroma/lexers/w
github.com/alecthomas/chroma/lexers/x
github.com/alecthomas/chroma/lexers/y
github.com/alecthomas/chroma/lexers/z
github.com/alecthomas/chroma/quick
github.com/alecthomas/chroma/styles
# github.com/araddon/dateparse v0.0.0-20210207001429-0eec95c9db7e
github.com/araddon/dateparse
# github.com/charmbracelet/glamour v0.2.0
# github.com/aymerick/douceur v0.2.0
github.com/aymerick/douceur/css
# github.com/charmbracelet/glamour v0.2.0 => github.com/noerw/glamour v0.2.1-0.20210305125354-f0a29f1de0c2
github.com/charmbracelet/glamour
github.com/charmbracelet/glamour/ansi
# github.com/chris-ramon/douceur v0.2.0
github.com/chris-ramon/douceur/parser
# github.com/cpuguy83/go-md2man/v2 v2.0.0
github.com/cpuguy83/go-md2man/v2/md2man
# github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964
@ -124,6 +129,8 @@ github.com/go-git/go-git/v5/utils/merkletrie/filesystem
github.com/go-git/go-git/v5/utils/merkletrie/index
github.com/go-git/go-git/v5/utils/merkletrie/internal/frame
github.com/go-git/go-git/v5/utils/merkletrie/noder
# github.com/gorilla/css v1.0.0
github.com/gorilla/css/scanner
# github.com/hashicorp/go-version v1.2.1
github.com/hashicorp/go-version
# github.com/imdario/mergo v0.3.11
@ -144,11 +151,11 @@ github.com/mattn/go-isatty
github.com/mattn/go-runewidth
# github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d
github.com/mgutz/ansi
# github.com/microcosm-cc/bluemonday v1.0.2
# github.com/microcosm-cc/bluemonday v1.0.4
github.com/microcosm-cc/bluemonday
# github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/go-homedir
# github.com/muesli/reflow v0.1.0
# github.com/muesli/reflow v0.2.0
github.com/muesli/reflow/ansi
github.com/muesli/reflow/indent
github.com/muesli/reflow/padding
@ -173,7 +180,7 @@ github.com/stretchr/testify/assert
github.com/urfave/cli/v2
# github.com/xanzy/ssh-agent v0.3.0
github.com/xanzy/ssh-agent
# github.com/yuin/goldmark v1.2.1
# github.com/yuin/goldmark v1.3.1
github.com/yuin/goldmark
github.com/yuin/goldmark/ast
github.com/yuin/goldmark/extension
@ -183,6 +190,10 @@ github.com/yuin/goldmark/renderer
github.com/yuin/goldmark/renderer/html
github.com/yuin/goldmark/text
github.com/yuin/goldmark/util
# github.com/yuin/goldmark-emoji v1.0.1
github.com/yuin/goldmark-emoji
github.com/yuin/goldmark-emoji/ast
github.com/yuin/goldmark-emoji/definition
# golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
golang.org/x/crypto/blowfish
golang.org/x/crypto/cast5