Class: Ast::Merge::YamlFrontmatterDetector

Inherits:
RegionDetectorBase show all
Defined in:
lib/ast/merge/yaml_frontmatter_detector.rb

Overview

Detects YAML frontmatter at the beginning of a document.

YAML frontmatter is delimited by ‘—` at the start and end, and must begin on the first line of the document (optionally preceded by a UTF-8 BOM).

Examples:

YAML frontmatter

---
title: My Document
author: Jane Doe
---

Usage

detector = YamlFrontmatterDetector.new
regions = detector.detect_all(markdown_source)
# => [#<Region type=:yaml_frontmatter content="title: My Document\n...">]

Constant Summary collapse

FRONTMATTER_PATTERN =

Pattern for detecting YAML frontmatter.

  • Must start at beginning of document (or after BOM)

  • Opening delimiter is ‘—` followed by optional whitespace and newline

  • Content is captured (non-greedy)

  • Closing delimiter is ‘—` at start of line, followed by optional whitespace and newline/EOF

/\A(?:\xEF\xBB\xBF)?(---[ \t]*\r?\n)(.*?)(^---[ \t]*(?:\r?\n|\z))/m

Instance Method Summary collapse

Methods inherited from RegionDetectorBase

#inspect, #name, #strip_delimiters?

Instance Method Details

#detect_all(source) ⇒ Array<Region>

Detects YAML frontmatter at the beginning of the document.

Parameters:

  • source (String)

    the source document to scan

Returns:

  • (Array<Region>)

    array containing at most one Region for frontmatter



46
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/ast/merge/yaml_frontmatter_detector.rb', line 46

def detect_all(source)
  return [] if source.nil? || source.empty?

  match = source.match(FRONTMATTER_PATTERN)
  return [] unless match

  opening_delimiter = match[1]
  content = match[2]
  closing_delimiter = match[3]

  # Calculate line numbers
  # Frontmatter starts at line 1 (or after BOM)
  start_line = 1
  # Count newlines in content to determine end line
  # Opening delimiter ends at line 1
  # Content spans from line 2 to line 2 + content_lines - 1
  # Closing delimiter is on the next line
  content_newlines = content.count("\n")
  # end_line is the line with the closing ---
  end_line = start_line + 1 + content_newlines

  # Adjust if content ends without newline
  end_line - 1 if content.end_with?("\n") && content_newlines > 0

  # Actually, let's calculate more carefully
  # Line 1: ---
  # Line 2 to N: content
  # Line N+1: ---
  if content.empty?
    0
  else
    content.count("\n") + (content.end_with?("\n") ? 0 : 1)
  end

  # Simplify: count total newlines in the full match to determine end line
  full_match = match[0]
  total_newlines = full_match.count("\n")
  end_line = total_newlines + (full_match.end_with?("\n") ? 0 : 1)

  [
    Region.new(
      type: region_type,
      content: content,
      start_line: start_line,
      end_line: end_line,
      delimiters: [opening_delimiter.strip, closing_delimiter.strip],
      metadata: {format: :yaml},
    ),
  ]
end

#region_typeSymbol

Returns the type identifier for YAML frontmatter regions.

Returns:

  • (Symbol)

    the type identifier for YAML frontmatter regions



36
37
38
# File 'lib/ast/merge/yaml_frontmatter_detector.rb', line 36

def region_type
  :yaml_frontmatter
end