Module: Presently::Slide::Parser
- Defined in:
- lib/presently/slide.rb
Overview
Parses a Markdown slide file into structured data for Presently::Slide.
Handles YAML front_matter extraction, presenter note separation, and Markdown AST construction via Markly.
Class Method Summary collapse
-
.expand_includes!(document, base_dir, depth: 0) ⇒ Object
Expand ‘![[path/to/file.md]]` include directives in a parsed document.
-
.load(path) ⇒ Object
Parse the file and return a Presently::Slide.
-
.parse_sections(document) ⇒ Object
Parse a Markly document into content sections based on top-level headings.
Class Method Details
.expand_includes!(document, base_dir, depth: 0) ⇒ Object
Expand ‘![[path/to/file.md]]` include directives in a parsed document.
Scans top-level paragraph nodes for the Obsidian-style embed syntax and replaces each one with the parsed AST of the referenced file. Includes are resolved relative to ‘base_dir`. Front matter in included files is stripped. Nested includes are expanded recursively up to a depth of 10.
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/presently/slide.rb', line 127 def (document, base_dir, depth: 0) raise "Include depth limit exceeded" if depth > 10 # Collect matching paragraphs first — mutating the tree while iterating is unsafe. to_replace = [] document.each do |node| next unless node.type == :paragraph child = node.first_child next unless child && child.next.nil? && child.type == :text next unless child.string_content =~ /\A!\[\[(.+?)\]\]\z/ to_replace << [node, $1.strip] end to_replace.each do |paragraph, relative_path| included_path = File.(relative_path, base_dir) included_raw = File.read(included_path) included_document = Markly.parse(included_raw, flags: Markly::UNSAFE | Markly::FRONT_MATTER, extensions: Fragment::EXTENSIONS) # Strip front matter from included file if present. front_matter_node = included_document.first_child if front_matter_node&.type == :front_matter front_matter_node.delete end (included_document, File.dirname(included_path), depth: depth + 1) included_document.each{|node| paragraph.insert_before(node.dup)} paragraph.delete end end |
.load(path) ⇒ Object
Parse the file and return a Presently::Slide.
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/presently/slide.rb', line 66 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) (document, File.dirname(path)) # 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 |
.parse_sections(document) ⇒ Object
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 Fragment wrapping a document node.
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/presently/slide.rb', line 164 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 |