yard-markdown 0.6.0 → 0.7.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 92bac83ffe7c1f83b26164adef991efe13678b96096ba61af2626f83e9505991
4
- data.tar.gz: 7c871bffa551b5acc83e0b954163d933b7289db6542b1485c385ab97fb53e4eb
3
+ metadata.gz: 41e14595e84affb837da2c36afb9a40f0e7ea866472fa32e5aefabc7c32c2ec8
4
+ data.tar.gz: 605f1ad9c9496ebaf48afc7245ab25db98b5e22aed88c7c718bb3968f78b3b5e
5
5
  SHA512:
6
- metadata.gz: 7d7fb3ae97903f7c61aa0a057cdefd878a562264d15d5e7a3da17a5a2bdd5ea35c63cc47244022ab10fe20356c7adf367eee27e1de5b67281376174645c29b54
7
- data.tar.gz: 126033d377d8e652a19b33731d57ccfad197731cf819521766dbee5b37d1ee293baf41fc58c7927073ae3cddea9c5a4227b57539023de96ff66740e3d53e88d7
6
+ metadata.gz: '0887584b2fe63298271fad1483237eb303d215068a6da79aee7cb59c463a6c2838a6a72171676e7876f7a7d8c9003c0e23eb0fcb9dbfd88e329f028c5907bdef'
7
+ data.tar.gz: 9d50e908447b65b564315c996af01a43e35d360f2bafd600d34197ed20fc0cf8fde1b506850eb2fd476ee65d33c44ff10de8658abcd51037011060d4e69ec59f
data/.yard-lint.yml ADDED
@@ -0,0 +1,317 @@
1
+ # YARD-Lint Configuration (Strict Mode)
2
+ # See https://github.com/mensfeld/yard-lint for documentation
3
+ #
4
+ # This is a strict configuration suitable for new projects with high documentation standards.
5
+ # All validators are set to 'error' severity (no warnings or conventions).
6
+ # Minimum coverage is set to 100%.
7
+
8
+ # Global settings for all validators
9
+ AllValidators:
10
+ # YARD command-line options (applied to all validators by default)
11
+ YardOptions:
12
+ - --private
13
+ - --protected
14
+
15
+ # Global file exclusion patterns
16
+ Exclude:
17
+ - '\.git'
18
+ - "vendor/**/*"
19
+ - "node_modules/**/*"
20
+ - "spec/**/*"
21
+ - "test/**/*"
22
+ - "tmp/**/*"
23
+ - "example_rdoc.rb"
24
+ - "example_yard.rb"
25
+
26
+ # Exit code behavior (error, warning, convention, never)
27
+ FailOnSeverity: error
28
+
29
+ # Minimum documentation coverage percentage (0-100)
30
+ # Fails if coverage is below this threshold
31
+ MinCoverage: 100.0
32
+
33
+ # Diff mode settings
34
+ DiffMode:
35
+ # Default base ref for --diff (auto-detects main/master if not specified)
36
+ DefaultBaseRef: ~
37
+
38
+ # Documentation validators
39
+ Documentation/UndocumentedObjects:
40
+ Description: "Checks for classes, modules, and methods without documentation."
41
+ Enabled: true
42
+ Severity: error
43
+ ExcludedMethods:
44
+ - "initialize/0" # Exclude parameter-less initialize
45
+ - "/^_/" # Exclude private methods (by convention)
46
+
47
+ Documentation/UndocumentedMethodArguments:
48
+ Description: "Checks for method parameters without @param tags."
49
+ Enabled: true
50
+ Severity: error
51
+
52
+ Documentation/UndocumentedBooleanMethods:
53
+ Description: "Checks that question mark methods document their boolean return."
54
+ Enabled: true
55
+ Severity: error
56
+
57
+ Documentation/UndocumentedOptions:
58
+ Description: "Detects methods with options hash parameters but no @option tags."
59
+ Enabled: true
60
+ Severity: error
61
+
62
+ Documentation/MissingReturn:
63
+ Description: "Requires @return tags on all methods (opt-in for strict documentation)."
64
+ Enabled: true # Enabled in strict mode
65
+ Severity: error
66
+ ExcludedMethods:
67
+ - "initialize" # Exclude all initialize methods
68
+ # - '/^_/' # Uncomment to exclude private methods (by convention)
69
+
70
+ Documentation/MarkdownSyntax:
71
+ Description: "Detects common markdown syntax errors in documentation."
72
+ Enabled: true
73
+ Severity: error
74
+
75
+ Documentation/EmptyCommentLine:
76
+ Description: "Detects empty comment lines at the start or end of documentation blocks."
77
+ Enabled: true
78
+ Severity: error
79
+ EnabledPatterns:
80
+ Leading: true
81
+ Trailing: true
82
+
83
+ Documentation/BlankLineBeforeDefinition:
84
+ Description: "Detects blank lines between YARD documentation and method definition."
85
+ Enabled: true
86
+ Severity: error
87
+ OrphanedSeverity: error
88
+ EnabledPatterns:
89
+ SingleBlankLine: true
90
+ OrphanedDocs: true
91
+
92
+ # Tags validators
93
+ Tags/Order:
94
+ Description: "Enforces consistent ordering of YARD tags."
95
+ Enabled: true
96
+ Severity: error
97
+ EnforcedOrder:
98
+ - param
99
+ - option
100
+ - yield
101
+ - yieldparam
102
+ - yieldreturn
103
+ - return
104
+ - raise
105
+ - see
106
+ - example
107
+ - note
108
+ - todo
109
+
110
+ Tags/InvalidTypes:
111
+ Description: "Validates type definitions in @param, @return, @option tags."
112
+ Enabled: true
113
+ Severity: error
114
+ ValidatedTags:
115
+ - param
116
+ - option
117
+ - return
118
+
119
+ Tags/TypeSyntax:
120
+ Description: "Validates YARD type syntax using YARD parser."
121
+ Enabled: true
122
+ Severity: error
123
+ ValidatedTags:
124
+ - param
125
+ - option
126
+ - return
127
+ - yieldreturn
128
+
129
+ Tags/MeaninglessTag:
130
+ Description: "Detects @param/@option tags on classes, modules, or constants."
131
+ Enabled: true
132
+ Severity: error
133
+ CheckedTags:
134
+ - param
135
+ - option
136
+ InvalidObjectTypes:
137
+ - class
138
+ - module
139
+ - constant
140
+
141
+ Tags/CollectionType:
142
+ Description: "Validates Hash collection syntax consistency."
143
+ Enabled: true
144
+ Severity: error
145
+ EnforcedStyle: long # 'long' for Hash{K => V} (YARD standard), 'short' for {K => V}
146
+ ValidatedTags:
147
+ - param
148
+ - option
149
+ - return
150
+ - yieldreturn
151
+
152
+ Tags/TagTypePosition:
153
+ Description: "Validates type annotation position in tags."
154
+ Enabled: true
155
+ Severity: error
156
+ CheckedTags:
157
+ - param
158
+ - option
159
+ # EnforcedStyle: 'type_after_name' (YARD standard: @param name [Type])
160
+ # or 'type_first' (@param [Type] name)
161
+ EnforcedStyle: type_after_name
162
+
163
+ Tags/ApiTags:
164
+ Description: "Enforces @api tags on public objects."
165
+ Enabled: false # Opt-in validator
166
+ Severity: error
167
+ AllowedApis:
168
+ - public
169
+ - private
170
+ - internal
171
+
172
+ Tags/OptionTags:
173
+ Description: "Requires @option tags for methods with options parameters."
174
+ Enabled: true
175
+ Severity: error
176
+
177
+ Tags/ExampleSyntax:
178
+ Description: "Validates Ruby syntax in @example tags."
179
+ Enabled: true
180
+ Severity: error
181
+
182
+ Tags/ExampleStyle:
183
+ Description: "Validates code style in @example tags using RuboCop/StandardRB."
184
+ Enabled: false # Opt-in validator (requires RuboCop or StandardRB)
185
+ Severity: convention
186
+ # Linter: auto # Uncomment to explicitly configure: 'auto', 'rubocop', 'standard', 'none'
187
+ # SkipPatterns: # Uncomment to skip examples matching patterns
188
+ # - '/skip-lint/i'
189
+ # - '/bad code/i'
190
+
191
+ Tags/RedundantParamDescription:
192
+ Description: "Detects meaningless parameter descriptions that add no value."
193
+ Enabled: true
194
+ Severity: error
195
+ CheckedTags:
196
+ - param
197
+ - option
198
+ Articles:
199
+ - The
200
+ - the
201
+ - A
202
+ - a
203
+ - An
204
+ - an
205
+ MaxRedundantWords: 6
206
+ GenericTerms:
207
+ - object
208
+ - instance
209
+ - value
210
+ - data
211
+ - item
212
+ - element
213
+ EnabledPatterns:
214
+ ArticleParam: true
215
+ PossessiveParam: true
216
+ TypeRestatement: true
217
+ ParamToVerb: true
218
+ IdPattern: true
219
+ DirectionalDate: true
220
+ TypeGeneric: true
221
+
222
+ Tags/InformalNotation:
223
+ Description: 'Detects informal tag notation patterns like "Note:" instead of @note.'
224
+ Enabled: true
225
+ Severity: error
226
+ CaseSensitive: false
227
+ RequireStartOfLine: true
228
+ Patterns:
229
+ Note: "@note"
230
+ Todo: "@todo"
231
+ TODO: "@todo"
232
+ FIXME: "@todo"
233
+ See: "@see"
234
+ See also: "@see"
235
+ Warning: "@deprecated"
236
+ Deprecated: "@deprecated"
237
+ Author: "@author"
238
+ Version: "@version"
239
+ Since: "@since"
240
+ Returns: "@return"
241
+ Raises: "@raise"
242
+ Example: "@example"
243
+
244
+ Tags/NonAsciiType:
245
+ Description: "Detects non-ASCII characters in type annotations."
246
+ Enabled: true
247
+ Severity: error
248
+ ValidatedTags:
249
+ - param
250
+ - option
251
+ - return
252
+ - yieldreturn
253
+ - yieldparam
254
+
255
+ Tags/TagGroupSeparator:
256
+ Description: "Enforces blank line separators between different YARD tag groups."
257
+ Enabled: false # Opt-in validator
258
+ Severity: error
259
+ TagGroups:
260
+ param: [param, option]
261
+ return: [return]
262
+ error: [raise, throws]
263
+ example: [example]
264
+ meta: [see, note, todo, deprecated, since, version, api]
265
+ yield: [yield, yieldparam, yieldreturn]
266
+ RequireAfterDescription: false
267
+
268
+ Tags/ForbiddenTags:
269
+ Description: "Detects forbidden tag and type combinations."
270
+ Enabled: false # Opt-in validator
271
+ Severity: error
272
+ ForbiddenPatterns: []
273
+ # Example patterns:
274
+ # - Tag: return
275
+ # Types:
276
+ # - void
277
+ # - Tag: param
278
+ # Types:
279
+ # - Object
280
+ # - Tag: api # Forbids @api tag entirely (no Types = any occurrence)
281
+
282
+ # Warnings validators - catches YARD parser errors
283
+ Warnings/UnknownTag:
284
+ Description: "Detects unknown YARD tags."
285
+ Enabled: true
286
+ Severity: error
287
+
288
+ Warnings/UnknownDirective:
289
+ Description: "Detects unknown YARD directives."
290
+ Enabled: true
291
+ Severity: error
292
+
293
+ Warnings/InvalidTagFormat:
294
+ Description: "Detects malformed tag syntax."
295
+ Enabled: true
296
+ Severity: error
297
+
298
+ Warnings/InvalidDirectiveFormat:
299
+ Description: "Detects malformed directive syntax."
300
+ Enabled: true
301
+ Severity: error
302
+
303
+ Warnings/DuplicatedParameterName:
304
+ Description: "Detects duplicate @param tags."
305
+ Enabled: true
306
+ Severity: error
307
+
308
+ Warnings/UnknownParameterName:
309
+ Description: "Detects @param tags for non-existent parameters."
310
+ Enabled: true
311
+ Severity: error
312
+
313
+ # Semantic validators
314
+ Semantic/AbstractMethods:
315
+ Description: "Ensures @abstract methods do not have real implementations."
316
+ Enabled: true
317
+ Severity: error
data/AGENTS.md ADDED
@@ -0,0 +1,54 @@
1
+ You are working in a Ruby project that uses mutation testing.
2
+
3
+ ## Goal
4
+
5
+ Achieve 100% mutation coverage. Verify with:
6
+
7
+ ```
8
+ bundle exec mutant run
9
+ ```
10
+
11
+ When iterating, prefer `--fail-fast` so you address one surviving
12
+ mutant at a time:
13
+
14
+ ```
15
+ bundle exec mutant run --fail-fast
16
+ ```
17
+
18
+ ## When you find an alive mutation
19
+
20
+ Decide which bucket it falls into:
21
+
22
+ - **A) The code does too much** for what the tests ask for. The
23
+ surviving mutation reveals behavior that no test requires. The
24
+ fix is to simplify the implementation.
25
+ - **B) A test is missing.** The behavior is intentional but no test
26
+ observes it. The fix is to add a test.
27
+
28
+ Decide between A) and B) before changing anything. If unsure, ask
29
+ the user.
30
+
31
+ ## Constraints
32
+
33
+ - Line coverage must stay at 100%. Verify with:
34
+
35
+ ```
36
+ SIMPLECOV=1 bundle exec rake test
37
+ ```
38
+
39
+ - You may not skip mutants by configuring mutant to ignore them.
40
+ No `expressions:` filters, no `coverage_criteria:` tweaks.
41
+ - You may not use `send` or `__send__` to invoke private methods
42
+ in tests just to satisfy mutant.
43
+ - You may not stub or mock the system under test (`Age`).
44
+
45
+ ## Done
46
+
47
+ You are done when both of these are green and don't return any offenses:
48
+
49
+ ```
50
+ SIMPLECOV=1 bundle exec rake test
51
+ bundle exec mutant run
52
+ bundle exec rake markdown:validate_real_world
53
+ yard-lint lib/
54
+ ```
data/CHANGELOG.md ADDED
@@ -0,0 +1,17 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ This format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5
+
6
+ ## Unreleased
7
+
8
+ ## 0.7.0
9
+ ## Added
10
+ - Adding yard-lint and all method documentation
11
+ - Generating rbs signature based on yard tags
12
+ - Adding mutant testing into a project
13
+
14
+ ## Changed
15
+ - Refactoring and simplification of teamplate, mostly driven by mutantion testing
16
+ - Improve documentation for entire project
17
+
data/README.md CHANGED
@@ -63,6 +63,12 @@ Validate generated markdown in sample docs:
63
63
  bundle exec rake markdown:validate_examples
64
64
  ```
65
65
 
66
+ Regenerate the checked-in RBS types derived from YARD docs:
67
+
68
+ ```bash
69
+ bundle exec rake types:generate
70
+ ```
71
+
66
72
  There is also a real-world validation harness for repositories with substantial YARD documentation (`rspec-core`, `sidekiq`):
67
73
 
68
74
  ```bash
@@ -73,4 +79,6 @@ This task validates generated markdown against CommonMark + GFM rendering, and r
73
79
 
74
80
  GitHub Actions CI now runs this task on every push/PR, so `sidekiq` and other real-world fixture gems are verified continuously.
75
81
 
82
+ CI also regenerates `sig/yard/markdown.rbs` with `sord` and fails if the checked-in RBS file is out of date.
83
+
76
84
  For reproducible checks, the task clones pinned tags (`rspec-core` `v3.13.2`, `sidekiq` `v7.3.10`) into `tmp/real-world/repos` before generating output.
data/Rakefile CHANGED
@@ -17,6 +17,8 @@ end
17
17
 
18
18
  task default: %i[test stree:write]
19
19
 
20
+ TYPES_OUTPUT_PATH = "sig/yard/markdown.rbs"
21
+
20
22
  def shell_escape(path)
21
23
  Shellwords.escape(path)
22
24
  end
@@ -84,6 +86,33 @@ def checkout_repo(url, destination, ref: nil)
84
86
  run_command_with_analysis(command, label: "git_clone_#{destination}")
85
87
  end
86
88
 
89
+ def generate_types(output_path = TYPES_OUTPUT_PATH)
90
+ FileUtils.mkdir_p(File.dirname(output_path))
91
+
92
+ command = [
93
+ "sord gen",
94
+ "--rbs",
95
+ "--no-sord-comments",
96
+ "--replace-unresolved-with-untyped",
97
+ "--replace-errors-with-untyped",
98
+ shell_escape(output_path)
99
+ ].join(" ")
100
+
101
+ run_command_with_analysis(command, label: "sord_generate")
102
+ end
103
+
104
+ def ensure_clean_generated_file(path)
105
+ command = "git status --short -- #{shell_escape(path)}"
106
+ stdout, stderr, status = Open3.capture3(command)
107
+ combined_output = [stdout, stderr].reject(&:empty?).join("\n")
108
+
109
+ raise "Unable to verify generated types for #{path}" unless status.success?
110
+ return if combined_output.strip.empty?
111
+
112
+ puts combined_output
113
+ raise "#{path} is out of date. Run `bundle exec rake types:generate` and commit the updated file."
114
+ end
115
+
87
116
 
88
117
  namespace :examples do
89
118
  desc "Generate basic example documentation using yard-markdown plugin"
@@ -153,3 +182,15 @@ namespace :markdown do
153
182
  end
154
183
  end
155
184
  end
185
+
186
+ namespace :types do
187
+ desc "Generate checked-in RBS types from YARD documentation"
188
+ task :generate do
189
+ generate_types
190
+ end
191
+
192
+ desc "Verify checked-in RBS types are up to date"
193
+ task check: :generate do
194
+ ensure_clean_generated_file(TYPES_OUTPUT_PATH)
195
+ end
196
+ end
data/config/mutant.yml ADDED
@@ -0,0 +1,25 @@
1
+ usage: opensource
2
+
3
+ integration:
4
+ name: minitest
5
+
6
+ includes:
7
+ - lib
8
+ - test
9
+
10
+ requires:
11
+ - ./test/support/mutant_setup.rb
12
+
13
+ matcher:
14
+ subjects:
15
+ - YARD::Markdown::AnchorComponentHelper#
16
+ - YARD::Markdown::ArefHelper#
17
+ - YARD::Markdown::CollectionRenderingHelper#
18
+ - YARD::Markdown::DocumentationHelper#
19
+ - YARD::Markdown::HeadingHelper#
20
+ - YARD::Markdown::LinkNormalizationHelper#
21
+ - YARD::Markdown::MethodPresentationHelper#
22
+ - YARD::Markdown::ObjectListingHelper#
23
+ - YARD::Markdown::RelationshipSectionHelper#
24
+ - YARD::Markdown::SectionAssemblyHelper#
25
+ - YARD::Markdown::TagFormattingHelper#
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Namespace for YARD extensions used by this gem.
4
+ module YARD
5
+ # Shared helpers for rendering YARD objects as Markdown.
6
+ module Markdown
7
+ # Builds anchor-safe identifier fragments from arbitrary values.
8
+ module AnchorComponentHelper
9
+ # Encodes a value so it can be embedded safely in an HTML anchor id.
10
+ #
11
+ # @param value [Object] Raw anchor fragment to encode.
12
+ # @return [String] Anchor-safe identifier fragment.
13
+ def anchor_component(value)
14
+ value.to_s.each_char.map do |char|
15
+ char.match?(/[A-Za-z0-9_-]/) ? char : format('-%X', char.ord)
16
+ end.join
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module YARD
4
+ module Markdown
5
+ # Computes anchor ids that match the generated Markdown headings.
6
+ module ArefHelper
7
+ include AnchorComponentHelper
8
+
9
+ # Returns the primary anchor id for a documented object.
10
+ #
11
+ # @param object [YARD::CodeObjects::Base] Object being rendered.
12
+ # @return [String] Anchor id for the object's heading.
13
+ def aref(object)
14
+ type = object.type
15
+
16
+ return "class-#{object.path.gsub('::', '-')}" if type == :class
17
+ return "module-#{object.path.gsub('::', '-')}" if type == :module
18
+ return "constant-#{object.name}" if type == :constant
19
+ return "classvariable-#{anchor_component(object.name)}" if type == :classvariable
20
+
21
+ scope = object.scope == :class ? 'c' : 'i'
22
+
23
+ if !object.attr_info.nil?
24
+ "attribute-#{scope}-#{object.name}"
25
+ else
26
+ "method-#{scope}-#{anchor_component(object.name)}"
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -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,30 @@
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
+ RDoc::Markup::ToMarkdown.new.convert(docstring).rstrip
27
+ end
28
+ end
29
+ end
30
+ end