summaryrefslogtreecommitdiffstats
path: root/utils/markdown/reference_definition.go
blob: e2d0be350e7c945854536f1e7028c14163b66957 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.

package markdown

type ReferenceDefinition struct {
	RawDestination Range

	markdown string
	rawLabel string
	rawTitle string
}

func (d *ReferenceDefinition) Destination() string {
	return Unescape(d.markdown[d.RawDestination.Position:d.RawDestination.End])
}

func (d *ReferenceDefinition) Label() string {
	return d.rawLabel
}

func (d *ReferenceDefinition) Title() string {
	return Unescape(d.rawTitle)
}

func parseReferenceDefinition(markdown string, ranges []Range) (*ReferenceDefinition, []Range) {
	raw := ""
	for _, r := range ranges {
		raw += markdown[r.Position:r.End]
	}

	label, next, ok := parseLinkLabel(raw, 0)
	if !ok {
		return nil, nil
	}
	position := next

	if position >= len(raw) || raw[position] != ':' {
		return nil, nil
	}
	position++

	destination, next, ok := parseLinkDestination(raw, nextNonWhitespace(raw, position))
	if !ok {
		return nil, nil
	}
	position = next

	absoluteDestination := relativeToAbsolutePosition(ranges, destination.Position)
	ret := &ReferenceDefinition{
		RawDestination: Range{absoluteDestination, absoluteDestination + destination.End - destination.Position},
		markdown:       markdown,
		rawLabel:       raw[label.Position:label.End],
	}

	if position < len(raw) && isWhitespaceByte(raw[position]) {
		title, next, ok := parseLinkTitle(raw, nextNonWhitespace(raw, position))
		if !ok {
			if nextLine, skippedNonWhitespace := nextLine(raw, position); !skippedNonWhitespace {
				return ret, trimBytesFromRanges(ranges, nextLine)
			}
			return nil, nil
		}
		if nextLine, skippedNonWhitespace := nextLine(raw, next); !skippedNonWhitespace {
			ret.rawTitle = raw[title.Position:title.End]
			return ret, trimBytesFromRanges(ranges, nextLine)
		}
	}

	if nextLine, skippedNonWhitespace := nextLine(raw, position); !skippedNonWhitespace {
		return ret, trimBytesFromRanges(ranges, nextLine)
	}

	return nil, nil
}