Module: YARD::Markdown::LinkNormalizationHelper

Defined in:
lib/yard/markdown/link_normalization_helper.rb

Overview

Rewrites generated Markdown links so they point at Markdown output.

Instance Method Summary collapse

Instance Method Details

#constant_reference_path?(value) ⇒ Boolean

Returns whether a path looks like a constant reference.

Parameters:

  • value (String)

    Link target to inspect.

Returns:

  • (Boolean)

    True when the path resembles a constant name.



101
102
103
104
105
106
# File 'lib/yard/markdown/link_normalization_helper.rb', line 101

def constant_reference_path?(value)
  parts = value.split(%r{::|/}).reject(&:empty?)
  return false if parts.empty?

  parts.all? { |part| part.match?(/\A[A-Z]\w*\z/) }
end

#finalize_markdown(content, current_path) ⇒ String

Normalizes generated Markdown before it is written to disk.

Parameters:

  • content (String, Array<String>)

    Markdown content to finalize.

  • current_path (String)

    Output path for the current document.

Returns:

  • (String)

    Normalized Markdown content with a trailing newline.



12
13
14
15
16
17
18
19
# File 'lib/yard/markdown/link_normalization_helper.rb', line 12

def finalize_markdown(content, current_path)
  output = content.instance_of?(Array) ? content.join("\n") : content
  output = output.lines.map(&:rstrip).join("\n")
  output = normalize_local_links(output, current_path)
  output = normalize_malformed_local_links(output)
  output = output.gsub(/\n{3,}/, "\n\n").strip
  "#{output}\n"
end

Rewrites local Markdown links relative to the current output path.

Parameters:

  • markdown (String)

    Markdown content to rewrite.

  • current_path (String)

    Output path for the current document.

Returns:

  • (String)

    Markdown with local links normalized.



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/yard/markdown/link_normalization_helper.rb', line 26

def normalize_local_links(markdown, current_path)
  current_dir = Pathname.new(current_path).dirname

  markdown.gsub(%r{\[(.+?)\]\((?!https?://|mailto:|#)([^)\n]+)\)}) do
    label = Regexp.last_match(1)
    target = Regexp.last_match(2)
    path = target.sub(/[?#].*\z/, "")
    suffix = target[path.length..]
    rewritten_path = resolve_local_link_target(path, current_dir)

    if rewritten_path.nil?
      "`#{label.tr("`", "")}`"
    else
      "[#{label}](#{rewritten_path}#{suffix})"
    end
  end
end

Replaces malformed local Markdown links with inline code.

Parameters:

  • markdown (String)

    Markdown content to normalize.

Returns:

  • (String)

    Markdown with malformed local links replaced.



137
138
139
# File 'lib/yard/markdown/link_normalization_helper.rb', line 137

def normalize_malformed_local_links(markdown)
  markdown.gsub(%r{\[([^\]]+)\]\((?!https?://|mailto:|#)(?:[^)\n]*['"][^)\n]*)\)}, '`\1`')
end

#relative_output_path(current_dir, target_path) ⇒ String

Computes a relative path from the current output directory.

Parameters:

  • current_dir (Pathname)

    Directory for the current output file.

  • target_path (String, Pathname)

    Output path being linked to.

Returns:

  • (String)

    Relative path suitable for a Markdown link.



124
125
126
127
128
129
130
131
# File 'lib/yard/markdown/link_normalization_helper.rb', line 124

def relative_output_path(current_dir, target_path)
  target = target_path.to_s
  return target if target.start_with?("../")

  Pathname.new(target).relative_path_from(current_dir).to_s
rescue
  target
end

Resolves a local link target to the final relative Markdown path.

Parameters:

  • path (String)

    Link target path to resolve.

  • current_dir (Pathname)

    Directory for the current output file.

Returns:

  • (String, nil)

    Relative Markdown path, or nil when unresolved.



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/yard/markdown/link_normalization_helper.rb', line 77

def resolve_local_link_target(path, current_dir)
  normalized = path.sub(%r{\A/+}, "")

  obj = resolve_registry_object(normalized, current_dir)
  if obj
    object_path = options.serializer.serialized_path(obj)
    return relative_output_path(current_dir, object_path)
  end

  if normalized.match?(/\.html\z/i)
    normalized = normalized.sub(/\.html\z/i, ".md")
  elsif File.extname(normalized).empty?
    return nil if unresolved_identifier_target?(normalized)

    normalized = "#{normalized}.md" if normalized.include?("/")
  end

  relative_output_path(current_dir, normalized)
end

#resolve_registry_object(path, current_dir) ⇒ YARD::CodeObjects::Base?

Resolves a local link path to a YARD registry object when possible.

Parameters:

  • path (String)

    Link target path to resolve.

  • current_dir (Pathname)

    Directory for the current output file.

Returns:

  • (YARD::CodeObjects::Base, nil)

    Matched registry object, if any.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/yard/markdown/link_normalization_helper.rb', line 49

def resolve_registry_object(path, current_dir)
  cleaned = path.sub(%r{\A(?:(?:\.\./)+|\./)}, "")
  candidates = [path]

  if constant_reference_path?(cleaned)
    current_parts = current_dir.to_s.split("/").reject { |part| part.empty? || part == "." }
    target_parts = cleaned.split("/")

    current_parts.length.downto(0) do |depth|
      candidates << (current_parts.first(depth) + target_parts).join("::")
    end
  end

  candidates.each do |candidate|
    obj = Registry.at(candidate)
    next if obj.nil? || obj.equal?(Registry.root)

    return obj
  end

  nil
end

#unresolved_identifier_target?(path) ⇒ Boolean

Returns whether a path looks like an unresolved bare identifier.

Parameters:

  • path (String)

    Link target to inspect.

Returns:

  • (Boolean)

    True when the target should be treated as unresolved.



112
113
114
115
116
117
# File 'lib/yard/markdown/link_normalization_helper.rb', line 112

def unresolved_identifier_target?(path)
  cleaned = path.sub(%r{\A(?:(?:\.\./)+|\./)}, "")
  return true if cleaned.start_with?(":") || cleaned.match?(/\A\d/)

  cleaned.match?(/\A[a-z_]\w*\z/)
end