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.



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

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.



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

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.



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

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.



139
140
141
# File 'lib/yard/markdown/link_normalization_helper.rb', line 139

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.



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

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 StandardError
  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.



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

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.



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

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.



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

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