func DetectImage(s *Context) (consumed int) { rest := s.Buf[s.Pos:] if !bytes.HasPrefix(rest, []byte(`![`)) { return 0 } m := reImageTagStarter.FindSubmatch(rest) if m == nil { return 2 } altText, residual := m[1], m[3] // e.g.: "] [ref id]" ? r := reImageRef.FindSubmatch(residual) if r != nil { tag := rest[:len(rest)-len(residual)+len(r[0])] refID := mdutils.Simplify(r[1]) // NOTE(akavel): below refID resolution seems not in spec, but expected according to testdata/test/span_level/image{/expected,}/link_text_with_newline.* and makes sense to me as such. // TODO(akavel): send fix for this to the spec if refID == "" { refID = mdutils.Simplify(altText) } s.Emit(tag, md.Image{ AltText: mdutils.DeEscape(string(altText)), ReferenceID: refID, RawEnd: md.Raw{ md.Run{-1, r[0]}, }, }, true) return len(tag) } // e.g.: "] ("... consumed = imageParen(s, altText, len(rest)-len(residual), residual) if consumed > 0 { return consumed } // "neither of the above conditions" closing := residual[:1] r = reImageEmptyRef.FindSubmatch(residual) if r != nil { closing = r[0] } tag := rest[:len(rest)-len(residual)+len(closing)] s.Emit(tag, md.Image{ ReferenceID: mdutils.Simplify(altText), AltText: mdutils.DeEscape(string(altText)), RawEnd: md.Raw{ md.Run{-1, closing}, }, }, true) return len(tag) }
func imageParen(s *Context, altText []byte, prefix int, residual []byte) (consumed int) { // fmt.Println("imageParen @", s.Pos, string(s.Buf[s.Pos:s.Pos+prefix])) // e.g.: "] (" if !reImageParen.Match(residual) { // fmt.Println("no ](") return 0 } // fmt.Println("yes ](") r := reImageURLWithoutAngle.FindSubmatch(residual) if r == nil { r = reImageURLWithAngle.FindSubmatch(residual) } if r == nil { // fmt.Println("no imgurl") return 0 } // fmt.Println("yes imgurl") unprocessedSrc, attrs := r[1], r[2] a := reImageAttrParen.FindSubmatch(attrs) if a == nil { a = reImageAttrTitle.FindSubmatch(attrs) } if a == nil { // fmt.Println("no imgattr") return 0 } // fmt.Println("yes imgattr") title := "" if len(a) >= 2 { unprocessedTitle := a[1][1 : len(a[1])-1] title = strings.Replace(string(unprocessedTitle), "\x0a", "", -1) } rest := s.Buf[s.Pos:] tag := rest[:prefix+(len(residual)-len(attrs))+len(a[0])] s.Emit(tag, md.Image{ // TODO(akavel): keep only raw slices as fields, add methods to return processed strings URL: mdutils.DelWhites(string(unprocessedSrc)), Title: mdutils.DeEscape(title), AltText: mdutils.DeEscape(string(altText)), RawEnd: md.Raw{ md.Run{-1, tag[prefix:]}, }, }, true) return len(tag) }
func closingLinkTag(s *Context) (consumed int) { // TODO(akavel): refactor to use s.PopTo (or not, for efficiency?) if s.Openings.NullTopmostTagged("[") { return 1 // consume the ']' } rest := s.Buf[s.Pos:] // e.g.: "] [ref id]" ? m := reClosingTagRef.FindSubmatch(rest) if m != nil { // cancel all unclosed spans inside the link for s.Openings.Peek().Tag != "[" { s.Openings.Pop() } // emit a link opening := s.Openings.Peek() s.Emit(s.Buf[opening.Pos:][:len(opening.Tag)], md.Link{ ReferenceID: mdutils.Simplify(m[1]), RawEnd: md.Raw{ md.Run{-1, m[0]}, }, }, false) s.Emit(m[0], md.End{}, false) s.Openings.Pop() // cancel all unclosed links s.Openings.deleteLinks() return len(m[0]) } // e.g.: "] (http://www.example.net"... ? m = reClosingTagWithoutAngle.FindSubmatch(rest) if m == nil { // e.g.: "] ( <http://example.net/?q=)>"... ? m = reClosingTagWithAngle.FindSubmatch(rest) } if m != nil { linkURL := mdutils.DelWhites(string(m[1])) residual := m[2] title := "" t := reJustClosingParen.FindSubmatch(residual) if t == nil { t = reTitleAndClosingParen.FindSubmatch(residual) } if t != nil { if len(t) > 1 { attribs := t[1] unquoted := attribs[1 : len(attribs)-1] title = strings.Replace(string(unquoted), "\u000a", "", -1) } // cancel all unclosed spans inside the link for s.Openings.Peek().Tag != "[" { s.Openings.Pop() } // emit a link opening := s.Openings.Peek() closing := rest[:len(rest)-len(residual)+len(t[0])] s.Emit(s.Buf[opening.Pos:][:len(opening.Tag)], md.Link{ URL: linkURL, Title: mdutils.DeEscape(title), RawEnd: md.Raw{ md.Run{-1, closing}, }, }, false) s.Emit(closing, md.End{}, false) s.Openings.Pop() // cancel all unclosed links s.Openings.deleteLinks() return len(closing) } } // e.g.: "] []" ? m = reEmptyRef.FindSubmatch(rest) if m == nil { // just: "]" m = [][]byte{rest[:1]} } // cancel all unclosed spans inside the link for s.Openings.Peek().Tag != "[" { s.Openings.Pop() } // emit a link begin := s.Openings.Peek() s.Emit(s.Buf[begin.Pos:][:len(begin.Tag)], md.Link{ ReferenceID: mdutils.Simplify(s.Buf[begin.Pos+len(begin.Tag) : s.Pos]), RawEnd: md.Raw{ md.Run{-1, m[0]}, }, }, false) s.Emit(m[0], md.End{}, false) s.Openings.Pop() // cancel all unclosed links s.Openings.deleteLinks() return len(m[0]) }
func DetectReferenceResolution(start, second Line, detectors Detectors) Handler { if start.hasFourSpacePrefix() { return nil } m := reRefResolution1.FindSubmatch(bytes.TrimRight(start.Bytes, "\n")) if len(m) == 0 { return nil } b := md.ReferenceResolutionBlock{} unprocessedReferenceID := m[1] b.ReferenceID = mdutils.Simplify(unprocessedReferenceID) refValueSequence := m[9] // TODO(akavel): verify if right one m = reRefResolution2.FindSubmatch(refValueSequence) if len(m) == 0 { return nil } unprocessedURL := m[1] { tmp := make([]byte, 0, len(unprocessedURL)) for _, c := range unprocessedURL { if c != ' ' && c != '<' && c != '>' { tmp = append(tmp, c) } } b.URL = string(tmp) } refDefinitionTrailingSequence := m[2] // Detected ok. Now check if 1 or 2 lines. var nlines int titleContainer := "" if bytes.IndexAny(refDefinitionTrailingSequence, " ") == -1 && !second.EOF() && reRefResolution3.Match(bytes.TrimRight(second.Bytes, "\n")) { nlines = 2 titleContainer = string(bytes.TrimRight(second.Bytes, "\n")) } else { nlines = 1 titleContainer = string(refDefinitionTrailingSequence) } // NOTE(akavel): below line seems not in the spec, but seems necessary (refDefinitionTrailingSequence always starts with space IIUC). titleContainer = strings.TrimLeft(titleContainer, " ") if m := reRefResolution4.FindStringSubmatch(titleContainer); len(m) != 0 { b.Title = mdutils.DeEscape(m[1]) } if s := hasQuotedStringPrefix(titleContainer); s != "" { b.Title = mdutils.DeEscape(s[1 : len(s)-1]) } return HandlerFunc(func(line Line, ctx Context) (bool, error) { if nlines == 0 { return false, nil } nlines-- b.Raw = append(b.Raw, md.Run(line)) if nlines == 0 { ctx.Emit(b) ctx.Emit(md.End{}) } return true, nil }) }