diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e7c6c0..110e110 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,11 @@ ## [Unreleased] - Added support for wikilinks that embed files. These are rendered as images or links in the HTML content. -- Added method `#page.generate_html` to replace `#page.content.generate_html`, and removed the `MarkdownDocument` class. `#page.content` is now a callable that returns the markdown content. -- Added `#mark_referenced` and `#referenced?` to page objects -- Added `#prune!` method to page objects, to remove non-referenced pages +- Added method `Page#generate_html` to replace `Page#content.generate_html`, and removed the `MarkdownDocument` class. `Page#content` is now a callable that returns the markdown content. +- Added `Page#mark_referenced` and `Page#referenced?` +- Added `Page#prune!` method to page objects, to remove non-referenced pages +- Added `Page#parse` method to expose `ParsedMarkdownDocument` objects +- Added `#frontmatter` to `ParsedMarkdownDocument` ## [0.7.0] - 2023-08-03 diff --git a/SMELLS.txt b/SMELLS.txt new file mode 100644 index 0000000..7895735 --- /dev/null +++ b/SMELLS.txt @@ -0,0 +1,9 @@ +Bad naming + +- MarkdownParser does more than parse markdown - it takes a file from the vault and parses it into an object that can be inspected +- The document argument of ParsedMarkdownDocument is very vague + +Huge classes + +- Page has lots of optional arguments that should not really be optional +- Could perhaps extract out some graph behaviour to a mixin diff --git a/lib/obsidian/parser/frontmatter_parser.rb b/lib/obsidian/parser/frontmatter_parser.rb index eb2b80c..52232a6 100644 --- a/lib/obsidian/parser/frontmatter_parser.rb +++ b/lib/obsidian/parser/frontmatter_parser.rb @@ -8,19 +8,27 @@ class Obsidian::MarkdownParser::FrontMatterParser def parse(content) enumerator = content.each_line first_line = enumerator.next + complete = false if first_line.chomp != "---" + puts "bye" return {} end lines = [] loop do line = enumerator.next - break if line.chomp == "---" + if line.chomp == "---" + complete = true + break + end lines << line end - YAML.safe_load(lines.join) + puts lines + puts complete + + complete ? YAML.safe_load(lines.join) : {} rescue YAML::SyntaxError, StopIteration {} end diff --git a/lib/obsidian/parser/markdown_parser.rb b/lib/obsidian/parser/markdown_parser.rb index 6bfc06d..acc65c4 100644 --- a/lib/obsidian/parser/markdown_parser.rb +++ b/lib/obsidian/parser/markdown_parser.rb @@ -28,6 +28,11 @@ class MarkdownParser \]\] }x + def initialize(frontmatter_parser: FrontMatterParser.new, renderer: HtmlRenderer.new) + @frontmatter_parser = frontmatter_parser + @renderer = renderer + end + # Convert Obsidian-flavored-markdown syntax to something parseable # (i.e. with Github-flavored-markdown syntax) def expand_wikilinks(markdown_text, root:) @@ -79,11 +84,16 @@ def expand_attachments(markdown_text, root:, media_root:) end def parse(markdown_text, root: nil, media_root: nil) - renderer = HtmlRenderer.new + frontmatter = frontmatter_parser.parse(markdown_text) normalized1 = expand_attachments(markdown_text, root: root, media_root: media_root) normalized2 = expand_wikilinks(normalized1, root: root) document = Markly.parse(normalized2, flags: Markly::SMART | Markly::UNSAFE | Markly::HARD_BREAKS, extensions: [:table, :tasklist, :autolink]) - Obsidian::ParsedMarkdownDocument.new(document, renderer: renderer) + Obsidian::ParsedMarkdownDocument.new(document, renderer: renderer, frontmatter: frontmatter) end + + private + + attr_reader :renderer + attr_reader :frontmatter_parser end end diff --git a/lib/obsidian/parser/page.rb b/lib/obsidian/parser/page.rb index 4cd428f..203bf3a 100644 --- a/lib/obsidian/parser/page.rb +++ b/lib/obsidian/parser/page.rb @@ -161,10 +161,14 @@ def find_in_tree(query_slug) nil end - def generate_html(markdown_parser: MarkdownParser.new) + def parse(markdown_parser: MarkdownParser.new) return nil if content.nil? - markdown_parser.parse(content.call, root: root, media_root: media_root).to_html + markdown_parser.parse(content.call, root: root, media_root: media_root) + end + + def generate_html(markdown_parser: MarkdownParser.new) + parse(markdown_parser: markdown_parser).to_html end def referenced? diff --git a/lib/obsidian/parser/parsed_markdown_document.rb b/lib/obsidian/parser/parsed_markdown_document.rb index 39a809d..b68b918 100644 --- a/lib/obsidian/parser/parsed_markdown_document.rb +++ b/lib/obsidian/parser/parsed_markdown_document.rb @@ -2,9 +2,11 @@ module Obsidian class ParsedMarkdownDocument - def initialize(document, renderer:) + def initialize(document, renderer:, frontmatter: {}) @document = document @renderer = renderer + @links = extract_links + @frontmatter = frontmatter end def extract_links @@ -25,6 +27,9 @@ def to_html renderer.render(document) end + attr_reader :frontmatter + attr_reader :links + private attr_reader :document diff --git a/spec/obsidian/parser/frontmatter_parser.rb b/spec/obsidian/parser/frontmatter_parser_spec.rb similarity index 99% rename from spec/obsidian/parser/frontmatter_parser.rb rename to spec/obsidian/parser/frontmatter_parser_spec.rb index 740b53f..0ed9371 100644 --- a/spec/obsidian/parser/frontmatter_parser.rb +++ b/spec/obsidian/parser/frontmatter_parser_spec.rb @@ -2,6 +2,7 @@ RSpec.describe(Obsidian::MarkdownParser::FrontMatterParser) do subject(:parser) { described_class.new } + it "parses valid yaml" do content = %(--- foo: 1 diff --git a/spec/obsidian/parser/markdown_parser_spec.rb b/spec/obsidian/parser/markdown_parser_spec.rb index fde6aba..6fec535 100644 --- a/spec/obsidian/parser/markdown_parser_spec.rb +++ b/spec/obsidian/parser/markdown_parser_spec.rb @@ -1,9 +1,10 @@ # frozen_string_literal: true RSpec.describe Obsidian::MarkdownParser do - describe ".expand_wikilinks" do + subject(:parser) { described_class.new } + + describe "#expand_wikilinks" do let(:index) { Obsidian::Page.create_root } - subject(:parser) { described_class.new } before do index.add_page("foo/bar") @@ -110,4 +111,23 @@ end end end + + describe "#parse" do + it "parses frontmatter if available" do + content = %(--- + foo: 1 + bar: banana +--- + some text + ) + result = parser.parse(content) + expect(result.frontmatter).to eq("foo" => 1, "bar" => "banana") + end + + it "returns empty frontmatter if not available" do + content = "some text" + result = parser.parse(content) + expect(result.frontmatter).to eq({}) + end + end end