summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/mattermost/mattermost-server/utils/markdown/inlines.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/mattermost/mattermost-server/utils/markdown/inlines.go')
-rw-r--r--vendor/github.com/mattermost/mattermost-server/utils/markdown/inlines.go664
1 files changed, 0 insertions, 664 deletions
diff --git a/vendor/github.com/mattermost/mattermost-server/utils/markdown/inlines.go b/vendor/github.com/mattermost/mattermost-server/utils/markdown/inlines.go
deleted file mode 100644
index a3abccef..00000000
--- a/vendor/github.com/mattermost/mattermost-server/utils/markdown/inlines.go
+++ /dev/null
@@ -1,664 +0,0 @@
-// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-package markdown
-
-import (
- "container/list"
- "strings"
- "unicode"
- "unicode/utf8"
-)
-
-type Inline interface {
- IsInline() bool
-}
-
-type inlineBase struct{}
-
-func (inlineBase) IsInline() bool { return true }
-
-type Text struct {
- inlineBase
-
- Text string
- Range Range
-}
-
-type CodeSpan struct {
- inlineBase
-
- Code string
-}
-
-type HardLineBreak struct {
- inlineBase
-}
-
-type SoftLineBreak struct {
- inlineBase
-}
-
-type InlineLinkOrImage struct {
- inlineBase
-
- Children []Inline
-
- RawDestination Range
-
- markdown string
- rawTitle string
-}
-
-func (i *InlineLinkOrImage) Destination() string {
- return Unescape(i.markdown[i.RawDestination.Position:i.RawDestination.End])
-}
-
-func (i *InlineLinkOrImage) Title() string {
- return Unescape(i.rawTitle)
-}
-
-type InlineLink struct {
- InlineLinkOrImage
-}
-
-type InlineImage struct {
- InlineLinkOrImage
-}
-
-type ReferenceLinkOrImage struct {
- inlineBase
- *ReferenceDefinition
-
- Children []Inline
-}
-
-type ReferenceLink struct {
- ReferenceLinkOrImage
-}
-
-type ReferenceImage struct {
- ReferenceLinkOrImage
-}
-
-type Autolink struct {
- inlineBase
-
- Children []Inline
-
- RawDestination Range
-
- markdown string
-}
-
-func (i *Autolink) Destination() string {
- destination := Unescape(i.markdown[i.RawDestination.Position:i.RawDestination.End])
-
- if strings.HasPrefix(destination, "www") {
- destination = "http://" + destination
- }
-
- return destination
-}
-
-type delimiterType int
-
-const (
- linkOpeningDelimiter delimiterType = iota
- imageOpeningDelimiter
-)
-
-type delimiter struct {
- Type delimiterType
- IsInactive bool
- TextNode int
- Range Range
-}
-
-type inlineParser struct {
- markdown string
- ranges []Range
- referenceDefinitions []*ReferenceDefinition
-
- raw string
- position int
- inlines []Inline
- delimiterStack *list.List
-}
-
-func newInlineParser(markdown string, ranges []Range, referenceDefinitions []*ReferenceDefinition) *inlineParser {
- return &inlineParser{
- markdown: markdown,
- ranges: ranges,
- referenceDefinitions: referenceDefinitions,
- delimiterStack: list.New(),
- }
-}
-
-func (p *inlineParser) parseBackticks() {
- count := 1
- for i := p.position + 1; i < len(p.raw) && p.raw[i] == '`'; i++ {
- count++
- }
- opening := p.raw[p.position : p.position+count]
- search := p.position + count
- for search < len(p.raw) {
- end := strings.Index(p.raw[search:], opening)
- if end == -1 {
- break
- }
- if search+end+count < len(p.raw) && p.raw[search+end+count] == '`' {
- search += end + count
- for search < len(p.raw) && p.raw[search] == '`' {
- search++
- }
- continue
- }
- code := strings.Join(strings.Fields(p.raw[p.position+count:search+end]), " ")
- p.position = search + end + count
- p.inlines = append(p.inlines, &CodeSpan{
- Code: code,
- })
- return
- }
- p.position += len(opening)
- absPos := relativeToAbsolutePosition(p.ranges, p.position-len(opening))
- p.inlines = append(p.inlines, &Text{
- Text: opening,
- Range: Range{absPos, absPos + len(opening)},
- })
-}
-
-func (p *inlineParser) parseLineEnding() {
- if p.position >= 1 && p.raw[p.position-1] == '\t' {
- p.inlines = append(p.inlines, &HardLineBreak{})
- } else if p.position >= 2 && p.raw[p.position-1] == ' ' && (p.raw[p.position-2] == '\t' || p.raw[p.position-1] == ' ') {
- p.inlines = append(p.inlines, &HardLineBreak{})
- } else {
- p.inlines = append(p.inlines, &SoftLineBreak{})
- }
- p.position++
- if p.position < len(p.raw) && p.raw[p.position] == '\n' {
- p.position++
- }
-}
-
-func (p *inlineParser) parseEscapeCharacter() {
- if p.position+1 < len(p.raw) && isEscapableByte(p.raw[p.position+1]) {
- absPos := relativeToAbsolutePosition(p.ranges, p.position+1)
- p.inlines = append(p.inlines, &Text{
- Text: string(p.raw[p.position+1]),
- Range: Range{absPos, absPos + len(string(p.raw[p.position+1]))},
- })
- p.position += 2
- } else {
- absPos := relativeToAbsolutePosition(p.ranges, p.position)
- p.inlines = append(p.inlines, &Text{
- Text: `\`,
- Range: Range{absPos, absPos + 1},
- })
- p.position++
- }
-}
-
-func (p *inlineParser) parseText() {
- if next := strings.IndexAny(p.raw[p.position:], "\r\n\\`&![]wW:"); next == -1 {
- absPos := relativeToAbsolutePosition(p.ranges, p.position)
- p.inlines = append(p.inlines, &Text{
- Text: strings.TrimRightFunc(p.raw[p.position:], isWhitespace),
- Range: Range{absPos, absPos + len(p.raw[p.position:])},
- })
- p.position = len(p.raw)
- } else {
- absPos := relativeToAbsolutePosition(p.ranges, p.position)
- if p.raw[p.position+next] == '\r' || p.raw[p.position+next] == '\n' {
- s := strings.TrimRightFunc(p.raw[p.position:p.position+next], isWhitespace)
- p.inlines = append(p.inlines, &Text{
- Text: s,
- Range: Range{absPos, absPos + len(s)},
- })
- } else {
- if next == 0 {
- // Always read at least one character since 'w', 'W', and ':' may not actually match another
- // type of node
- next = 1
- }
-
- p.inlines = append(p.inlines, &Text{
- Text: p.raw[p.position : p.position+next],
- Range: Range{absPos, absPos + next},
- })
- }
- p.position += next
- }
-}
-
-func (p *inlineParser) parseLinkOrImageDelimiter() {
- absPos := relativeToAbsolutePosition(p.ranges, p.position)
- if p.raw[p.position] == '[' {
- p.inlines = append(p.inlines, &Text{
- Text: "[",
- Range: Range{absPos, absPos + 1},
- })
- p.delimiterStack.PushBack(&delimiter{
- Type: linkOpeningDelimiter,
- TextNode: len(p.inlines) - 1,
- Range: Range{p.position, p.position + 1},
- })
- p.position++
- } else if p.raw[p.position] == '!' && p.position+1 < len(p.raw) && p.raw[p.position+1] == '[' {
- p.inlines = append(p.inlines, &Text{
- Text: "![",
- Range: Range{absPos, absPos + 2},
- })
- p.delimiterStack.PushBack(&delimiter{
- Type: imageOpeningDelimiter,
- TextNode: len(p.inlines) - 1,
- Range: Range{p.position, p.position + 2},
- })
- p.position += 2
- } else {
- p.inlines = append(p.inlines, &Text{
- Text: "!",
- Range: Range{absPos, absPos + 1},
- })
- p.position++
- }
-}
-
-func (p *inlineParser) peekAtInlineLinkDestinationAndTitle(position int, isImage bool) (destination, title Range, end int, ok bool) {
- if position >= len(p.raw) || p.raw[position] != '(' {
- return
- }
- position++
-
- destinationStart := nextNonWhitespace(p.raw, position)
- if destinationStart >= len(p.raw) {
- return
- } else if p.raw[destinationStart] == ')' {
- return Range{destinationStart, destinationStart}, Range{destinationStart, destinationStart}, destinationStart + 1, true
- }
-
- destination, end, ok = parseLinkDestination(p.raw, destinationStart)
- if !ok {
- return
- }
- position = end
-
- if isImage && position < len(p.raw) && isWhitespaceByte(p.raw[position]) {
- dimensionsStart := nextNonWhitespace(p.raw, position)
- if dimensionsStart >= len(p.raw) {
- return
- }
-
- if p.raw[dimensionsStart] == '=' {
- // Read optional image dimensions even if we don't use them
- _, end, ok = parseImageDimensions(p.raw, dimensionsStart)
- if !ok {
- return
- }
-
- position = end
- }
- }
-
- if position < len(p.raw) && isWhitespaceByte(p.raw[position]) {
- titleStart := nextNonWhitespace(p.raw, position)
- if titleStart >= len(p.raw) {
- return
- } else if p.raw[titleStart] == ')' {
- return destination, Range{titleStart, titleStart}, titleStart + 1, true
- }
-
- if p.raw[titleStart] == '"' || p.raw[titleStart] == '\'' || p.raw[titleStart] == '(' {
- title, end, ok = parseLinkTitle(p.raw, titleStart)
- if !ok {
- return
- }
- position = end
- }
- }
-
- closingPosition := nextNonWhitespace(p.raw, position)
- if closingPosition >= len(p.raw) || p.raw[closingPosition] != ')' {
- return Range{}, Range{}, 0, false
- }
-
- return destination, title, closingPosition + 1, true
-}
-
-func (p *inlineParser) referenceDefinition(label string) *ReferenceDefinition {
- clean := strings.Join(strings.Fields(label), " ")
- for _, d := range p.referenceDefinitions {
- if strings.EqualFold(clean, strings.Join(strings.Fields(d.Label()), " ")) {
- return d
- }
- }
- return nil
-}
-
-func (p *inlineParser) lookForLinkOrImage() {
- for element := p.delimiterStack.Back(); element != nil; element = element.Prev() {
- d := element.Value.(*delimiter)
- if d.Type != imageOpeningDelimiter && d.Type != linkOpeningDelimiter {
- continue
- }
- if d.IsInactive {
- p.delimiterStack.Remove(element)
- break
- }
-
- isImage := d.Type == imageOpeningDelimiter
-
- var inline Inline
-
- if destination, title, next, ok := p.peekAtInlineLinkDestinationAndTitle(p.position+1, isImage); ok {
- destinationMarkdownPosition := relativeToAbsolutePosition(p.ranges, destination.Position)
- linkOrImage := InlineLinkOrImage{
- Children: append([]Inline(nil), p.inlines[d.TextNode+1:]...),
- RawDestination: Range{destinationMarkdownPosition, destinationMarkdownPosition + destination.End - destination.Position},
- markdown: p.markdown,
- rawTitle: p.raw[title.Position:title.End],
- }
- if d.Type == imageOpeningDelimiter {
- inline = &InlineImage{linkOrImage}
- } else {
- inline = &InlineLink{linkOrImage}
- }
- p.position = next
- } else {
- referenceLabel := ""
- label, next, hasLinkLabel := parseLinkLabel(p.raw, p.position+1)
- if hasLinkLabel && label.End > label.Position {
- referenceLabel = p.raw[label.Position:label.End]
- } else {
- referenceLabel = p.raw[d.Range.End:p.position]
- if !hasLinkLabel {
- next = p.position + 1
- }
- }
- if referenceLabel != "" {
- if reference := p.referenceDefinition(referenceLabel); reference != nil {
- linkOrImage := ReferenceLinkOrImage{
- ReferenceDefinition: reference,
- Children: append([]Inline(nil), p.inlines[d.TextNode+1:]...),
- }
- if d.Type == imageOpeningDelimiter {
- inline = &ReferenceImage{linkOrImage}
- } else {
- inline = &ReferenceLink{linkOrImage}
- }
- p.position = next
- }
- }
- }
-
- if inline != nil {
- if d.Type == imageOpeningDelimiter {
- p.inlines = append(p.inlines[:d.TextNode], inline)
- } else {
- p.inlines = append(p.inlines[:d.TextNode], inline)
- for element := element.Prev(); element != nil; element = element.Prev() {
- if d := element.Value.(*delimiter); d.Type == linkOpeningDelimiter {
- d.IsInactive = true
- }
- }
- }
- p.delimiterStack.Remove(element)
- return
- } else {
- p.delimiterStack.Remove(element)
- break
- }
- }
- absPos := relativeToAbsolutePosition(p.ranges, p.position)
- p.inlines = append(p.inlines, &Text{
- Text: "]",
- Range: Range{absPos, absPos + 1},
- })
- p.position++
-}
-
-func CharacterReference(ref string) string {
- if ref == "" {
- return ""
- }
- if ref[0] == '#' {
- if len(ref) < 2 {
- return ""
- }
- n := 0
- if ref[1] == 'X' || ref[1] == 'x' {
- if len(ref) < 3 {
- return ""
- }
- for i := 2; i < len(ref); i++ {
- if i > 9 {
- return ""
- }
- d := ref[i]
- switch {
- case d >= '0' && d <= '9':
- n = n*16 + int(d-'0')
- case d >= 'a' && d <= 'f':
- n = n*16 + 10 + int(d-'a')
- case d >= 'A' && d <= 'F':
- n = n*16 + 10 + int(d-'A')
- default:
- return ""
- }
- }
- } else {
- for i := 1; i < len(ref); i++ {
- if i > 8 || ref[i] < '0' || ref[i] > '9' {
- return ""
- }
- n = n*10 + int(ref[i]-'0')
- }
- }
- c := rune(n)
- if c == '\u0000' || !utf8.ValidRune(c) {
- return string(unicode.ReplacementChar)
- }
- return string(c)
- }
- if entity, ok := htmlEntities[ref]; ok {
- return entity
- }
- return ""
-}
-
-func (p *inlineParser) parseCharacterReference() {
- absPos := relativeToAbsolutePosition(p.ranges, p.position)
- p.position++
- if semicolon := strings.IndexByte(p.raw[p.position:], ';'); semicolon == -1 {
- p.inlines = append(p.inlines, &Text{
- Text: "&",
- Range: Range{absPos, absPos + 1},
- })
- } else if s := CharacterReference(p.raw[p.position : p.position+semicolon]); s != "" {
- p.position += semicolon + 1
- p.inlines = append(p.inlines, &Text{
- Text: s,
- Range: Range{absPos, absPos + len(s)},
- })
- } else {
- p.inlines = append(p.inlines, &Text{
- Text: "&",
- Range: Range{absPos, absPos + 1},
- })
- }
-}
-
-func (p *inlineParser) parseAutolink(c rune) bool {
- for element := p.delimiterStack.Back(); element != nil; element = element.Prev() {
- d := element.Value.(*delimiter)
- if !d.IsInactive {
- return false
- }
- }
-
- var link Range
- if c == ':' {
- var ok bool
- link, ok = parseURLAutolink(p.raw, p.position)
-
- if !ok {
- return false
- }
-
- // Since the current position is at the colon, we have to rewind the parsing slightly so that
- // we don't duplicate the URL scheme
- rewind := strings.Index(p.raw[link.Position:link.End], ":")
- if rewind != -1 {
- lastInline := p.inlines[len(p.inlines)-1]
- lastText, ok := lastInline.(*Text)
-
- if !ok {
- // This should never occur since parseURLAutolink will only return a non-empty value
- // when the previous text ends in a valid URL protocol which would mean that the previous
- // node is a Text node
- return false
- }
-
- p.inlines = p.inlines[0 : len(p.inlines)-1]
- p.inlines = append(p.inlines, &Text{
- Text: lastText.Text[:len(lastText.Text)-rewind],
- Range: Range{lastText.Range.Position, lastText.Range.End - rewind},
- })
- p.position -= rewind
- }
- } else if c == 'w' || c == 'W' {
- var ok bool
- link, ok = parseWWWAutolink(p.raw, p.position)
-
- if !ok {
- return false
- }
- }
-
- linkMarkdownPosition := relativeToAbsolutePosition(p.ranges, link.Position)
- linkRange := Range{linkMarkdownPosition, linkMarkdownPosition + link.End - link.Position}
-
- p.inlines = append(p.inlines, &Autolink{
- Children: []Inline{
- &Text{
- Text: p.raw[link.Position:link.End],
- Range: linkRange,
- },
- },
- RawDestination: linkRange,
- markdown: p.markdown,
- })
- p.position += (link.End - link.Position)
-
- return true
-}
-
-func (p *inlineParser) Parse() []Inline {
- for _, r := range p.ranges {
- p.raw += p.markdown[r.Position:r.End]
- }
-
- for p.position < len(p.raw) {
- c, _ := utf8.DecodeRuneInString(p.raw[p.position:])
-
- switch c {
- case '\r', '\n':
- p.parseLineEnding()
- case '\\':
- p.parseEscapeCharacter()
- case '`':
- p.parseBackticks()
- case '&':
- p.parseCharacterReference()
- case '!', '[':
- p.parseLinkOrImageDelimiter()
- case ']':
- p.lookForLinkOrImage()
- case 'w', 'W', ':':
- matched := p.parseAutolink(c)
-
- if !matched {
- p.parseText()
- }
- default:
- p.parseText()
- }
- }
-
- return p.inlines
-}
-
-func ParseInlines(markdown string, ranges []Range, referenceDefinitions []*ReferenceDefinition) (inlines []Inline) {
- return newInlineParser(markdown, ranges, referenceDefinitions).Parse()
-}
-
-func MergeInlineText(inlines []Inline) []Inline {
- var ret []Inline
- for i, v := range inlines {
- // always add first node
- if i == 0 {
- ret = append(ret, v)
- continue
- }
- // not a text node? nothing to merge
- text, ok := v.(*Text)
- if !ok {
- ret = append(ret, v)
- continue
- }
- // previous node is not a text node? nothing to merge
- prevText, ok := ret[len(ret)-1].(*Text)
- if !ok {
- ret = append(ret, v)
- continue
- }
- // previous node is not right before this one
- if prevText.Range.End != text.Range.Position {
- ret = append(ret, v)
- continue
- }
- // we have two consecutive text nodes
- ret[len(ret)-1] = &Text{
- Text: prevText.Text + text.Text,
- Range: Range{prevText.Range.Position, text.Range.End},
- }
- }
- return ret
-}
-
-func Unescape(markdown string) string {
- ret := ""
-
- position := 0
- for position < len(markdown) {
- c, cSize := utf8.DecodeRuneInString(markdown[position:])
-
- switch c {
- case '\\':
- if position+1 < len(markdown) && isEscapableByte(markdown[position+1]) {
- ret += string(markdown[position+1])
- position += 2
- } else {
- ret += `\`
- position++
- }
- case '&':
- position++
- if semicolon := strings.IndexByte(markdown[position:], ';'); semicolon == -1 {
- ret += "&"
- } else if s := CharacterReference(markdown[position : position+semicolon]); s != "" {
- position += semicolon + 1
- ret += s
- } else {
- ret += "&"
- }
- default:
- ret += string(c)
- position += cSize
- }
- }
-
- return ret
-}