Class: RedQuilt::CodeBlock::Parser

Inherits:
Object
  • Object
show all
Defined in:
lib/red_quilt/code_block.rb

Overview

Cached collaborator for BlockParser. A single instance is created in BlockParser#initialize and reused; per-call state lives in method locals so reentrant calls are safe.

Instance Method Summary collapse

Constructor Details

#initialize(block_parser) ⇒ Parser

Returns a new instance of Parser.



40
41
42
# File 'lib/red_quilt/code_block.rb', line 40

def initialize(block_parser)
  @arena = block_parser.arena
end

Instance Method Details

#parse_fenced(parent_id, lines, index, fence) ⇒ Object

Parses a fenced block. ‘fence` is CodeBlock.fenced_start’s result for lines. Returns the index past the block.



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
# File 'lib/red_quilt/code_block.rb', line 46

def parse_fenced(parent_id, lines, index, fence)
  start_line = lines[index]
  content_lines = []
  index += 1
  while index < lines.length
    break if fence_close?(lines[index].content, fence[:char], fence[:count])

    content_lines << lines[index]
    index += 1
  end
  index += 1 if index < lines.length

  # Each content line is stripped of up to the fence's own leading
  # indent (CommonMark spec: a fence indented by N spaces strips up
  # to N spaces from every content line, but never more). Manual
  # byte scan beats compiling an interpolated regex per block and
  # short-circuits when the fence had no indent (the common case).
  indent_n = fence[:indent] || 0
  code = content_lines.map { |l| Indentation.strip_leading_spaces(l.content, indent_n) }.join("\n")
  code << "\n" unless content_lines.empty?
  source_start = content_lines.empty? ? start_line.start_byte : content_lines.first.start_byte
  source_end = content_lines.empty? ? start_line.end_byte : content_lines.last.end_byte
  code_id = @arena.add_node(NodeType::CODE_BLOCK,
                            source_start: source_start,
                            source_len: source_end - source_start,
                            str1: code,
                            str2: fence[:info])
  @arena.append_child(parent_id, code_id)
  index
end

#parse_indented(parent_id, lines, index) ⇒ Object

Parses an indented code block. Returns the index past the block.



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/red_quilt/code_block.rb', line 78

def parse_indented(parent_id, lines, index)
  start_index = index
  code_lines = []
  while index < lines.length
    line = lines[index]
    break unless line.blank || CodeBlock.indented_line?(line.content)

    # CommonMark: strip up to 4 columns of leading whitespace
    # (tab-aware) from every line, including blank lines whose
    # content beyond column 4 must be preserved verbatim.
    code_lines << Indentation.strip_columns(line.content, 4)
    index += 1
  end

  # Trailing blank lines are not part of the code block.
  while !code_lines.empty? && code_lines.last.strip.empty?
    code_lines.pop
    index -= 1
  end

  start_byte = lines[start_index].start_byte
  end_byte = lines[index - 1].end_byte
  code = code_lines.empty? ? "" : code_lines.join("\n") + "\n"

  code_id = @arena.add_node(NodeType::CODE_BLOCK,
                            source_start: start_byte,
                            source_len: end_byte - start_byte,
                            str1: code)
  @arena.append_child(parent_id, code_id)
  index
end