diff options
Diffstat (limited to 'vendor/github.com/yuin/goldmark/parser/link.go')
-rw-r--r-- | vendor/github.com/yuin/goldmark/parser/link.go | 449 |
1 files changed, 0 insertions, 449 deletions
diff --git a/vendor/github.com/yuin/goldmark/parser/link.go b/vendor/github.com/yuin/goldmark/parser/link.go deleted file mode 100644 index 7390d7beb..000000000 --- a/vendor/github.com/yuin/goldmark/parser/link.go +++ /dev/null @@ -1,449 +0,0 @@ -package parser - -import ( - "fmt" - "strings" - - "github.com/yuin/goldmark/ast" - "github.com/yuin/goldmark/text" - "github.com/yuin/goldmark/util" -) - -var linkLabelStateKey = NewContextKey() - -type linkLabelState struct { - ast.BaseInline - - Segment text.Segment - - IsImage bool - - Prev *linkLabelState - - Next *linkLabelState - - First *linkLabelState - - Last *linkLabelState -} - -func newLinkLabelState(segment text.Segment, isImage bool) *linkLabelState { - return &linkLabelState{ - Segment: segment, - IsImage: isImage, - } -} - -func (s *linkLabelState) Text(source []byte) []byte { - return s.Segment.Value(source) -} - -func (s *linkLabelState) Dump(source []byte, level int) { - fmt.Printf("%slinkLabelState: \"%s\"\n", strings.Repeat(" ", level), s.Text(source)) -} - -var kindLinkLabelState = ast.NewNodeKind("LinkLabelState") - -func (s *linkLabelState) Kind() ast.NodeKind { - return kindLinkLabelState -} - -func linkLabelStateLength(v *linkLabelState) int { - if v == nil || v.Last == nil || v.First == nil { - return 0 - } - return v.Last.Segment.Stop - v.First.Segment.Start -} - -func pushLinkLabelState(pc Context, v *linkLabelState) { - tlist := pc.Get(linkLabelStateKey) - var list *linkLabelState - if tlist == nil { - list = v - v.First = v - v.Last = v - pc.Set(linkLabelStateKey, list) - } else { - list = tlist.(*linkLabelState) - l := list.Last - list.Last = v - l.Next = v - v.Prev = l - } -} - -func removeLinkLabelState(pc Context, d *linkLabelState) { - tlist := pc.Get(linkLabelStateKey) - var list *linkLabelState - if tlist == nil { - return - } - list = tlist.(*linkLabelState) - - if d.Prev == nil { - list = d.Next - if list != nil { - list.First = d - list.Last = d.Last - list.Prev = nil - pc.Set(linkLabelStateKey, list) - } else { - pc.Set(linkLabelStateKey, nil) - } - } else { - d.Prev.Next = d.Next - if d.Next != nil { - d.Next.Prev = d.Prev - } - } - if list != nil && d.Next == nil { - list.Last = d.Prev - } - d.Next = nil - d.Prev = nil - d.First = nil - d.Last = nil -} - -type linkParser struct { -} - -var defaultLinkParser = &linkParser{} - -// NewLinkParser return a new InlineParser that parses links. -func NewLinkParser() InlineParser { - return defaultLinkParser -} - -func (s *linkParser) Trigger() []byte { - return []byte{'!', '[', ']'} -} - -var linkBottom = NewContextKey() - -func (s *linkParser) Parse(parent ast.Node, block text.Reader, pc Context) ast.Node { - line, segment := block.PeekLine() - if line[0] == '!' { - if len(line) > 1 && line[1] == '[' { - block.Advance(1) - pushLinkBottom(pc) - return processLinkLabelOpen(block, segment.Start+1, true, pc) - } - return nil - } - if line[0] == '[' { - pushLinkBottom(pc) - return processLinkLabelOpen(block, segment.Start, false, pc) - } - - // line[0] == ']' - tlist := pc.Get(linkLabelStateKey) - if tlist == nil { - return nil - } - last := tlist.(*linkLabelState).Last - if last == nil { - _ = popLinkBottom(pc) - return nil - } - block.Advance(1) - removeLinkLabelState(pc, last) - // CommonMark spec says: - // > A link label can have at most 999 characters inside the square brackets. - if linkLabelStateLength(tlist.(*linkLabelState)) > 998 { - ast.MergeOrReplaceTextSegment(last.Parent(), last, last.Segment) - _ = popLinkBottom(pc) - return nil - } - - if !last.IsImage && s.containsLink(last) { // a link in a link text is not allowed - ast.MergeOrReplaceTextSegment(last.Parent(), last, last.Segment) - _ = popLinkBottom(pc) - return nil - } - - c := block.Peek() - l, pos := block.Position() - var link *ast.Link - var hasValue bool - if c == '(' { // normal link - link = s.parseLink(parent, last, block, pc) - } else if c == '[' { // reference link - link, hasValue = s.parseReferenceLink(parent, last, block, pc) - if link == nil && hasValue { - ast.MergeOrReplaceTextSegment(last.Parent(), last, last.Segment) - _ = popLinkBottom(pc) - return nil - } - } - - if link == nil { - // maybe shortcut reference link - block.SetPosition(l, pos) - ssegment := text.NewSegment(last.Segment.Stop, segment.Start) - maybeReference := block.Value(ssegment) - // CommonMark spec says: - // > A link label can have at most 999 characters inside the square brackets. - if len(maybeReference) > 999 { - ast.MergeOrReplaceTextSegment(last.Parent(), last, last.Segment) - _ = popLinkBottom(pc) - return nil - } - - ref, ok := pc.Reference(util.ToLinkReference(maybeReference)) - if !ok { - ast.MergeOrReplaceTextSegment(last.Parent(), last, last.Segment) - _ = popLinkBottom(pc) - return nil - } - link = ast.NewLink() - s.processLinkLabel(parent, link, last, pc) - link.Title = ref.Title() - link.Destination = ref.Destination() - } - if last.IsImage { - last.Parent().RemoveChild(last.Parent(), last) - return ast.NewImage(link) - } - last.Parent().RemoveChild(last.Parent(), last) - return link -} - -func (s *linkParser) containsLink(n ast.Node) bool { - if n == nil { - return false - } - for c := n; c != nil; c = c.NextSibling() { - if _, ok := c.(*ast.Link); ok { - return true - } - if s.containsLink(c.FirstChild()) { - return true - } - } - return false -} - -func processLinkLabelOpen(block text.Reader, pos int, isImage bool, pc Context) *linkLabelState { - start := pos - if isImage { - start-- - } - state := newLinkLabelState(text.NewSegment(start, pos+1), isImage) - pushLinkLabelState(pc, state) - block.Advance(1) - return state -} - -func (s *linkParser) processLinkLabel(parent ast.Node, link *ast.Link, last *linkLabelState, pc Context) { - bottom := popLinkBottom(pc) - ProcessDelimiters(bottom, pc) - for c := last.NextSibling(); c != nil; { - next := c.NextSibling() - parent.RemoveChild(parent, c) - link.AppendChild(link, c) - c = next - } -} - -var linkFindClosureOptions text.FindClosureOptions = text.FindClosureOptions{ - Nesting: false, - Newline: true, - Advance: true, -} - -func (s *linkParser) parseReferenceLink(parent ast.Node, last *linkLabelState, - block text.Reader, pc Context) (*ast.Link, bool) { - _, orgpos := block.Position() - block.Advance(1) // skip '[' - segments, found := block.FindClosure('[', ']', linkFindClosureOptions) - if !found { - return nil, false - } - - var maybeReference []byte - if segments.Len() == 1 { // avoid allocate a new byte slice - maybeReference = block.Value(segments.At(0)) - } else { - maybeReference = []byte{} - for i := 0; i < segments.Len(); i++ { - s := segments.At(i) - maybeReference = append(maybeReference, block.Value(s)...) - } - } - if util.IsBlank(maybeReference) { // collapsed reference link - s := text.NewSegment(last.Segment.Stop, orgpos.Start-1) - maybeReference = block.Value(s) - } - // CommonMark spec says: - // > A link label can have at most 999 characters inside the square brackets. - if len(maybeReference) > 999 { - return nil, true - } - - ref, ok := pc.Reference(util.ToLinkReference(maybeReference)) - if !ok { - return nil, true - } - - link := ast.NewLink() - s.processLinkLabel(parent, link, last, pc) - link.Title = ref.Title() - link.Destination = ref.Destination() - return link, true -} - -func (s *linkParser) parseLink(parent ast.Node, last *linkLabelState, block text.Reader, pc Context) *ast.Link { - block.Advance(1) // skip '(' - block.SkipSpaces() - var title []byte - var destination []byte - var ok bool - if block.Peek() == ')' { // empty link like '[link]()' - block.Advance(1) - } else { - destination, ok = parseLinkDestination(block) - if !ok { - return nil - } - block.SkipSpaces() - if block.Peek() == ')' { - block.Advance(1) - } else { - title, ok = parseLinkTitle(block) - if !ok { - return nil - } - block.SkipSpaces() - if block.Peek() == ')' { - block.Advance(1) - } else { - return nil - } - } - } - - link := ast.NewLink() - s.processLinkLabel(parent, link, last, pc) - link.Destination = destination - link.Title = title - return link -} - -func parseLinkDestination(block text.Reader) ([]byte, bool) { - block.SkipSpaces() - line, _ := block.PeekLine() - if block.Peek() == '<' { - i := 1 - for i < len(line) { - c := line[i] - if c == '\\' && i < len(line)-1 && util.IsPunct(line[i+1]) { - i += 2 - continue - } else if c == '>' { - block.Advance(i + 1) - return line[1:i], true - } - i++ - } - return nil, false - } - opened := 0 - i := 0 - for i < len(line) { - c := line[i] - if c == '\\' && i < len(line)-1 && util.IsPunct(line[i+1]) { - i += 2 - continue - } else if c == '(' { - opened++ - } else if c == ')' { - opened-- - if opened < 0 { - break - } - } else if util.IsSpace(c) { - break - } - i++ - } - block.Advance(i) - return line[:i], len(line[:i]) != 0 -} - -func parseLinkTitle(block text.Reader) ([]byte, bool) { - block.SkipSpaces() - opener := block.Peek() - if opener != '"' && opener != '\'' && opener != '(' { - return nil, false - } - closer := opener - if opener == '(' { - closer = ')' - } - block.Advance(1) - segments, found := block.FindClosure(opener, closer, linkFindClosureOptions) - if found { - if segments.Len() == 1 { - return block.Value(segments.At(0)), true - } - var title []byte - for i := 0; i < segments.Len(); i++ { - s := segments.At(i) - title = append(title, block.Value(s)...) - } - return title, true - } - return nil, false -} - -func pushLinkBottom(pc Context) { - bottoms := pc.Get(linkBottom) - b := pc.LastDelimiter() - if bottoms == nil { - pc.Set(linkBottom, b) - return - } - if s, ok := bottoms.([]ast.Node); ok { - pc.Set(linkBottom, append(s, b)) - return - } - pc.Set(linkBottom, []ast.Node{bottoms.(ast.Node), b}) -} - -func popLinkBottom(pc Context) ast.Node { - bottoms := pc.Get(linkBottom) - if bottoms == nil { - return nil - } - if v, ok := bottoms.(ast.Node); ok { - pc.Set(linkBottom, nil) - return v - } - s := bottoms.([]ast.Node) - v := s[len(s)-1] - n := s[0 : len(s)-1] - switch len(n) { - case 0: - pc.Set(linkBottom, nil) - case 1: - pc.Set(linkBottom, n[0]) - default: - pc.Set(linkBottom, s[0:len(s)-1]) - } - return v -} - -func (s *linkParser) CloseBlock(parent ast.Node, block text.Reader, pc Context) { - pc.Set(linkBottom, nil) - tlist := pc.Get(linkLabelStateKey) - if tlist == nil { - return - } - for s := tlist.(*linkLabelState); s != nil; { - next := s.Next - removeLinkLabelState(pc, s) - s.Parent().ReplaceChild(s.Parent(), s, ast.NewTextSegment(s.Segment)) - s = next - } -} |