Class: MTProto::Markdown::Parser

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

Overview

Single-pass scanner. Paired markers (bold/italic/underline/strike/spoiler) use a nesting stack; code/pre/links/custom-emoji are consumed as self-contained spans; blockquotes are recognised at line starts. Everything is tracked in UTF-16 units so the emitted entity offsets match the wire.

Constant Summary collapse

MARKERS =
{ '||' => :spoiler, '__' => :underline, '*' => :bold, '_' => :italic, '~' => :strike }.freeze
MARKER_ORDER =

Longest first so __/|| win over _/|.

['```', '||', '__', '`', '*', '_', '~'].freeze

Instance Method Summary collapse

Constructor Details

#initialize(source) ⇒ Parser

Returns a new instance of Parser.



67
68
69
70
71
72
73
74
75
76
77
# File 'lib/mtproto/markdown.rb', line 67

def initialize(source)
  @chars = source.to_s.chars
  @len = @chars.length
  @i = 0
  @text = +''
  @u16 = 0
  @stack = []
  @entities = []
  @quote = nil
  @at_line_start = true
end

Instance Method Details

#parseObject

Raises:



79
80
81
82
83
84
85
86
87
88
# File 'lib/mtproto/markdown.rb', line 79

def parse
  scan while @i < @len
  close_quote
  raise ParseError, "unclosed #{@stack.last[:type]}" unless @stack.empty?

  # Drop zero-length entities (e.g. from empty markup like "**") — Telegram
  # rejects them with ENTITY_BOUNDS_INVALID.
  kept = @entities.reject { |e| e.length.to_i <= 0 }
  [@text, kept.sort_by { |e| [e.offset, -e.length] }]
end