PresentlySourcePresentlySlideParser

module Parser

Parses a Markdown slide file into structured data for class Presently::Slide.

Handles YAML front_matter extraction, presenter note separation, and Markdown AST construction via Markly.

Definitions

def load(path)

Parse the file and return a class Presently::Slide.

Signature

parameter path String

The file path to parse.

returns Slide

Implementation

def load(path)
	raw = File.read(path)
	
	# Parse once, with native front matter support.
	document = Markly.parse(raw, flags: Markly::UNSAFE | Markly::FRONT_MATTER, extensions: Fragment::EXTENSIONS)
	
	# Extract front matter from the first AST node if present.
	front_matter = nil
	if (front_matter_node = document.first_child) && front_matter_node.type == :front_matter
		front_matter = YAML.safe_load(front_matter_node.string_content)
		front_matter_node.delete
	end
	
	# Find the last hrule, which acts as the separator between slide content and presenter notes.
	last_hrule = nil
	document.each{|node| last_hrule = node if node.type == :hrule}
	
	if last_hrule
		notes_node = Markly::Node.new(:document)
		while child = last_hrule.next
			notes_node.append_child(child)
		end
		last_hrule.delete
		
		# Extract the last javascript code block from the notes as the slide script.
		script_node = nil
		notes_node.each do |node|
			if node.type == :code_block && node.fence_info.to_s.strip == "javascript"
				script_node = node
			end
		end
		
		script = nil
		if script_node
			script = script_node.string_content
			script_node.delete
		end
		
		content = parse_sections(document)
		notes = Fragment.new(notes_node)
	else
		content = parse_sections(document)
		notes = nil
		script = nil
	end
	
	Slide.new(path, front_matter: front_matter, content: content, notes: notes, script: script)
end

def parse_sections(document)

Parse a Markly document into content sections based on top-level headings.

Each heading becomes a named key; content before the first heading is collected under "body". Each value is a class Presently::Slide::Fragment wrapping a document node.

Signature

parameter document Markly::Node

The document to parse.

returns Hash(String, Fragment)

Sections keyed by heading name.

Implementation

def parse_sections(document)
	sections = {}
	current_key = "body"
	current_node = Markly::Node.new(:document)
	
	document.each do |node|
		if node.type == :header
			sections[current_key] = Fragment.new(current_node) unless current_node.first_child.nil?
			current_key = node.to_plaintext.strip.downcase.gsub(/\s+/, "_")
			current_node = Markly::Node.new(:document)
		else
			current_node.append_child(node.dup)
		end
	end
	
	sections[current_key] = Fragment.new(current_node) unless current_node.first_child.nil?
	
	sections
end