Skip to content

Commit 450df2f

Browse files
committed
feat(yard): add initial support of YARD tags
At this time, `@yield`, `@private` and `@api private` are supported.
1 parent 1a7aa33 commit 450df2f

File tree

5 files changed

+692
-0
lines changed

5 files changed

+692
-0
lines changed

lib/rdoc/code_object.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
# frozen_string_literal: true
2+
3+
require_relative 'yard'
4+
25
##
36
# Base class for the RDoc code tree.
47
#

lib/rdoc/code_object/constant.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,20 @@ def store=(store)
174174
@file = @store.add_file @file.full_name if @file
175175
end
176176

177+
##
178+
# Process comment as YARD comment
179+
def comment=(comment)
180+
# Process YARD tags while we still have the RDoc::Comment object
181+
if comment.is_a?(RDoc::Comment)
182+
RDoc::YARD.process(comment, self)
183+
end
184+
185+
# Then set the comment normally
186+
super(comment)
187+
188+
@comment
189+
end
190+
177191
def to_s # :nodoc:
178192
parent_name = parent ? parent.full_name : '(unknown)'
179193
if is_alias_for

lib/rdoc/code_object/method_attr.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,20 @@ def store=(store)
156156
@file = @store.add_file @file.full_name if @file
157157
end
158158

159+
##
160+
# Process comment as YARD comment
161+
def comment=(comment)
162+
# Process YARD tags while we still have the RDoc::Comment object
163+
if comment.is_a?(RDoc::Comment)
164+
RDoc::YARD.process(comment, self)
165+
end
166+
167+
# Then set the comment normally
168+
super(comment)
169+
170+
@comment
171+
end
172+
159173
def find_see # :nodoc:
160174
return nil if singleton || is_alias_for
161175

lib/rdoc/yard.rb

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
# frozen_string_literal: true
2+
3+
##
4+
# YARD compatibility module for RDoc
5+
#
6+
# This module provides support for parsing YARD tags in RDoc comments.
7+
# Currently supports:
8+
# - @yield [params] - Describes block parameters
9+
# - @private - Marks method/class as private
10+
# - @api private - Alternative private marking
11+
12+
class RDoc::YARD
13+
14+
##
15+
# Processes YARD tags in the given comment text for a code object
16+
#
17+
# Returns modified comment text with YARD tags processed
18+
def self.process(comment, code_object)
19+
new(comment, code_object).process
20+
end
21+
22+
attr_reader :comment, :code_object, :text
23+
24+
def initialize(comment, code_object)
25+
@comment = comment
26+
@code_object = code_object
27+
end
28+
29+
def process
30+
return comment unless comment && code_object
31+
32+
# Only process RDoc::Comment objects to avoid modifying plain strings
33+
# that aren't actually documentation comments
34+
return comment unless comment.is_a?(RDoc::Comment)
35+
36+
# Skip processing if comment is in TomDoc format
37+
return comment if comment.format == 'tomdoc'
38+
39+
@text = comment.text.dup
40+
41+
# Process @yield tags
42+
process_yield_tags
43+
44+
# Process @private tags
45+
process_private_tags
46+
47+
# Process @api tags
48+
process_api_tags
49+
50+
# Only update comment.text if we actually modified it
51+
# This preserves the @document for comments created from parsed documents
52+
if text != comment.text
53+
comment.text = text
54+
end
55+
56+
comment
57+
end
58+
59+
private
60+
61+
##
62+
# Process @yield tags to extract block parameters
63+
def process_yield_tags
64+
return unless code_object.respond_to?(:block_params=)
65+
66+
# Skip if already has block_params from :yields: directive
67+
return if code_object.block_params && !code_object.block_params.empty?
68+
69+
# Match @yield [params] description
70+
if text =~ /^\s*#?\s*@yield\s*(?:\[([^\]]*)\])?\s*(.*?)$/
71+
params = $1
72+
73+
if params && !params.empty?
74+
# Clean up the params - remove types if present
75+
clean_params = extract_param_names(params)
76+
code_object.block_params = clean_params unless clean_params.empty?
77+
end
78+
79+
# Remove the @yield line from comment
80+
text.gsub!(/^\s*#?\s*@yield.*?$\n?/, '')
81+
end
82+
end
83+
84+
##
85+
# Process @private tags to set visibility
86+
def process_private_tags
87+
return unless code_object.respond_to?(:visibility=)
88+
89+
if text =~ /^\s*#?\s*@private\s*$/
90+
code_object.visibility = :private
91+
92+
# Remove the @private line from comment
93+
text.gsub!(/^\s*#?\s*@private\s*$\n?/, '')
94+
end
95+
end
96+
97+
##
98+
# Process @api tags (specifically @api private)
99+
def process_api_tags
100+
return unless code_object.respond_to?(:visibility=)
101+
102+
if text =~ /^\s*#?\s*@api\s+private\s*$/
103+
code_object.visibility = :private
104+
105+
# Remove the @api private line from comment
106+
text.gsub!(/^\s*#?\s*@api\s+private\s*$\n?/, '')
107+
end
108+
end
109+
110+
##
111+
# Extract parameter names from YARD type specification
112+
# e.g., "[String, Integer]" -> "value1, value2"
113+
# e.g., "[item, index]" -> "item, index"
114+
def extract_param_names(params_string)
115+
return '' if params_string.nil? || params_string.empty?
116+
117+
# If params look like variable names (lowercase start), use them
118+
if params_string =~ /^[a-z_]/
119+
params_string.split(',').map(&:strip).join(', ')
120+
else
121+
# If they look like types (uppercase start), generate generic names
122+
types = params_string.split(',').map(&:strip)
123+
types.each_with_index.map do |type, i|
124+
if type =~ /^[A-Z]/
125+
# It's a type, generate a generic param name
126+
"arg#{i + 1}"
127+
else
128+
# It's already a param name
129+
type
130+
end
131+
end.join(', ')
132+
end
133+
end
134+
end

0 commit comments

Comments
 (0)