Example #1
0
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)
}
Example #2
0
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)
}
Example #3
0
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])
}
Example #4
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
	})
}