09995cde5c
Example: * Text: "strong and emphasized only" * Markups: * Strong: 0..10 * Emphasis: 7..21 First, get all the borders of the markups, including the start (0) and end (text.size) indexes of the text in order: ``` [0, 7, 10, 21, 26] ``` Then attach markups to each range. Note that the ranges are exclusive; they don't include the final number: * 0...7: Strong * 7...10: Strong, Emphasized * 10...21: Emphasized * 21...26: N/A Bundle each range and it's related markups into a value object RangeWithMarkup and return the list. Loop through that list and recursively apply each markup to each segment of text: * Apply a `Strong` markup to the text "strong " * Apply a `Strong` markup to the text "and" * Wrap that in an `Emphasis` markup * Apply an `Emphasis` markup to the text " emphasized" * Leave the text " only" as is --- This has the side effect of breaking up the nodes more than they need to be broken up. For example right now the algorithm creates this HTML: ``` <strong>strong </strong><em><strong>and</strong></em> ``` instead of: ``` <strong>strong <em>and</em></strong> ``` But that's a task for another day.
73 lines
2.2 KiB
Crystal
73 lines
2.2 KiB
Crystal
struct RangeWithMarkup
|
|
getter range : Range(Int32, Int32)
|
|
getter markups : Array(PostResponse::Markup)
|
|
|
|
def initialize(@range : Range, @markups : Array(PostResponse::Markup))
|
|
end
|
|
end
|
|
|
|
class MarkupConverter
|
|
include Nodes
|
|
|
|
getter markups : Array(PostResponse::Markup)
|
|
getter text : String
|
|
|
|
def self.convert(text : String, markups : Array(PostResponse::Markup))
|
|
new(text, markups).convert
|
|
end
|
|
|
|
def initialize(@text : String, @markups : Array(PostResponse::Markup))
|
|
end
|
|
|
|
def convert : Array(Child)
|
|
ranges.flat_map do |range_with_markups|
|
|
text_to_wrap = text[range_with_markups.range]
|
|
wrap_in_markups(text_to_wrap, range_with_markups.markups)
|
|
end
|
|
end
|
|
|
|
private def ranges
|
|
markup_boundaries = markups.flat_map { |markup| [markup.start, markup.end] }
|
|
bookended_markup_boundaries = ([0] + markup_boundaries + [text.size]).uniq.sort
|
|
bookended_markup_boundaries.each_cons(2).map do |boundaries|
|
|
range = (boundaries[0]...boundaries[1])
|
|
covered_markups = markups.select do |markup|
|
|
range.covers?(markup.start) || range.covers?(markup.end - 1)
|
|
end
|
|
RangeWithMarkup.new(range, covered_markups)
|
|
end.to_a
|
|
end
|
|
|
|
def wrap_in_markups(child : String | Child, markups : Array(PostResponse::Markup)) : Array(Child)
|
|
if child.is_a?(String)
|
|
child = Text.new(child)
|
|
end
|
|
if markups.first?.nil?
|
|
return [child] of Child
|
|
end
|
|
marked_up = markup_node_in_container(child, markups[0])
|
|
wrap_in_markups(marked_up, markups[1..])
|
|
end
|
|
|
|
private def markup_node_in_container(child : Child, markup : PostResponse::Markup)
|
|
case markup.type
|
|
when PostResponse::MarkupType::A
|
|
if href = markup.href
|
|
Anchor.new(href: href, children: [child] of Child)
|
|
elsif userId = markup.userId
|
|
UserAnchor.new(userId: userId, children: [child] of Child)
|
|
else
|
|
Empty.new
|
|
end
|
|
when PostResponse::MarkupType::CODE
|
|
Code.new(children: [child] of Child)
|
|
when PostResponse::MarkupType::EM
|
|
Emphasis.new(children: [child] of Child)
|
|
when PostResponse::MarkupType::STRONG
|
|
Strong.new(children: [child] of Child)
|
|
else
|
|
Code.new(children: [child] of Child)
|
|
end
|
|
end
|
|
end
|