Module: Glossarist::Mentions::Parser

Defined in:
lib/glossarist/mentions/parser.rb

Overview

Scans a string for {kind:target} patterns and returns a mixed array of text segments and parsed mention objects.

Invalid mentions raise InvalidMentionError — they are never silently passed through.

See Also:

  • docs/design/inline-mentions.md

Constant Summary collapse

MENTION_REGEX =
/\{\{([^}]+)\}\}/.freeze
KINDS =
%w[concept cite fig table formula bib link image].freeze

Class Method Summary collapse

Class Method Details

.parse(text) ⇒ Array<Hash>

Returns segments — text and mention objects.

Parameters:

  • text (String)

    the source text to scan

Returns:

  • (Array<Hash>)

    segments — text and mention objects

Raises:



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/glossarist/mentions/parser.rb', line 22

def parse(text)
  return [{ kind: "text", content: "" }] unless text.is_a?(String) && !text.empty?

  segments = []
  pos = 0

  text.to_enum(:scan, MENTION_REGEX).each do |match|
    raw_content = match[0]
    match_start = Regexp.last_match.offset(0)[0]

    if match_start > pos
      segments << { kind: "text", content: text[pos...match_start] }
    end

    segment = parse_mention_content(
      raw_content,
      raw: "{{#{raw_content}}}",
      position: match_start,
    )
    segments << segment
    pos = Regexp.last_match.offset(0)[1]
  end

  segments << { kind: "text", content: text[pos..] } if pos < text.length
  segments
end

.parse_mention_content(content, raw:, position:) ⇒ Object



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
# File 'lib/glossarist/mentions/parser.rb', line 49

def parse_mention_content(content, raw:, position:)
  content = content.strip

  unless content =~ /\A(\w+):(.*)\z/m
    raise InvalidMentionError.new(
      raw: raw,
      position: position,
      reason: "Missing kind prefix; use {{concept:DATASET:ID}} or " \
              "{{cite:DATASET:ID}} — got #{raw}",
    )
  end

  kind = Regexp.last_match(1).downcase
  rest = Regexp.last_match(2).strip

  unless KINDS.include?(kind)
    raise InvalidMentionError.new(
      raw: raw,
      position: position,
      reason: "Unknown kind '#{kind}'; valid kinds: #{KINDS.join(', ')}",
    )
  end

  target_str, label = split_target_and_label(rest)

  target = parse_target(target_str, kind: kind, raw: raw, position: position)

  validate_target!(kind, target, raw: raw, position: position)

  {
    kind: kind,
    target: target,
    label: strip_label(label),
    raw: raw,
    start: position,
    end: position + raw.length,
  }
end