def self.next_section(reader, parent, attributes = {})
preamble = false
part = false
intro = false
if parent.context == :document && parent.blocks.empty? &&
((has_header = parent.has_header?) || attributes.delete('invalid-header') || !is_next_line_section?(reader, attributes))
doctype = parent.doctype
if has_header || (doctype == 'book' && attributes[1] != 'abstract')
preamble = intro = Block.new(parent, :preamble, :content_model => :compound)
if doctype == 'book' && (parent.attr? 'preface-title')
preamble.title = parent.attr 'preface-title'
end
parent << preamble
end
section = parent
current_level = 0
if parent.attributes.has_key? 'fragment'
expected_next_levels = nil
elsif doctype == 'book'
expected_next_levels = [0, 1]
else
expected_next_levels = [1]
end
else
doctype = parent.document.doctype
section = initialize_section(reader, parent, attributes)
attributes = (title = attributes['title']) ? { 'title' => title } : {}
current_level = section.level
if current_level == 0 && doctype == 'book'
part = !section.special
if section.special && (['preface', 'appendix'].include? section.sectname)
expected_next_levels = [current_level + 2]
else
expected_next_levels = [current_level + 1]
end
else
expected_next_levels = [current_level + 1]
end
end
reader.skip_blank_lines
while reader.has_more_lines?
parse_block_metadata_lines(reader, section, attributes)
if (next_level = is_next_line_section? reader, attributes)
next_level += section.document.attr('leveloffset', 0).to_i
if next_level > current_level || (section.context == :document && next_level == 0)
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
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
document = parent
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