diff options
Diffstat (limited to 'vendor/github.com/yuin/goldmark/parser/link.go')
| -rw-r--r-- | vendor/github.com/yuin/goldmark/parser/link.go | 409 |
1 files changed, 409 insertions, 0 deletions
diff --git a/vendor/github.com/yuin/goldmark/parser/link.go b/vendor/github.com/yuin/goldmark/parser/link.go new file mode 100644 index 0000000..99583ac --- /dev/null +++ b/vendor/github.com/yuin/goldmark/parser/link.go | |||
| @@ -0,0 +1,409 @@ | |||
| 1 | package parser | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "fmt" | ||
| 5 | "strings" | ||
| 6 | |||
| 7 | "github.com/yuin/goldmark/ast" | ||
| 8 | "github.com/yuin/goldmark/text" | ||
| 9 | "github.com/yuin/goldmark/util" | ||
| 10 | ) | ||
| 11 | |||
| 12 | var linkLabelStateKey = NewContextKey() | ||
| 13 | |||
| 14 | type linkLabelState struct { | ||
| 15 | ast.BaseInline | ||
| 16 | |||
| 17 | Segment text.Segment | ||
| 18 | |||
| 19 | IsImage bool | ||
| 20 | |||
| 21 | Prev *linkLabelState | ||
| 22 | |||
| 23 | Next *linkLabelState | ||
| 24 | |||
| 25 | First *linkLabelState | ||
| 26 | |||
| 27 | Last *linkLabelState | ||
| 28 | } | ||
| 29 | |||
| 30 | func newLinkLabelState(segment text.Segment, isImage bool) *linkLabelState { | ||
| 31 | return &linkLabelState{ | ||
| 32 | Segment: segment, | ||
| 33 | IsImage: isImage, | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | func (s *linkLabelState) Text(source []byte) []byte { | ||
| 38 | return s.Segment.Value(source) | ||
| 39 | } | ||
| 40 | |||
| 41 | func (s *linkLabelState) Dump(source []byte, level int) { | ||
| 42 | fmt.Printf("%slinkLabelState: \"%s\"\n", strings.Repeat(" ", level), s.Text(source)) | ||
| 43 | } | ||
| 44 | |||
| 45 | var kindLinkLabelState = ast.NewNodeKind("LinkLabelState") | ||
| 46 | |||
| 47 | func (s *linkLabelState) Kind() ast.NodeKind { | ||
| 48 | return kindLinkLabelState | ||
| 49 | } | ||
| 50 | |||
| 51 | func linkLabelStateLength(v *linkLabelState) int { | ||
| 52 | if v == nil || v.Last == nil || v.First == nil { | ||
| 53 | return 0 | ||
| 54 | } | ||
| 55 | return v.Last.Segment.Stop - v.First.Segment.Start | ||
| 56 | } | ||
| 57 | |||
| 58 | func pushLinkLabelState(pc Context, v *linkLabelState) { | ||
| 59 | tlist := pc.Get(linkLabelStateKey) | ||
| 60 | var list *linkLabelState | ||
| 61 | if tlist == nil { | ||
| 62 | list = v | ||
| 63 | v.First = v | ||
| 64 | v.Last = v | ||
| 65 | pc.Set(linkLabelStateKey, list) | ||
| 66 | } else { | ||
| 67 | list = tlist.(*linkLabelState) | ||
| 68 | l := list.Last | ||
| 69 | list.Last = v | ||
| 70 | l.Next = v | ||
| 71 | v.Prev = l | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | func removeLinkLabelState(pc Context, d *linkLabelState) { | ||
| 76 | tlist := pc.Get(linkLabelStateKey) | ||
| 77 | var list *linkLabelState | ||
| 78 | if tlist == nil { | ||
| 79 | return | ||
| 80 | } | ||
| 81 | list = tlist.(*linkLabelState) | ||
| 82 | |||
| 83 | if d.Prev == nil { | ||
| 84 | list = d.Next | ||
| 85 | if list != nil { | ||
| 86 | list.First = d | ||
| 87 | list.Last = d.Last | ||
| 88 | list.Prev = nil | ||
| 89 | pc.Set(linkLabelStateKey, list) | ||
| 90 | } else { | ||
| 91 | pc.Set(linkLabelStateKey, nil) | ||
| 92 | } | ||
| 93 | } else { | ||
| 94 | d.Prev.Next = d.Next | ||
| 95 | if d.Next != nil { | ||
| 96 | d.Next.Prev = d.Prev | ||
| 97 | } | ||
| 98 | } | ||
| 99 | if list != nil && d.Next == nil { | ||
| 100 | list.Last = d.Prev | ||
| 101 | } | ||
| 102 | d.Next = nil | ||
| 103 | d.Prev = nil | ||
| 104 | d.First = nil | ||
| 105 | d.Last = nil | ||
| 106 | } | ||
| 107 | |||
| 108 | type linkParser struct { | ||
| 109 | } | ||
| 110 | |||
| 111 | var defaultLinkParser = &linkParser{} | ||
| 112 | |||
| 113 | // NewLinkParser return a new InlineParser that parses links. | ||
| 114 | func NewLinkParser() InlineParser { | ||
| 115 | return defaultLinkParser | ||
| 116 | } | ||
| 117 | |||
| 118 | func (s *linkParser) Trigger() []byte { | ||
| 119 | return []byte{'!', '[', ']'} | ||
| 120 | } | ||
| 121 | |||
| 122 | var linkBottom = NewContextKey() | ||
| 123 | |||
| 124 | func (s *linkParser) Parse(parent ast.Node, block text.Reader, pc Context) ast.Node { | ||
| 125 | line, segment := block.PeekLine() | ||
| 126 | if line[0] == '!' { | ||
| 127 | if len(line) > 1 && line[1] == '[' { | ||
| 128 | block.Advance(1) | ||
| 129 | pc.Set(linkBottom, pc.LastDelimiter()) | ||
| 130 | return processLinkLabelOpen(block, segment.Start+1, true, pc) | ||
| 131 | } | ||
| 132 | return nil | ||
| 133 | } | ||
| 134 | if line[0] == '[' { | ||
| 135 | pc.Set(linkBottom, pc.LastDelimiter()) | ||
| 136 | return processLinkLabelOpen(block, segment.Start, false, pc) | ||
| 137 | } | ||
| 138 | |||
| 139 | // line[0] == ']' | ||
| 140 | tlist := pc.Get(linkLabelStateKey) | ||
| 141 | if tlist == nil { | ||
| 142 | return nil | ||
| 143 | } | ||
| 144 | last := tlist.(*linkLabelState).Last | ||
| 145 | if last == nil { | ||
| 146 | return nil | ||
| 147 | } | ||
| 148 | block.Advance(1) | ||
| 149 | removeLinkLabelState(pc, last) | ||
| 150 | // CommonMark spec says: | ||
| 151 | // > A link label can have at most 999 characters inside the square brackets. | ||
| 152 | if linkLabelStateLength(tlist.(*linkLabelState)) > 998 { | ||
| 153 | ast.MergeOrReplaceTextSegment(last.Parent(), last, last.Segment) | ||
| 154 | return nil | ||
| 155 | } | ||
| 156 | |||
| 157 | if !last.IsImage && s.containsLink(last) { // a link in a link text is not allowed | ||
| 158 | ast.MergeOrReplaceTextSegment(last.Parent(), last, last.Segment) | ||
| 159 | return nil | ||
| 160 | } | ||
| 161 | |||
| 162 | c := block.Peek() | ||
| 163 | l, pos := block.Position() | ||
| 164 | var link *ast.Link | ||
| 165 | var hasValue bool | ||
| 166 | if c == '(' { // normal link | ||
| 167 | link = s.parseLink(parent, last, block, pc) | ||
| 168 | } else if c == '[' { // reference link | ||
| 169 | link, hasValue = s.parseReferenceLink(parent, last, block, pc) | ||
| 170 | if link == nil && hasValue { | ||
| 171 | ast.MergeOrReplaceTextSegment(last.Parent(), last, last.Segment) | ||
| 172 | return nil | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | if link == nil { | ||
| 177 | // maybe shortcut reference link | ||
| 178 | block.SetPosition(l, pos) | ||
| 179 | ssegment := text.NewSegment(last.Segment.Stop, segment.Start) | ||
| 180 | maybeReference := block.Value(ssegment) | ||
| 181 | // CommonMark spec says: | ||
| 182 | // > A link label can have at most 999 characters inside the square brackets. | ||
| 183 | if len(maybeReference) > 999 { | ||
| 184 | ast.MergeOrReplaceTextSegment(last.Parent(), last, last.Segment) | ||
| 185 | return nil | ||
| 186 | } | ||
| 187 | |||
| 188 | ref, ok := pc.Reference(util.ToLinkReference(maybeReference)) | ||
| 189 | if !ok { | ||
| 190 | ast.MergeOrReplaceTextSegment(last.Parent(), last, last.Segment) | ||
| 191 | return nil | ||
| 192 | } | ||
| 193 | link = ast.NewLink() | ||
| 194 | s.processLinkLabel(parent, link, last, pc) | ||
| 195 | link.Title = ref.Title() | ||
| 196 | link.Destination = ref.Destination() | ||
| 197 | } | ||
| 198 | if last.IsImage { | ||
| 199 | last.Parent().RemoveChild(last.Parent(), last) | ||
| 200 | return ast.NewImage(link) | ||
| 201 | } | ||
| 202 | last.Parent().RemoveChild(last.Parent(), last) | ||
| 203 | return link | ||
| 204 | } | ||
| 205 | |||
| 206 | func (s *linkParser) containsLink(n ast.Node) bool { | ||
| 207 | if n == nil { | ||
| 208 | return false | ||
| 209 | } | ||
| 210 | for c := n; c != nil; c = c.NextSibling() { | ||
| 211 | if _, ok := c.(*ast.Link); ok { | ||
| 212 | return true | ||
| 213 | } | ||
| 214 | if s.containsLink(c.FirstChild()) { | ||
| 215 | return true | ||
| 216 | } | ||
| 217 | } | ||
| 218 | return false | ||
| 219 | } | ||
| 220 | |||
| 221 | func processLinkLabelOpen(block text.Reader, pos int, isImage bool, pc Context) *linkLabelState { | ||
| 222 | start := pos | ||
| 223 | if isImage { | ||
| 224 | start-- | ||
| 225 | } | ||
| 226 | state := newLinkLabelState(text.NewSegment(start, pos+1), isImage) | ||
| 227 | pushLinkLabelState(pc, state) | ||
| 228 | block.Advance(1) | ||
| 229 | return state | ||
| 230 | } | ||
| 231 | |||
| 232 | func (s *linkParser) processLinkLabel(parent ast.Node, link *ast.Link, last *linkLabelState, pc Context) { | ||
| 233 | var bottom ast.Node | ||
| 234 | if v := pc.Get(linkBottom); v != nil { | ||
| 235 | bottom = v.(ast.Node) | ||
| 236 | } | ||
| 237 | pc.Set(linkBottom, nil) | ||
| 238 | ProcessDelimiters(bottom, pc) | ||
| 239 | for c := last.NextSibling(); c != nil; { | ||
| 240 | next := c.NextSibling() | ||
| 241 | parent.RemoveChild(parent, c) | ||
| 242 | link.AppendChild(link, c) | ||
| 243 | c = next | ||
| 244 | } | ||
| 245 | } | ||
| 246 | |||
| 247 | var linkFindClosureOptions text.FindClosureOptions = text.FindClosureOptions{ | ||
| 248 | Nesting: false, | ||
| 249 | Newline: true, | ||
| 250 | Advance: true, | ||
| 251 | } | ||
| 252 | |||
| 253 | func (s *linkParser) parseReferenceLink(parent ast.Node, last *linkLabelState, block text.Reader, pc Context) (*ast.Link, bool) { | ||
| 254 | _, orgpos := block.Position() | ||
| 255 | block.Advance(1) // skip '[' | ||
| 256 | segments, found := block.FindClosure('[', ']', linkFindClosureOptions) | ||
| 257 | if !found { | ||
| 258 | return nil, false | ||
| 259 | } | ||
| 260 | |||
| 261 | var maybeReference []byte | ||
| 262 | if segments.Len() == 1 { // avoid allocate a new byte slice | ||
| 263 | maybeReference = block.Value(segments.At(0)) | ||
| 264 | } else { | ||
| 265 | maybeReference = []byte{} | ||
| 266 | for i := 0; i < segments.Len(); i++ { | ||
| 267 | s := segments.At(i) | ||
| 268 | maybeReference = append(maybeReference, block.Value(s)...) | ||
| 269 | } | ||
| 270 | } | ||
| 271 | if util.IsBlank(maybeReference) { // collapsed reference link | ||
| 272 | s := text.NewSegment(last.Segment.Stop, orgpos.Start-1) | ||
| 273 | maybeReference = block.Value(s) | ||
| 274 | } | ||
| 275 | // CommonMark spec says: | ||
| 276 | // > A link label can have at most 999 characters inside the square brackets. | ||
| 277 | if len(maybeReference) > 999 { | ||
| 278 | return nil, true | ||
| 279 | } | ||
| 280 | |||
| 281 | ref, ok := pc.Reference(util.ToLinkReference(maybeReference)) | ||
| 282 | if !ok { | ||
| 283 | return nil, true | ||
| 284 | } | ||
| 285 | |||
| 286 | link := ast.NewLink() | ||
| 287 | s.processLinkLabel(parent, link, last, pc) | ||
| 288 | link.Title = ref.Title() | ||
| 289 | link.Destination = ref.Destination() | ||
| 290 | return link, true | ||
| 291 | } | ||
| 292 | |||
| 293 | func (s *linkParser) parseLink(parent ast.Node, last *linkLabelState, block text.Reader, pc Context) *ast.Link { | ||
| 294 | block.Advance(1) // skip '(' | ||
| 295 | block.SkipSpaces() | ||
| 296 | var title []byte | ||
| 297 | var destination []byte | ||
| 298 | var ok bool | ||
| 299 | if block.Peek() == ')' { // empty link like '[link]()' | ||
| 300 | block.Advance(1) | ||
| 301 | } else { | ||
| 302 | destination, ok = parseLinkDestination(block) | ||
| 303 | if !ok { | ||
| 304 | return nil | ||
| 305 | } | ||
| 306 | block.SkipSpaces() | ||
| 307 | if block.Peek() == ')' { | ||
| 308 | block.Advance(1) | ||
| 309 | } else { | ||
| 310 | title, ok = parseLinkTitle(block) | ||
| 311 | if !ok { | ||
| 312 | return nil | ||
| 313 | } | ||
| 314 | block.SkipSpaces() | ||
| 315 | if block.Peek() == ')' { | ||
| 316 | block.Advance(1) | ||
| 317 | } else { | ||
| 318 | return nil | ||
| 319 | } | ||
| 320 | } | ||
| 321 | } | ||
| 322 | |||
| 323 | link := ast.NewLink() | ||
| 324 | s.processLinkLabel(parent, link, last, pc) | ||
| 325 | link.Destination = destination | ||
| 326 | link.Title = title | ||
| 327 | return link | ||
| 328 | } | ||
| 329 | |||
| 330 | func parseLinkDestination(block text.Reader) ([]byte, bool) { | ||
| 331 | block.SkipSpaces() | ||
| 332 | line, _ := block.PeekLine() | ||
| 333 | if block.Peek() == '<' { | ||
| 334 | i := 1 | ||
| 335 | for i < len(line) { | ||
| 336 | c := line[i] | ||
| 337 | if c == '\\' && i < len(line)-1 && util.IsPunct(line[i+1]) { | ||
| 338 | i += 2 | ||
| 339 | continue | ||
| 340 | } else if c == '>' { | ||
| 341 | block.Advance(i + 1) | ||
| 342 | return line[1:i], true | ||
| 343 | } | ||
| 344 | i++ | ||
| 345 | } | ||
| 346 | return nil, false | ||
| 347 | } | ||
| 348 | opened := 0 | ||
| 349 | i := 0 | ||
| 350 | for i < len(line) { | ||
| 351 | c := line[i] | ||
| 352 | if c == '\\' && i < len(line)-1 && util.IsPunct(line[i+1]) { | ||
| 353 | i += 2 | ||
| 354 | continue | ||
| 355 | } else if c == '(' { | ||
| 356 | opened++ | ||
| 357 | } else if c == ')' { | ||
| 358 | opened-- | ||
| 359 | if opened < 0 { | ||
| 360 | break | ||
| 361 | } | ||
| 362 | } else if util.IsSpace(c) { | ||
| 363 | break | ||
| 364 | } | ||
| 365 | i++ | ||
| 366 | } | ||
| 367 | block.Advance(i) | ||
| 368 | return line[:i], len(line[:i]) != 0 | ||
| 369 | } | ||
| 370 | |||
| 371 | func parseLinkTitle(block text.Reader) ([]byte, bool) { | ||
| 372 | block.SkipSpaces() | ||
| 373 | opener := block.Peek() | ||
| 374 | if opener != '"' && opener != '\'' && opener != '(' { | ||
| 375 | return nil, false | ||
| 376 | } | ||
| 377 | closer := opener | ||
| 378 | if opener == '(' { | ||
| 379 | closer = ')' | ||
| 380 | } | ||
| 381 | block.Advance(1) | ||
| 382 | segments, found := block.FindClosure(opener, closer, linkFindClosureOptions) | ||
| 383 | if found { | ||
| 384 | if segments.Len() == 1 { | ||
| 385 | return block.Value(segments.At(0)), true | ||
| 386 | } | ||
| 387 | var title []byte | ||
| 388 | for i := 0; i < segments.Len(); i++ { | ||
| 389 | s := segments.At(i) | ||
| 390 | title = append(title, block.Value(s)...) | ||
| 391 | } | ||
| 392 | return title, true | ||
| 393 | } | ||
| 394 | return nil, false | ||
| 395 | } | ||
| 396 | |||
| 397 | func (s *linkParser) CloseBlock(parent ast.Node, block text.Reader, pc Context) { | ||
| 398 | pc.Set(linkBottom, nil) | ||
| 399 | tlist := pc.Get(linkLabelStateKey) | ||
| 400 | if tlist == nil { | ||
| 401 | return | ||
| 402 | } | ||
| 403 | for s := tlist.(*linkLabelState); s != nil; { | ||
| 404 | next := s.Next | ||
| 405 | removeLinkLabelState(pc, s) | ||
| 406 | s.Parent().ReplaceChild(s.Parent(), s, ast.NewTextSegment(s.Segment)) | ||
| 407 | s = next | ||
| 408 | } | ||
| 409 | } | ||
