Module: RedQuilt::Blockquote

Defined in:
lib/red_quilt/blockquote.rb

Overview

CommonMark spec 5.1 blockquotes.

Module-level functions are stateless helpers used by BlockParser's predicate dispatch. Blockquote::Parser is a cached collaborator created once in BlockParser#initialize and reused for every blockquote (including nested ones) — per-call state lives in method locals so reentrant #parse calls are safe.

Defined Under Namespace

Classes: Parser

Constant Summary collapse

BLOCKQUOTE_PREFIX_RE =
/\A {0,3}>/

Class Method Summary collapse

Class Method Details

.marker_offset(text) ⇒ Object

Byte offset of the > within the line, or nil when there is no prefix. Only spaces may precede it (at most 3), so the character index returned by the match doubles as a byte offset.



23
24
25
26
# File 'lib/red_quilt/blockquote.rb', line 23

def marker_offset(text)
  match = BLOCKQUOTE_PREFIX_RE.match(text)
  match && (match.end(0) - 1)
end

.match?(text) ⇒ Boolean

Returns:

  • (Boolean)


16
17
18
# File 'lib/red_quilt/blockquote.rb', line 16

def match?(text)
  text.match?(BLOCKQUOTE_PREFIX_RE)
end

.strip_prefix(line) ⇒ Object

Strip the leading > (and at most one column of whitespace after it) from a blockquote line. Returns a new Line whose content is the inner text. If the line has no > prefix, the original line is returned unchanged (wrapped in a fresh Line so the caller treats it uniformly).



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/red_quilt/blockquote.rb', line 33

def strip_prefix(line)
  content = line.content
  bytes = content.bytesize
  i = 0
  abs_col = 0
  # Up to 3 spaces of indent before `>`.
  while i < 3 && i < bytes && content.getbyte(i) == 0x20
    i += 1
    abs_col += 1
  end
  unless i < bytes && content.getbyte(i) == 0x3E
    return Line.new(content, line.start_byte, line.end_byte, !content.match?(/\S/))
  end

  i += 1
  abs_col += 1 # consume `>`

  # Count column width of leading whitespace after `>` using
  # absolute-column tracking so a tab right after `>` (at col 1) is
  # correctly billed as only 3 columns of indent, not 4.
  ws_start_col = abs_col
  j = i
  while j < bytes
    b = content.getbyte(j)
    if b == 0x20
      abs_col += 1
    elsif b == 0x09
      abs_col = ((abs_col / 4) + 1) * 4
    else
      break
    end
    j += 1
  end
  ws_cols = abs_col - ws_start_col

  if ws_cols >= 1
    tail = (" " * (ws_cols - 1)) + content.byteslice(j..)
    offset = j
  else
    tail = content.byteslice(i..)
    offset = i
  end

  Line.new(tail, line.start_byte + offset, line.end_byte, !tail.match?(/\S/))
end