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



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/markdown/merge/gap_line_node.rb', line 40

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

  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

#preceding_signatureArray?

Returns Signature of the preceding structural node when available.

Returns:

  • (Array, nil)

    Signature of the preceding structural node when available



34
35
36
# File 'lib/markdown/merge/gap_line_node.rb', line 34

def preceding_signature
  @preceding_signature
end

Instance Method Details

#blank?Boolean

Check if this is a blank line

Returns:

  • (Boolean)

    true if line is empty or whitespace only



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

def blank?
  @content.strip.empty?
end

#childrenArray

TreeHaver::Node protocol: children (none)

Returns:

  • (Array)

    Empty array



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

def children
  []
end

#inspectObject

For debugging



137
138
139
# File 'lib/markdown/merge/gap_line_node.rb', line 137

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



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/markdown/merge/gap_line_node.rb', line 75

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

      context_signature = @preceding_signature || if @preceding_node.respond_to?(:type)
                                                    @preceding_node.type
                                                  else
                                                    :unknown
                                                  end

      [:gap_line_after, context_signature, 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



103
104
105
106
107
108
109
110
# File 'lib/markdown/merge/gap_line_node.rb', line 103

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



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

def text
  @content
end

#to_commonmarkString

Convert to commonmark format

Returns:

  • (String)

    The line with trailing newline



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

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

#typeSymbol Also known as: merge_type

TreeHaver::Node protocol: type

Returns:

  • (Symbol)

    :gap_line



58
59
60
# File 'lib/markdown/merge/gap_line_node.rb', line 58

def type
  :gap_line
end