rdoc-markdown 0.17.2 → 0.19.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -1
- data/README.md +19 -82
- data/lib/markdown.rb +0 -4
- data/lib/rdoc/discover.rb +1 -7
- data/lib/rdoc/generator/markdown/conversion.rb +196 -0
- data/lib/rdoc/generator/markdown/crossref.rb +13 -5
- data/lib/rdoc/generator/markdown/descriptions.rb +163 -0
- data/lib/rdoc/generator/markdown/index.rb +47 -0
- data/lib/rdoc/generator/markdown/paths.rb +118 -0
- data/lib/rdoc/generator/markdown/selection.rb +39 -0
- data/lib/rdoc/generator/markdown/signatures.rb +174 -0
- data/lib/rdoc/generator/markdown.rb +80 -680
- data/lib/rdoc/markdown/version.rb +1 -1
- data/lib/templates/classfile.md.erb +14 -11
- metadata +19 -43
- data/.editorconfig +0 -13
- data/.erb_lint.yml +0 -36
- data/.erb_linters/no_embedded_assets.rb +0 -29
- data/.erb_linters/non_raw_html.rb +0 -29
- data/.standard.yml +0 -3
- data/.yard-lint.yml +0 -290
- data/AGENTS.md +0 -50
- data/CODE_OF_CONDUCT.md +0 -84
- data/Gemfile +0 -13
- data/Gemfile.lock +0 -202
- data/Rakefile +0 -296
- data/example/Bird.md +0 -21
- data/example/Duck.md +0 -56
- data/example/Object.md +0 -8
- data/example/Waterfowl.md +0 -9
- data/example/index.csv +0 -16
- data/example/jekyll-seo-tag/Jekyll/SeoTag/AuthorDrop.md +0 -35
- data/example/jekyll-seo-tag/Jekyll/SeoTag/Drop.md +0 -108
- data/example/jekyll-seo-tag/Jekyll/SeoTag/Filters.md +0 -8
- data/example/jekyll-seo-tag/Jekyll/SeoTag/ImageDrop.md +0 -29
- data/example/jekyll-seo-tag/Jekyll/SeoTag/JSONLD.md +0 -15
- data/example/jekyll-seo-tag/Jekyll/SeoTag/JSONLDDrop.md +0 -34
- data/example/jekyll-seo-tag/Jekyll/SeoTag/UrlHelper.md +0 -4
- data/example/jekyll-seo-tag/Jekyll/SeoTag.md +0 -48
- data/example/jekyll-seo-tag/Jekyll.md +0 -3
- data/example/jekyll-seo-tag/Liquid/Tag.md +0 -3
- data/example/jekyll-seo-tag/Liquid.md +0 -4
- data/example/jekyll-seo-tag/index.csv +0 -60
- data/mutant.yml +0 -15
- data/rdoc-markdown.gemspec +0 -46
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Resolves source and generated documentation paths.
|
|
4
|
+
module RDoc::Generator::Markdown::Paths
|
|
5
|
+
# Converts a qualified object name into a Markdown path.
|
|
6
|
+
#
|
|
7
|
+
# @param class_name [String] Qualified class or module name.
|
|
8
|
+
#
|
|
9
|
+
# @return [String] Relative Markdown path.
|
|
10
|
+
def turn_to_path(class_name)
|
|
11
|
+
"#{class_name.gsub("::", "/")}.md"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Builds the Markdown output path for an RDoc page.
|
|
15
|
+
#
|
|
16
|
+
# @param page [RDoc::TopLevel] Page object to render.
|
|
17
|
+
#
|
|
18
|
+
# @return [String] Relative Markdown path.
|
|
19
|
+
def page_output_path(page)
|
|
20
|
+
relative_name = page.relative_name
|
|
21
|
+
source_path = normalize_input_path_for_output(relative_name)
|
|
22
|
+
return source_path if relative_name.match?(/\.(?:md|markdown)\z/i)
|
|
23
|
+
|
|
24
|
+
dirname = File.dirname(source_path)
|
|
25
|
+
basename = "#{File.basename(source_path).tr(".", "_")}.md"
|
|
26
|
+
|
|
27
|
+
return basename if dirname == "."
|
|
28
|
+
|
|
29
|
+
"#{dirname}/#{basename}"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Returns the canonical Markdown path for a class or module.
|
|
33
|
+
#
|
|
34
|
+
# @param code_object [RDoc::Context] Class or module object.
|
|
35
|
+
#
|
|
36
|
+
# @return [String] Relative Markdown path.
|
|
37
|
+
def output_path_for(code_object)
|
|
38
|
+
turn_to_path(code_object.full_name)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Rewrites local Markdown links relative to the current output file.
|
|
42
|
+
#
|
|
43
|
+
# @param markdown [String] Markdown content.
|
|
44
|
+
# @param current_output_path [String] Output path for the file being written.
|
|
45
|
+
#
|
|
46
|
+
# @return [String] Markdown with normalized internal links.
|
|
47
|
+
def normalize_internal_links(markdown, current_output_path:)
|
|
48
|
+
RDoc::Generator::Markdown::Paths.rewrite_internal_links(
|
|
49
|
+
markdown,
|
|
50
|
+
current_output_path,
|
|
51
|
+
generation_state
|
|
52
|
+
)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private :turn_to_path, :page_output_path, :output_path_for, :normalize_internal_links
|
|
56
|
+
|
|
57
|
+
# Rewrites links using explicit generated-path state.
|
|
58
|
+
#
|
|
59
|
+
# @param markdown [String] Markdown content.
|
|
60
|
+
# @param current_output_path [String] Output path for the current file.
|
|
61
|
+
# @param state [RDoc::Generator::Markdown::GenerationState] Prepared generation state.
|
|
62
|
+
#
|
|
63
|
+
# @return [String] Markdown with normalized internal links.
|
|
64
|
+
def self.rewrite_internal_links(markdown, current_output_path, state)
|
|
65
|
+
current_dir = Pathname.new(current_output_path).dirname
|
|
66
|
+
|
|
67
|
+
markdown.gsub(%r{\]\(([^)]+)\)}) do
|
|
68
|
+
target = Regexp.last_match(1)
|
|
69
|
+
path = target.sub(/[?#].*\z/, "")
|
|
70
|
+
suffix = target[path.length..]
|
|
71
|
+
|
|
72
|
+
resolved = resolve_output_path_from(path, current_dir, state)
|
|
73
|
+
rewritten = resolved ? Pathname.new(resolved).relative_path_from(current_dir) : path
|
|
74
|
+
"](#{rewritten}#{suffix})"
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Resolves a link against explicit generated-path state.
|
|
79
|
+
#
|
|
80
|
+
# @param path [String] Link path from Markdown content.
|
|
81
|
+
# @param current_dir [Pathname] Directory of the current output file.
|
|
82
|
+
# @param state [RDoc::Generator::Markdown::GenerationState] Prepared generation state.
|
|
83
|
+
#
|
|
84
|
+
# @return [String, nil] Resolved output path, or nil when unresolved.
|
|
85
|
+
def self.resolve_output_path_from(path, current_dir, state)
|
|
86
|
+
candidates = [path, path.delete_prefix("#{state.root_path_segment}/")]
|
|
87
|
+
candidates += candidates.map { |candidate| candidate.sub(/_(md|markdown)\.md\z/i, '.\1') }
|
|
88
|
+
expanded_candidates = candidates.map { |candidate| current_dir.join(candidate).cleanpath.to_s }
|
|
89
|
+
|
|
90
|
+
(candidates + expanded_candidates).find { |candidate| state.known_output_paths.include?(candidate) }
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Normalizes an input filename into an output-relative source path.
|
|
94
|
+
#
|
|
95
|
+
# @param path [String] RDoc input path.
|
|
96
|
+
#
|
|
97
|
+
# @return [String] Normalized path without root prefixes.
|
|
98
|
+
def normalize_input_path_for_output(path)
|
|
99
|
+
RDoc::Generator::Markdown::Paths.normalize_input_path(path, source_dir)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
private :normalize_input_path_for_output
|
|
103
|
+
|
|
104
|
+
# Normalizes an input path against an explicit source directory.
|
|
105
|
+
#
|
|
106
|
+
# @param path [String] RDoc input path.
|
|
107
|
+
# @param source_dir [String] Absolute documentation source directory.
|
|
108
|
+
#
|
|
109
|
+
# @return [String] Normalized path without root prefixes.
|
|
110
|
+
def self.normalize_input_path(path, source_dir)
|
|
111
|
+
normalized = path.tr("\\", "/").sub(%r{\A\./}, "")
|
|
112
|
+
normalized = normalized.sub(%r{\A#{Regexp.escape(source_dir)}/}, "")
|
|
113
|
+
normalized = normalized.sub(%r{\A/}, "")
|
|
114
|
+
|
|
115
|
+
root_basename = File.basename(source_dir)
|
|
116
|
+
normalized.sub(%r{\A#{Regexp.escape(root_basename)}/}, "")
|
|
117
|
+
end
|
|
118
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Selects RDoc objects that produce Markdown output.
|
|
4
|
+
module RDoc::Generator::Markdown::Selection
|
|
5
|
+
# Returns sorted classes and modules with renderable content.
|
|
6
|
+
#
|
|
7
|
+
# @param store [RDoc::Store] Documentation store.
|
|
8
|
+
#
|
|
9
|
+
# @return [Array<RDoc::Context>] Selected classes and modules.
|
|
10
|
+
def self.classes(store)
|
|
11
|
+
store.unique_classes_and_modules.select(&:display?).select { |klass| renderable_class?(klass) }.sort
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Checks whether a class or module has content worth rendering.
|
|
15
|
+
#
|
|
16
|
+
# @param klass [RDoc::Context] Candidate class or module.
|
|
17
|
+
#
|
|
18
|
+
# @return [Boolean] Whether the object should produce a page.
|
|
19
|
+
def self.renderable_class?(klass)
|
|
20
|
+
klass.in_files.any? ||
|
|
21
|
+
klass.documented? ||
|
|
22
|
+
klass.includes.any? ||
|
|
23
|
+
klass.method_list.any?(&:display?) ||
|
|
24
|
+
klass.constants.any?(&:display?) ||
|
|
25
|
+
klass.attributes.any?(&:display?) ||
|
|
26
|
+
klass.sections.any? { |section| section.title.to_s.match?(/\S/) || !section.to_document.empty? }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Returns sorted documentation pages supported by the generator.
|
|
30
|
+
#
|
|
31
|
+
# @param store [RDoc::Store] Documentation store.
|
|
32
|
+
#
|
|
33
|
+
# @return [Array<RDoc::TopLevel>] Selected pages.
|
|
34
|
+
def self.pages(store)
|
|
35
|
+
store.all_files.select(&:text?).select(&:display?)
|
|
36
|
+
.select { |page| page.relative_name.match?(/\.(?:md|markdown|rdoc)\z/i) }
|
|
37
|
+
.sort_by(&:base_name)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Normalizes RDoc and RBS method signatures for headings.
|
|
4
|
+
module RDoc::Generator::Markdown::Signatures
|
|
5
|
+
# Signature opening delimiters and their matching closers.
|
|
6
|
+
DELIMITER_PAIRS = {
|
|
7
|
+
"(" => ")", "[" => "]", "{" => "}"
|
|
8
|
+
}.freeze
|
|
9
|
+
|
|
10
|
+
# Builds the visible method signature used in headings.
|
|
11
|
+
#
|
|
12
|
+
# @param method [RDoc::AnyMethod] Method object to render.
|
|
13
|
+
#
|
|
14
|
+
# @return [String] Normalized method signature.
|
|
15
|
+
def method_signature(method)
|
|
16
|
+
RDoc::Generator::Markdown::Signatures.render_method_signature(method, store)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private :method_signature
|
|
20
|
+
|
|
21
|
+
# Builds a method signature from RDoc and store metadata.
|
|
22
|
+
#
|
|
23
|
+
# @param method [RDoc::AnyMethod] Method object to render.
|
|
24
|
+
# @param store [RDoc::Store] Documentation store with sidecar signatures.
|
|
25
|
+
#
|
|
26
|
+
# @return [String] Normalized method signature.
|
|
27
|
+
def self.render_method_signature(method, store)
|
|
28
|
+
signatures = method.type_signature_lines || store.rbs_signature_for(method) || [method.param_seq]
|
|
29
|
+
|
|
30
|
+
signatures = signatures.filter_map do |signature|
|
|
31
|
+
next unless signature&.match?(/\S/)
|
|
32
|
+
|
|
33
|
+
signature = signature.gsub("->", " -> ")
|
|
34
|
+
signature = signature.gsub(/\s+/, " ").strip
|
|
35
|
+
signature = " #{signature}" if signature.start_with?("->")
|
|
36
|
+
merge_method_signature_arguments(signature, method.params)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
return "()" if signatures.empty?
|
|
40
|
+
|
|
41
|
+
signatures.join(" | ")
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Merges RDoc parameter names into a type-only signature.
|
|
45
|
+
#
|
|
46
|
+
# @param signature [String] Method signature from RDoc call sequence.
|
|
47
|
+
# @param raw_params [String, nil] Method parameter list from RDoc.
|
|
48
|
+
#
|
|
49
|
+
# @return [String] Signature with names added when safe.
|
|
50
|
+
def self.merge_method_signature_arguments(signature, raw_params)
|
|
51
|
+
params = normalized_method_params(raw_params)
|
|
52
|
+
|
|
53
|
+
signature_args, signature_suffix = split_signature_arguments_and_suffix(signature)
|
|
54
|
+
return signature unless signature_args
|
|
55
|
+
|
|
56
|
+
param_parts = split_signature_list(params)
|
|
57
|
+
signature_parts = split_signature_list(signature_args)
|
|
58
|
+
return signature unless param_parts.length.eql?(signature_parts.length)
|
|
59
|
+
|
|
60
|
+
merged = merged_signature(param_parts, signature_parts, signature_suffix)
|
|
61
|
+
merged || signature
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Merges matching parameter and signature fragments.
|
|
65
|
+
#
|
|
66
|
+
# @param param_parts [Array<String>] RDoc parameter fragments.
|
|
67
|
+
# @param signature_parts [Array<String>] Signature type fragments.
|
|
68
|
+
# @param signature_suffix [String] Text following the argument list.
|
|
69
|
+
#
|
|
70
|
+
# @return [String, nil] Merged signature, or nil when merging is unsafe or unnecessary.
|
|
71
|
+
def self.merged_signature(param_parts, signature_parts, signature_suffix)
|
|
72
|
+
param_names = param_parts.map { |part| extract_parameter_name(part) }
|
|
73
|
+
return if param_names.any?(&:nil?)
|
|
74
|
+
return if signature_parts.zip(param_names).all? { |part, name| signature_part_mentions_name?(part, name) }
|
|
75
|
+
|
|
76
|
+
merged_args = param_parts.zip(signature_parts).map do |param, type|
|
|
77
|
+
separator = param.end_with?(":") ? " " : ": "
|
|
78
|
+
"#{param}#{separator}#{type}"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
"(#{merged_args.join(", ")})#{signature_suffix}"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Normalizes RDoc's raw parameter string.
|
|
85
|
+
#
|
|
86
|
+
# @param raw_params [String, nil] Parameter list from RDoc.
|
|
87
|
+
#
|
|
88
|
+
# @return [String] Parameter list without outer parentheses.
|
|
89
|
+
def self.normalized_method_params(raw_params)
|
|
90
|
+
params = raw_params.to_s.strip
|
|
91
|
+
params = params[1...-1] if params.start_with?("(") && params.end_with?(")")
|
|
92
|
+
|
|
93
|
+
params
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Splits a parenthesized signature into arguments and suffix.
|
|
97
|
+
#
|
|
98
|
+
# @param signature [String] Method signature.
|
|
99
|
+
#
|
|
100
|
+
# @return [Array<String>, nil] Argument text and suffix, or nil when not parenthesized.
|
|
101
|
+
def self.split_signature_arguments_and_suffix(signature)
|
|
102
|
+
return unless signature.start_with?("(")
|
|
103
|
+
|
|
104
|
+
depth = 0
|
|
105
|
+
|
|
106
|
+
signature.each_char.with_index do |char, index|
|
|
107
|
+
depth += 1 if char == "("
|
|
108
|
+
|
|
109
|
+
next unless char == ")"
|
|
110
|
+
|
|
111
|
+
depth -= 1
|
|
112
|
+
return [signature[1...index], signature[(index + 1)..]] if depth.zero?
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Splits a comma-separated signature list while preserving nested groups.
|
|
117
|
+
#
|
|
118
|
+
# @param list [String] Signature argument list.
|
|
119
|
+
#
|
|
120
|
+
# @return [Array<String>] Signature parts.
|
|
121
|
+
def self.split_signature_list(list)
|
|
122
|
+
parts = []
|
|
123
|
+
current = +""
|
|
124
|
+
delimiters = []
|
|
125
|
+
|
|
126
|
+
list.each_char do |char|
|
|
127
|
+
if top_level_signature_separator?(char, delimiters)
|
|
128
|
+
parts << current.strip
|
|
129
|
+
current.clear
|
|
130
|
+
else
|
|
131
|
+
closing = DELIMITER_PAIRS[char]
|
|
132
|
+
if closing
|
|
133
|
+
delimiters << closing
|
|
134
|
+
elsif char == delimiters.last
|
|
135
|
+
delimiters.pop
|
|
136
|
+
end
|
|
137
|
+
current << char
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
parts << current.strip unless current.empty?
|
|
142
|
+
parts
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Checks whether a character separates top-level signature parts.
|
|
146
|
+
#
|
|
147
|
+
# @param char [String] Current signature character.
|
|
148
|
+
# @param delimiters [Array<String>] Expected closing delimiters.
|
|
149
|
+
#
|
|
150
|
+
# @return [Boolean] Whether the character is a top-level comma.
|
|
151
|
+
def self.top_level_signature_separator?(char, delimiters)
|
|
152
|
+
char == "," && delimiters.empty?
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Extracts a bare Ruby parameter name from a parameter fragment.
|
|
156
|
+
#
|
|
157
|
+
# @param parameter [String] Parameter fragment.
|
|
158
|
+
#
|
|
159
|
+
# @return [String, nil] Parameter name, or nil when invalid.
|
|
160
|
+
def self.extract_parameter_name(parameter)
|
|
161
|
+
match = parameter.match(/\A(?:\*\*|\*|&)?([a-z_]\w*):?\z/)
|
|
162
|
+
match && match[1]
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Checks whether a signature fragment already includes a parameter name.
|
|
166
|
+
#
|
|
167
|
+
# @param text [String] Signature fragment.
|
|
168
|
+
# @param name [String] Parameter name.
|
|
169
|
+
#
|
|
170
|
+
# @return [Boolean] True when the name appears as a standalone word.
|
|
171
|
+
def self.signature_part_mentions_name?(text, name)
|
|
172
|
+
text.match?(/(?<!\w)#{name}(?!\w)/)
|
|
173
|
+
end
|
|
174
|
+
end
|