class NRSER::MeanStreak::Document
@todo document NRSER::MeanStreak::Document
class.
Attributes
The root {CommonMarker::Node} (with {CommonMarker::Node#type}=`:document`).
@return [CommonMarker::Node]
The {NRSER::MeanStreak} instance associated with this document, which contains the rendering configuration.
@return [NRSER::MeanStreak]
The source string.
@return [String]
Public Class Methods
Instantiate a new `NRSER::MeanStreak::Document`.
@param mean_streak
See {NRSER::MeanStreak::Document#mean_streak}
# File lib/nrser/mean_streak/document.rb, line 99 def initialize mean_streak:, source:, doc: @mean_streak = mean_streak @source = source.dup.freeze @doc = doc end
@todo Document
from method.
@return [NRSER::MeanStreak::Document]
@todo Document return value.
# File lib/nrser/mean_streak/document.rb, line 53 def self.parse source, mean_streak:, cm_options: :DEFAULT, cm_extensions: [] new mean_streak: mean_streak, source: source, doc: CommonMarker.render_doc( source, cm_options, cm_extensions ) end
Public Instance Methods
# File lib/nrser/mean_streak/document.rb, line 350 def find_by **attrs doc.walk.find_all { |node| attrs.all? { |name, value| begin value === node.send( name ) rescue false end } } end
# File lib/nrser/mean_streak/document.rb, line 363 def find_type type doc.walk.find_all { |node| node.type == type } end
# File lib/nrser/mean_streak/document.rb, line 345 def render render_node doc end
# File lib/nrser/mean_streak/document.rb, line 279 def render_children node prev = nil parts = [] node.each do |child| unless prev.nil? between = source_between( prev, child ) # We may need to modify the source strings *surrounding* specific # nodes... # # `:code` is an example thus far: it stores the code string in # `#string_content` so - unlike `:emph` and `:strong` where the # delimiters end up in the "before first child" and "after last child" # *inside* the node's source slice - in `:code` they end up *outside*, # in the source between it and the previous and next sibling nodes. # # # if mean_streak.type_renderers[:code] # There is a renderer for the `:code` type, so assume that it will # take are of any surrounding characters for `:code` strings and # chomp off the starting and ending backticks if prev.type == :code # Previous node is `:code`, chomp off any leading backtick # between = between[1..-1] if between.start_with?( '`' ) between = between.sub /\A`+/, '' elsif child.type == :code # Current node is `:code`, chomp off any leading backtick between = between.sub /`+\z/, '' end end parts << between end parts << render_node( child ) prev = child end parts.join end
# File lib/nrser/mean_streak/document.rb, line 328 def render_node node if mean_streak.type_renderers[node.type] mean_streak.type_renderers[node.type].call self, node else if node.first # Has children source_before_first_child( node ) + render_children( node ) + source_after_last_child( node ) else # No children! Easy! source_for_node node end end end
# File lib/nrser/mean_streak/document.rb, line 323 def render_node_2 prev_node, source_before, node, source_after, next_node end
# File lib/nrser/mean_streak/document.rb, line 260 def source_after_last_child node last_child = node.each.to_a.last slice = source_byteslice( start_after: source_byte_indexes( last_child )[:last_byte][:index], end_on: source_byte_indexes( node )[:last_byte][:index] ) # See comments in {#render_children} if mean_streak.type_renderers[:code] && last_child.type == :code && slice.end_with?( '`' ) slice = slice.sub /`+\z/, '' end slice end
# File lib/nrser/mean_streak/document.rb, line 243 def source_before_first_child node slice = source_byteslice( start_on: source_byte_indexes( node )[:first_byte][:index], end_before: source_byte_indexes( node.first )[:first_byte][:index], ) # See comments in {#render_children} if mean_streak.type_renderers[:code] && node.first.type == :code && slice.start_with?( '`' ) slice = slice.sub /\A`+/, '' end slice end
# File lib/nrser/mean_streak/document.rb, line 230 def source_between start_node, end_node # Ok, all we do is take a byte slice based on their byte indexes source_byteslice( # The index of the start node's last type is the byte just *before* # the slice starts start_after: source_byte_indexes( start_node )[:last_byte][:index], # The index of the end node's first byte is the byte just *after* the # slice ends end_before: source_byte_indexes( end_node )[:first_byte][:index] ) end
Computes the byte index for a `line` and `column` in the source, both of which *are in bytes*.
@param [Integer] line
# File lib/nrser/mean_streak/document.rb, line 169 def source_byte_index line:, column: t.hash_(keys: t.sym, values: t.non_neg_int?).check( line: line, column: column) # source_lines[0...line].map( &:bytesize ).reduce( column, :+ ) byte_index = column if line > 0 source_lines[0..(line - 1)].each { |_| byte_index += _.bytesize } end byte_index end
@return [Hash<Symbol, Hash
<Symbol, Integer>>]
Looks like: { first_byte: { line: Non-negative Integer, column: Non-negative Integer, index: Non-negative Integer, }, last_byte: { line: Non-negative Integer, column: Non-negative Integer, index: Non-negative Integer, }, }
All integers are zero-indexed byte array indexes.
# File lib/nrser/mean_streak/document.rb, line 141 def source_byte_indexes node pos = node.sourcepos indexes = { first_byte: { line: [0, pos[:start_line] - 1].max, column: [0, pos[:start_column] - 1].max, }, last_byte: { line: [0, pos[:end_line] - 1].max, column: [0, pos[:end_column] - 1].max, }, } indexes.each do |key, byte_index_pos| byte_index_pos[:index] = source_byte_index **byte_index_pos end indexes end
# File lib/nrser/mean_streak/document.rb, line 181 def source_byteslice **kwds t.and( # All the values must be `nil` or non-negative integer t.hash_(keys: t.sym, values: t.non_neg_int?), # Exactly one of `start_on` and `start_after` must be `nil` t.xor(t.shape(start_on: nil), t.shape(start_after: nil)), # Exactly one of `end_on` and `end_before` must be `nil` t.xor(t.shape(end_on: nil), t.shape(end_before: nil)) ).check kwds # The first byte we're gonna slice is either the `start_on` keyword # provided or the `start_after` bumped forward by 1 (which we can do # because it must point to the last *byte* before the slice, *not the # character*, so +1 gets us to the first slice byte) start_on = kwds[:start_on] || (kwds[:start_after] + 1) # In the same way, we can figure out the last byte after the slice end_before = kwds[:end_before] || (kwds[:end_on] + 1) # Sanity check if start_on > end_before # We done fucked up, which is not that unusual for me with this shit raise "Shit... start_on: #{ start_on }, end_before: #{ end_before }" end # Take the slice... the `...` range seems kinda easier 'cause the resulting # byte size is the difference `next_byte - end_before` source.byteslice start_on...end_before end
Get the substring of the source that a node came from (via its `#sourcepos`).
@return [String]
# File lib/nrser/mean_streak/document.rb, line 217 def source_for_node node indexes = source_byte_indexes node # This one is *really* easy now! source_byteslice( # Start on the first byte start_on: indexes[:first_byte][:index], # End on the last end_on: indexes[:last_byte][:index] ) end
The lines in {#source} as a {Hamster::Vector} of frozen strings.
@return [Hamster::Vector<String>]
# File lib/nrser/mean_streak/document.rb, line 118 def source_lines @source_lines ||= Hamster::List[*source.lines.map( &:freeze )] end