Module: Markdown::Merge::MarkdownStructure

Defined in:
lib/markdown/merge/markdown_structure.rb

Overview

Defines structural spacing rules for markdown elements.

When merging markdown from different sources, gap lines from the original sources may not exist at transition points (e.g., when a dest-only table is followed by a template-only table). This module defines which node types require spacing before/after them for proper markdown formatting.

Node types are categorized by their spacing needs:

  • NEEDS_BLANK_BEFORE: Nodes that need a blank line before them (headings, tables, etc.)

  • NEEDS_BLANK_AFTER: Nodes that need a blank line after them

  • CONTIGUOUS_TYPES: Nodes that should NOT have blank lines between consecutive instances (e.g., link_definition blocks should be together)

Examples:

MarkdownStructure.needs_blank_before?(:table)  # => true
MarkdownStructure.needs_blank_after?(:heading) # => true
MarkdownStructure.contiguous_type?(:link_definition) # => true

Constant Summary collapse

NEEDS_BLANK_BEFORE =

Node types that should have a blank line BEFORE them (when preceded by other content)

%i[
  heading
  table
  code_block
  thematic_break
  list
  block_quote
].freeze
NEEDS_BLANK_AFTER =

Node types that should have a blank line AFTER them (when followed by other content)

%i[
  heading
  table
  code_block
  thematic_break
  list
  block_quote
  link_definition
].freeze
CONTIGUOUS_TYPES =

Node types that should be contiguous (no blank lines between consecutive nodes of the same type). These form “blocks” that should stay together.

%i[
  link_definition
].freeze

Class Method Summary collapse

Class Method Details

.contiguous_type?(node_type) ⇒ Boolean

Check if a node type is a contiguous type (should not have blank lines between consecutive nodes of the same type).

Parameters:

  • node_type (Symbol, String)

    Node type to check

Returns:

  • (Boolean)


74
75
76
# File 'lib/markdown/merge/markdown_structure.rb', line 74

def contiguous_type?(node_type)
  CONTIGUOUS_TYPES.include?(node_type.to_sym)
end

.needs_blank_after?(node_type) ⇒ Boolean

Check if a node type needs a blank line after it

Parameters:

  • node_type (Symbol, String)

    Node type to check

Returns:

  • (Boolean)


65
66
67
# File 'lib/markdown/merge/markdown_structure.rb', line 65

def needs_blank_after?(node_type)
  NEEDS_BLANK_AFTER.include?(node_type.to_sym)
end

.needs_blank_before?(node_type) ⇒ Boolean

Check if a node type needs a blank line before it

Parameters:

  • node_type (Symbol, String)

    Node type to check

Returns:

  • (Boolean)


57
58
59
# File 'lib/markdown/merge/markdown_structure.rb', line 57

def needs_blank_before?(node_type)
  NEEDS_BLANK_BEFORE.include?(node_type.to_sym)
end

.needs_blank_between?(prev_type, next_type) ⇒ Boolean

Check if we should insert a blank line between two node types

Rules:

  1. If both types are the same contiguous type, NO blank line

  2. If previous node needs blank after, YES blank line

  3. If next node needs blank before, YES blank line

Parameters:

  • prev_type (Symbol, String, nil)

    Previous node type

  • next_type (Symbol, String, nil)

    Next node type

Returns:

  • (Boolean)


88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/markdown/merge/markdown_structure.rb', line 88

def needs_blank_between?(prev_type, next_type)
  return false if prev_type.nil? || next_type.nil?

  prev_sym = prev_type.to_sym
  next_sym = next_type.to_sym

  # Same contiguous type - no blank line between them
  if prev_sym == next_sym && contiguous_type?(prev_sym)
    return false
  end

  needs_blank_after?(prev_sym) || needs_blank_before?(next_sym)
end

.node_type(node) ⇒ Symbol?

Get the node type from a node object

Priority order:

  1. merge_type - Explicit merge behavior classification (preferred)

  2. type - Parser-specific type fallback

Parameters:

  • node (Object)

    Node to get type from

Returns:

  • (Symbol, nil)

    Node type



110
111
112
113
114
115
116
117
118
119
# File 'lib/markdown/merge/markdown_structure.rb', line 110

def node_type(node)
  return unless node

  # Prefer merge_type when available - it's the explicit merge behavior classifier
  if node.respond_to?(:merge_type)
    node.merge_type.to_sym
  elsif node.respond_to?(:type)
    node.type.to_sym
  end
end