Property definitions

markdown2 $ MdListBlockParserFactory :: defaultinit
# List blocks parser factory
class MdListBlockParserFactory
	super MdBlockQuoteParserFactory

	redef fun try_start(state, matched_block_parser) do
		if state.indent >= 4 and not matched_block_parser isa MdListBlockParser then return null

		var marker_index = state.next_non_space_index
		var marker_column = state.column + state.indent

		var in_paragraph = matched_block_parser isa MdParagraphParser and matched_block_parser.content != null
		var list_data = parse_list_marker(state, state.line_string, marker_index, marker_column, in_paragraph)
		if list_data == null then return null


		var new_column = list_data.content_column
		var list_item_parser = new MdListItemParser(
			state.line,
			state.column + 1,
			new_column,
			new_column - state.column)

		# prepend the list block if needed
		if not matched_block_parser isa MdListBlockParser or not lists_match(matched_block_parser.block, list_data) then
			var list_block_parser = new MdListBlockParser(state.line, state.column + 1, new_column - state.column, list_data.is_ordered, list_data.bullet, list_data.digit, list_data.delim)
			list_block_parser.block.is_tight = true

			return (new MdBlockStart([list_block_parser, list_item_parser: MdBlockParser])).at_column(new_column)
		end
		return (new MdBlockStart([list_item_parser])).at_column(new_column)
	end

	private fun parse_list_marker(state: MdParser, line: String, marker_index, marker_column: Int, in_paragraph: Bool): nullable MdListData do
		var rest = line.substring(marker_index, line.length - marker_index)
		var match = rest.search(re_list_marker)
		if match == null then return null

		var is_ordered
		var bullet = null
		var digit = null
		var delim = null

		var bullet_match = match.subs[0]
		if bullet_match != null then
			is_ordered = false
			bullet = bullet_match.to_s.chars[0]
		else
			is_ordered = true
			digit = match.subs[2].as(not null).to_s.to_i
			delim = match.subs[3].as(not null).to_s.chars[0]
		end

		var marker_length = match.length
		if match.to_s.has_suffix(" ") or match.to_s.has_suffix("\t") then
			marker_length -= 1
		end
		var index_after_marker = marker_index + marker_length

		# marker doesn't include tabs, so counting them as column directly is ok
		var column_after_marker = marker_column + marker_length
		# the column within the line where the content starts
		var content_column = column_after_marker

		# see at which column the content starts if there is content
		var has_content = false
		for i in [index_after_marker .. line.length[ do
			var c = line.chars[i]
			if c == '\t' then
				content_column += content_column.columns_to_next_tab_stop
			else if c == ' ' then
				content_column += 1
			else
				has_content = true
				break
			end
		end

		if in_paragraph then
			# if the list item is ordered, then start number must be 1 to interrupt a paragraph
			if is_ordered and digit != 1 then
				return null
			end
			# empty list item can not interrupt a paragraph
			if not has_content then
				return null
			end
		end

		if not has_content or (content_column - column_after_marker) > 4 then
			# if this line is blank or has a code block, default to 1 space after marker
			content_column = column_after_marker + 1
		end
		return new MdListData(is_ordered, bullet, digit, delim, content_column)
	end

	# Return true if the two list items are of the same type
	#
	# With the same delimiter and bullet character.
	# This is used in agglomerating list items into lists
	private fun lists_match(a: MdListBlock, b: MdListData): Bool do
		if a isa MdUnorderedList and not b.is_ordered then
			return a.bullet_marker == b.bullet
		else if a isa MdOrderedList and b.is_ordered then
			return a.delimiter == b.delim
		end
		return false
	end
end
lib/markdown2/markdown_block_parsing.nit:1020,1--1127,3