package parser

import (
	
	
	
	
	

	
)

// Parsing block-level elements.

const (
	charEntity = "&(?:#x[a-f0-9]{1,8}|#[0-9]{1,8}|[a-z][a-z0-9]{1,31});"
	escapable  = "[!\"#$%&'()*+,./:;<=>?@[\\\\\\]^_`{|}~-]"
)

const (
	captionTable  = "Table: "
	captionFigure = "Figure: "
	captionQuote  = "Quote: "
)

var (
	reBackslashOrAmp      = regexp.MustCompile(`[\&]`)
	reEntityOrEscapedChar = regexp.MustCompile(`(?i)\\` + escapable + "|" + charEntity)

	// blockTags is a set of tags that are recognized as HTML block tags.
	// Any of these can be included in markdown text without special escaping.
	blockTags = map[string]struct{}{
		"blockquote": {},
		"del":        {},
		"dd":         {},
		"div":        {},
		"dl":         {},
		"dt":         {},
		"fieldset":   {},
		"form":       {},
		"h1":         {},
		"h2":         {},
		"h3":         {},
		"h4":         {},
		"h5":         {},
		"h6":         {},
		// TODO: technically block but breaks Inline HTML (Simple).text
		//"hr":         {},
		"iframe":   {},
		"ins":      {},
		"li":       {},
		"math":     {},
		"noscript": {},
		"ol":       {},
		"pre":      {},
		"p":        {},
		"script":   {},
		"style":    {},
		"table":    {},
		"ul":       {},

		// HTML5
		"address":    {},
		"article":    {},
		"aside":      {},
		"canvas":     {},
		"details":    {},
		"dialog":     {},
		"figcaption": {},
		"figure":     {},
		"footer":     {},
		"header":     {},
		"hgroup":     {},
		"main":       {},
		"nav":        {},
		"output":     {},
		"progress":   {},
		"section":    {},
		"svg":        {},
		"video":      {},
	}
)

// sanitizeHeadingID returns a sanitized anchor name for the given text.
// Taken from https://github.com/shurcooL/sanitized_anchor_name/blob/master/main.go#L14:1
func sanitizeHeadingID( string) string {
	var  []rune
	var  = false
	for ,  := range  {
		switch {
		case unicode.IsLetter() || unicode.IsNumber():
			if  && len() > 0 {
				 = append(, '-')
			}
			 = false
			 = append(, unicode.ToLower())
		default:
			 = true
		}
	}
	if len() == 0 {
		return "empty"
	}
	return string()
}

// Parse Block-level data.
// Note: this function and many that it calls assume that
// the input buffer ends with a newline.
func ( *Parser) ( []byte) {
	// this is called recursively: enforce a maximum depth
	if .nesting >= .maxNesting {
		return
	}
	.nesting++

	// parse out one block-level construct at a time
	for len() > 0 {
		// attributes that can be specific before a block element:
		//
		// {#id .class1 .class2 key="value"}
		if .extensions&Attributes != 0 {
			 = .attribute()
		}

		if .extensions&Includes != 0 {
			 := .readInclude
			, ,  := .isInclude()
			if  == 0 {
				, ,  = .isCodeInclude()
				 = .readCodeInclude
			}
			if  > 0 {
				 := (.includeStack.Last(), , )

				// if we find a caption below this, we need to include it in 'included', so
				// that the caption will be part of the include text. (+1 to skip newline)
				for ,  := range []string{captionFigure, captionTable, captionQuote} {
					if , ,  := .caption([+1:], []byte());  > 0 {
						 = append(, [+1:+1+]...)
						 += 1 + 
						break // there can only be 1 caption.
					}
				}
				.includeStack.Push()
				.()
				.includeStack.Pop()
				 = [:]
				continue
			}
		}

		// user supplied parser function
		if .Opts.ParserHook != nil {
			, ,  := .Opts.ParserHook()
			if  > 0 {
				 = [:]

				if  != nil {
					.AddBlock()
					if  != nil {
						.()
						.Finalize()
					}
				}
				continue
			}
		}

		// prefixed heading:
		//
		// # Heading 1
		// ## Heading 2
		// ...
		// ###### Heading 6
		if .isPrefixHeading() {
			 = [.prefixHeading():]
			continue
		}

		// prefixed special heading:
		// (there are no levels.)
		//
		// .# Abstract
		if .isPrefixSpecialHeading() {
			 = [.prefixSpecialHeading():]
			continue
		}

		// block of preformatted HTML:
		//
		// <div>
		//     ...
		// </div>

		if len() == 0 {
			continue
		}

		if [0] == '<' {
			if  := .html(, true);  > 0 {
				 = [:]
				continue
			}
		}

		// title block
		//
		// % stuff
		// % more stuff
		// % even more stuff
		if .extensions&Titleblock != 0 {
			if [0] == '%' {
				if  := .titleBlock(, true);  > 0 {
					 = [:]
					continue
				}
			}
		}

		// blank lines.  note: returns the # of bytes to skip
		if  := IsEmpty();  > 0 {
			 = [:]
			continue
		}

		// indented code block:
		//
		//     func max(a, b int) int {
		//         if a > b {
		//             return a
		//         }
		//         return b
		//      }
		if .codePrefix() > 0 {
			 = [.code():]
			continue
		}

		// fenced code block:
		//
		// ``` go
		// func fact(n int) int {
		//     if n <= 1 {
		//         return n
		//     }
		//     return n * fact(n-1)
		// }
		// ```
		if .extensions&FencedCode != 0 {
			if  := .fencedCodeBlock(, true);  > 0 {
				 = [:]
				continue
			}
		}

		// horizontal rule:
		//
		// ------
		// or
		// ******
		// or
		// ______
		if isHRule() {
			 := skipUntilChar(, 0, '\n')
			 := ast.HorizontalRule{}
			.Literal = bytes.Trim([:], " \n")
			.AddBlock(&)
			 = [:]
			continue
		}

		// block quote:
		//
		// > A big quote I found somewhere
		// > on the web
		if .quotePrefix() > 0 {
			 = [.quote():]
			continue
		}

		// aside:
		//
		// A> The proof is too large to fit
		// A> in the margin.
		if .extensions&Mmark != 0 {
			if .asidePrefix() > 0 {
				 = [.aside():]
				continue
			}
		}

		// figure block:
		//
		// !---
		// ![Alt Text](img.jpg "This is an image")
		// ![Alt Text](img2.jpg "This is a second image")
		// !---
		if .extensions&Mmark != 0 {
			if  := .figureBlock(, true);  > 0 {
				 = [:]
				continue
			}
		}

		if .extensions&Tables != 0 {
			if  := .table();  > 0 {
				 = [:]
				continue
			}
		}

		// an itemized/unordered list:
		//
		// * Item 1
		// * Item 2
		//
		// also works with + or -
		if .uliPrefix() > 0 {
			 = [.list(, 0, 0, '.'):]
			continue
		}

		// a numbered/ordered list:
		//
		// 1. Item 1
		// 2. Item 2
		if  := .oliPrefix();  > 0 {
			 := 0
			 := byte('.')
			if  > 2 {
				if .extensions&OrderedListStart != 0 {
					 := string([:-2])
					, _ = strconv.Atoi()
					if  == 1 {
						 = 0
					}
				}
				 = [-2]
			}
			 = [.list(, ast.ListTypeOrdered, , ):]
			continue
		}

		// definition lists:
		//
		// Term 1
		// :   Definition a
		// :   Definition b
		//
		// Term 2
		// :   Definition c
		if .extensions&DefinitionLists != 0 {
			if .dliPrefix() > 0 {
				 = [.list(, ast.ListTypeDefinition, 0, '.'):]
				continue
			}
		}

		if .extensions&MathJax != 0 {
			if  := .blockMath();  > 0 {
				 = [:]
				continue
			}
		}

		// document matters:
		//
		// {frontmatter}/{mainmatter}/{backmatter}
		if .extensions&Mmark != 0 {
			if  := .documentMatter();  > 0 {
				 = [:]
				continue
			}
		}

		// anything else must look like a normal paragraph
		// note: this finds underlined headings, too
		 := .paragraph()
		 = [:]
	}

	.nesting--
}

func ( *Parser) ( ast.Node) ast.Node {
	.closeUnmatchedBlocks()

	if .attr != nil {
		if  := .AsContainer();  != nil {
			.Attribute = .attr
		}
		if  := .AsLeaf();  != nil {
			.Attribute = .attr
		}
		.attr = nil
	}
	return .addChild()
}

func ( *Parser) ( []byte) bool {
	if len() > 0 && [0] != '#' {
		return false
	}

	if .extensions&SpaceHeadings != 0 {
		 := skipCharN(, 0, '#', 6)
		if  == len() || [] != ' ' {
			return false
		}
	}
	return true
}

func ( *Parser) ( []byte) int {
	 := skipCharN(, 0, '#', 6)
	 := skipChar(, , ' ')
	 := skipUntilChar(, , '\n')
	 := 
	 := ""
	if .extensions&HeadingIDs != 0 {
		,  := 0, 0
		// find start/end of heading id
		for  = ;  < -1 && ([] != '{' || [+1] != '#'); ++ {
		}
		for  =  + 1;  <  && [] != '}'; ++ {
		}
		// extract heading id iff found
		if  <  &&  <  {
			 = string([+2 : ])
			 = 
			 =  + 1
			for  > 0 && [-1] == ' ' {
				--
			}
		}
	}
	for  > 0 && [-1] == '#' {
		if isBackslashEscaped(, -1) {
			break
		}
		--
	}
	for  > 0 && [-1] == ' ' {
		--
	}
	if  >  {
		 := &ast.Heading{
			HeadingID: ,
			Level:     ,
		}
		if  == "" && .extensions&AutoHeadingIDs != 0 {
			.HeadingID = sanitizeHeadingID(string([:]))
			.allHeadingsWithAutoID = append(.allHeadingsWithAutoID, )
		}
		.Content = [:]
		.AddBlock()
	}
	return 
}

func ( *Parser) ( []byte) bool {
	if .extensions|Mmark == 0 {
		return false
	}
	if len() < 4 {
		return false
	}
	if [0] != '.' {
		return false
	}
	if [1] != '#' {
		return false
	}
	if [2] == '#' { // we don't support level, so nack this.
		return false
	}

	if .extensions&SpaceHeadings != 0 {
		if [2] != ' ' {
			return false
		}
	}
	return true
}

func ( *Parser) ( []byte) int {
	 := skipChar(, 2, ' ') // ".#" skipped
	 := skipUntilChar(, , '\n')
	 := 
	 := ""
	if .extensions&HeadingIDs != 0 {
		,  := 0, 0
		// find start/end of heading id
		for  = ;  < -1 && ([] != '{' || [+1] != '#'); ++ {
		}
		for  =  + 1;  <  && [] != '}'; ++ {
		}
		// extract heading id iff found
		if  <  &&  <  {
			 = string([+2 : ])
			 = 
			 =  + 1
			for  > 0 && [-1] == ' ' {
				--
			}
		}
	}
	for  > 0 && [-1] == '#' {
		if isBackslashEscaped(, -1) {
			break
		}
		--
	}
	for  > 0 && [-1] == ' ' {
		--
	}
	if  >  {
		 := &ast.Heading{
			HeadingID: ,
			IsSpecial: true,
			Level:     1, // always level 1.
		}
		if  == "" && .extensions&AutoHeadingIDs != 0 {
			.HeadingID = sanitizeHeadingID(string([:]))
			.allHeadingsWithAutoID = append(.allHeadingsWithAutoID, )
		}
		.Literal = [:]
		.Content = [:]
		.AddBlock()
	}
	return 
}

func ( *Parser) ( []byte) int {
	// test of level 1 heading
	if [0] == '=' {
		 := skipChar(, 1, '=')
		 = skipChar(, , ' ')
		if  < len() && [] == '\n' {
			return 1
		}
		return 0
	}

	// test of level 2 heading
	if [0] == '-' {
		 := skipChar(, 1, '-')
		 = skipChar(, , ' ')
		if  < len() && [] == '\n' {
			return 2
		}
		return 0
	}

	return 0
}

func ( *Parser) ( []byte,  bool) int {
	if [0] != '%' {
		return 0
	}
	 := bytes.Split(, []byte("\n"))
	var  int
	for ,  := range  {
		if !bytes.HasPrefix(, []byte("%")) {
			 =  // - 1
			break
		}
	}

	 = bytes.Join([0:], []byte("\n"))
	 := len()
	 = bytes.TrimPrefix(, []byte("% "))
	 = bytes.Replace(, []byte("\n% "), []byte("\n"), -1)
	 := &ast.Heading{
		Level:        1,
		IsTitleblock: true,
	}
	.Content = 
	.AddBlock()

	return 
}

func ( *Parser) ( []byte,  bool) int {
	var ,  int

	// identify the opening tag
	if [0] != '<' {
		return 0
	}
	,  := .htmlFindTag([1:])

	// handle special cases
	if ! {
		// check for an HTML comment
		if  := .htmlComment(, );  > 0 {
			return 
		}

		// check for an <hr> tag
		if  := .htmlHr(, );  > 0 {
			return 
		}

		// no special case recognized
		return 0
	}

	// look for an unindented matching closing tag
	// followed by a blank line
	 := false
	/*
		closetag := []byte("\n</" + curtag + ">")
		j = len(curtag) + 1
		for !found {
			// scan for a closing tag at the beginning of a line
			if skip := bytes.Index(data[j:], closetag); skip >= 0 {
				j += skip + len(closetag)
			} else {
				break
			}

			// see if it is the only thing on the line
			if skip := IsEmpty(data[j:]); skip > 0 {
				// see if it is followed by a blank line/eof
				j += skip
				if j >= len(data) {
					found = true
					i = j
				} else {
					if skip := IsEmpty(data[j:]); skip > 0 {
						j += skip
						found = true
						i = j
					}
				}
			}
		}
	*/

	// if not found, try a second pass looking for indented match
	// but not if tag is "ins" or "del" (following original Markdown.pl)
	if ! &&  != "ins" &&  != "del" {
		 = 1
		for  < len() {
			++
			for  < len() && !([-1] == '<' && [] == '/') {
				++
			}

			if +2+len() >= len() {
				break
			}

			 = .htmlFindEnd(, [-1:])

			if  > 0 {
				 +=  - 1
				 = true
				break
			}
		}
	}

	if ! {
		return 0
	}

	// the end of the block has been found
	if  {
		// trim newlines
		 := backChar(, , '\n')
		 := &ast.HTMLBlock{Leaf: ast.Leaf{Content: [:]}}
		.AddBlock()
		finalizeHTMLBlock()
	}

	return 
}

func finalizeHTMLBlock( *ast.HTMLBlock) {
	.Literal = .Content
	.Content = nil
}

// HTML comment, lax form
func ( *Parser) ( []byte,  bool) int {
	 := .inlineHTMLComment()
	// needs to end with a blank line
	if  := IsEmpty([:]);  > 0 {
		 :=  + 
		if  {
			// trim trailing newlines
			 := backChar(, , '\n')
			 := &ast.HTMLBlock{Leaf: ast.Leaf{Content: [:]}}
			.AddBlock()
			finalizeHTMLBlock()
		}
		return 
	}
	return 0
}

// HR, which is the only self-closing block tag considered
func ( *Parser) ( []byte,  bool) int {
	if len() < 4 {
		return 0
	}
	if [0] != '<' || ([1] != 'h' && [1] != 'H') || ([2] != 'r' && [2] != 'R') {
		return 0
	}
	if [3] != ' ' && [3] != '/' && [3] != '>' {
		// not an <hr> tag after all; at least not a valid one
		return 0
	}
	 := 3
	for  < len() && [] != '>' && [] != '\n' {
		++
	}
	if  < len() && [] == '>' {
		++
		if  := IsEmpty([:]);  > 0 {
			 :=  + 
			if  {
				// trim newlines
				 := backChar(, , '\n')
				 := &ast.HTMLBlock{Leaf: ast.Leaf{Content: [:]}}
				.AddBlock()
				finalizeHTMLBlock()
			}
			return 
		}
	}
	return 0
}

func ( *Parser) ( []byte) (string, bool) {
	 := skipAlnum(, 0)
	 := string([:])
	if ,  := blockTags[];  {
		return , true
	}
	return "", false
}

func ( *Parser) ( string,  []byte) int {
	// assume data[0] == '<' && data[1] == '/' already tested
	if  == "hr" {
		return 2
	}
	// check if tag is a match
	 := []byte("</" +  + ">")
	if !bytes.HasPrefix(, ) {
		return 0
	}
	 := len()

	// check that the rest of the line is blank
	 := 0
	if  = IsEmpty([:]);  == 0 {
		return 0
	}
	 += 
	 = 0

	if  >= len() {
		return 
	}

	if .extensions&LaxHTMLBlocks != 0 {
		return 
	}
	if  = IsEmpty([:]);  == 0 {
		// following line must be blank
		return 0
	}

	return  + 
}

func ( []byte) int {
	// it is okay to call isEmpty on an empty buffer
	if len() == 0 {
		return 0
	}

	var  int
	for  = 0;  < len() && [] != '\n'; ++ {
		if [] != ' ' && [] != '\t' {
			return 0
		}
	}
	 = skipCharN(, , '\n', 1)
	return 
}

func isHRule( []byte) bool {
	 := 0

	// skip up to three spaces
	for  < 3 && [] == ' ' {
		++
	}

	// look at the hrule char
	if [] != '*' && [] != '-' && [] != '_' {
		return false
	}
	 := []

	// the whole line must be the char or whitespace
	 := 0
	for  < len() && [] != '\n' {
		switch {
		case [] == :
			++
		case [] != ' ':
			return false
		}
		++
	}

	return  >= 3
}

// isFenceLine checks if there's a fence line (e.g., ``` or ``` go) at the beginning of data,
// and returns the end index if so, or 0 otherwise. It also returns the marker found.
// If syntax is not nil, it gets set to the syntax specified in the fence line.
func isFenceLine( []byte,  *string,  string) ( int,  string) {
	,  := 0, 0

	 := len()
	// skip up to three spaces
	for  <  &&  < 3 && [] == ' ' {
		++
	}

	// check for the marker characters: ~ or `
	if  >=  {
		return 0, ""
	}
	if [] != '~' && [] != '`' {
		return 0, ""
	}

	 := []

	// the whole line must be the same char or whitespace
	for  <  && [] ==  {
		++
		++
	}

	// the marker char must occur at least 3 times
	if  < 3 {
		return 0, ""
	}
	 = string([- : ])

	// if this is the end marker, it must match the beginning marker
	if  != "" &&  !=  {
		return 0, ""
	}

	// if just read the beginning marker, read the syntax
	if  == "" {
		 = skipChar(, , ' ')
		if  >=  {
			if  ==  {
				return , 
			}
			return 0, ""
		}

		,  := syntaxRange(, &)
		if  == 0 &&  == 0 {
			return 0, ""
		}

		// caller wants the syntax
		if  != nil {
			* = string([ : +])
		}
	}

	 = skipChar(, , ' ')
	if  >=  || [] != '\n' {
		if  ==  {
			return , 
		}
		return 0, ""
	}
	return  + 1,  // Take newline into account.
}

func syntaxRange( []byte,  *int) (int, int) {
	 := len()
	 := 0
	 := *
	 := 
	if [] == '{' {
		++
		++

		for  <  && [] != '}' && [] != '\n' {
			++
			++
		}

		if  >=  || [] != '}' {
			return 0, 0
		}

		// strip all whitespace at the beginning and the end
		// of the {} block
		for  > 0 && IsSpace([]) {
			++
			--
		}

		for  > 0 && IsSpace([+-1]) {
			--
		}

		++
	} else {
		for  <  && [] != '\n' {
			++
			++
		}
	}

	* = 
	return , 
}

// fencedCodeBlock returns the end index if data contains a fenced code block at the beginning,
// or 0 otherwise. It writes to out if doRender is true, otherwise it has no side effects.
// If doRender is true, a final newline is mandatory to recognize the fenced code block.
func ( *Parser) ( []byte,  bool) int {
	var  string
	,  := isFenceLine(, &, "")
	if  == 0 ||  >= len() {
		return 0
	}

	var  bytes.Buffer
	.WriteString()
	.WriteByte('\n')

	for {
		// check for the end of the code block
		,  := isFenceLine([:], nil, )
		if  != 0 {
			 += 
			break
		}

		// copy the current line
		 := skipUntilChar(, , '\n') + 1

		// did we reach the end of the buffer without a closing marker?
		if  >= len() {
			return 0
		}

		// verbatim copy to the working buffer
		.Write([:])
		 = 
	}

	if ! {
		return 
	}
	 := &ast.CodeBlock{
		IsFenced: true,
	}
	.Content = .Bytes() // TODO: get rid of temp buffer

	if .extensions&Mmark == 0 {
		.AddBlock()
		finalizeCodeBlock()
		return 
	}

	// Check for caption and if found make it a figure.
	if , ,  := .caption([:], []byte(captionFigure));  > 0 {
		 := &ast.CaptionFigure{}
		 := &ast.Caption{}
		.HeadingID = 
		.Inline(, )

		.AddBlock()
		.AsLeaf().Attribute = .AsContainer().Attribute
		.addChild()
		finalizeCodeBlock()
		.addChild()
		.Finalize()

		 += 

		return 
	}

	// Still here, normal block
	.AddBlock()
	finalizeCodeBlock()

	return 
}

func unescapeChar( []byte) []byte {
	if [0] == '\\' {
		return []byte{[1]}
	}
	return []byte(html.UnescapeString(string()))
}

func unescapeString( []byte) []byte {
	if reBackslashOrAmp.Match() {
		return reEntityOrEscapedChar.ReplaceAllFunc(, unescapeChar)
	}
	return 
}

func finalizeCodeBlock( *ast.CodeBlock) {
	 := .Content
	if .IsFenced {
		 := bytes.IndexByte(, '\n')
		 := [:]
		 := [+1:]
		.Info = unescapeString(bytes.Trim(, "\n"))
		.Literal = 
	} else {
		.Literal = 
	}
	.Content = nil
}

// returns blockquote prefix length
func ( *Parser) ( []byte) int {
	 := 0
	 := len()
	for  < 3 &&  <  && [] == ' ' {
		++
	}
	if  <  && [] == '>' {
		if +1 <  && [+1] == ' ' {
			return  + 2
		}
		return  + 1
	}
	return 0
}

// blockquote ends with at least one blank line
// followed by something without a blockquote prefix
func ( *Parser) ( []byte, ,  int) bool {
	if IsEmpty([:]) <= 0 {
		return false
	}
	if  >= len() {
		return true
	}
	return .quotePrefix([:]) == 0 && IsEmpty([:]) == 0
}

// parse a blockquote fragment
func ( *Parser) ( []byte) int {
	var  bytes.Buffer
	,  := 0, 0
	for  < len() {
		 = 
		// Step over whole lines, collecting them. While doing that, check for
		// fenced code and if one's found, incorporate it altogether,
		// irregardless of any contents inside it
		for  < len() && [] != '\n' {
			if .extensions&FencedCode != 0 {
				if  := .fencedCodeBlock([:], false);  > 0 {
					// -1 to compensate for the extra end++ after the loop:
					 +=  - 1
					break
				}
			}
			++
		}
		 = skipCharN(, , '\n', 1)
		if  := .quotePrefix([:]);  > 0 {
			// skip the prefix
			 += 
		} else if .terminateBlockquote(, , ) {
			break
		}
		// this line is part of the blockquote
		.Write([:])
		 = 
	}

	if .extensions&Mmark == 0 {
		 := .AddBlock(&ast.BlockQuote{})
		.Block(.Bytes())
		.Finalize()
		return 
	}

	if , ,  := .caption([:], []byte(captionQuote));  > 0 {
		 := &ast.CaptionFigure{}
		 := &ast.Caption{}
		.HeadingID = 
		.Inline(, )

		.AddBlock() // this discard any attributes
		 := &ast.BlockQuote{}
		.AsContainer().Attribute = .AsContainer().Attribute
		.addChild()
		.Block(.Bytes())
		.Finalize()

		.addChild()
		.Finalize()

		 += 

		return 
	}

	 := .AddBlock(&ast.BlockQuote{})
	.Block(.Bytes())
	.Finalize()

	return 
}

// returns prefix length for block code
func ( *Parser) ( []byte) int {
	 := len()
	if  >= 1 && [0] == '\t' {
		return 1
	}
	if  >= 4 && [3] == ' ' && [2] == ' ' && [1] == ' ' && [0] == ' ' {
		return 4
	}
	return 0
}

func ( *Parser) ( []byte) int {
	var  bytes.Buffer

	 := 0
	for  < len() {
		 := 

		 = skipUntilChar(, , '\n')
		 = skipCharN(, , '\n', 1)

		 := IsEmpty([:]) > 0
		if  := .codePrefix([:]);  > 0 {
			 += 
		} else if ! {
			// non-empty, non-prefixed line breaks the pre
			 = 
			break
		}

		// verbatim copy to the working buffer
		if  {
			.WriteByte('\n')
		} else {
			.Write([:])
		}
	}

	// trim all the \n off the end of work
	 := .Bytes()

	 := backChar(, len(), '\n')

	if  != len() {
		.Truncate()
	}

	.WriteByte('\n')

	 := &ast.CodeBlock{
		IsFenced: false,
	}
	// TODO: get rid of temp buffer
	.Content = .Bytes()
	.AddBlock()
	finalizeCodeBlock()

	return 
}

// returns unordered list item prefix
func ( *Parser) ( []byte) int {
	// start with up to 3 spaces
	 := skipCharN(, 0, ' ', 3)

	if  >= len()-1 {
		return 0
	}
	// need one of {'*', '+', '-'} followed by a space or a tab
	if ([] != '*' && [] != '+' && [] != '-') ||
		([+1] != ' ' && [+1] != '\t') {
		return 0
	}
	return  + 2
}

// returns ordered list item prefix
func ( *Parser) ( []byte) int {
	// start with up to 3 spaces
	 := skipCharN(, 0, ' ', 3)

	// count the digits
	 := 
	for  < len() && [] >= '0' && [] <= '9' {
		++
	}
	if  ==  ||  >= len()-1 {
		return 0
	}

	// we need >= 1 digits followed by a dot and a space or a tab
	if [] != '.' && [] != ')' || !([+1] == ' ' || [+1] == '\t') {
		return 0
	}
	return  + 2
}

// returns definition list item prefix
func ( *Parser) ( []byte) int {
	if len() < 2 {
		return 0
	}
	// need a ':' followed by a space or a tab
	if [0] != ':' || !([1] == ' ' || [1] == '\t') {
		return 0
	}
	// TODO: this is a no-op (data[0] is ':' so not ' ').
	// Maybe the intent was to eat spaces before ':' ?
	// either way, no change in tests
	 := skipChar(, 0, ' ')
	return  + 2
}

// TODO: maybe it was meant to be like below
// either way, no change in tests
/*
func (p *Parser) dliPrefix(data []byte) int {
	i := skipChar(data, 0, ' ')
	if i+len(data) < 2 {
		return 0
	}
	// need a ':' followed by a space or a tab
	if data[i] != ':' || !(data[i+1] == ' ' || data[i+1] == '\t') {
		return 0
	}
	return i + 2
}
*/

// parse ordered or unordered list block
func ( *Parser) ( []byte,  ast.ListType,  int,  byte) int {
	 := 0
	 |= ast.ListItemBeginningOfList
	 := &ast.List{
		ListFlags: ,
		Tight:     true,
		Start:     ,
		Delimiter: ,
	}
	 := .AddBlock()

	for  < len() {
		 := .listItem([:], &)
		if &ast.ListItemContainsBlock != 0 {
			.Tight = false
		}
		 += 
		if  == 0 || &ast.ListItemEndOfList != 0 {
			break
		}
		 &= ^ast.ListItemBeginningOfList
	}

	 := .GetParent()
	finalizeList()
	.tip = 
	return 
}

// Returns true if the list item is not the same type as its parent list
func ( *Parser) ( []byte,  *ast.ListType) bool {
	if .dliPrefix() > 0 && *&ast.ListTypeDefinition == 0 {
		return true
	} else if .oliPrefix() > 0 && *&ast.ListTypeOrdered == 0 {
		return true
	} else if .uliPrefix() > 0 && (*&ast.ListTypeOrdered != 0 || *&ast.ListTypeDefinition != 0) {
		return true
	}
	return false
}

// Returns true if block ends with a blank line, descending if needed
// into lists and sublists.
func endsWithBlankLine( ast.Node) bool {
	// TODO: figure this out. Always false now.
	for  != nil {
		//if block.lastLineBlank {
		//return true
		//}
		switch .(type) {
		case *ast.List, *ast.ListItem:
			 = ast.GetLastChild()
		default:
			return false
		}
	}
	return false
}

func finalizeList( *ast.List) {
	 := .Parent.GetChildren()
	 := len() - 1
	for ,  := range  {
		 :=  == 
		// check for non-final list item ending with blank line:
		if ! && endsWithBlankLine() {
			.Tight = false
			break
		}
		// recurse into children of list item, to see if there are spaces
		// between any of them:
		 := .GetParent().GetChildren()
		 := len() - 1
		for ,  := range  {
			 :=  == 
			if (! || !) && endsWithBlankLine() {
				.Tight = false
				break
			}
		}
	}
}

// Parse a single list item.
// Assumes initial prefix is already removed if this is a sublist.
func ( *Parser) ( []byte,  *ast.ListType) int {
	 := *&ast.ListTypeDefinition != 0
	// keep track of the indentation of the first line
	 := 0
	if [0] == '\t' {
		 += 4
	} else {
		for  < 3 && [] == ' ' {
			++
		}
	}

	var (
		 byte = '*'
		  byte = '.'
	)
	 := .uliPrefix()
	if  == 0 {
		 = .oliPrefix()
		if  > 0 {
			 = [-2]
		}
	} else {
		 = [-2]
	}
	if  == 0 {
		 = .dliPrefix()
		// reset definition term flag
		if  > 0 {
			* &= ^ast.ListTypeTerm
		}
	}
	if  == 0 {
		// if in definition list, set term flag and continue
		if  {
			* |= ast.ListTypeTerm
		} else {
			return 0
		}
	}

	// skip leading whitespace on first line
	 = skipChar(, , ' ')

	// find the end of the line
	 := 
	for  > 0 &&  < len() && [-1] != '\n' {
		++
	}

	// get working buffer
	var  bytes.Buffer

	// put the first line into the working buffer
	.Write([:])
	 = 

	// process the following lines
	 := false
	 := 0

:
	for  < len() {
		++

		// find the end of this line
		for  < len() && [-1] != '\n' {
			++
		}

		// if it is an empty line, guess that it is part of this item
		// and move on to the next line
		if IsEmpty([:]) > 0 {
			 = true
			 = 
			continue
		}

		// calculate the indentation
		 := 0
		 := 0
		if [] == '\t' {
			++
			 += 4
		} else {
			for  < 4 && + <  && [+] == ' ' {
				++
				++
			}
		}

		 := [+ : ]

		// If there is a fence line (marking starting of a code block)
		// without indent do not process it as part of the list.
		//
		// does not apply for definition lists because it causes infinite
		// loop if text before defintion term is fenced code block start
		// marker but not part of actual fenced code block
		// for defnition lists we're called after parsing fence code blocks
		// so we kno this cannot be a fenced block
		// https://github.com/gomarkdown/markdown/issues/326
		if ! && .extensions&FencedCode != 0 {
			,  := isFenceLine(, nil, "")
			if  > 0 &&  == 0 {
				* |= ast.ListItemEndOfList
				break 
			}
		}

		// evaluate how this line fits in
		switch {
		// is this a nested list item?
		case (.uliPrefix() > 0 && !isHRule()) || .oliPrefix() > 0 || .dliPrefix() > 0:

			// if indent is 4 or more spaces on unordered or ordered lists
			// we need to add leadingWhiteSpaces + 1 spaces in the beginning of the chunk
			if  >= 4 && .dliPrefix() <= 0 {
				 := skipChar(, 0, ' ')
				 = [+-(+1) : ]
			}

			// to be a nested list, it must be indented more
			// if not, it is either a different kind of list
			// or the next item in the same list
			if  <=  {
				if .listTypeChanged(, ) {
					* |= ast.ListItemEndOfList
				} else if  {
					* |= ast.ListItemContainsBlock
				}

				break 
			}

			if  {
				* |= ast.ListItemContainsBlock
			}

			// is this the first item in the nested list?
			if  == 0 {
				 = .Len()
				// in the case of dliPrefix we are too late and need to search back for the definition item, which
				// should be on the previous line, we then adjust sublist to start there.
				if .dliPrefix() > 0 {
					 = backUntilChar(.Bytes(), .Len()-1, '\n')
				}
			}

			// is this a nested prefix heading?
		case .isPrefixHeading(), .isPrefixSpecialHeading():
			// if the heading is not indented, it is not nested in the list
			// and thus ends the list
			if  &&  < 4 {
				* |= ast.ListItemEndOfList
				break 
			}
			* |= ast.ListItemContainsBlock

		// anything following an empty line is only part
		// of this item if it is indented 4 spaces
		// (regardless of the indentation of the beginning of the item)
		case  &&  < 4:
			if *&ast.ListTypeDefinition != 0 &&  < len()-1 {
				// is the next item still a part of this list?
				 := skipUntilChar(, , '\n')
				for  < len()-1 && [] == '\n' {
					++
				}
				if  < len()-1 && [] != ':' &&  < len()-1 && [] != ':' {
					* |= ast.ListItemEndOfList
				}
			} else {
				* |= ast.ListItemEndOfList
			}
			break 

		// a blank line means this should be parsed as a block
		case :
			.WriteByte('\n')
			* |= ast.ListItemContainsBlock
		}

		// if this line was preceded by one or more blanks,
		// re-introduce the blank into the buffer
		if  {
			 = false
			.WriteByte('\n')
		}

		// add the line into the working buffer without prefix
		.Write()

		 = 
	}

	 := .Bytes()

	 := &ast.ListItem{
		ListFlags:  *,
		Tight:      false,
		BulletChar: ,
		Delimiter:  ,
	}
	.AddBlock()

	// render the contents of the list item
	if *&ast.ListItemContainsBlock != 0 && *&ast.ListTypeTerm == 0 {
		// intermediate render of block item, except for definition term
		if  > 0 {
			.Block([:])
			.Block([:])
		} else {
			.Block()
		}
	} else {
		// intermediate render of inline item
		 := &ast.Paragraph{}
		if  > 0 {
			.Content = [:]
		} else {
			.Content = 
		}
		.addChild()
		if  > 0 {
			.Block([:])
		}
	}
	return 
}

// render a single paragraph that has already been parsed out
func ( *Parser) ( []byte) {
	if len() == 0 {
		return
	}

	// trim leading spaces
	 := skipChar(, 0, ' ')

	 := len()
	// trim trailing newline
	if [len()-1] == '\n' {
		--
	}

	// trim trailing spaces
	for  >  && [-1] == ' ' {
		--
	}
	 := &ast.Paragraph{}
	.Content = [:]
	.AddBlock()
}

// blockMath handle block surround with $$
func ( *Parser) ( []byte) int {
	if len() <= 4 || [0] != '$' || [1] != '$' || [2] == '$' {
		return 0
	}

	// find next $$
	var  int
	for  = 2; +1 < len() && ([] != '$' || [+1] != '$'); ++ {
	}

	// $$ not match
	if +1 == len() {
		return 0
	}

	// render the display math
	 := &ast.MathBlock{}
	.Literal = [2:]
	.AddBlock()

	return  + 2
}

func ( *Parser) ( []byte) int {
	// prev: index of 1st char of previous line
	// line: index of 1st char of current line
	// i: index of cursor/end of current line
	var , ,  int
	 := tabSizeDefault
	if .extensions&TabSizeEight != 0 {
		 = tabSizeDouble
	}
	// keep going until we find something to mark the end of the paragraph
	for  < len() {
		// mark the beginning of the current line
		 = 
		 := [:]
		 = 

		// did we find a reference or a footnote? If so, end a paragraph
		// preceding it and report that we have consumed up to the end of that
		// reference:
		if  := isReference(, , );  > 0 {
			.renderParagraph([:])
			return  + 
		}

		// did we find a blank line marking the end of the paragraph?
		if  := IsEmpty();  > 0 {
			// did this blank line followed by a definition list item?
			if .extensions&DefinitionLists != 0 {
				if  < len()-1 && [+1] == ':' {
					 := .list([:], ast.ListTypeDefinition, 0, '.')
					if  > 0 {
						return  + 
					}
				}
			}

			.renderParagraph([:])
			return  + 
		}

		// an underline under some text marks a heading, so our paragraph ended on prev line
		if  > 0 {
			if  := .isUnderlinedHeading();  > 0 {
				// render the paragraph
				.renderParagraph([:])

				// ignore leading and trailing whitespace
				 :=  - 1
				for  <  && [] == ' ' {
					++
				}
				for  >  && [-1] == ' ' {
					--
				}

				 := &ast.Heading{
					Level: ,
				}
				if .extensions&AutoHeadingIDs != 0 {
					.HeadingID = sanitizeHeadingID(string([:]))
					.allHeadingsWithAutoID = append(.allHeadingsWithAutoID, )
				}

				.Content = [:]
				.AddBlock()

				// find the end of the underline
				return skipUntilChar(, , '\n')
			}
		}

		// if the next line starts a block of HTML, then the paragraph ends here
		if .extensions&LaxHTMLBlocks != 0 {
			if [] == '<' && .html(, false) > 0 {
				// rewind to before the HTML block
				.renderParagraph([:])
				return 
			}
		}

		// if there's a prefixed heading or a horizontal rule after this, paragraph is over
		if .isPrefixHeading() || .isPrefixSpecialHeading() || isHRule() {
			.renderParagraph([:])
			return 
		}

		// if there's a block quote, paragraph is over
		if .quotePrefix() > 0 {
			.renderParagraph([:])
			return 
		}

		// if there's a fenced code block, paragraph is over
		if .extensions&FencedCode != 0 {
			if .fencedCodeBlock(, false) > 0 {
				.renderParagraph([:])
				return 
			}
		}

		// if there's a figure block, paragraph is over
		if .extensions&Mmark != 0 {
			if .figureBlock(, false) > 0 {
				.renderParagraph([:])
				return 
			}
		}

		// if there's a table, paragraph is over
		if .extensions&Tables != 0 {
			if , ,  := .tableHeader(, false);  > 0 {
				.renderParagraph([:])
				return 
			}
		}

		// if there's a definition list item, prev line is a definition term
		if .extensions&DefinitionLists != 0 {
			if .dliPrefix() != 0 {
				 := .list([:], ast.ListTypeDefinition, 0, '.')
				return  + 
			}
		}

		// if there's a list after this, paragraph is over
		if .extensions&NoEmptyLineBeforeBlock != 0 {
			if .uliPrefix() != 0 ||
				.oliPrefix() != 0 ||
				.quotePrefix() != 0 ||
				.codePrefix() != 0 {
				.renderParagraph([:])
				return 
			}
		}

		// otherwise, scan to the beginning of the next line
		 := bytes.IndexByte([:], '\n')
		if  >= 0 {
			 +=  + 1
		} else {
			 += len([:])
		}
	}

	.renderParagraph([:])
	return 
}

// skipChar advances i as long as data[i] == c
func skipChar( []byte,  int,  byte) int {
	 := len()
	for  <  && [] ==  {
		++
	}
	return 
}

// like skipChar but only skips up to max characters
func skipCharN( []byte,  int,  byte,  int) int {
	 := len()
	for  <  &&  > 0 && [] ==  {
		++
		--
	}
	return 
}

// skipUntilChar advances i as long as data[i] != c
func skipUntilChar( []byte,  int,  byte) int {
	 := len()
	for  <  && [] !=  {
		++
	}
	return 
}

func skipAlnum( []byte,  int) int {
	 := len()
	for  <  && IsAlnum([]) {
		++
	}
	return 
}

func skipSpace( []byte,  int) int {
	 := len()
	for  <  && IsSpace([]) {
		++
	}
	return 
}

func backChar( []byte,  int,  byte) int {
	for  > 0 && [-1] ==  {
		--
	}
	return 
}

func backUntilChar( []byte,  int,  byte) int {
	for  > 0 && [-1] !=  {
		--
	}
	return 
}