mirror of
https://gitea.com/gitea/tea.git
synced 2025-09-07 20:32:55 +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:
15
vendor/github.com/yuin/goldmark-emoji/.gitignore
generated
vendored
Normal file
15
vendor/github.com/yuin/goldmark-emoji/.gitignore
generated
vendored
Normal 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
21
vendor/github.com/yuin/goldmark-emoji/LICENSE
generated
vendored
Normal 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
71
vendor/github.com/yuin/goldmark-emoji/README.md
generated
vendored
Normal 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
42
vendor/github.com/yuin/goldmark-emoji/ast/emoji.go
generated
vendored
Normal 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,
|
||||
}
|
||||
}
|
106
vendor/github.com/yuin/goldmark-emoji/definition/definition.go
generated
vendored
Normal file
106
vendor/github.com/yuin/goldmark-emoji/definition/definition.go
generated
vendored
Normal 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...))
|
||||
}
|
||||
}
|
1757
vendor/github.com/yuin/goldmark-emoji/definition/github.go
generated
vendored
Normal file
1757
vendor/github.com/yuin/goldmark-emoji/definition/github.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
360
vendor/github.com/yuin/goldmark-emoji/emoji.go
generated
vendored
Normal file
360
vendor/github.com/yuin/goldmark-emoji/emoji.go
generated
vendored
Normal 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("‍")
|
||||
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
5
vendor/github.com/yuin/goldmark-emoji/go.mod
generated
vendored
Normal 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
2
vendor/github.com/yuin/goldmark-emoji/go.sum
generated
vendored
Normal 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=
|
88
vendor/github.com/yuin/goldmark/README.md
generated
vendored
88
vendor/github.com/yuin/goldmark/README.md
generated
vendored
@ -1,7 +1,7 @@
|
||||
goldmark
|
||||
==========================================
|
||||
|
||||
[](http://godoc.org/github.com/yuin/goldmark)
|
||||
[](https://pkg.go.dev/github.com/yuin/goldmark)
|
||||
[](https://github.com/yuin/goldmark/actions?query=workflow:test)
|
||||
[](https://coveralls.io/github/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 `↩︎`. |
|
||||
|
||||
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)
|
||||
|
28
vendor/github.com/yuin/goldmark/ast/ast.go
generated
vendored
28
vendor/github.com/yuin/goldmark/ast/ast.go
generated
vendored
@ -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
|
||||
|
23
vendor/github.com/yuin/goldmark/ast/block.go
generated
vendored
23
vendor/github.com/yuin/goldmark/ast/block.go
generated
vendored
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
2
vendor/github.com/yuin/goldmark/ast/inline.go
generated
vendored
2
vendor/github.com/yuin/goldmark/ast/inline.go
generated
vendored
@ -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
|
||||
}
|
||||
|
35
vendor/github.com/yuin/goldmark/extension/ast/footnote.go
generated
vendored
35
vendor/github.com/yuin/goldmark/extension/ast/footnote.go
generated
vendored
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
382
vendor/github.com/yuin/goldmark/extension/footnote.go
generated
vendored
382
vendor/github.com/yuin/goldmark/extension/footnote.go
generated
vendored
@ -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("↩︎"),
|
||||
}
|
||||
}
|
||||
|
||||
// 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("↩︎")
|
||||
_, _ = 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),
|
||||
))
|
||||
}
|
||||
|
4
vendor/github.com/yuin/goldmark/extension/linkify.go
generated
vendored
4
vendor/github.com/yuin/goldmark/extension/linkify.go
generated
vendored
@ -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.
|
||||
|
104
vendor/github.com/yuin/goldmark/extension/table.go
generated
vendored
104
vendor/github.com/yuin/goldmark/extension/table.go
generated
vendored
@ -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),
|
||||
))
|
||||
|
2
vendor/github.com/yuin/goldmark/go.mod
generated
vendored
2
vendor/github.com/yuin/goldmark/go.mod
generated
vendored
@ -1,3 +1,3 @@
|
||||
module github.com/yuin/goldmark
|
||||
|
||||
go 1.13
|
||||
go 1.15
|
||||
|
8
vendor/github.com/yuin/goldmark/parser/link.go
generated
vendored
8
vendor/github.com/yuin/goldmark/parser/link.go
generated
vendored
@ -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)
|
||||
|
12
vendor/github.com/yuin/goldmark/parser/parser.go
generated
vendored
12
vendor/github.com/yuin/goldmark/parser/parser.go
generated
vendored
@ -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
|
||||
}
|
||||
|
9
vendor/github.com/yuin/goldmark/parser/raw_html.go
generated
vendored
9
vendor/github.com/yuin/goldmark/parser/raw_html.go
generated
vendored
@ -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
|
||||
}
|
||||
|
16
vendor/github.com/yuin/goldmark/util/util.go
generated
vendored
16
vendor/github.com/yuin/goldmark/util/util.go
generated
vendored
@ -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)
|
||||
}
|
||||
|
Reference in New Issue
Block a user