Class: MTProto::Markdown::Renderer

Inherits:
Object
  • Object
show all
Defined in:
lib/mtproto/markdown.rb

Overview

entities -> MarkdownV2 markup. Escapes literal markup characters and wraps each entity's UTF-16 range with its markers. Entities are opened outer-first and closed inner-first so proper nesting round-trips. Works in UTF-16 unit space.

Constant Summary collapse

SPECIAL =
'_*[]()~`>#+-=|{}.!\\'
WRAP =
{ bold: '*', italic: '_', underline: '__', strike: '~', spoiler: '||' }.freeze

Instance Method Summary collapse

Constructor Details

#initialize(text, entities) ⇒ Renderer

Returns a new instance of Renderer.



338
339
340
341
# File 'lib/mtproto/markdown.rb', line 338

def initialize(text, entities)
  @units = (text || '').encode('UTF-16LE').unpack('v*')
  @entities = (entities || []).map { |e| e.is_a?(Hash) ? Entity.new(**e) : e }
end

Instance Method Details

#renderObject



343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
# File 'lib/mtproto/markdown.rb', line 343

def render
  opens = Hash.new { |h, k| h[k] = [] }
  closes = Hash.new { |h, k| h[k] = [] }
  @entities.each_with_index do |e, i|
    opens[e.offset] << [e, i]
    closes[e.offset + e.length] << [e, i]
  end
  verbatim = ranges_of(%i[code pre]) # content here is literal — never escaped
  quotes = ranges_of(%i[blockquote]) # rendered as a '>' line prefix

  out = +''
  pos = 0
  line_start = true
  loop do
    # Close inner-first, the exact mirror of the open order (outer-first), so
    # entities that end together don't emit crossed markers. Ties broken by the
    # reverse of the open tiebreak.
    closes[pos].sort_by { |(e, i)| [e.length, -i] }.each { |(e, _)| out << close_marker(e) }
    out << '>' if line_start && in_range?(quotes, pos)
    opens[pos].sort_by { |(e, i)| [-e.length, i] }.each { |(e, _)| out << open_marker(e) }
    break if pos >= @units.length

    u = @units[pos]
    if high_surrogate?(u) && pos + 1 < @units.length
      out << pair_to_s(u, @units[pos + 1]) # an astral char never needs escaping
      pos += 2
      line_start = false
    else
      ch = unit_to_s(u)
      out << render_char(ch, in_range?(verbatim, pos))
      line_start = ch == "\n"
      pos += 1
    end
  end
  out
end