# frozen_string_literal: true # Released under the MIT License. # Copyright, 2020-2024, by Samuel Williams. require_relative 'renderer' module Utopia module Project class Document def initialize(text, base = nil, definition: nil, default_language: nil) @text = text @base = base @index = base&.index @definition = definition @default_language = default_language @root = nil end def root @root ||= resolve(Markly.parse(@text, extensions: [:table])) end def title child = self.root.first_child if child && child.type == :header return child.first_child.to_plaintext end end def first_child self.root.first_child end def replace_section(name) child = self.first_child while child if child.type == :header header = child # We found the matched header: if header.first_child.to_plaintext.include?(name) # Now subsequent children: current = header.next # Delete everything in the section until we encounter another header: while current && current.type != :header current_next = current.next current.delete current = current_next end return yield(header) end end child = child.next end end def to_html(node = self.root, **options) renderer = Renderer.new(ids: true, flags: Markly::UNSAFE, **options) XRB::MarkupString.raw(renderer.render(node)) end def paragraph_node(child) node = Markly::Node.new(:paragraph) node.append_child(child) return node end def html_node(content, type = :html) node = Markly::Node.new(:html) node.string_content = content return node end def inline_html_node(content) node = Markly::Node.new(:inline_html) node.string_content = content return node end def text_node(content) node = Markly::Node.new(:text) node.string_content = content return node end def link_node(title, url, child) node = Markly::Node.new(:link) node.title = title node.url = url.to_s node.append_child(child) return node end def code_node(content, language = nil) if language node = inline_html_node( "#{XRB::Strings.to_html(content)}" ) else node = Markly::Node.new(:code) node.string_content = content return node end return node end private # Replace source code references in the given text with HTML anchors. # def reference_node(content) if reference = @index.languages.parse_reference(content, default_language: @default_language) definition = @index.lookup(reference, relative_to: @definition) end if definition link_node(reference.identifier, @base.link_for(definition), code_node(definition.qualified_form, reference.language.name) ) elsif reference code_node(reference.identifier, reference.language.name) else code_node(content) end end def resolve(root) return root if @index.nil? root.walk do |node| if node.type == :text content = node.string_content offset = 0 while match = content.match(/{(?.*?)}/, offset) a, b = match.offset(0) if a > offset node.insert_before( text_node(content[offset...a]) ) end node.insert_before( reference_node(match[:reference]) ) offset = b end if offset == content.bytesize node.delete else node.string_content = content[offset..-1] end end end return root end end end end