Class: Markdown::Merge::GapLineNode

Inherits:
Ast::Merge::AstNode
  • Object
show all
Defined in:
lib/markdown/merge/gap_line_node.rb

Overview

Represents a “gap” line that exists between parsed Markdown nodes.

Markdown parsers like Markly consume certain content during parsing (like link reference definitions) and don’t preserve blank lines between nodes in the AST. This class represents lines that fall into “gaps” between parsed nodes, allowing them to be preserved during merge operations.

Gap lines include:

  • Blank lines between sections

  • Link reference definitions (handled specially by LinkDefinitionNode)

  • Any other content consumed by the parser

Examples:

node = GapLineNode.new("", line_number: 5)
node.type      # => :gap_line
node.blank?    # => true
node.signature # => [:gap_line, 5, ""]

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(content, line_number:) ⇒ GapLineNode

Initialize a new GapLineNode

Parameters:

  • content (String)

    The line content (without trailing newline)

  • line_number (Integer)

    1-based line number



37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/markdown/merge/gap_line_node.rb', line 37

def initialize(content, line_number:)
  @content = content.chomp
  @line_number = line_number
  @preceding_node = nil  # Set later during integration

  location = Ast::Merge::AstNode::Location.new(
    start_line: line_number,
    end_line: line_number,
    start_column: 0,
    end_column: @content.length,
  )

  super(slice: @content, location: location)
end

Instance Attribute Details

#contentString (readonly)

Returns The line content (may be empty for blank lines).

Returns:

  • (String)

    The line content (may be empty for blank lines)



24
25
26
# File 'lib/markdown/merge/gap_line_node.rb', line 24

def content
  @content
end

#line_numberInteger (readonly)

Returns 1-based line number.

Returns:

  • (Integer)

    1-based line number



27
28
29
# File 'lib/markdown/merge/gap_line_node.rb', line 27

def line_number
  @line_number
end

#preceding_nodeObject?

This is set after integration to avoid circular dependencies during creation

Returns:

  • (Object, nil)

    The preceding structural node (for context-aware signatures)



31
32
33
# File 'lib/markdown/merge/gap_line_node.rb', line 31

def preceding_node
  @preceding_node
end

Instance Method Details

#blank?Boolean

Check if this is a blank line

Returns:

  • (Boolean)

    true if line is empty or whitespace only



120
121
122
# File 'lib/markdown/merge/gap_line_node.rb', line 120

def blank?
  @content.strip.empty?
end

#childrenArray

TreeHaver::Node protocol: children (none)

Returns:

  • (Array)

    Empty array



108
109
110
# File 'lib/markdown/merge/gap_line_node.rb', line 108

def children
  []
end

#inspectObject

For debugging



131
132
133
# File 'lib/markdown/merge/gap_line_node.rb', line 131

def inspect
  "#<#{self.class.name} line=#{@line_number} content=#{@content.inspect}>"
end

#signatureArray

Generate a signature for matching gap lines. Gap lines are matched by their position relative to the preceding structural node. This allows blank lines after a heading in template to match blank lines after the same heading in destination, even if they’re on different absolute line numbers.

For gap lines at the start of the document (no preceding node), we use line number. For gap lines after a structural node, we use offset from that node’s end line.

Returns:

  • (Array)

    Signature array



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/markdown/merge/gap_line_node.rb', line 71

def signature
  if @preceding_node&.respond_to?(:source_position)
    pos = @preceding_node.source_position
    preceding_end_line = pos[:end_line] if pos

    if preceding_end_line
      # Offset from preceding node's end (e.g., heading ends on line 1, gap is line 2, offset = 1)
      offset = @line_number - preceding_end_line

      # Use the preceding node's type as context (simpler than full signature)
      # This works because gap lines after headings match gap lines after headings, etc.
      preceding_type = @preceding_node.respond_to?(:type) ? @preceding_node.type : :unknown

      [:gap_line_after, preceding_type, offset, @content]
    else
      # Fallback if we can't get position
      [:gap_line, @line_number, @content]
    end
  else
    # No preceding node - use absolute line number (for gaps at document start)
    [:gap_line, @line_number, @content]
  end
end

#source_positionHash

TreeHaver::Node protocol: source_position

Returns:

  • (Hash)

    Position info for source extraction



97
98
99
100
101
102
103
104
# File 'lib/markdown/merge/gap_line_node.rb', line 97

def source_position
  {
    start_line: @line_number,
    end_line: @line_number,
    start_column: 0,
    end_column: @content.length,
  }
end

#textString

TreeHaver::Node protocol: text

Returns:

  • (String)

    The line content



114
115
116
# File 'lib/markdown/merge/gap_line_node.rb', line 114

def text
  @content
end

#to_commonmarkString

Convert to commonmark format

Returns:

  • (String)

    The line with trailing newline



126
127
128
# File 'lib/markdown/merge/gap_line_node.rb', line 126

def to_commonmark
  "#{@content}\n"
end

#typeSymbol Also known as: merge_type

TreeHaver::Node protocol: type

Returns:

  • (Symbol)

    :gap_line



54
55
56
# File 'lib/markdown/merge/gap_line_node.rb', line 54

def type
  :gap_line
end