Class: RedQuilt::FootnoteDefinition::Parser

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

Overview

Cached collaborator for BlockParser (created once per document and reused for every definition). Footnote definitions are document-global: the parser lazily creates a single FOOTNOTES_SECTION under the root and memoizes it; per-call state otherwise lives in locals so the recursive parse_lines call is safe.

Constant Summary collapse

CONTENT_INDENT =

GFM footnote continuation indent (columns). Lines indented at least this much (plus blank lines between them) belong to the definition.

4

Instance Method Summary collapse

Constructor Details

#initialize(block_parser) ⇒ Parser

Returns a new instance of Parser.



47
48
49
50
51
# File 'lib/red_quilt/footnote_definition.rb', line 47

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

Instance Method Details

#move_section_to_end(root_id) ⇒ Object

Make the footnotes section root’s last child so it renders last and the inline pass numbers body references before any nested references inside the definitions. No-op when no definition was found.



84
85
86
87
88
89
# File 'lib/red_quilt/footnote_definition.rb', line 84

def move_section_to_end(root_id)
  return if @section_id.nil?

  @arena.detach(@section_id)
  @arena.append_child(root_id, @section_id)
end

#parse(lines, index, match, registry, root_id) ⇒ Object

Consumes the definition opening at ‘lines` (its `match` already parsed), registers it in `registry`, and returns the next unconsumed line index.



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/red_quilt/footnote_definition.rb', line 56

def parse(lines, index, match, registry, root_id)
  first = lines[index]
  content_lines = [content_line(match[:content], first.start_byte + match[:content_start], first.end_byte)]
  consumed_index = collect_continuation(lines, index + 1, content_lines)

  label = ReferenceDefinition.normalize_label(match[:label])
  span = SourceSpan.new(first.start_byte, lines[consumed_index - 1].end_byte)
  if registry.defined?(label)
    @block_parser.diagnostics << Diagnostic.new(
      severity: :warning, rule: :duplicate_footnote,
      message: "Duplicate footnote definition #{label.inspect} — keeping the first",
      source_span: span,
    )
    return consumed_index
  end

  def_id = @arena.add_node(NodeType::FOOTNOTE_DEFINITION,
                           source_start: span.start_byte, source_len: span.length,
                           str1: label)
  @arena.append_child(section_id(root_id), def_id)
  registry.define(label, def_id)
  @block_parser.parse_lines(def_id, content_lines, transformed: true)
  consumed_index
end