Class: Markdown::Merge::OutputBuilder

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

Overview

Builds markdown output from merge operations.

Handles markdown-specific concerns like:

  • Extracting source from original nodes
  • Reconstructing consumed link reference definitions
  • Preserving gap lines (blank line spacing)
  • Automatic structural spacing (blank lines between tables, headings, etc.)
  • Assembling final merged content

Unlike Emitter classes used in JSON/YAML/etc, OutputBuilder focuses on source preservation and reconstruction rather than generation from scratch.

Examples:

Basic usage

builder = OutputBuilder.new
builder.add_node_source(node, analysis)
builder.add_link_definition(link_def_node)
builder.add_gap_line(count: 2)
content = builder.to_s

Instance Method Summary collapse

Constructor Details

#initialize(preserve_formatting: true, auto_spacing: true) ⇒ OutputBuilder

Initialize a new OutputBuilder

Parameters:

  • preserve_formatting (Boolean) (defaults to: true)

    Whether to preserve original formatting

  • auto_spacing (Boolean) (defaults to: true)

    Whether to automatically insert blank lines between structural elements



28
29
30
31
32
33
34
35
36
# File 'lib/markdown/merge/output_builder.rb', line 28

def initialize(preserve_formatting: true, auto_spacing: true)
  @parts = []
  @length = 0
  @preserve_formatting = preserve_formatting
  @auto_spacing = auto_spacing
  @last_node_type = nil  # Track previous node type for spacing decisions
  @last_end_line = nil   # Track previous node's end line for adjacency detection
  @last_analysis = nil   # Track previous node's analysis for same-source detection
end

Instance Method Details

#add_gap_line(count: 1) ⇒ Object

Add gap lines (blank line preservation)

Parameters:

  • count (Integer) (defaults to: 1)

    Number of blank lines to add



91
92
93
# File 'lib/markdown/merge/output_builder.rb', line 91

def add_gap_line(count: 1)
  append_part("\n" * count) if count > 0
end

Add a reconstructed link definition

Parameters:



83
84
85
86
# File 'lib/markdown/merge/output_builder.rb', line 83

def add_link_definition(node)
  formatted = LinkDefinitionFormatter.format(node)
  append_part(formatted) if formatted && !formatted.empty?
end

#add_node_source(node, analysis) ⇒ Object

Add a node's source content

Automatically inserts structural blank lines when transitioning between certain node types (tables, headings, code blocks, etc.) if auto_spacing is enabled. Skips auto-spacing when nodes are adjacent in the same source — the original formatting is preserved by the source extraction.

Parameters:

  • node (Object)

    Node to add (can be parser node, FreezeNode, LinkDefinitionNode, etc.)

  • analysis (FileAnalysisBase)

    Analysis for accessing source



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
78
# File 'lib/markdown/merge/output_builder.rb', line 47

def add_node_source(node, analysis)
  # Determine node type for spacing decisions
  current_type = MarkdownStructure.node_type(node)

  # Auto-spacing logic:
  # - Skip for gap_line and freeze_block (they handle their own spacing)
  # - Skip if last node was a gap_line (we already have spacing)
  # - Skip if nodes are adjacent in the same source (original spacing is correct)
  # - Otherwise, check MarkdownStructure.needs_blank_between? which handles
  #   contiguous types (like link_definitions that shouldn't have blanks between them)
  # Skip auto-spacing when nodes are adjacent lines in the same source.
  # The original source formatting is correct — no blank line was there.
  if !(%i[gap_line freeze_block].include?(current_type) ||
                 @last_node_type == :gap_line) && @auto_spacing && @last_node_type && current_type && MarkdownStructure.needs_blank_between?(@last_node_type,
                                                                                                                                             current_type) && !same_source_adjacent?(
                                                                                                                                               node, analysis
                                                                                                                                             ) && !(@parts.empty? || blank_line_terminated?)
    # Only add spacing if we don't already have adequate blank lines
    # Check the last part to see if it already ends with blank line(s)
    add_gap_line(count: 1)
  end

  content = extract_source(node, analysis)
  return unless content && !content.empty?

  range = append_part(content)
  # Update last node type (track all node types for proper spacing)
  @last_node_type = current_type
  @last_end_line = node_end_line(node)
  @last_analysis = analysis
  range
end

#add_raw(text) ⇒ Object

Add raw text content

Parameters:

  • text (String)

    Raw text to add



98
99
100
# File 'lib/markdown/merge/output_builder.rb', line 98

def add_raw(text)
  append_part(text) if text && !text.empty?
end

#blank_line_terminated?Boolean

Check whether the current output already ends with a blank-line separator.

This looks across part boundaries so a trailing blank line represented as separate content + gap parts still counts as an existing separator.

Returns:

  • (Boolean)


122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/markdown/merge/output_builder.rb', line 122

def blank_line_terminated?
  trailing_newlines = 0

  @parts.reverse_each do |part|
    next if part.nil? || part.empty?

    idx = part.length - 1
    while idx >= 0 && part[idx] == "\n"
      trailing_newlines += 1
      idx -= 1
    end

    break if idx >= 0
  end

  trailing_newlines >= 2
end

#clearObject

Clear all content



141
142
143
144
# File 'lib/markdown/merge/output_builder.rb', line 141

def clear
  @parts.clear
  @length = 0
end

#empty?Boolean

Check if builder has any content

Returns:

  • (Boolean)


112
113
114
# File 'lib/markdown/merge/output_builder.rb', line 112

def empty?
  @parts.empty?
end

#to_sString

Get final content

Returns:

  • (String)

    Assembled markdown content



105
106
107
# File 'lib/markdown/merge/output_builder.rb', line 105

def to_s
  @parts.join
end