yard-markdown 0.6.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YARD
4
+ module Markdown
5
+ # Renders grouped Markdown sections for constants, attributes, and methods.
6
+ module CollectionRenderingHelper
7
+ # Renders the constants section for an object page.
8
+ #
9
+ # @param constants [Array<YARD::CodeObjects::Base>] Constant objects collected for the current page.
10
+ # @param group_order [Array<String>, nil] Preferred ordering for group headings.
11
+ # @return [String] Markdown for the constants section.
12
+ def render_constants(constants, group_order)
13
+ lines = ["## Constants"]
14
+ grouped_constants = grouped_items(constants.sort_by { |item| item.name }, group_order)
15
+ uses_groups = grouped_constants.any? { |name, _items| !name.nil? }
16
+
17
+ grouped_constants.each do |group_name, items|
18
+ if uses_groups
19
+ lines << "### #{group_name || "General"}"
20
+ item_heading = "####"
21
+ else
22
+ item_heading = "###"
23
+ end
24
+
25
+ lines << items.map { |item|
26
+ item_lines = [heading_with_anchors("#{item_heading} `#{item.name}`", item)]
27
+ append_lines(item_lines, documented_text(item), separated: false)
28
+ append_lines(item_lines, render_tags(item), separated: false)
29
+ item_lines.join("\n")
30
+ }.join("\n\n")
31
+ end
32
+
33
+ lines.join("\n")
34
+ end
35
+
36
+ # Renders the attributes section for an object page.
37
+ #
38
+ # @param attrs [Array<YARD::CodeObjects::MethodObject>] Attributes to render.
39
+ # @param group_order [Array<String>, nil] Preferred ordering for group headings.
40
+ # @return [String] Markdown for the attributes section.
41
+ def render_attributes(attrs, group_order)
42
+ lines = ["## Attributes"]
43
+ grouped_attrs = grouped_items(attrs, group_order)
44
+ uses_groups = grouped_attrs.any? { |name, _items| !name.nil? }
45
+
46
+ grouped_attrs.each do |group_name, items|
47
+ if uses_groups
48
+ lines << "### #{group_name || "General"}"
49
+ item_heading = "####"
50
+ else
51
+ item_heading = "###"
52
+ end
53
+
54
+ lines << items.map { |item|
55
+ item_lines = [heading_with_anchors("#{item_heading} `#{item.name}` [#{attribute_access(item)}]", item)]
56
+ append_lines(item_lines, documented_text(item), separated: false)
57
+ append_lines(item_lines, render_tags(item), separated: false)
58
+ item_lines.join("\n")
59
+ }.join("\n\n")
60
+ end
61
+
62
+ lines.join("\n")
63
+ end
64
+
65
+ # Renders a method section for an object page.
66
+ #
67
+ # @param section_title [String] Section title to render.
68
+ # @param methods [Array<YARD::CodeObjects::MethodObject>] Method objects collected for the current section.
69
+ # @param group_order [Array<String>, nil] Preferred ordering for group headings.
70
+ # @return [String] Markdown for the method section.
71
+ def render_methods(section_title, methods, group_order)
72
+ lines = ["## #{section_title}"]
73
+ grouped_methods = grouped_items(methods, group_order)
74
+ uses_groups = grouped_methods.any? { |name, _items| !name.nil? }
75
+
76
+ grouped_methods.each do |group_name, items|
77
+ if uses_groups
78
+ lines << "### #{group_name || "General"}"
79
+ item_heading = "####"
80
+ else
81
+ item_heading = "###"
82
+ end
83
+
84
+ lines << items.map { |item|
85
+ item_lines = [heading_with_anchors("#{item_heading} `#{formatted_method_heading(item)}`", item)]
86
+ append_lines(item_lines, documented_text(item), separated: false)
87
+ append_lines(item_lines, render_tags(item), separated: false)
88
+ item_lines.join("\n")
89
+ }.join("\n\n")
90
+ end
91
+
92
+ lines.join("\n")
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rdoc"
4
+
5
+ module YARD
6
+ module Markdown
7
+ # Converts YARD docstrings into Markdown-friendly text.
8
+ module DocumentationHelper
9
+ # Returns the rendered documentation text for an object.
10
+ #
11
+ # @param object [YARD::CodeObjects::Base] Object whose docstring is being rendered.
12
+ # @return [String] Converted documentation text or a fallback message.
13
+ def documented_text(object)
14
+ text = rdoc_to_md(object.docstring)
15
+ return text unless text.empty?
16
+ return "" unless object.tags.empty?
17
+
18
+ "Not documented."
19
+ end
20
+
21
+ # Converts an RDoc-formatted docstring to Markdown.
22
+ #
23
+ # @param docstring [Object] Raw docstring content.
24
+ # @return [String] Markdown-rendered docstring content.
25
+ def rdoc_to_md(docstring)
26
+ fenced_code_blocks = []
27
+ placeholder = "YARD_MARKDOWN_FENCED_CODE_BLOCK_%d"
28
+ content = docstring.gsub(/^```[^\n]*\n.*?^```[ \t]*$/m) do |block|
29
+ fenced_code_blocks << block
30
+ format(placeholder, fenced_code_blocks.length - 1)
31
+ end
32
+
33
+ markdown = RDoc::Markup::ToMarkdown.new.convert(content).rstrip
34
+ fenced_code_blocks.each_with_index do |block, index|
35
+ markdown = markdown.sub(format(placeholder, index), block)
36
+ end
37
+
38
+ markdown
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YARD
4
+ module Markdown
5
+ # Builds headings and legacy anchors for rendered object sections.
6
+ module HeadingHelper
7
+ include ArefHelper
8
+
9
+ # Returns the legacy YARD anchor for an object when one exists.
10
+ #
11
+ # @param object [YARD::CodeObjects::Base] Object being rendered.
12
+ # @return [String, nil] Legacy anchor id, if supported.
13
+ def legacy_aref(object)
14
+ type = object.type
15
+
16
+ return "#{object.name}-constant" if type == :constant
17
+ return "#{object.name}-classvariable" if type == :classvariable
18
+ return nil unless object.respond_to?(:scope)
19
+
20
+ return "#{object.name}-class_method" if object.scope == :class
21
+
22
+ "#{object.name}-instance_method"
23
+ end
24
+
25
+ # Returns all anchor tags that should be attached to a heading.
26
+ #
27
+ # @param object [YARD::CodeObjects::Base] Object being rendered.
28
+ # @return [Array<String>] HTML anchor tags for the object.
29
+ def anchor_tags_for(object)
30
+ anchors = [aref(object), legacy_aref(object)].compact
31
+ anchors.map { |id| anchor_tag(id) }
32
+ end
33
+
34
+ # Appends the generated anchor tags to a Markdown heading.
35
+ #
36
+ # @param heading [String] Heading text to decorate.
37
+ # @param object [YARD::CodeObjects::Base] Object being rendered.
38
+ # @return [String] Heading text with embedded anchor tags.
39
+ def heading_with_anchors(heading, object)
40
+ "#{heading} #{anchor_tags_for(object).join(" ")}"
41
+ end
42
+
43
+ # Builds an HTML anchor tag for a generated id.
44
+ #
45
+ # @param id [String] Anchor id value.
46
+ # @return [String] HTML anchor tag.
47
+ def anchor_tag(id)
48
+ %(<a id="#{id}"></a>)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YARD
4
+ module Markdown
5
+ # Rewrites generated Markdown links so they point at Markdown output.
6
+ module LinkNormalizationHelper
7
+ # Normalizes generated Markdown before it is written to disk.
8
+ #
9
+ # @param content [String, Array<String>] Markdown content to finalize.
10
+ # @param current_path [String] Output path for the current document.
11
+ # @return [String] Normalized Markdown content with a trailing newline.
12
+ def finalize_markdown(content, current_path)
13
+ output = content.instance_of?(Array) ? content.join("\n") : content
14
+ output = output.lines.map(&:rstrip).join("\n")
15
+ output = normalize_local_links(output, current_path)
16
+ output = normalize_malformed_local_links(output)
17
+ output = output.gsub(/\n{3,}/, "\n\n").strip
18
+ "#{output}\n"
19
+ end
20
+
21
+ # Rewrites local Markdown links relative to the current output path.
22
+ #
23
+ # @param markdown [String] Markdown content to rewrite.
24
+ # @param current_path [String] Output path for the current document.
25
+ # @return [String] Markdown with local links normalized.
26
+ def normalize_local_links(markdown, current_path)
27
+ current_dir = Pathname.new(current_path).dirname
28
+
29
+ markdown.gsub(%r{\[(.+?)\]\((?!https?://|mailto:|#)([^)\n]+)\)}) do
30
+ label = Regexp.last_match(1)
31
+ target = Regexp.last_match(2)
32
+ path = target.sub(/[?#].*\z/, "")
33
+ suffix = target[path.length..]
34
+ rewritten_path = resolve_local_link_target(path, current_dir)
35
+
36
+ if rewritten_path.nil?
37
+ "`#{label.tr("`", "")}`"
38
+ else
39
+ "[#{label}](#{rewritten_path}#{suffix})"
40
+ end
41
+ end
42
+ end
43
+
44
+ # Resolves a local link path to a YARD registry object when possible.
45
+ #
46
+ # @param path [String] Link target path to resolve.
47
+ # @param current_dir [Pathname] Directory for the current output file.
48
+ # @return [YARD::CodeObjects::Base, nil] Matched registry object, if any.
49
+ def resolve_registry_object(path, current_dir)
50
+ cleaned = path.sub(%r{\A(?:(?:\.\./)+|\./)}, "")
51
+ candidates = [path]
52
+
53
+ if constant_reference_path?(cleaned)
54
+ current_parts = current_dir.to_s.split("/").reject { |part| part.empty? || part == "." }
55
+ target_parts = cleaned.split("/")
56
+
57
+ current_parts.length.downto(0) do |depth|
58
+ candidates << (current_parts.first(depth) + target_parts).join("::")
59
+ end
60
+ end
61
+
62
+ candidates.each do |candidate|
63
+ obj = Registry.at(candidate)
64
+ next if obj.nil? || obj.equal?(Registry.root)
65
+
66
+ return obj
67
+ end
68
+
69
+ nil
70
+ end
71
+
72
+ # Resolves a local link target to the final relative Markdown path.
73
+ #
74
+ # @param path [String] Link target path to resolve.
75
+ # @param current_dir [Pathname] Directory for the current output file.
76
+ # @return [String, nil] Relative Markdown path, or nil when unresolved.
77
+ def resolve_local_link_target(path, current_dir)
78
+ normalized = path.sub(%r{\A/+}, "")
79
+
80
+ obj = resolve_registry_object(normalized, current_dir)
81
+ if obj
82
+ object_path = options.serializer.serialized_path(obj)
83
+ return relative_output_path(current_dir, object_path)
84
+ end
85
+
86
+ if normalized.match?(/\.html\z/i)
87
+ normalized = normalized.sub(/\.html\z/i, ".md")
88
+ elsif File.extname(normalized).empty?
89
+ return nil if unresolved_identifier_target?(normalized)
90
+
91
+ normalized = "#{normalized}.md" if normalized.include?("/")
92
+ end
93
+
94
+ relative_output_path(current_dir, normalized)
95
+ end
96
+
97
+ # Returns whether a path looks like a constant reference.
98
+ #
99
+ # @param value [String] Link target to inspect.
100
+ # @return [Boolean] True when the path resembles a constant name.
101
+ def constant_reference_path?(value)
102
+ parts = value.split(%r{::|/}).reject(&:empty?)
103
+ return false if parts.empty?
104
+
105
+ parts.all? { |part| part.match?(/\A[A-Z]\w*\z/) }
106
+ end
107
+
108
+ # Returns whether a path looks like an unresolved bare identifier.
109
+ #
110
+ # @param path [String] Link target to inspect.
111
+ # @return [Boolean] True when the target should be treated as unresolved.
112
+ def unresolved_identifier_target?(path)
113
+ cleaned = path.sub(%r{\A(?:(?:\.\./)+|\./)}, "")
114
+ return true if cleaned.start_with?(":") || cleaned.match?(/\A\d/)
115
+
116
+ cleaned.match?(/\A[a-z_]\w*\z/)
117
+ end
118
+
119
+ # Computes a relative path from the current output directory.
120
+ #
121
+ # @param current_dir [Pathname] Directory for the current output file.
122
+ # @param target_path [String, Pathname] Output path being linked to.
123
+ # @return [String] Relative path suitable for a Markdown link.
124
+ def relative_output_path(current_dir, target_path)
125
+ target = target_path.to_s
126
+ return target if target.start_with?("../")
127
+
128
+ Pathname.new(target).relative_path_from(current_dir).to_s
129
+ rescue
130
+ target
131
+ end
132
+
133
+ # Replaces malformed local Markdown links with inline code.
134
+ #
135
+ # @param markdown [String] Markdown content to normalize.
136
+ # @return [String] Markdown with malformed local links replaced.
137
+ def normalize_malformed_local_links(markdown)
138
+ markdown.gsub(%r{\[([^\]]+)\]\((?!https?://|mailto:|#)(?:[^)\n]*['"][^)\n]*)\)}, '`\1`')
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YARD
4
+ module Markdown
5
+ # Formats method and attribute names for Markdown headings.
6
+ module MethodPresentationHelper
7
+ # Builds the display heading for a method.
8
+ #
9
+ # @param method_object [YARD::CodeObjects::MethodObject] Method being rendered.
10
+ # @return [String] Method heading text.
11
+ def formatted_method_heading(method_object)
12
+ name = method_object.name
13
+ signature = method_signature(method_object)
14
+ signature = " #{signature}" if name.end_with?("]")
15
+ "#{name}#{signature}"
16
+ end
17
+
18
+ # Returns the rendered parameter list for a method.
19
+ #
20
+ # @param method_object [YARD::CodeObjects::MethodObject] Method being rendered.
21
+ # @return [String] Parenthesized method signature.
22
+ def method_signature(method_object)
23
+ return "()" if method_object.parameters.nil?
24
+
25
+ rendered = method_object.parameters.map do |name, default|
26
+ (default.nil? || default.empty?) ? name : "#{name} = #{default}"
27
+ end
28
+
29
+ "(#{rendered.join(", ")})"
30
+ end
31
+
32
+ # Returns the access marker for an attribute.
33
+ #
34
+ # @param attribute [YARD::CodeObjects::MethodObject] Attribute reader or writer.
35
+ # @return [String] Access mode marker such as `R`, `W`, or `RW`.
36
+ def attribute_access(attribute)
37
+ info = attribute.attr_info || {}
38
+ return "RW" if info[:read] && info[:write]
39
+ return "R" if info[:read]
40
+ return "W" if info[:write]
41
+
42
+ return "RW" if attribute.reader? && attribute.writer?
43
+ return "R" if attribute.reader?
44
+
45
+ "W"
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YARD
4
+ module Markdown
5
+ # Collects and sorts the objects shown on a rendered object page.
6
+ module ObjectListingHelper
7
+ # Returns the constants and class variables defined on an object.
8
+ #
9
+ # @param object [YARD::CodeObjects::NamespaceObject] Object being rendered.
10
+ # @return [Array<YARD::CodeObjects::Base>] Constants and class variables.
11
+ def constant_listing(object)
12
+ constants = object.constants(included: false, inherited: false)
13
+ constants + object.cvars
14
+ end
15
+
16
+ # Returns the visible public methods defined directly on an object.
17
+ #
18
+ # @param object [YARD::CodeObjects::NamespaceObject] Object being rendered.
19
+ # @return [Array<YARD::CodeObjects::MethodObject>] Sorted public methods.
20
+ def public_method_list(object)
21
+ prune_method_listing(object.meths(inherited: false, visibility: :public))
22
+ .reject { |item| hidden_object?(item) }
23
+ .sort_by { |method_object| method_object.name }
24
+ end
25
+
26
+ # Returns the public class methods defined directly on an object.
27
+ #
28
+ # @param object [YARD::CodeObjects::NamespaceObject] Object being rendered.
29
+ # @return [Array<YARD::CodeObjects::MethodObject>] Sorted public class methods.
30
+ def public_class_methods(object)
31
+ public_method_list(object).select { |item| item.scope == :class }
32
+ end
33
+
34
+ # Returns the public instance methods defined directly on an object.
35
+ #
36
+ # @param object [YARD::CodeObjects::NamespaceObject] Object being rendered.
37
+ # @return [Array<YARD::CodeObjects::MethodObject>] Sorted public instance methods.
38
+ def public_instance_methods(object)
39
+ public_method_list(object).select { |item| item.scope == :instance }
40
+ end
41
+
42
+ # Returns the visible attribute methods for an object.
43
+ #
44
+ # @param object [YARD::CodeObjects::NamespaceObject] Object being rendered.
45
+ # @return [Array<YARD::CodeObjects::MethodObject>] Sorted attribute methods.
46
+ def attr_listing(object)
47
+ attrs = []
48
+
49
+ object.inheritance_tree(true).each do |superclass|
50
+ next if !options.embed_mixins.empty? && !options.embed_mixins_match?(superclass)
51
+
52
+ %i[class instance].each do |scope|
53
+ superclass.attributes.fetch(scope).each do |_name, rw|
54
+ attr = prune_method_listing([rw.fetch(:read), rw.fetch(:write)].compact, false).first
55
+ attrs << attr if attr
56
+ end
57
+ end
58
+
59
+ break if options.embed_mixins.empty?
60
+ end
61
+
62
+ sort_listing(attrs)
63
+ end
64
+
65
+ # Sorts a listing by scope and case-insensitive name.
66
+ #
67
+ # @param list [Array<YARD::CodeObjects::Base>] Objects to sort.
68
+ # @return [Array<YARD::CodeObjects::Base>] Sorted objects.
69
+ def sort_listing(list)
70
+ list.sort do |left, right|
71
+ scope_comparison = left.scope <=> right.scope
72
+ next scope_comparison unless scope_comparison.zero?
73
+
74
+ left.name.to_s.casecmp(right.name.to_s)
75
+ end
76
+ end
77
+
78
+ # Returns whether an object is explicitly hidden with `:nodoc:`.
79
+ #
80
+ # @param object [YARD::CodeObjects::Base] Listed object whose docstring may start with `:nodoc:`.
81
+ # @return [Boolean] True when the object should be hidden.
82
+ def hidden_object?(object)
83
+ object.docstring.start_with?(":nodoc:")
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YARD
4
+ module Markdown
5
+ # Renders inheritance and mixin relationship summaries.
6
+ module RelationshipSectionHelper
7
+ # Returns section content with the expected trailing spacing.
8
+ #
9
+ # @param content [Object] Section content to render.
10
+ # @return [String] Section content followed by blank-line spacing.
11
+ def render_section_content(content)
12
+ text = content.to_s.strip
13
+ return "" if text.empty?
14
+
15
+ "#{text}\n\n"
16
+ end
17
+
18
+ # Returns inheritance and mixin relationships for an object.
19
+ #
20
+ # @param object [YARD::CodeObjects::NamespaceObject] Object being rendered.
21
+ # @return [String] Markdown summary of the object's relationships.
22
+ def object_relationships(object)
23
+ lines = []
24
+
25
+ lines << "**Inherits:** `#{object.superclass}`" if object.instance_of?(CodeObjects::ClassObject)
26
+
27
+ [[:class, "Extended by"], [:instance, "Includes"]].each do |scope, label|
28
+ mixins = run_verifier(object.mixins(scope)).sort_by { |item| item.path }
29
+ next if mixins.empty?
30
+
31
+ lines << "**#{label}:** #{mixins.map { |mixin| "`#{mixin.path}`" }.join(", ")}"
32
+ end
33
+
34
+ lines.join("\n")
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YARD
4
+ module Markdown
5
+ # Assembles grouped content into ordered Markdown sections.
6
+ module SectionAssemblyHelper
7
+ # Groups items by their YARD group and orders them for rendering.
8
+ #
9
+ # @param items [Array<#group>] Renderable objects that expose a YARD group name.
10
+ # @param group_order [Array<String>, nil] Preferred ordering for named groups.
11
+ # @return [Array<Array>] Ordered pairs of group names and grouped items.
12
+ def grouped_items(items, group_order)
13
+ grouped = Hash.new { |hash, key| hash[key] = [] }
14
+ items.each { |item| grouped[item.group] << item }
15
+
16
+ ordered = []
17
+
18
+ Array(group_order).each do |name|
19
+ next unless grouped.key?(name)
20
+
21
+ ordered << [name, grouped.delete(name)]
22
+ end
23
+
24
+ grouped.keys.compact.sort.each do |name|
25
+ ordered << [name, grouped.delete(name)]
26
+ end
27
+
28
+ ordered << [nil, grouped.delete(nil)] if grouped.key?(nil)
29
+ ordered
30
+ end
31
+
32
+ # Appends non-empty content to a mutable list of lines.
33
+ #
34
+ # @param lines [Array<String>] Destination line buffer.
35
+ # @param content [String] Rendered Markdown block to split into lines.
36
+ # @param separated [Boolean] Whether to insert a blank separator line first.
37
+ # @return [void]
38
+ def append_lines(lines, content, separated: true)
39
+ return if content.lstrip.empty?
40
+
41
+ lines << "" if separated && !lines.empty? && !lines.last.empty?
42
+ lines.concat(content.split("\n"))
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YARD
4
+ module Markdown
5
+ # Formats YARD tags into Markdown list items and fenced examples.
6
+ module TagFormattingHelper
7
+ # Renders all tags for an object as Markdown.
8
+ #
9
+ # @param object [YARD::CodeObjects::Base] Object whose tags are being rendered.
10
+ # @return [String] Markdown representation of the object's tags.
11
+ def render_tags(object)
12
+ lines = []
13
+ regular_tags = object.tags.reject { |tag| tag.tag_name == "example" }
14
+ example_tags = object.tags.select { |tag| tag.tag_name == "example" }
15
+
16
+ regular_tags.each do |tag|
17
+ lines << "- #{format_tag(tag)}"
18
+ end
19
+
20
+ example_tags.each do |tag|
21
+ lines << nil unless lines.empty?
22
+ title = tag.name.to_s.rstrip.empty? ? "**@example**" : "**@example #{tag.name}**"
23
+ lines << title
24
+ lines << "```ruby"
25
+ lines << tag.text.to_s.rstrip
26
+ lines << "```"
27
+ end
28
+
29
+ lines.join("\n")
30
+ end
31
+
32
+ # Formats a non-example YARD tag as a Markdown list item body.
33
+ #
34
+ # @param tag [YARD::Tags::Tag] Non-example tag being converted into list item text.
35
+ # @return [String] Markdown representation of the tag.
36
+ def format_tag(tag)
37
+ parts = ["**@#{tag.tag_name}**"]
38
+ parts << "`#{tag.name}`" unless tag.name.to_s.lstrip.empty?
39
+
40
+ cleaned_types = normalized_tag_types(tag.types)
41
+ parts << "[#{cleaned_types.join(", ")}]" unless cleaned_types.empty?
42
+ parts << tag.text.strip unless tag.text.to_s.lstrip.empty?
43
+
44
+ parts.join(" ")
45
+ end
46
+
47
+ # Normalizes tag type declarations into printable strings.
48
+ #
49
+ # @param types [Array<Object>, Hash, nil] Raw tag types from YARD.
50
+ # @return [Array<String>] Cleaned type strings.
51
+ def normalized_tag_types(types)
52
+ values = if types.instance_of?(Hash)
53
+ types.map { |name, value| format_hash_tag_type(name, value) }
54
+ else
55
+ Array(types)
56
+ end
57
+
58
+ values.map(&:to_s).map(&:strip).reject(&:empty?)
59
+ end
60
+
61
+ # Formats a hash-style tag type entry.
62
+ #
63
+ # @param name [String] Type name to format.
64
+ # @param value [Object] Associated type detail.
65
+ # @return [String, nil] Formatted type entry, or nil when blank.
66
+ def format_hash_tag_type(name, value)
67
+ key = name.rstrip
68
+ return nil if key.empty?
69
+ return key if value.nil? || value == true || (value.respond_to?(:empty?) && value.empty?)
70
+
71
+ "#{key}: #{value}"
72
+ end
73
+ end
74
+ end
75
+ end
data/lib/yard-markdown.rb CHANGED
@@ -1,6 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "yard"
4
+ require_relative "yard/markdown/anchor_component_helper"
5
+ require_relative "yard/markdown/aref_helper"
6
+ require_relative "yard/markdown/collection_rendering_helper"
7
+ require_relative "yard/markdown/documentation_helper"
8
+ require_relative "yard/markdown/heading_helper"
9
+ require_relative "yard/markdown/link_normalization_helper"
10
+ require_relative "yard/markdown/method_presentation_helper"
11
+ require_relative "yard/markdown/object_listing_helper"
12
+ require_relative "yard/markdown/relationship_section_helper"
13
+ require_relative "yard/markdown/section_assembly_helper"
14
+ require_relative "yard/markdown/tag_formatting_helper"
4
15
 
5
16
  module YARD
6
17
  module Markdown