mirror of
				https://gitea.com/gitea/tea.git
				synced 2025-10-31 01:05:26 +01: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
	 Norwin
					Norwin