def self.next_section reader, parent, attributes = {}
preamble = intro = part = false
if parent.context == :document && parent.blocks.empty? && ((has_header = parent.has_header?) ||
(attributes.delete 'invalid-header') || !(is_next_line_section? reader, attributes))
doctype = (document = parent).doctype
if has_header || (doctype == 'book' && attributes[1] != 'abstract')
preamble = intro = (Block.new parent, :preamble, :content_model => :compound)
preamble.title = parent.attr 'preface-title' if doctype == 'book' && (parent.attr? 'preface-title')
parent << preamble
end
section = parent
current_level = 0
if parent.attributes.key? 'fragment'
expected_next_levels = nil
elsif doctype == 'book'
expected_next_levels = [0, 1]
else
expected_next_levels = [1]
end
else
doctype = (document = parent.document).doctype
section = initialize_section reader, parent, attributes
attributes = (title = attributes['title']) ? { 'title' => title } : {}
part = section.sectname == 'part'
expected_next_levels = [(current_level = section.level) + 1]
end
reader.skip_blank_lines
while reader.has_more_lines?
parse_block_metadata_lines reader, document, attributes
if (next_level = is_next_line_section?(reader, attributes))
next_level += document.attr('leveloffset').to_i if document.attr?('leveloffset')
if next_level > current_level || (next_level == 0 && section.context == :document)
if next_level == 0 && doctype != 'book'
warn %(asciidoctor: ERROR: #{reader.line_info}: only book doctypes can contain level 0 sections)
elsif expected_next_levels && !expected_next_levels.include?(next_level)
warn %(asciidoctor: WARNING: #{reader.line_info}: section title out of sequence: expected #{expected_next_levels.size > 1 ? 'levels' : 'level'} #{expected_next_levels * ' or '}, got level #{next_level})
end
new_section, attributes = next_section reader, section, attributes
section << new_section
else
if next_level == 0 && doctype != 'book'
warn %(asciidoctor: ERROR: #{reader.line_info}: only book doctypes can contain level 0 sections)
end
break
end
else
block_line_info = reader.line_info
if (new_block = next_block reader, intro || section, attributes, :parse_metadata => false)
if part
if !section.blocks?
if new_block.style != 'partintro'
if new_block.context == :paragraph
new_block.context = :open
new_block.style = 'partintro'
else
intro = Block.new section, :open, :content_model => :compound
intro.style = 'partintro'
new_block.parent = intro
section << intro
end
end
elsif section.blocks.size == 1
first_block = section.blocks[0]
if !intro && first_block.content_model == :compound
warn %(asciidoctor: ERROR: #{block_line_info}: illegal block content outside of partintro block)
elsif first_block.content_model != :compound
intro = Block.new section, :open, :content_model => :compound
intro.style = 'partintro'
section.blocks.shift
if first_block.style == 'partintro'
first_block.context = :paragraph
first_block.style = nil
end
first_block.parent = intro
intro << first_block
new_block.parent = intro
section << intro
end
end
end
(intro || section) << new_block
attributes = {}
end
end
reader.skip_blank_lines || break
end
if part
unless section.blocks? && section.blocks[-1].context == :section
warn %(asciidoctor: ERROR: #{reader.line_info}: invalid part, must have at least one section (e.g., chapter, appendix, etc.))
end
elsif preamble
if preamble.blocks?
if Compliance.unwrap_standalone_preamble && document.blocks.size == 1 && doctype != 'book'
document.blocks.shift
while (child_block = preamble.blocks.shift)
child_block.parent = document
document << child_block
end
end
else
document.blocks.shift
end
end
[section != parent ? section : nil, attributes.dup]
end