Skip to content

Commit efbba99

Browse files
committed
Add :attr*: directives to the ruby parser.
1 parent e34345b commit efbba99

File tree

3 files changed

+273
-27
lines changed

3 files changed

+273
-27
lines changed

History.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
=== 2.4.1 / ??
22

3+
* N Minor Enhancements
4+
* Added :attr:, :attr_reader:, :attr_writer:, :attr_accessor: directives.
5+
See RDoc::Parser::Ruby for details.
6+
37
* N Bug Fixes
48
* Don't complain when exiting normally. Bug by Matt Neuburg.
59
* Restore --inline-source that warns.

lib/rdoc/parser/ruby.rb

Lines changed: 78 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1439,12 +1439,23 @@ def read_escape
14391439
# ##
14401440
# # :singleton-method: woo_hoo!
14411441
#
1442-
# == Hidden methods
1442+
# Additionally you can mark a method as an attribute by using :attr:,
1443+
# :attr_reader:, :attr_writer: or :attr_accessor:. Just like for :method:,
1444+
# the name is optional.
1445+
#
1446+
# ##
1447+
# # :attr_reader: my_attr_name
1448+
#
1449+
# == Hidden methods and attributes
14431450
#
14441451
# You can provide documentation for methods that don't appear using
1445-
# the :method: and :singleton-method: directives:
1452+
# the :method:, :singleton-method: and :attr: directives:
14461453
#
14471454
# ##
1455+
# # :attr_writer: ghost_writer
1456+
# # There is an attribute here, but you can't see it!
1457+
#
1458+
# ##
14481459
# # :method: ghost_method
14491460
# # There is a method here, but you can't see it!
14501461
#
@@ -1765,8 +1776,12 @@ def make_message(msg)
17651776
return prefix + msg
17661777
end
17671778

1779+
##
1780+
# Creates an RDoc::Attr for the name following +tk+, setting the comment to
1781+
# +comment+.
1782+
17681783
def parse_attr(context, single, tk, comment)
1769-
args = parse_symbol_arg(1)
1784+
args = parse_symbol_arg 1
17701785
if args.size > 0
17711786
name = args[0]
17721787
rw = "R"
@@ -1787,12 +1802,16 @@ def parse_attr(context, single, tk, comment)
17871802
end
17881803
end
17891804

1805+
##
1806+
# Creates an RDoc::Attr for each attribute listed after +tk+, setting the
1807+
# comment for each to +comment+.
1808+
17901809
def parse_attr_accessor(context, single, tk, comment)
17911810
args = parse_symbol_arg
17921811
read = get_tkread
17931812
rw = "?"
17941813

1795-
# If nodoc is given, don't document any of them
1814+
# TODO If nodoc is given, don't document any of them
17961815

17971816
tmp = RDoc::CodeObject.new
17981817
read_documentation_modifiers tmp, RDoc::ATTR_MODIFIERS
@@ -1973,37 +1992,51 @@ def parse_constant(container, single, tk, comment)
19731992
end
19741993
end
19751994

1995+
##
1996+
# Generates an RDoc::Method or RDoc::Attr from +comment+ by looking for
1997+
# :method: or :attr: directives in +comment+.
1998+
19761999
def parse_comment(container, tk, comment)
19772000
line_no = tk.line_no
19782001
column = tk.char_no
19792002

19802003
singleton = !!comment.sub!(/(^# +:?)(singleton-)(method:)/, '\1\3')
19812004

2005+
# REFACTOR
19822006
if comment.sub!(/^# +:?method: *(\S*).*?\n/i, '') then
19832007
name = $1 unless $1.empty?
1984-
else
1985-
return nil
1986-
end
19872008

1988-
meth = RDoc::GhostMethod.new get_tkread, name
1989-
meth.singleton = singleton
2009+
meth = RDoc::GhostMethod.new get_tkread, name
2010+
meth.singleton = singleton
19902011

1991-
@stats.add_method meth
2012+
@stats.add_method meth
19922013

1993-
meth.start_collecting_tokens
1994-
indent = TkSPACE.new 1, 1
1995-
indent.set_text " " * column
2014+
meth.start_collecting_tokens
2015+
indent = TkSPACE.new 1, 1
2016+
indent.set_text " " * column
19962017

1997-
position_comment = TkCOMMENT.new(line_no, 1, "# File #{@top_level.absolute_name}, line #{line_no}")
1998-
meth.add_tokens [position_comment, NEWLINE_TOKEN, indent]
2018+
position_comment = TkCOMMENT.new(line_no, 1, "# File #{@top_level.absolute_name}, line #{line_no}")
2019+
meth.add_tokens [position_comment, NEWLINE_TOKEN, indent]
19992020

2000-
meth.params = ''
2021+
meth.params = ''
20012022

2002-
extract_call_seq comment, meth
2023+
extract_call_seq comment, meth
20032024

2004-
container.add_method meth if meth.document_self
2025+
container.add_method meth if meth.document_self
20052026

2006-
meth.comment = comment
2027+
meth.comment = comment
2028+
elsif comment.sub!(/# +:?(attr(_reader|_writer|_accessor)?:) *(\S*).*?\n/i, '') then
2029+
rw = case $1
2030+
when 'attr_reader' then 'R'
2031+
when 'attr_writer' then 'W'
2032+
else 'RW'
2033+
end
2034+
2035+
name = $3 unless $3.empty?
2036+
2037+
att = RDoc::Attr.new get_tkread, name, rw, comment
2038+
container.add_attribute att
2039+
end
20072040
end
20082041

20092042
def parse_include(context, comment)
@@ -2018,6 +2051,32 @@ def parse_include(context, comment)
20182051
end
20192052
end
20202053

2054+
def parse_meta_attr(context, single, tk, comment)
2055+
args = parse_symbol_arg
2056+
read = get_tkread
2057+
rw = "?"
2058+
2059+
# If nodoc is given, don't document any of them
2060+
2061+
tmp = RDoc::CodeObject.new
2062+
read_documentation_modifiers tmp, RDoc::ATTR_MODIFIERS
2063+
return unless tmp.document_self
2064+
2065+
if comment.sub!(/^# +:?(attr(_reader|_writer|_accessor)?): *(\S*).*?\n/i, '') then
2066+
rw = case $1
2067+
when 'attr_reader' then 'R'
2068+
when 'attr_writer' then 'W'
2069+
else 'RW'
2070+
end
2071+
name = $3 unless $3.empty?
2072+
end
2073+
2074+
for name in args
2075+
att = RDoc::Attr.new get_tkread, name, rw, comment
2076+
context.add_attribute att
2077+
end
2078+
end
2079+
20212080
##
20222081
# Parses a meta-programmed method
20232082

test/test_rdoc_parser_ruby.rb

Lines changed: 191 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,28 @@ def teardown
2828
@tempfile2.close
2929
end
3030

31+
def test_look_for_directives_in_attr
32+
util_parser ""
33+
34+
comment = "# :attr: my_attr\n"
35+
36+
@parser.look_for_directives_in @top_level, comment
37+
38+
assert_equal "# :attr: my_attr\n", comment
39+
40+
comment = "# :attr_reader: my_method\n"
41+
42+
@parser.look_for_directives_in @top_level, comment
43+
44+
assert_equal "# :attr_reader: my_method\n", comment
45+
46+
comment = "# :attr_writer: my_method\n"
47+
48+
@parser.look_for_directives_in @top_level, comment
49+
50+
assert_equal "# :attr_writer: my_method\n", comment
51+
end
52+
3153
def test_look_for_directives_in_commented
3254
util_parser ""
3355

@@ -136,6 +158,147 @@ def test_look_for_directives_in_unhandled
136158
assert_equal 'hi', @options.title
137159
end
138160

161+
def test_parse_attr
162+
klass = RDoc::NormalClass.new 'Foo'
163+
klass.parent = @top_level
164+
165+
comment = "##\n# my attr\n"
166+
167+
util_parser "attr :foo, :bar"
168+
169+
tk = @parser.get_tk
170+
171+
@parser.parse_attr klass, RDoc::Parser::Ruby::NORMAL, tk, comment
172+
173+
assert_equal 1, klass.attributes.length
174+
175+
foo = klass.attributes.first
176+
assert_equal 'foo', foo.name
177+
assert_equal "##\n# my attr\n", foo.comment
178+
end
179+
180+
def test_parse_attr_accessor
181+
klass = RDoc::NormalClass.new 'Foo'
182+
klass.parent = @top_level
183+
184+
comment = "##\n# my attr\n"
185+
186+
util_parser "attr_accessor :foo, :bar"
187+
188+
tk = @parser.get_tk
189+
190+
@parser.parse_attr_accessor klass, RDoc::Parser::Ruby::NORMAL, tk, comment
191+
192+
assert_equal 2, klass.attributes.length
193+
194+
foo = klass.attributes.first
195+
assert_equal 'foo', foo.name
196+
assert_equal 'RW', foo.rw
197+
assert_equal "##\n# my attr\n", foo.comment
198+
199+
bar = klass.attributes.last
200+
assert_equal 'bar', bar.name
201+
assert_equal 'RW', bar.rw
202+
assert_equal "##\n# my attr\n", bar.comment
203+
end
204+
205+
def test_parse_attr_accessor_writer
206+
klass = RDoc::NormalClass.new 'Foo'
207+
klass.parent = @top_level
208+
209+
comment = "##\n# my attr\n"
210+
211+
util_parser "attr_writer :foo, :bar"
212+
213+
tk = @parser.get_tk
214+
215+
@parser.parse_attr_accessor klass, RDoc::Parser::Ruby::NORMAL, tk, comment
216+
217+
assert_equal 2, klass.attributes.length
218+
219+
foo = klass.attributes.first
220+
assert_equal 'foo', foo.name
221+
assert_equal 'W', foo.rw
222+
assert_equal "##\n# my attr\n", foo.comment
223+
224+
bar = klass.attributes.last
225+
assert_equal 'bar', bar.name
226+
assert_equal 'W', bar.rw
227+
assert_equal "##\n# my attr\n", bar.comment
228+
end
229+
230+
def test_parse_meta_attr
231+
klass = RDoc::NormalClass.new 'Foo'
232+
klass.parent = @top_level
233+
234+
comment = "##\n# :attr: \n# my method\n"
235+
236+
util_parser "add_my_method :foo, :bar"
237+
238+
tk = @parser.get_tk
239+
240+
@parser.parse_meta_attr klass, RDoc::Parser::Ruby::NORMAL, tk, comment
241+
242+
foo = klass.attributes.first
243+
assert_equal 'foo', foo.name
244+
assert_equal 'RW', foo.rw
245+
assert_equal "##\n# my method\n", foo.comment
246+
end
247+
248+
def test_parse_meta_attr_accessor
249+
klass = RDoc::NormalClass.new 'Foo'
250+
klass.parent = @top_level
251+
252+
comment = "##\n# :attr_accessor: \n# my method\n"
253+
254+
util_parser "add_my_method :foo, :bar"
255+
256+
tk = @parser.get_tk
257+
258+
@parser.parse_meta_attr klass, RDoc::Parser::Ruby::NORMAL, tk, comment
259+
260+
foo = klass.attributes.first
261+
assert_equal 'foo', foo.name
262+
assert_equal 'RW', foo.rw
263+
assert_equal "##\n# my method\n", foo.comment
264+
end
265+
266+
def test_parse_meta_attr_reader
267+
klass = RDoc::NormalClass.new 'Foo'
268+
klass.parent = @top_level
269+
270+
comment = "##\n# :attr_reader: \n# my method\n"
271+
272+
util_parser "add_my_method :foo, :bar"
273+
274+
tk = @parser.get_tk
275+
276+
@parser.parse_meta_attr klass, RDoc::Parser::Ruby::NORMAL, tk, comment
277+
278+
foo = klass.attributes.first
279+
assert_equal 'foo', foo.name
280+
assert_equal 'R', foo.rw
281+
assert_equal "##\n# my method\n", foo.comment
282+
end
283+
284+
def test_parse_meta_attr_writer
285+
klass = RDoc::NormalClass.new 'Foo'
286+
klass.parent = @top_level
287+
288+
comment = "##\n# :attr_writer: \n# my method\n"
289+
290+
util_parser "add_my_method :foo, :bar"
291+
292+
tk = @parser.get_tk
293+
294+
@parser.parse_meta_attr klass, RDoc::Parser::Ruby::NORMAL, tk, comment
295+
296+
foo = klass.attributes.first
297+
assert_equal 'foo', foo.name
298+
assert_equal 'W', foo.rw
299+
assert_equal "##\n# my method\n", foo.comment
300+
end
301+
139302
def test_parse_class
140303
comment = "##\n# my method\n"
141304

@@ -265,15 +428,35 @@ class Helper
265428
assert_equal 'Foo::Helper', helper.full_name
266429
end
267430

268-
def test_parse_comment
269-
content = <<-EOF
270-
class Foo
271-
##
272-
# :method: my_method
273-
# my method comment
431+
def test_parse_comment_attr
432+
klass = RDoc::NormalClass.new 'Foo'
433+
klass.parent = @top_level
274434

275-
end
276-
EOF
435+
comment = "##\n# :attr: foo\n# my attr\n"
436+
437+
util_parser "\n"
438+
439+
tk = @parser.get_tk
440+
441+
@parser.parse_comment klass, tk, comment
442+
443+
foo = klass.attributes.first
444+
assert_equal 'foo', foo.name
445+
assert_equal 'RW', foo.rw
446+
assert_equal comment, foo.comment
447+
448+
assert_equal nil, foo.viewer
449+
assert_equal true, foo.document_children
450+
assert_equal true, foo.document_self
451+
assert_equal false, foo.done_documenting
452+
assert_equal false, foo.force_documentation
453+
assert_equal klass, foo.parent
454+
assert_equal :public, foo.visibility
455+
assert_equal "\n", foo.text
456+
assert_equal klass.current_section, foo.section
457+
end
458+
459+
def test_parse_comment_method
277460
klass = RDoc::NormalClass.new 'Foo'
278461
klass.parent = @top_level
279462

0 commit comments

Comments
 (0)